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
5 * This file is part of GPGME.
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.
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.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, see <https://gnu.org/licenses/>.
19 * SPDX-License-Identifier: LGPL-2.1-or-later
22 /* Peculiar: Use special keys from email address for recipient and
23 signer (==sender). Use no data objects with encryption for
32 #ifdef HAVE_SYS_TYPES_H
33 # include <sys/types.h>
40 #include <fcntl.h> /* FIXME */
54 #include "engine-backend.h"
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. */
70 struct engine_uiserver
72 assuan_context_t assuan_ctx;
76 gpgme_protocol_t protocol;
78 iocb_data_t status_cb;
80 /* Input, output etc are from the servers perspective. */
82 gpgme_data_t input_helper_data; /* Input helper data object. */
83 void *input_helper_memory; /* Input helper memory block. */
85 iocb_data_t output_cb;
87 iocb_data_t message_cb;
91 engine_status_handler_t fnc;
93 gpgme_status_cb_t mon_cb;
99 engine_colon_line_handler_t fnc;
107 int any; /* any data line seen */
110 gpgme_data_t inline_data; /* Used to collect D lines. */
112 struct gpgme_io_cbs io_cbs;
115 typedef struct engine_uiserver *engine_uiserver_t;
118 static void uiserver_io_event (void *engine,
119 gpgme_event_io_t type, void *type_data);
124 uiserver_get_version (const char *file_name)
132 uiserver_get_req_version (void)
139 close_notify_handler (int fd, void *opaque)
141 engine_uiserver_t uiserver = opaque;
144 if (uiserver->status_cb.fd == fd)
146 if (uiserver->status_cb.tag)
147 (*uiserver->io_cbs.remove) (uiserver->status_cb.tag);
148 uiserver->status_cb.fd = -1;
149 uiserver->status_cb.tag = NULL;
151 else if (uiserver->input_cb.fd == fd)
153 if (uiserver->input_cb.tag)
154 (*uiserver->io_cbs.remove) (uiserver->input_cb.tag);
155 uiserver->input_cb.fd = -1;
156 uiserver->input_cb.tag = NULL;
157 if (uiserver->input_helper_data)
159 gpgme_data_release (uiserver->input_helper_data);
160 uiserver->input_helper_data = NULL;
162 if (uiserver->input_helper_memory)
164 free (uiserver->input_helper_memory);
165 uiserver->input_helper_memory = NULL;
168 else if (uiserver->output_cb.fd == fd)
170 if (uiserver->output_cb.tag)
171 (*uiserver->io_cbs.remove) (uiserver->output_cb.tag);
172 uiserver->output_cb.fd = -1;
173 uiserver->output_cb.tag = NULL;
175 else if (uiserver->message_cb.fd == fd)
177 if (uiserver->message_cb.tag)
178 (*uiserver->io_cbs.remove) (uiserver->message_cb.tag);
179 uiserver->message_cb.fd = -1;
180 uiserver->message_cb.tag = NULL;
185 /* This is the default inquiry callback. We use it to handle the
186 Pinentry notifications. */
188 default_inq_cb (engine_uiserver_t uiserver, const char *line)
192 if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
194 _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
202 uiserver_cancel (void *engine)
204 engine_uiserver_t uiserver = engine;
207 return gpg_error (GPG_ERR_INV_VALUE);
209 if (uiserver->status_cb.fd != -1)
210 _gpgme_io_close (uiserver->status_cb.fd);
211 if (uiserver->input_cb.fd != -1)
212 _gpgme_io_close (uiserver->input_cb.fd);
213 if (uiserver->output_cb.fd != -1)
214 _gpgme_io_close (uiserver->output_cb.fd);
215 if (uiserver->message_cb.fd != -1)
216 _gpgme_io_close (uiserver->message_cb.fd);
218 if (uiserver->assuan_ctx)
220 assuan_release (uiserver->assuan_ctx);
221 uiserver->assuan_ctx = NULL;
229 uiserver_release (void *engine)
231 engine_uiserver_t uiserver = engine;
236 uiserver_cancel (engine);
238 free (uiserver->colon.attic.line);
244 uiserver_new (void **engine, const char *file_name, const char *home_dir,
247 gpgme_error_t err = 0;
248 engine_uiserver_t uiserver;
249 char *dft_display = NULL;
250 char dft_ttyname[64];
251 char *env_tty = NULL;
252 char *dft_ttytype = NULL;
256 (void)version; /* Not yet used. */
258 uiserver = calloc (1, sizeof *uiserver);
260 return gpg_error_from_syserror ();
262 uiserver->protocol = GPGME_PROTOCOL_DEFAULT;
263 uiserver->status_cb.fd = -1;
264 uiserver->status_cb.dir = 1;
265 uiserver->status_cb.tag = 0;
266 uiserver->status_cb.data = uiserver;
268 uiserver->input_cb.fd = -1;
269 uiserver->input_cb.dir = 0;
270 uiserver->input_cb.tag = 0;
271 uiserver->input_cb.server_fd = -1;
272 *uiserver->input_cb.server_fd_str = 0;
273 uiserver->output_cb.fd = -1;
274 uiserver->output_cb.dir = 1;
275 uiserver->output_cb.tag = 0;
276 uiserver->output_cb.server_fd = -1;
277 *uiserver->output_cb.server_fd_str = 0;
278 uiserver->message_cb.fd = -1;
279 uiserver->message_cb.dir = 0;
280 uiserver->message_cb.tag = 0;
281 uiserver->message_cb.server_fd = -1;
282 *uiserver->message_cb.server_fd_str = 0;
284 uiserver->status.fnc = 0;
285 uiserver->colon.fnc = 0;
286 uiserver->colon.attic.line = 0;
287 uiserver->colon.attic.linesize = 0;
288 uiserver->colon.attic.linelen = 0;
289 uiserver->colon.any = 0;
291 uiserver->inline_data = NULL;
293 uiserver->io_cbs.add = NULL;
294 uiserver->io_cbs.add_priv = NULL;
295 uiserver->io_cbs.remove = NULL;
296 uiserver->io_cbs.event = NULL;
297 uiserver->io_cbs.event_priv = NULL;
299 err = assuan_new_ext (&uiserver->assuan_ctx, GPG_ERR_SOURCE_GPGME,
300 &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
304 assuan_ctx_set_system_hooks (uiserver->assuan_ctx,
305 &_gpgme_assuan_system_hooks);
307 err = assuan_socket_connect (uiserver->assuan_ctx,
309 file_name : _gpgme_get_default_uisrv_socket (),
310 0, ASSUAN_SOCKET_SERVER_FDPASSING);
314 err = _gpgme_getenv ("DISPLAY", &dft_display);
319 if (gpgrt_asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
321 err = gpg_error_from_syserror ();
327 err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
334 err = _gpgme_getenv ("GPG_TTY", &env_tty);
335 if (isatty (1) || env_tty || err)
343 snprintf (dft_ttyname, sizeof (dft_ttyname), "%s", env_tty);
347 rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
349 /* Even though isatty() returns 1, ttyname_r() may fail in many
350 ways, e.g., when /dev/pts is not accessible under chroot. */
353 if (gpgrt_asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
355 err = gpg_error_from_syserror ();
358 err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
364 err = _gpgme_getenv ("TERM", &dft_ttytype);
369 if (gpgrt_asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype)< 0)
371 err = gpg_error_from_syserror ();
377 err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
378 NULL, NULL, NULL, NULL);
386 #ifdef HAVE_W32_SYSTEM
387 /* Under Windows we need to use AllowSetForegroundWindow. Tell
388 uiserver to tell us when it needs it. */
391 err = assuan_transact (uiserver->assuan_ctx, "OPTION allow-pinentry-notify",
392 NULL, NULL, NULL, NULL, NULL, NULL);
393 if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
394 err = 0; /* This is a new feature of uiserver. */
396 #endif /*HAVE_W32_SYSTEM*/
400 uiserver_release (uiserver);
409 uiserver_set_locale (void *engine, int category, const char *value)
411 engine_uiserver_t uiserver = engine;
416 /* FIXME: If value is NULL, we need to reset the option to default.
417 But we can't do this. So we error out here. UISERVER needs support
419 if (category == LC_CTYPE)
422 if (!value && uiserver->lc_ctype_set)
423 return gpg_error (GPG_ERR_INV_VALUE);
425 uiserver->lc_ctype_set = 1;
428 else if (category == LC_MESSAGES)
430 catstr = "lc-messages";
431 if (!value && uiserver->lc_messages_set)
432 return gpg_error (GPG_ERR_INV_VALUE);
434 uiserver->lc_messages_set = 1;
436 #endif /* LC_MESSAGES */
438 return gpg_error (GPG_ERR_INV_VALUE);
440 /* FIXME: Reset value to default. */
444 if (gpgrt_asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
445 err = gpg_error_from_syserror ();
448 err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
449 NULL, NULL, NULL, NULL);
458 uiserver_set_protocol (void *engine, gpgme_protocol_t protocol)
460 engine_uiserver_t uiserver = engine;
462 if (protocol != GPGME_PROTOCOL_OpenPGP
463 && protocol != GPGME_PROTOCOL_CMS
464 && protocol != GPGME_PROTOCOL_DEFAULT)
465 return gpg_error (GPG_ERR_INV_VALUE);
467 uiserver->protocol = protocol;
473 uiserver_assuan_simple_command (engine_uiserver_t uiserver, const char *cmd,
474 engine_status_handler_t status_fnc,
475 void *status_fnc_value)
477 assuan_context_t ctx = uiserver->assuan_ctx;
482 err = assuan_write_line (ctx, cmd);
488 err = assuan_read_line (ctx, &line, &linelen);
492 if (*line == '#' || !linelen)
496 && line[0] == 'O' && line[1] == 'K'
497 && (line[2] == '\0' || line[2] == ' '))
499 else if (linelen >= 4
500 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
502 err = atoi (&line[4]);
503 else if (linelen >= 2
504 && line[0] == 'S' && line[1] == ' ')
507 gpgme_status_code_t r;
509 rest = strchr (line + 2, ' ');
511 rest = line + linelen; /* set to an empty string */
515 r = _gpgme_parse_status (line + 2);
516 if (uiserver->status.mon_cb && r != GPGME_STATUS_PROGRESS)
518 /* Note that we call the monitor even if we do
519 * not know the status code (r < 0). */
520 err = uiserver->status.mon_cb (uiserver->status.mon_cb_value,
526 else if (r >= 0 && status_fnc)
527 err = status_fnc (status_fnc_value, r, rest);
529 err = gpg_error (GPG_ERR_GENERAL);
532 err = gpg_error (GPG_ERR_GENERAL);
540 typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
542 #define COMMANDLINELEN 40
544 uiserver_set_fd (engine_uiserver_t uiserver, fd_type_t fd_type, const char *opt)
547 char line[COMMANDLINELEN];
549 iocb_data_t *iocb_data;
556 iocb_data = &uiserver->input_cb;
561 iocb_data = &uiserver->output_cb;
566 iocb_data = &uiserver->message_cb;
570 return gpg_error (GPG_ERR_INV_VALUE);
573 dir = iocb_data->dir;
575 /* We try to short-cut the communication by giving UISERVER direct
576 access to the file descriptor, rather than using a pipe. */
577 iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
578 if (iocb_data->server_fd < 0)
582 if (_gpgme_io_pipe (fds, 0) < 0)
583 return gpg_error_from_syserror ();
585 iocb_data->fd = dir ? fds[0] : fds[1];
586 iocb_data->server_fd = dir ? fds[1] : fds[0];
588 if (_gpgme_io_set_close_notify (iocb_data->fd,
589 close_notify_handler, uiserver))
591 err = gpg_error (GPG_ERR_GENERAL);
596 err = assuan_sendfd (uiserver->assuan_ctx, iocb_data->server_fd);
600 _gpgme_io_close (iocb_data->server_fd);
601 iocb_data->server_fd = -1;
604 snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
606 snprintf (line, COMMANDLINELEN, "%s FD", which);
608 err = uiserver_assuan_simple_command (uiserver, line, NULL, NULL);
613 _gpgme_io_close (iocb_data->fd);
615 if (iocb_data->server_fd != -1)
617 _gpgme_io_close (iocb_data->server_fd);
618 iocb_data->server_fd = -1;
627 map_data_enc (gpgme_data_t d)
629 switch (gpgme_data_get_encoding (d))
631 case GPGME_DATA_ENCODING_NONE:
633 case GPGME_DATA_ENCODING_BINARY:
635 case GPGME_DATA_ENCODING_BASE64:
637 case GPGME_DATA_ENCODING_ARMOR:
647 status_handler (void *opaque, int fd)
649 struct io_cb_data *data = (struct io_cb_data *) opaque;
650 engine_uiserver_t uiserver = (engine_uiserver_t) data->handler_value;
651 gpgme_error_t err = 0;
657 err = assuan_read_line (uiserver->assuan_ctx, &line, &linelen);
660 /* Try our best to terminate the connection friendly. */
661 /* assuan_write_line (uiserver->assuan_ctx, "BYE"); */
662 TRACE (DEBUG_CTX, "gpgme:status_handler", uiserver,
663 "fd 0x%x: error from assuan (%d) getting status line : %s",
664 fd, err, gpg_strerror (err));
666 else if (linelen >= 3
667 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
668 && (line[3] == '\0' || line[3] == ' '))
671 err = atoi (&line[4]);
673 err = gpg_error (GPG_ERR_GENERAL);
674 TRACE (DEBUG_CTX, "gpgme:status_handler", uiserver,
675 "fd 0x%x: ERR line - mapped to: %s",
676 fd, err ? gpg_strerror (err) : "ok");
677 /* Try our best to terminate the connection friendly. */
678 /* assuan_write_line (uiserver->assuan_ctx, "BYE"); */
680 else if (linelen >= 2
681 && line[0] == 'O' && line[1] == 'K'
682 && (line[2] == '\0' || line[2] == ' '))
684 if (uiserver->status.fnc)
686 char emptystring[1] = {0};
687 err = uiserver->status.fnc (uiserver->status.fnc_value,
688 GPGME_STATUS_EOF, emptystring);
689 if (gpg_err_code (err) == GPG_ERR_FALSE)
690 err = 0; /* Drop special error code. */
693 if (!err && uiserver->colon.fnc && uiserver->colon.any)
695 /* We must tell a colon function about the EOF. We do
696 this only when we have seen any data lines. Note
697 that this inlined use of colon data lines will
698 eventually be changed into using a regular data
700 uiserver->colon.any = 0;
701 err = uiserver->colon.fnc (uiserver->colon.fnc_value, NULL);
703 TRACE (DEBUG_CTX, "gpgme:status_handler", uiserver,
704 "fd 0x%x: OK line - final status: %s",
705 fd, err ? gpg_strerror (err) : "ok");
706 _gpgme_io_close (uiserver->status_cb.fd);
710 && line[0] == 'D' && line[1] == ' '
711 && uiserver->colon.fnc)
713 /* We are using the colon handler even for plain inline data
714 - strange name for that function but for historic reasons
716 /* FIXME We can't use this for binary data because we
717 assume this is a string. For the current usage of colon
718 output it is correct. */
719 char *src = line + 2;
720 char *end = line + linelen;
722 char **aline = &uiserver->colon.attic.line;
723 int *alinelen = &uiserver->colon.attic.linelen;
725 if (uiserver->colon.attic.linesize < *alinelen + linelen + 1)
727 char *newline = realloc (*aline, *alinelen + linelen + 1);
729 err = gpg_error_from_syserror ();
733 uiserver->colon.attic.linesize = *alinelen + linelen + 1;
738 dst = *aline + *alinelen;
740 while (!err && src < end)
742 if (*src == '%' && src + 2 < end)
744 /* Handle escaped characters. */
746 *dst = _gpgme_hextobyte (src);
758 /* Terminate the pending line, pass it to the colon
759 handler and reset it. */
761 uiserver->colon.any = 1;
762 if (*alinelen > 1 && *(dst - 1) == '\r')
766 /* FIXME How should we handle the return code? */
767 err = uiserver->colon.fnc (uiserver->colon.fnc_value, *aline);
778 TRACE (DEBUG_CTX, "gpgme:status_handler", uiserver,
779 "fd 0x%x: D line; final status: %s",
780 fd, err? gpg_strerror (err):"ok");
783 && line[0] == 'D' && line[1] == ' '
784 && uiserver->inline_data)
786 char *src = line + 2;
787 char *end = line + linelen;
789 gpgme_ssize_t nwritten;
794 if (*src == '%' && src + 2 < end)
796 /* Handle escaped characters. */
798 *dst++ = _gpgme_hextobyte (src);
810 nwritten = gpgme_data_write (uiserver->inline_data, src, linelen);
811 if (!nwritten || (nwritten < 0 && errno != EINTR)
812 || nwritten > linelen)
814 err = gpg_error_from_syserror ();
821 TRACE (DEBUG_CTX, "gpgme:status_handler", uiserver,
822 "fd 0x%x: D inlinedata; final status: %s",
823 fd, err? gpg_strerror (err):"ok");
826 && line[0] == 'S' && line[1] == ' ')
829 gpgme_status_code_t r;
831 rest = strchr (line + 2, ' ');
833 rest = line + linelen; /* set to an empty string */
837 r = _gpgme_parse_status (line + 2);
841 if (uiserver->status.fnc)
843 err = uiserver->status.fnc (uiserver->status.fnc_value,
845 if (gpg_err_code (err) == GPG_ERR_FALSE)
846 err = 0; /* Drop special error code. */
850 fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
851 TRACE (DEBUG_CTX, "gpgme:status_handler", uiserver,
852 "fd 0x%x: S line (%s) - final status: %s",
853 fd, line+2, err? gpg_strerror (err):"ok");
855 else if (linelen >= 7
856 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
857 && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
859 && (line[7] == '\0' || line[7] == ' '))
861 char *keyword = line+7;
863 while (*keyword == ' ')
865 default_inq_cb (uiserver, keyword);
866 assuan_write_line (uiserver->assuan_ctx, "END");
870 while (!err && assuan_pending_line (uiserver->assuan_ctx));
877 add_io_cb (engine_uiserver_t uiserver, iocb_data_t *iocbd, gpgme_io_cb_t handler)
881 TRACE_BEG (DEBUG_ENGINE, "engine-uiserver:add_io_cb", uiserver,
882 "fd=%d, dir %d", iocbd->fd, iocbd->dir);
883 err = (*uiserver->io_cbs.add) (uiserver->io_cbs.add_priv,
884 iocbd->fd, iocbd->dir,
885 handler, iocbd->data, &iocbd->tag);
887 return TRACE_ERR (err);
889 /* FIXME Kludge around poll() problem. */
890 err = _gpgme_io_set_nonblocking (iocbd->fd);
891 return TRACE_ERR (err);
896 start (engine_uiserver_t uiserver, const char *command)
902 /* We need to know the fd used by assuan for reads. We do this by
903 using the assumption that the first returned fd from
904 assuan_get_active_fds() is always this one. */
905 nfds = assuan_get_active_fds (uiserver->assuan_ctx, 0 /* read fds */,
906 fdlist, DIM (fdlist));
908 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
910 /* We "duplicate" the file descriptor, so we can close it here (we
911 can't close fdlist[0], as that is closed by libassuan, and
912 closing it here might cause libassuan to close some unrelated FD
913 later). Alternatively, we could special case status_fd and
914 register/unregister it manually as needed, but this increases
915 code duplication and is more complicated as we can not use the
916 close notifications etc. A third alternative would be to let
917 Assuan know that we closed the FD, but that complicates the
920 uiserver->status_cb.fd = _gpgme_io_dup (fdlist[0]);
921 if (uiserver->status_cb.fd < 0)
922 return gpg_error_from_syserror ();
924 if (_gpgme_io_set_close_notify (uiserver->status_cb.fd,
925 close_notify_handler, uiserver))
927 _gpgme_io_close (uiserver->status_cb.fd);
928 uiserver->status_cb.fd = -1;
929 return gpg_error (GPG_ERR_GENERAL);
932 err = add_io_cb (uiserver, &uiserver->status_cb, status_handler);
933 if (!err && uiserver->input_cb.fd != -1)
934 err = add_io_cb (uiserver, &uiserver->input_cb, _gpgme_data_outbound_handler);
935 if (!err && uiserver->output_cb.fd != -1)
936 err = add_io_cb (uiserver, &uiserver->output_cb, _gpgme_data_inbound_handler);
937 if (!err && uiserver->message_cb.fd != -1)
938 err = add_io_cb (uiserver, &uiserver->message_cb, _gpgme_data_outbound_handler);
941 err = assuan_write_line (uiserver->assuan_ctx, command);
944 uiserver_io_event (uiserver, GPGME_EVENT_START, NULL);
951 uiserver_reset (void *engine)
953 engine_uiserver_t uiserver = engine;
955 /* We must send a reset because we need to reset the list of
956 signers. Note that RESET does not reset OPTION commands. */
957 return uiserver_assuan_simple_command (uiserver, "RESET", NULL, NULL);
962 uiserver_decrypt (void *engine,
963 gpgme_decrypt_flags_t flags,
964 gpgme_data_t ciph, gpgme_data_t plain,
965 int export_session_key, const char *override_session_key,
966 int auto_key_retrieve)
968 engine_uiserver_t uiserver = engine;
970 const char *protocol;
972 int verify = !!(flags & GPGME_DECRYPT_VERIFY);
974 (void)override_session_key; /* Fixme: We need to see now to add this
975 * to the UI server protocol */
976 (void)auto_key_retrieve; /* Not yet supported. */
980 return gpg_error (GPG_ERR_INV_VALUE);
981 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
983 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
984 protocol = " --protocol=OpenPGP";
985 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
986 protocol = " --protocol=CMS";
988 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
990 if (gpgrt_asprintf (&cmd, "DECRYPT%s%s%s", protocol,
991 verify ? "" : " --no-verify",
992 export_session_key ? " --export-session-key" : "") < 0)
993 return gpg_error_from_syserror ();
995 uiserver->input_cb.data = ciph;
996 err = uiserver_set_fd (uiserver, INPUT_FD,
997 map_data_enc (uiserver->input_cb.data));
1001 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1003 uiserver->output_cb.data = plain;
1004 err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
1008 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1010 uiserver->inline_data = NULL;
1012 err = start (engine, cmd);
1018 static gpgme_error_t
1019 set_recipients (engine_uiserver_t uiserver, gpgme_key_t recp[])
1021 gpgme_error_t err = 0;
1024 int invalid_recipients = 0;
1027 linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */
1028 line = malloc (10 + 40 + 1);
1030 return gpg_error_from_syserror ();
1031 strcpy (line, "RECIPIENT ");
1032 for (i=0; !err && recp[i]; i++)
1037 /* We use only the first user ID of the key. */
1038 if (!recp[i]->uids || !(uid=recp[i]->uids->uid) || !*uid)
1040 invalid_recipients++;
1044 newlen = 11 + strlen (uid);
1045 if (linelen < newlen)
1047 char *newline = realloc (line, newlen);
1050 int saved_err = gpg_error_from_syserror ();
1057 /* FIXME: need to do proper escaping */
1058 strcpy (&line[10], uid);
1060 err = uiserver_assuan_simple_command (uiserver, line,
1061 uiserver->status.fnc,
1062 uiserver->status.fnc_value);
1063 /* FIXME: This might requires more work. */
1064 if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1065 invalid_recipients++;
1073 return gpg_error (invalid_recipients
1074 ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
1078 /* Take recipients from the LF delimited STRING and send RECIPIENT
1079 * commands to gpgsm. */
1080 static gpgme_error_t
1081 set_recipients_from_string (engine_uiserver_t uiserver, const char *string)
1083 gpg_error_t err = 0;
1091 while (*string == ' ' || *string == '\t')
1096 s = strchr (string, '\n');
1100 n = strlen (string);
1101 while (n && (string[n-1] == ' ' || string[n-1] == '\t'))
1105 if (gpgrt_asprintf (&line, "RECIPIENT %.*s", n, string) < 0)
1107 err = gpg_error_from_syserror ();
1112 err = uiserver_assuan_simple_command (uiserver, line,
1113 uiserver->status.fnc,
1114 uiserver->status.fnc_value);
1116 /* Fixme: Improve error reporting. */
1117 if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1123 return err? err : no_pubkey? gpg_error (GPG_ERR_NO_PUBKEY) : 0;
1127 static gpgme_error_t
1128 uiserver_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring,
1129 gpgme_encrypt_flags_t flags,
1130 gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
1132 engine_uiserver_t uiserver = engine;
1134 const char *protocol;
1138 return gpg_error (GPG_ERR_INV_VALUE);
1139 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1141 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1142 protocol = " --protocol=OpenPGP";
1143 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1144 protocol = " --protocol=CMS";
1146 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1148 if (flags & GPGME_ENCRYPT_PREPARE)
1150 if (!recp || plain || ciph)
1151 return gpg_error (GPG_ERR_INV_VALUE);
1153 if (gpgrt_asprintf (&cmd, "PREP_ENCRYPT%s%s", protocol,
1154 (flags & GPGME_ENCRYPT_EXPECT_SIGN)
1155 ? " --expect-sign" : "") < 0)
1156 return gpg_error_from_syserror ();
1160 if (!plain || !ciph)
1161 return gpg_error (GPG_ERR_INV_VALUE);
1163 if (gpgrt_asprintf (&cmd, "ENCRYPT%s", protocol) < 0)
1164 return gpg_error_from_syserror ();
1169 uiserver->input_cb.data = plain;
1170 err = uiserver_set_fd (uiserver, INPUT_FD,
1171 map_data_enc (uiserver->input_cb.data));
1181 uiserver->output_cb.data = ciph;
1182 err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
1183 : map_data_enc (uiserver->output_cb.data));
1191 uiserver->inline_data = NULL;
1193 if (recp || recpstring)
1196 err = set_recipients (uiserver, recp);
1198 err = set_recipients_from_string (uiserver, recpstring);
1206 err = start (uiserver, cmd);
1212 static gpgme_error_t
1213 uiserver_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
1214 gpgme_sig_mode_t mode, int use_armor, int use_textmode,
1215 int include_certs, gpgme_ctx_t ctx /* FIXME */)
1217 engine_uiserver_t uiserver = engine;
1218 gpgme_error_t err = 0;
1219 const char *protocol;
1224 (void)include_certs;
1226 if (!uiserver || !in || !out)
1227 return gpg_error (GPG_ERR_INV_VALUE);
1228 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1230 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1231 protocol = " --protocol=OpenPGP";
1232 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1233 protocol = " --protocol=CMS";
1235 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1237 if (gpgrt_asprintf (&cmd, "SIGN%s%s", protocol,
1238 (mode == GPGME_SIG_MODE_DETACH) ? " --detached" : "") < 0)
1239 return gpg_error_from_syserror ();
1241 key = gpgme_signers_enum (ctx, 0);
1244 const char *s = NULL;
1246 if (key && key->uids)
1247 s = key->uids->email;
1249 if (s && strlen (s) < 80)
1253 strcpy (stpcpy (buf, "SENDER --info "), s);
1254 err = uiserver_assuan_simple_command (uiserver, buf,
1255 uiserver->status.fnc,
1256 uiserver->status.fnc_value);
1259 err = gpg_error (GPG_ERR_INV_VALUE);
1260 gpgme_key_unref (key);
1268 uiserver->input_cb.data = in;
1269 err = uiserver_set_fd (uiserver, INPUT_FD,
1270 map_data_enc (uiserver->input_cb.data));
1276 uiserver->output_cb.data = out;
1277 err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
1278 : map_data_enc (uiserver->output_cb.data));
1284 uiserver->inline_data = NULL;
1286 err = start (uiserver, cmd);
1292 /* FIXME: Missing a way to specify --silent. */
1293 static gpgme_error_t
1294 uiserver_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
1295 gpgme_data_t plaintext, gpgme_ctx_t ctx)
1297 engine_uiserver_t uiserver = engine;
1299 const char *protocol;
1302 (void)ctx; /* FIXME: We should to add a --sender option to the
1303 * UISever protocol. */
1306 return gpg_error (GPG_ERR_INV_VALUE);
1307 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1309 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1310 protocol = " --protocol=OpenPGP";
1311 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1312 protocol = " --protocol=CMS";
1314 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1316 if (gpgrt_asprintf (&cmd, "VERIFY%s", protocol) < 0)
1317 return gpg_error_from_syserror ();
1319 uiserver->input_cb.data = sig;
1320 err = uiserver_set_fd (uiserver, INPUT_FD,
1321 map_data_enc (uiserver->input_cb.data));
1329 /* Normal or cleartext signature. */
1330 uiserver->output_cb.data = plaintext;
1331 err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
1335 /* Detached signature. */
1336 uiserver->message_cb.data = signed_text;
1337 err = uiserver_set_fd (uiserver, MESSAGE_FD, 0);
1339 uiserver->inline_data = NULL;
1342 err = start (uiserver, cmd);
1349 /* This sets a status callback for monitoring status lines before they
1350 * are passed to a caller set handler. */
1352 uiserver_set_status_cb (void *engine, gpgme_status_cb_t cb, void *cb_value)
1354 engine_uiserver_t uiserver = engine;
1356 uiserver->status.mon_cb = cb;
1357 uiserver->status.mon_cb_value = cb_value;
1362 uiserver_set_status_handler (void *engine, engine_status_handler_t fnc,
1365 engine_uiserver_t uiserver = engine;
1367 uiserver->status.fnc = fnc;
1368 uiserver->status.fnc_value = fnc_value;
1372 static gpgme_error_t
1373 uiserver_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
1376 engine_uiserver_t uiserver = engine;
1378 uiserver->colon.fnc = fnc;
1379 uiserver->colon.fnc_value = fnc_value;
1380 uiserver->colon.any = 0;
1386 uiserver_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1388 engine_uiserver_t uiserver = engine;
1389 uiserver->io_cbs = *io_cbs;
1394 uiserver_io_event (void *engine, gpgme_event_io_t type, void *type_data)
1396 engine_uiserver_t uiserver = engine;
1398 TRACE (DEBUG_ENGINE, "gpgme:uiserver_io_event", uiserver,
1399 "event %p, type %d, type_data %p",
1400 uiserver->io_cbs.event, type, type_data);
1401 if (uiserver->io_cbs.event)
1402 (*uiserver->io_cbs.event) (uiserver->io_cbs.event_priv, type, type_data);
1406 struct engine_ops _gpgme_engine_ops_uiserver =
1408 /* Static functions. */
1409 _gpgme_get_default_uisrv_socket,
1411 uiserver_get_version,
1412 uiserver_get_req_version,
1415 /* Member functions. */
1418 uiserver_set_status_cb,
1419 uiserver_set_status_handler,
1420 NULL, /* set_command_handler */
1421 uiserver_set_colon_line_handler,
1422 uiserver_set_locale,
1423 uiserver_set_protocol,
1424 NULL, /* set_engine_flags */
1429 NULL, /* encrypt_sign */
1431 NULL, /* export_ext */
1435 NULL, /* keylist_ext */
1436 NULL, /* keylist_data */
1438 NULL, /* tofu_policy */
1440 NULL, /* trustlist */
1442 NULL, /* getauditlog */
1443 NULL, /* opassuan_transact */
1444 NULL, /* conf_load */
1445 NULL, /* conf_save */
1446 NULL, /* conf_dir */
1447 NULL, /* query_swdb */
1448 uiserver_set_io_cbs,
1451 NULL, /* cancel_op */
1453 NULL, /* set_pinentry_mode */