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, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
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 */
52 #include "status-table.h"
55 #include "engine-backend.h"
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. */
71 struct engine_uiserver
73 assuan_context_t assuan_ctx;
77 gpgme_protocol_t protocol;
79 iocb_data_t status_cb;
81 /* Input, output etc are from the servers perspective. */
83 gpgme_data_t input_helper_data; /* Input helper data object. */
84 void *input_helper_memory; /* Input helper memory block. */
86 iocb_data_t output_cb;
88 iocb_data_t message_cb;
92 engine_status_handler_t fnc;
98 engine_colon_line_handler_t fnc;
106 int any; /* any data line seen */
109 gpgme_data_t inline_data; /* Used to collect D lines. */
111 struct gpgme_io_cbs io_cbs;
114 typedef struct engine_uiserver *engine_uiserver_t;
117 static void uiserver_io_event (void *engine,
118 gpgme_event_io_t type, void *type_data);
123 uiserver_get_version (const char *file_name)
125 return strdup ("1.0");
130 uiserver_get_req_version (void)
137 close_notify_handler (int fd, void *opaque)
139 engine_uiserver_t uiserver = opaque;
142 if (uiserver->status_cb.fd == fd)
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;
149 else if (uiserver->input_cb.fd == fd)
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)
157 gpgme_data_release (uiserver->input_helper_data);
158 uiserver->input_helper_data = NULL;
160 if (uiserver->input_helper_memory)
162 free (uiserver->input_helper_memory);
163 uiserver->input_helper_memory = NULL;
166 else if (uiserver->output_cb.fd == fd)
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;
173 else if (uiserver->message_cb.fd == fd)
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;
183 /* This is the default inquiry callback. We use it to handle the
184 Pinentry notifications. */
186 default_inq_cb (engine_uiserver_t uiserver, const char *line)
188 if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
190 _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
198 uiserver_cancel (void *engine)
200 engine_uiserver_t uiserver = engine;
203 return gpg_error (GPG_ERR_INV_VALUE);
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);
214 if (uiserver->assuan_ctx)
216 assuan_release (uiserver->assuan_ctx);
217 uiserver->assuan_ctx = NULL;
225 uiserver_release (void *engine)
227 engine_uiserver_t uiserver = engine;
232 uiserver_cancel (engine);
234 free (uiserver->colon.attic.line);
240 uiserver_new (void **engine, const char *file_name, const char *home_dir)
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;
249 uiserver = calloc (1, sizeof *uiserver);
251 return gpg_error_from_syserror ();
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;
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;
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;
282 uiserver->inline_data = NULL;
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;
290 err = assuan_new_ext (&uiserver->assuan_ctx, GPG_ERR_SOURCE_GPGME,
291 &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
295 assuan_ctx_set_system_hooks (uiserver->assuan_ctx,
296 &_gpgme_assuan_system_hooks);
298 err = assuan_socket_connect (uiserver->assuan_ctx,
300 file_name : _gpgme_get_uiserver_socket_path (),
301 0, ASSUAN_SOCKET_SERVER_FDPASSING);
305 err = _gpgme_getenv ("DISPLAY", &dft_display);
310 if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
313 err = gpg_error_from_errno (errno);
318 err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
329 rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
332 err = gpg_error_from_errno (rc);
337 if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
339 err = gpg_error_from_errno (errno);
342 err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
348 err = _gpgme_getenv ("TERM", &dft_ttytype);
353 if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
356 err = gpg_error_from_errno (errno);
361 err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
362 NULL, NULL, NULL, NULL);
370 #ifdef HAVE_W32_SYSTEM
371 /* Under Windows we need to use AllowSetForegroundWindow. Tell
372 uiserver to tell us when it needs it. */
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. */
380 #endif /*HAVE_W32_SYSTEM*/
384 uiserver_release (uiserver);
393 uiserver_set_locale (void *engine, int category, const char *value)
395 engine_uiserver_t uiserver = engine;
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
403 if (category == LC_CTYPE)
406 if (!value && uiserver->lc_ctype_set)
407 return gpg_error (GPG_ERR_INV_VALUE);
409 uiserver->lc_ctype_set = 1;
412 else if (category == LC_MESSAGES)
414 catstr = "lc-messages";
415 if (!value && uiserver->lc_messages_set)
416 return gpg_error (GPG_ERR_INV_VALUE);
418 uiserver->lc_messages_set = 1;
420 #endif /* LC_MESSAGES */
422 return gpg_error (GPG_ERR_INV_VALUE);
424 /* FIXME: Reset value to default. */
428 if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
429 err = gpg_error_from_errno (errno);
432 err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
433 NULL, NULL, NULL, NULL);
442 uiserver_set_protocol (void *engine, gpgme_protocol_t protocol)
444 engine_uiserver_t uiserver = engine;
446 if (protocol != GPGME_PROTOCOL_OpenPGP
447 && protocol != GPGME_PROTOCOL_CMS
448 && protocol != GPGME_PROTOCOL_DEFAULT)
449 return gpg_error (GPG_ERR_INV_VALUE);
451 uiserver->protocol = protocol;
457 uiserver_assuan_simple_command (assuan_context_t ctx, char *cmd,
458 engine_status_handler_t status_fnc,
459 void *status_fnc_value)
465 err = assuan_write_line (ctx, cmd);
471 err = assuan_read_line (ctx, &line, &linelen);
475 if (*line == '#' || !linelen)
479 && line[0] == 'O' && line[1] == 'K'
480 && (line[2] == '\0' || line[2] == ' '))
482 else if (linelen >= 4
483 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
485 err = atoi (&line[4]);
486 else if (linelen >= 2
487 && line[0] == 'S' && line[1] == ' ')
490 gpgme_status_code_t r;
492 rest = strchr (line + 2, ' ');
494 rest = line + linelen; /* set to an empty string */
498 r = _gpgme_parse_status (line + 2);
500 if (r >= 0 && status_fnc)
501 err = status_fnc (status_fnc_value, r, rest);
503 err = gpg_error (GPG_ERR_GENERAL);
506 err = gpg_error (GPG_ERR_GENERAL);
514 typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
516 #define COMMANDLINELEN 40
518 uiserver_set_fd (engine_uiserver_t uiserver, fd_type_t fd_type, const char *opt)
521 char line[COMMANDLINELEN];
523 iocb_data_t *iocb_data;
530 iocb_data = &uiserver->input_cb;
535 iocb_data = &uiserver->output_cb;
540 iocb_data = &uiserver->message_cb;
544 return gpg_error (GPG_ERR_INV_VALUE);
547 dir = iocb_data->dir;
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)
556 if (_gpgme_io_pipe (fds, 0) < 0)
557 return gpg_error_from_errno (errno);
559 iocb_data->fd = dir ? fds[0] : fds[1];
560 iocb_data->server_fd = dir ? fds[1] : fds[0];
562 if (_gpgme_io_set_close_notify (iocb_data->fd,
563 close_notify_handler, uiserver))
565 err = gpg_error (GPG_ERR_GENERAL);
570 err = assuan_sendfd (uiserver->assuan_ctx, iocb_data->server_fd);
574 _gpgme_io_close (iocb_data->server_fd);
575 iocb_data->server_fd = -1;
578 snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
580 snprintf (line, COMMANDLINELEN, "%s FD", which);
582 err = uiserver_assuan_simple_command (uiserver->assuan_ctx, line, NULL, NULL);
587 _gpgme_io_close (iocb_data->fd);
589 if (iocb_data->server_fd != -1)
591 _gpgme_io_close (iocb_data->server_fd);
592 iocb_data->server_fd = -1;
601 map_data_enc (gpgme_data_t d)
603 switch (gpgme_data_get_encoding (d))
605 case GPGME_DATA_ENCODING_NONE:
607 case GPGME_DATA_ENCODING_BINARY:
609 case GPGME_DATA_ENCODING_BASE64:
611 case GPGME_DATA_ENCODING_ARMOR:
621 status_handler (void *opaque, int fd)
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;
631 err = assuan_read_line (uiserver->assuan_ctx, &line, &linelen);
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));
640 else if (linelen >= 3
641 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
642 && (line[3] == '\0' || line[3] == ' '))
645 err = atoi (&line[4]);
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"); */
654 else if (linelen >= 2
655 && line[0] == 'O' && line[1] == 'K'
656 && (line[2] == '\0' || line[2] == ' '))
658 if (uiserver->status.fnc)
659 err = uiserver->status.fnc (uiserver->status.fnc_value,
660 GPGME_STATUS_EOF, "");
662 if (!err && uiserver->colon.fnc && uiserver->colon.any)
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
669 uiserver->colon.any = 0;
670 err = uiserver->colon.fnc (uiserver->colon.fnc_value, NULL);
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);
679 && line[0] == 'D' && line[1] == ' '
680 && uiserver->colon.fnc)
682 /* We are using the colon handler even for plain inline data
683 - strange name for that function but for historic reasons
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;
691 char **aline = &uiserver->colon.attic.line;
692 int *alinelen = &uiserver->colon.attic.linelen;
694 if (uiserver->colon.attic.linesize < *alinelen + linelen + 1)
696 char *newline = realloc (*aline, *alinelen + linelen + 1);
698 err = gpg_error_from_errno (errno);
702 uiserver->colon.attic.linesize += linelen + 1;
707 dst = *aline + *alinelen;
709 while (!err && src < end)
711 if (*src == '%' && src + 2 < end)
713 /* Handle escaped characters. */
715 *dst = _gpgme_hextobyte (src);
727 /* Terminate the pending line, pass it to the colon
728 handler and reset it. */
730 uiserver->colon.any = 1;
731 if (*alinelen > 1 && *(dst - 1) == '\r')
735 /* FIXME How should we handle the return code? */
736 err = uiserver->colon.fnc (uiserver->colon.fnc_value, *aline);
747 TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
748 "fd 0x%x: D line; final status: %s",
749 fd, err? gpg_strerror (err):"ok");
752 && line[0] == 'D' && line[1] == ' '
753 && uiserver->inline_data)
755 char *src = line + 2;
756 char *end = line + linelen;
763 if (*src == '%' && src + 2 < end)
765 /* Handle escaped characters. */
767 *dst++ = _gpgme_hextobyte (src);
779 nwritten = gpgme_data_write (uiserver->inline_data, src, linelen);
780 if (!nwritten || (nwritten < 0 && errno != EINTR)
781 || nwritten > linelen)
783 err = gpg_error_from_errno (errno);
790 TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
791 "fd 0x%x: D inlinedata; final status: %s",
792 fd, err? gpg_strerror (err):"ok");
795 && line[0] == 'S' && line[1] == ' ')
798 gpgme_status_code_t r;
800 rest = strchr (line + 2, ' ');
802 rest = line + linelen; /* set to an empty string */
806 r = _gpgme_parse_status (line + 2);
810 if (uiserver->status.fnc)
811 err = uiserver->status.fnc (uiserver->status.fnc_value, r, rest);
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");
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'
823 && (line[7] == '\0' || line[7] == ' '))
825 char *keyword = line+7;
827 while (*keyword == ' ')
829 default_inq_cb (uiserver, keyword);
830 assuan_write_line (uiserver->assuan_ctx, "END");
834 while (!err && assuan_pending_line (uiserver->assuan_ctx));
841 add_io_cb (engine_uiserver_t uiserver, iocb_data_t *iocbd, gpgme_io_cb_t handler)
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);
851 return TRACE_ERR (err);
853 /* FIXME Kludge around poll() problem. */
854 err = _gpgme_io_set_nonblocking (iocbd->fd);
855 return TRACE_ERR (err);
860 start (engine_uiserver_t uiserver, const char *command)
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));
872 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
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
884 uiserver->status_cb.fd = _gpgme_io_dup (fdlist[0]);
885 if (uiserver->status_cb.fd < 0)
886 return gpg_error_from_syserror ();
888 if (_gpgme_io_set_close_notify (uiserver->status_cb.fd,
889 close_notify_handler, uiserver))
891 _gpgme_io_close (uiserver->status_cb.fd);
892 uiserver->status_cb.fd = -1;
893 return gpg_error (GPG_ERR_GENERAL);
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);
905 err = assuan_write_line (uiserver->assuan_ctx, command);
908 uiserver_io_event (uiserver, GPGME_EVENT_START, NULL);
915 uiserver_reset (void *engine)
917 engine_uiserver_t uiserver = engine;
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);
926 _uiserver_decrypt (void *engine, int verify,
927 gpgme_data_t ciph, gpgme_data_t plain)
929 engine_uiserver_t uiserver = engine;
931 const char *protocol;
935 return gpg_error (GPG_ERR_INV_VALUE);
936 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
938 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
939 protocol = " --protocol=OpenPGP";
940 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
941 protocol = " --protocol=CMS";
943 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
945 if (asprintf (&cmd, "DECRYPT%s%s", protocol,
946 verify ? "" : " --no-verify") < 0)
947 return gpg_error_from_errno (errno);
949 uiserver->input_cb.data = ciph;
950 err = uiserver_set_fd (uiserver, INPUT_FD,
951 map_data_enc (uiserver->input_cb.data));
955 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
957 uiserver->output_cb.data = plain;
958 err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
962 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
964 uiserver->inline_data = NULL;
966 err = start (engine, cmd);
973 uiserver_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
975 return _uiserver_decrypt (engine, 0, ciph, plain);
980 uiserver_decrypt_verify (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
982 return _uiserver_decrypt (engine, 1, ciph, plain);
987 set_recipients (engine_uiserver_t uiserver, gpgme_key_t recp[])
989 gpgme_error_t err = 0;
990 assuan_context_t ctx = uiserver->assuan_ctx;
993 int invalid_recipients = 0;
996 linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */
997 line = malloc (10 + 40 + 1);
999 return gpg_error_from_errno (errno);
1000 strcpy (line, "RECIPIENT ");
1001 for (i=0; !err && recp[i]; i++)
1006 /* We use only the first user ID of the key. */
1007 if (!recp[i]->uids || !(uid=recp[i]->uids->uid) || !*uid)
1009 invalid_recipients++;
1013 newlen = 11 + strlen (uid);
1014 if (linelen < newlen)
1016 char *newline = realloc (line, newlen);
1019 int saved_errno = errno;
1021 return gpg_error_from_errno (saved_errno);
1026 /* FIXME: need to do proper escaping */
1027 strcpy (&line[10], uid);
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++;
1041 return gpg_error (invalid_recipients
1042 ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
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)
1050 engine_uiserver_t uiserver = engine;
1052 const char *protocol;
1056 return gpg_error (GPG_ERR_INV_VALUE);
1057 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1059 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1060 protocol = " --protocol=OpenPGP";
1061 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1062 protocol = " --protocol=CMS";
1064 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1066 if (flags & GPGME_ENCRYPT_PREPARE)
1068 if (!recp || plain || ciph)
1069 return gpg_error (GPG_ERR_INV_VALUE);
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);
1078 if (!plain || !ciph)
1079 return gpg_error (GPG_ERR_INV_VALUE);
1081 if (asprintf (&cmd, "ENCRYPT%s", protocol) < 0)
1082 return gpg_error_from_errno (errno);
1087 uiserver->input_cb.data = plain;
1088 err = uiserver_set_fd (uiserver, INPUT_FD,
1089 map_data_enc (uiserver->input_cb.data));
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));
1109 uiserver->inline_data = NULL;
1113 err = set_recipients (uiserver, recp);
1121 err = start (uiserver, cmd);
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 */)
1132 engine_uiserver_t uiserver = engine;
1133 gpgme_error_t err = 0;
1134 const char *protocol;
1138 if (!uiserver || !in || !out)
1139 return gpg_error (GPG_ERR_INV_VALUE);
1140 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1142 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1143 protocol = " --protocol=OpenPGP";
1144 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1145 protocol = " --protocol=CMS";
1147 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1149 if (asprintf (&cmd, "SIGN%s%s", protocol,
1150 (mode == GPGME_SIG_MODE_DETACH) ? " --detached" : "") < 0)
1151 return gpg_error_from_errno (errno);
1153 key = gpgme_signers_enum (ctx, 0);
1156 const char *s = NULL;
1158 if (key && key->uids)
1159 s = key->uids->email;
1161 if (s && strlen (s) < 80)
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);
1171 err = gpg_error (GPG_ERR_INV_VALUE);
1172 gpgme_key_unref (key);
1180 uiserver->input_cb.data = in;
1181 err = uiserver_set_fd (uiserver, INPUT_FD,
1182 map_data_enc (uiserver->input_cb.data));
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));
1196 uiserver->inline_data = NULL;
1198 err = start (uiserver, cmd);
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)
1209 engine_uiserver_t uiserver = engine;
1211 const char *protocol;
1215 return gpg_error (GPG_ERR_INV_VALUE);
1216 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1218 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1219 protocol = " --protocol=OpenPGP";
1220 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1221 protocol = " --protocol=CMS";
1223 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1225 if (asprintf (&cmd, "VERIFY%s", protocol) < 0)
1226 return gpg_error_from_errno (errno);
1228 uiserver->input_cb.data = sig;
1229 err = uiserver_set_fd (uiserver, INPUT_FD,
1230 map_data_enc (uiserver->input_cb.data));
1238 /* Normal or cleartext signature. */
1239 uiserver->output_cb.data = plaintext;
1240 err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
1244 /* Detached signature. */
1245 uiserver->message_cb.data = signed_text;
1246 err = uiserver_set_fd (uiserver, MESSAGE_FD, 0);
1248 uiserver->inline_data = NULL;
1251 err = start (uiserver, cmd);
1259 uiserver_set_status_handler (void *engine, engine_status_handler_t fnc,
1262 engine_uiserver_t uiserver = engine;
1264 uiserver->status.fnc = fnc;
1265 uiserver->status.fnc_value = fnc_value;
1269 static gpgme_error_t
1270 uiserver_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
1273 engine_uiserver_t uiserver = engine;
1275 uiserver->colon.fnc = fnc;
1276 uiserver->colon.fnc_value = fnc_value;
1277 uiserver->colon.any = 0;
1283 uiserver_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1285 engine_uiserver_t uiserver = engine;
1286 uiserver->io_cbs = *io_cbs;
1291 uiserver_io_event (void *engine, gpgme_event_io_t type, void *type_data)
1293 engine_uiserver_t uiserver = engine;
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);
1303 struct engine_ops _gpgme_engine_ops_uiserver =
1305 /* Static functions. */
1306 _gpgme_get_uiserver_socket_path,
1308 uiserver_get_version,
1309 uiserver_get_req_version,
1312 /* Member functions. */
1315 uiserver_set_status_handler,
1316 NULL, /* set_command_handler */
1317 uiserver_set_colon_line_handler,
1318 uiserver_set_locale,
1319 uiserver_set_protocol,
1321 uiserver_decrypt_verify,
1325 NULL, /* encrypt_sign */
1327 NULL, /* export_ext */
1331 NULL, /* keylist_ext */
1333 NULL, /* trustlist */
1335 NULL, /* getauditlog */
1336 NULL, /* opassuan_transact */
1337 NULL, /* conf_load */
1338 NULL, /* conf_save */
1339 uiserver_set_io_cbs,
1342 NULL /* cancel_op */