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;
456 /* Forward declaration. */
457 static gpgme_status_code_t parse_status (const char *name);
460 uiserver_assuan_simple_command (assuan_context_t ctx, char *cmd,
461 engine_status_handler_t status_fnc,
462 void *status_fnc_value)
468 err = assuan_write_line (ctx, cmd);
474 err = assuan_read_line (ctx, &line, &linelen);
478 if (*line == '#' || !linelen)
482 && line[0] == 'O' && line[1] == 'K'
483 && (line[2] == '\0' || line[2] == ' '))
485 else if (linelen >= 4
486 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
488 err = atoi (&line[4]);
489 else if (linelen >= 2
490 && line[0] == 'S' && line[1] == ' ')
493 gpgme_status_code_t r;
495 rest = strchr (line + 2, ' ');
497 rest = line + linelen; /* set to an empty string */
501 r = parse_status (line + 2);
503 if (r >= 0 && status_fnc)
504 err = status_fnc (status_fnc_value, r, rest);
506 err = gpg_error (GPG_ERR_GENERAL);
509 err = gpg_error (GPG_ERR_GENERAL);
517 typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
519 #define COMMANDLINELEN 40
521 uiserver_set_fd (engine_uiserver_t uiserver, fd_type_t fd_type, const char *opt)
524 char line[COMMANDLINELEN];
526 iocb_data_t *iocb_data;
533 iocb_data = &uiserver->input_cb;
538 iocb_data = &uiserver->output_cb;
543 iocb_data = &uiserver->message_cb;
547 return gpg_error (GPG_ERR_INV_VALUE);
550 dir = iocb_data->dir;
552 /* We try to short-cut the communication by giving UISERVER direct
553 access to the file descriptor, rather than using a pipe. */
554 iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
555 if (iocb_data->server_fd < 0)
559 if (_gpgme_io_pipe (fds, 0) < 0)
560 return gpg_error_from_errno (errno);
562 iocb_data->fd = dir ? fds[0] : fds[1];
563 iocb_data->server_fd = dir ? fds[1] : fds[0];
565 if (_gpgme_io_set_close_notify (iocb_data->fd,
566 close_notify_handler, uiserver))
568 err = gpg_error (GPG_ERR_GENERAL);
573 err = assuan_sendfd (uiserver->assuan_ctx, iocb_data->server_fd);
577 _gpgme_io_close (iocb_data->server_fd);
578 iocb_data->server_fd = -1;
581 snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
583 snprintf (line, COMMANDLINELEN, "%s FD", which);
585 err = uiserver_assuan_simple_command (uiserver->assuan_ctx, line, NULL, NULL);
590 _gpgme_io_close (iocb_data->fd);
592 if (iocb_data->server_fd != -1)
594 _gpgme_io_close (iocb_data->server_fd);
595 iocb_data->server_fd = -1;
604 map_data_enc (gpgme_data_t d)
606 switch (gpgme_data_get_encoding (d))
608 case GPGME_DATA_ENCODING_NONE:
610 case GPGME_DATA_ENCODING_BINARY:
612 case GPGME_DATA_ENCODING_BASE64:
614 case GPGME_DATA_ENCODING_ARMOR:
624 status_cmp (const void *ap, const void *bp)
626 const struct status_table_s *a = ap;
627 const struct status_table_s *b = bp;
629 return strcmp (a->name, b->name);
633 static gpgme_status_code_t
634 parse_status (const char *name)
636 struct status_table_s t, *r;
638 r = bsearch (&t, status_table, DIM(status_table) - 1,
639 sizeof t, status_cmp);
640 return r ? r->code : -1;
645 status_handler (void *opaque, int fd)
647 struct io_cb_data *data = (struct io_cb_data *) opaque;
648 engine_uiserver_t uiserver = (engine_uiserver_t) data->handler_value;
649 gpgme_error_t err = 0;
655 err = assuan_read_line (uiserver->assuan_ctx, &line, &linelen);
658 /* Try our best to terminate the connection friendly. */
659 /* assuan_write_line (uiserver->assuan_ctx, "BYE"); */
660 TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
661 "fd 0x%x: error from assuan (%d) getting status line : %s",
662 fd, err, gpg_strerror (err));
664 else if (linelen >= 3
665 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
666 && (line[3] == '\0' || line[3] == ' '))
669 err = atoi (&line[4]);
671 err = gpg_error (GPG_ERR_GENERAL);
672 TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
673 "fd 0x%x: ERR line - mapped to: %s",
674 fd, err ? gpg_strerror (err) : "ok");
675 /* Try our best to terminate the connection friendly. */
676 /* assuan_write_line (uiserver->assuan_ctx, "BYE"); */
678 else if (linelen >= 2
679 && line[0] == 'O' && line[1] == 'K'
680 && (line[2] == '\0' || line[2] == ' '))
682 if (uiserver->status.fnc)
683 err = uiserver->status.fnc (uiserver->status.fnc_value,
684 GPGME_STATUS_EOF, "");
686 if (!err && uiserver->colon.fnc && uiserver->colon.any)
688 /* We must tell a colon function about the EOF. We do
689 this only when we have seen any data lines. Note
690 that this inlined use of colon data lines will
691 eventually be changed into using a regular data
693 uiserver->colon.any = 0;
694 err = uiserver->colon.fnc (uiserver->colon.fnc_value, NULL);
696 TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
697 "fd 0x%x: OK line - final status: %s",
698 fd, err ? gpg_strerror (err) : "ok");
699 _gpgme_io_close (uiserver->status_cb.fd);
703 && line[0] == 'D' && line[1] == ' '
704 && uiserver->colon.fnc)
706 /* We are using the colon handler even for plain inline data
707 - strange name for that function but for historic reasons
709 /* FIXME We can't use this for binary data because we
710 assume this is a string. For the current usage of colon
711 output it is correct. */
712 char *src = line + 2;
713 char *end = line + linelen;
715 char **aline = &uiserver->colon.attic.line;
716 int *alinelen = &uiserver->colon.attic.linelen;
718 if (uiserver->colon.attic.linesize < *alinelen + linelen + 1)
720 char *newline = realloc (*aline, *alinelen + linelen + 1);
722 err = gpg_error_from_errno (errno);
726 uiserver->colon.attic.linesize += linelen + 1;
731 dst = *aline + *alinelen;
733 while (!err && src < end)
735 if (*src == '%' && src + 2 < end)
737 /* Handle escaped characters. */
739 *dst = _gpgme_hextobyte (src);
751 /* Terminate the pending line, pass it to the colon
752 handler and reset it. */
754 uiserver->colon.any = 1;
755 if (*alinelen > 1 && *(dst - 1) == '\r')
759 /* FIXME How should we handle the return code? */
760 err = uiserver->colon.fnc (uiserver->colon.fnc_value, *aline);
771 TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
772 "fd 0x%x: D line; final status: %s",
773 fd, err? gpg_strerror (err):"ok");
776 && line[0] == 'D' && line[1] == ' '
777 && uiserver->inline_data)
779 char *src = line + 2;
780 char *end = line + linelen;
787 if (*src == '%' && src + 2 < end)
789 /* Handle escaped characters. */
791 *dst++ = _gpgme_hextobyte (src);
803 nwritten = gpgme_data_write (uiserver->inline_data, src, linelen);
804 if (!nwritten || (nwritten < 0 && errno != EINTR)
805 || nwritten > linelen)
807 err = gpg_error_from_errno (errno);
814 TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
815 "fd 0x%x: D inlinedata; final status: %s",
816 fd, err? gpg_strerror (err):"ok");
819 && line[0] == 'S' && line[1] == ' ')
822 gpgme_status_code_t r;
824 rest = strchr (line + 2, ' ');
826 rest = line + linelen; /* set to an empty string */
830 r = parse_status (line + 2);
834 if (uiserver->status.fnc)
835 err = uiserver->status.fnc (uiserver->status.fnc_value, r, rest);
838 fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
839 TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
840 "fd 0x%x: S line (%s) - final status: %s",
841 fd, line+2, err? gpg_strerror (err):"ok");
843 else if (linelen >= 7
844 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
845 && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
847 && (line[7] == '\0' || line[7] == ' '))
849 char *keyword = line+7;
851 while (*keyword == ' ')
853 default_inq_cb (uiserver, keyword);
854 assuan_write_line (uiserver->assuan_ctx, "END");
858 while (!err && assuan_pending_line (uiserver->assuan_ctx));
865 add_io_cb (engine_uiserver_t uiserver, iocb_data_t *iocbd, gpgme_io_cb_t handler)
869 TRACE_BEG2 (DEBUG_ENGINE, "engine-uiserver:add_io_cb", uiserver,
870 "fd %d, dir %d", iocbd->fd, iocbd->dir);
871 err = (*uiserver->io_cbs.add) (uiserver->io_cbs.add_priv,
872 iocbd->fd, iocbd->dir,
873 handler, iocbd->data, &iocbd->tag);
875 return TRACE_ERR (err);
877 /* FIXME Kludge around poll() problem. */
878 err = _gpgme_io_set_nonblocking (iocbd->fd);
879 return TRACE_ERR (err);
884 start (engine_uiserver_t uiserver, const char *command)
890 /* We need to know the fd used by assuan for reads. We do this by
891 using the assumption that the first returned fd from
892 assuan_get_active_fds() is always this one. */
893 nfds = assuan_get_active_fds (uiserver->assuan_ctx, 0 /* read fds */,
894 fdlist, DIM (fdlist));
896 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
898 /* We "duplicate" the file descriptor, so we can close it here (we
899 can't close fdlist[0], as that is closed by libassuan, and
900 closing it here might cause libassuan to close some unrelated FD
901 later). Alternatively, we could special case status_fd and
902 register/unregister it manually as needed, but this increases
903 code duplication and is more complicated as we can not use the
904 close notifications etc. A third alternative would be to let
905 Assuan know that we closed the FD, but that complicates the
908 uiserver->status_cb.fd = _gpgme_io_dup (fdlist[0]);
909 if (uiserver->status_cb.fd < 0)
910 return gpg_error_from_syserror ();
912 if (_gpgme_io_set_close_notify (uiserver->status_cb.fd,
913 close_notify_handler, uiserver))
915 _gpgme_io_close (uiserver->status_cb.fd);
916 uiserver->status_cb.fd = -1;
917 return gpg_error (GPG_ERR_GENERAL);
920 err = add_io_cb (uiserver, &uiserver->status_cb, status_handler);
921 if (!err && uiserver->input_cb.fd != -1)
922 err = add_io_cb (uiserver, &uiserver->input_cb, _gpgme_data_outbound_handler);
923 if (!err && uiserver->output_cb.fd != -1)
924 err = add_io_cb (uiserver, &uiserver->output_cb, _gpgme_data_inbound_handler);
925 if (!err && uiserver->message_cb.fd != -1)
926 err = add_io_cb (uiserver, &uiserver->message_cb, _gpgme_data_outbound_handler);
929 err = assuan_write_line (uiserver->assuan_ctx, command);
932 uiserver_io_event (uiserver, GPGME_EVENT_START, NULL);
939 uiserver_reset (void *engine)
941 engine_uiserver_t uiserver = engine;
943 /* We must send a reset because we need to reset the list of
944 signers. Note that RESET does not reset OPTION commands. */
945 return uiserver_assuan_simple_command (uiserver->assuan_ctx, "RESET", NULL, NULL);
950 _uiserver_decrypt (void *engine, int verify,
951 gpgme_data_t ciph, gpgme_data_t plain)
953 engine_uiserver_t uiserver = engine;
955 const char *protocol;
959 return gpg_error (GPG_ERR_INV_VALUE);
960 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
962 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
963 protocol = " --protocol=OpenPGP";
964 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
965 protocol = " --protocol=CMS";
967 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
969 if (asprintf (&cmd, "DECRYPT%s%s", protocol,
970 verify ? "" : " --no-verify") < 0)
971 return gpg_error_from_errno (errno);
973 uiserver->input_cb.data = ciph;
974 err = uiserver_set_fd (uiserver, INPUT_FD,
975 map_data_enc (uiserver->input_cb.data));
979 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
981 uiserver->output_cb.data = plain;
982 err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
986 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
988 uiserver->inline_data = NULL;
990 err = start (engine, cmd);
997 uiserver_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
999 return _uiserver_decrypt (engine, 0, ciph, plain);
1003 static gpgme_error_t
1004 uiserver_decrypt_verify (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
1006 return _uiserver_decrypt (engine, 1, ciph, plain);
1010 static gpgme_error_t
1011 set_recipients (engine_uiserver_t uiserver, gpgme_key_t recp[])
1013 gpgme_error_t err = 0;
1014 assuan_context_t ctx = uiserver->assuan_ctx;
1017 int invalid_recipients = 0;
1020 linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */
1021 line = malloc (10 + 40 + 1);
1023 return gpg_error_from_errno (errno);
1024 strcpy (line, "RECIPIENT ");
1025 for (i=0; !err && recp[i]; i++)
1030 /* We use only the first user ID of the key. */
1031 if (!recp[i]->uids || !(uid=recp[i]->uids->uid) || !*uid)
1033 invalid_recipients++;
1037 newlen = 11 + strlen (uid);
1038 if (linelen < newlen)
1040 char *newline = realloc (line, newlen);
1043 int saved_errno = errno;
1045 return gpg_error_from_errno (saved_errno);
1050 /* FIXME: need to do proper escaping */
1051 strcpy (&line[10], uid);
1053 err = uiserver_assuan_simple_command (ctx, line, uiserver->status.fnc,
1054 uiserver->status.fnc_value);
1055 /* FIXME: This might requires more work. */
1056 if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1057 invalid_recipients++;
1065 return gpg_error (invalid_recipients
1066 ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
1070 static gpgme_error_t
1071 uiserver_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
1072 gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
1074 engine_uiserver_t uiserver = engine;
1076 const char *protocol;
1080 return gpg_error (GPG_ERR_INV_VALUE);
1081 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1083 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1084 protocol = " --protocol=OpenPGP";
1085 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1086 protocol = " --protocol=CMS";
1088 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1090 if (flags & GPGME_ENCRYPT_PREPARE)
1092 if (!recp || plain || ciph)
1093 return gpg_error (GPG_ERR_INV_VALUE);
1095 if (asprintf (&cmd, "PREP_ENCRYPT%s%s", protocol,
1096 (flags & GPGME_ENCRYPT_EXPECT_SIGN)
1097 ? " --expect-sign" : "") < 0)
1098 return gpg_error_from_errno (errno);
1102 if (!plain || !ciph)
1103 return gpg_error (GPG_ERR_INV_VALUE);
1105 if (asprintf (&cmd, "ENCRYPT%s", protocol) < 0)
1106 return gpg_error_from_errno (errno);
1111 uiserver->input_cb.data = plain;
1112 err = uiserver_set_fd (uiserver, INPUT_FD,
1113 map_data_enc (uiserver->input_cb.data));
1123 uiserver->output_cb.data = ciph;
1124 err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
1125 : map_data_enc (uiserver->output_cb.data));
1133 uiserver->inline_data = NULL;
1137 err = set_recipients (uiserver, recp);
1145 err = start (uiserver, cmd);
1151 static gpgme_error_t
1152 uiserver_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
1153 gpgme_sig_mode_t mode, int use_armor, int use_textmode,
1154 int include_certs, gpgme_ctx_t ctx /* FIXME */)
1156 engine_uiserver_t uiserver = engine;
1157 gpgme_error_t err = 0;
1158 const char *protocol;
1162 if (!uiserver || !in || !out)
1163 return gpg_error (GPG_ERR_INV_VALUE);
1164 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1166 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1167 protocol = " --protocol=OpenPGP";
1168 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1169 protocol = " --protocol=CMS";
1171 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1173 if (asprintf (&cmd, "SIGN%s%s", protocol,
1174 (mode == GPGME_SIG_MODE_DETACH) ? " --detached" : "") < 0)
1175 return gpg_error_from_errno (errno);
1177 key = gpgme_signers_enum (ctx, 0);
1180 const char *s = NULL;
1182 if (key && key->uids)
1183 s = key->uids->email;
1185 if (s && strlen (s) < 80)
1189 strcpy (stpcpy (buf, "SENDER --info "), s);
1190 err = uiserver_assuan_simple_command (uiserver->assuan_ctx, buf,
1191 uiserver->status.fnc,
1192 uiserver->status.fnc_value);
1195 err = gpg_error (GPG_ERR_INV_VALUE);
1196 gpgme_key_unref (key);
1204 uiserver->input_cb.data = in;
1205 err = uiserver_set_fd (uiserver, INPUT_FD,
1206 map_data_enc (uiserver->input_cb.data));
1212 uiserver->output_cb.data = out;
1213 err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
1214 : map_data_enc (uiserver->output_cb.data));
1220 uiserver->inline_data = NULL;
1222 err = start (uiserver, cmd);
1228 /* FIXME: Missing a way to specify --silent. */
1229 static gpgme_error_t
1230 uiserver_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
1231 gpgme_data_t plaintext)
1233 engine_uiserver_t uiserver = engine;
1235 const char *protocol;
1239 return gpg_error (GPG_ERR_INV_VALUE);
1240 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1242 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1243 protocol = " --protocol=OpenPGP";
1244 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1245 protocol = " --protocol=CMS";
1247 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1249 if (asprintf (&cmd, "VERIFY%s", protocol) < 0)
1250 return gpg_error_from_errno (errno);
1252 uiserver->input_cb.data = sig;
1253 err = uiserver_set_fd (uiserver, INPUT_FD,
1254 map_data_enc (uiserver->input_cb.data));
1262 /* Normal or cleartext signature. */
1263 uiserver->output_cb.data = plaintext;
1264 err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
1268 /* Detached signature. */
1269 uiserver->message_cb.data = signed_text;
1270 err = uiserver_set_fd (uiserver, MESSAGE_FD, 0);
1272 uiserver->inline_data = NULL;
1275 err = start (uiserver, cmd);
1283 uiserver_set_status_handler (void *engine, engine_status_handler_t fnc,
1286 engine_uiserver_t uiserver = engine;
1288 uiserver->status.fnc = fnc;
1289 uiserver->status.fnc_value = fnc_value;
1293 static gpgme_error_t
1294 uiserver_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
1297 engine_uiserver_t uiserver = engine;
1299 uiserver->colon.fnc = fnc;
1300 uiserver->colon.fnc_value = fnc_value;
1301 uiserver->colon.any = 0;
1307 uiserver_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1309 engine_uiserver_t uiserver = engine;
1310 uiserver->io_cbs = *io_cbs;
1315 uiserver_io_event (void *engine, gpgme_event_io_t type, void *type_data)
1317 engine_uiserver_t uiserver = engine;
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);
1327 struct engine_ops _gpgme_engine_ops_uiserver =
1329 /* Static functions. */
1330 _gpgme_get_uiserver_socket_path,
1332 uiserver_get_version,
1333 uiserver_get_req_version,
1336 /* Member functions. */
1339 uiserver_set_status_handler,
1340 NULL, /* set_command_handler */
1341 uiserver_set_colon_line_handler,
1342 uiserver_set_locale,
1343 uiserver_set_protocol,
1345 uiserver_decrypt_verify,
1349 NULL, /* encrypt_sign */
1351 NULL, /* export_ext */
1355 NULL, /* keylist_ext */
1357 NULL, /* trustlist */
1359 NULL, /* getauditlog */
1360 NULL, /* opassuan_transact */
1361 NULL, /* conf_load */
1362 NULL, /* conf_save */
1363 uiserver_set_io_cbs,
1366 NULL /* cancel_op */