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 #include <sys/types.h>
36 #include <fcntl.h> /* FIXME */
48 #include "status-table.h"
51 #include "engine-backend.h"
56 int fd; /* FD we talk about. */
57 int server_fd;/* Server FD for this connection. */
58 int dir; /* Inbound/Outbound, maybe given implicit? */
59 void *data; /* Handler-specific data. */
60 void *tag; /* ID from the user for gpgme_remove_io_callback. */
61 char server_fd_str[15]; /* Same as SERVER_FD but as a string. We
62 need this because _gpgme_io_fd2str can't
63 be used on a closed descriptor. */
67 struct engine_uiserver
69 assuan_context_t assuan_ctx;
73 gpgme_protocol_t protocol;
75 iocb_data_t status_cb;
77 /* Input, output etc are from the servers perspective. */
79 gpgme_data_t input_helper_data; /* Input helper data object. */
80 void *input_helper_memory; /* Input helper memory block. */
82 iocb_data_t output_cb;
84 iocb_data_t message_cb;
88 engine_status_handler_t fnc;
94 engine_colon_line_handler_t fnc;
102 int any; /* any data line seen */
105 gpgme_data_t inline_data; /* Used to collect D lines. */
107 struct gpgme_io_cbs io_cbs;
110 typedef struct engine_uiserver *engine_uiserver_t;
113 static void uiserver_io_event (void *engine,
114 gpgme_event_io_t type, void *type_data);
119 uiserver_get_version (const char *file_name)
121 return strdup ("1.0");
126 uiserver_get_req_version (void)
133 close_notify_handler (int fd, void *opaque)
135 engine_uiserver_t uiserver = opaque;
138 if (uiserver->status_cb.fd == fd)
140 if (uiserver->status_cb.tag)
141 (*uiserver->io_cbs.remove) (uiserver->status_cb.tag);
142 uiserver->status_cb.fd = -1;
143 uiserver->status_cb.tag = NULL;
145 else if (uiserver->input_cb.fd == fd)
147 if (uiserver->input_cb.tag)
148 (*uiserver->io_cbs.remove) (uiserver->input_cb.tag);
149 uiserver->input_cb.fd = -1;
150 uiserver->input_cb.tag = NULL;
151 if (uiserver->input_helper_data)
153 gpgme_data_release (uiserver->input_helper_data);
154 uiserver->input_helper_data = NULL;
156 if (uiserver->input_helper_memory)
158 free (uiserver->input_helper_memory);
159 uiserver->input_helper_memory = NULL;
162 else if (uiserver->output_cb.fd == fd)
164 if (uiserver->output_cb.tag)
165 (*uiserver->io_cbs.remove) (uiserver->output_cb.tag);
166 uiserver->output_cb.fd = -1;
167 uiserver->output_cb.tag = NULL;
169 else if (uiserver->message_cb.fd == fd)
171 if (uiserver->message_cb.tag)
172 (*uiserver->io_cbs.remove) (uiserver->message_cb.tag);
173 uiserver->message_cb.fd = -1;
174 uiserver->message_cb.tag = NULL;
179 /* This is the default inquiry callback. We use it to handle the
180 Pinentry notifications. */
182 default_inq_cb (engine_uiserver_t uiserver, const char *line)
184 if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
186 _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
194 uiserver_cancel (void *engine)
196 engine_uiserver_t uiserver = engine;
199 return gpg_error (GPG_ERR_INV_VALUE);
201 if (uiserver->status_cb.fd != -1)
202 _gpgme_io_close (uiserver->status_cb.fd);
203 if (uiserver->input_cb.fd != -1)
204 _gpgme_io_close (uiserver->input_cb.fd);
205 if (uiserver->output_cb.fd != -1)
206 _gpgme_io_close (uiserver->output_cb.fd);
207 if (uiserver->message_cb.fd != -1)
208 _gpgme_io_close (uiserver->message_cb.fd);
210 if (uiserver->assuan_ctx)
212 assuan_release (uiserver->assuan_ctx);
213 uiserver->assuan_ctx = NULL;
221 uiserver_release (void *engine)
223 engine_uiserver_t uiserver = engine;
228 uiserver_cancel (engine);
230 free (uiserver->colon.attic.line);
236 uiserver_new (void **engine, const char *file_name, const char *home_dir)
238 gpgme_error_t err = 0;
239 engine_uiserver_t uiserver;
240 char *dft_display = NULL;
241 char dft_ttyname[64];
242 char *dft_ttytype = NULL;
245 uiserver = calloc (1, sizeof *uiserver);
247 return gpg_error_from_syserror ();
249 uiserver->protocol = GPGME_PROTOCOL_DEFAULT;
250 uiserver->status_cb.fd = -1;
251 uiserver->status_cb.dir = 1;
252 uiserver->status_cb.tag = 0;
253 uiserver->status_cb.data = uiserver;
255 uiserver->input_cb.fd = -1;
256 uiserver->input_cb.dir = 0;
257 uiserver->input_cb.tag = 0;
258 uiserver->input_cb.server_fd = -1;
259 *uiserver->input_cb.server_fd_str = 0;
260 uiserver->output_cb.fd = -1;
261 uiserver->output_cb.dir = 1;
262 uiserver->output_cb.tag = 0;
263 uiserver->output_cb.server_fd = -1;
264 *uiserver->output_cb.server_fd_str = 0;
265 uiserver->message_cb.fd = -1;
266 uiserver->message_cb.dir = 0;
267 uiserver->message_cb.tag = 0;
268 uiserver->message_cb.server_fd = -1;
269 *uiserver->message_cb.server_fd_str = 0;
271 uiserver->status.fnc = 0;
272 uiserver->colon.fnc = 0;
273 uiserver->colon.attic.line = 0;
274 uiserver->colon.attic.linesize = 0;
275 uiserver->colon.attic.linelen = 0;
276 uiserver->colon.any = 0;
278 uiserver->inline_data = NULL;
280 uiserver->io_cbs.add = NULL;
281 uiserver->io_cbs.add_priv = NULL;
282 uiserver->io_cbs.remove = NULL;
283 uiserver->io_cbs.event = NULL;
284 uiserver->io_cbs.event_priv = NULL;
286 err = assuan_new_ext (&uiserver->assuan_ctx, GPG_ERR_SOURCE_GPGME,
287 &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
291 assuan_ctx_set_system_hooks (uiserver->assuan_ctx,
292 &_gpgme_assuan_system_hooks);
294 err = assuan_socket_connect (uiserver->assuan_ctx,
296 file_name : _gpgme_get_uiserver_socket_path (),
301 err = _gpgme_getenv ("DISPLAY", &dft_display);
306 if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
309 err = gpg_error_from_errno (errno);
314 err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
325 rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
328 err = gpg_error_from_errno (rc);
333 if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
335 err = gpg_error_from_errno (errno);
338 err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
344 err = _gpgme_getenv ("TERM", &dft_ttytype);
349 if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
352 err = gpg_error_from_errno (errno);
357 err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
358 NULL, NULL, NULL, NULL);
366 #ifdef HAVE_W32_SYSTEM
367 /* Under Windows we need to use AllowSetForegroundWindow. Tell
368 uiserver to tell us when it needs it. */
371 err = assuan_transact (uiserver->assuan_ctx, "OPTION allow-pinentry-notify",
372 NULL, NULL, NULL, NULL, NULL, NULL);
373 if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
374 err = 0; /* This is a new feature of uiserver. */
376 #endif /*HAVE_W32_SYSTEM*/
380 uiserver_release (uiserver);
389 uiserver_set_locale (void *engine, int category, const char *value)
391 engine_uiserver_t uiserver = engine;
396 /* FIXME: If value is NULL, we need to reset the option to default.
397 But we can't do this. So we error out here. UISERVER needs support
399 if (category == LC_CTYPE)
402 if (!value && uiserver->lc_ctype_set)
403 return gpg_error (GPG_ERR_INV_VALUE);
405 uiserver->lc_ctype_set = 1;
408 else if (category == LC_MESSAGES)
410 catstr = "lc-messages";
411 if (!value && uiserver->lc_messages_set)
412 return gpg_error (GPG_ERR_INV_VALUE);
414 uiserver->lc_messages_set = 1;
416 #endif /* LC_MESSAGES */
418 return gpg_error (GPG_ERR_INV_VALUE);
420 /* FIXME: Reset value to default. */
424 if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
425 err = gpg_error_from_errno (errno);
428 err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
429 NULL, NULL, NULL, NULL);
438 uiserver_set_protocol (void *engine, gpgme_protocol_t protocol)
440 engine_uiserver_t uiserver = engine;
442 if (protocol != GPGME_PROTOCOL_OpenPGP
443 && protocol != GPGME_PROTOCOL_CMS
444 && protocol != GPGME_PROTOCOL_DEFAULT)
445 return gpg_error (GPG_ERR_INV_VALUE);
447 uiserver->protocol = protocol;
452 /* Forward declaration. */
453 static gpgme_status_code_t parse_status (const char *name);
456 uiserver_assuan_simple_command (assuan_context_t ctx, char *cmd,
457 engine_status_handler_t status_fnc,
458 void *status_fnc_value)
464 err = assuan_write_line (ctx, cmd);
470 err = assuan_read_line (ctx, &line, &linelen);
474 if (*line == '#' || !linelen)
478 && line[0] == 'O' && line[1] == 'K'
479 && (line[2] == '\0' || line[2] == ' '))
481 else if (linelen >= 4
482 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
484 err = atoi (&line[4]);
485 else if (linelen >= 2
486 && line[0] == 'S' && line[1] == ' ')
489 gpgme_status_code_t r;
491 rest = strchr (line + 2, ' ');
493 rest = line + linelen; /* set to an empty string */
497 r = parse_status (line + 2);
499 if (r >= 0 && status_fnc)
500 err = status_fnc (status_fnc_value, r, rest);
502 err = gpg_error (GPG_ERR_GENERAL);
505 err = gpg_error (GPG_ERR_GENERAL);
513 typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
515 #define COMMANDLINELEN 40
517 uiserver_set_fd (engine_uiserver_t uiserver, fd_type_t fd_type, const char *opt)
520 char line[COMMANDLINELEN];
522 iocb_data_t *iocb_data;
529 iocb_data = &uiserver->input_cb;
534 iocb_data = &uiserver->output_cb;
539 iocb_data = &uiserver->message_cb;
543 return gpg_error (GPG_ERR_INV_VALUE);
546 dir = iocb_data->dir;
548 /* We try to short-cut the communication by giving UISERVER direct
549 access to the file descriptor, rather than using a pipe. */
550 iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
551 if (iocb_data->server_fd < 0)
555 if (_gpgme_io_pipe (fds, 0) < 0)
556 return gpg_error_from_errno (errno);
558 iocb_data->fd = dir ? fds[0] : fds[1];
559 iocb_data->server_fd = dir ? fds[1] : fds[0];
561 if (_gpgme_io_set_close_notify (iocb_data->fd,
562 close_notify_handler, uiserver))
564 err = gpg_error (GPG_ERR_GENERAL);
569 err = assuan_sendfd (uiserver->assuan_ctx, iocb_data->server_fd);
573 _gpgme_io_close (iocb_data->server_fd);
574 iocb_data->server_fd = -1;
577 snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
579 snprintf (line, COMMANDLINELEN, "%s FD", which);
581 err = uiserver_assuan_simple_command (uiserver->assuan_ctx, line, NULL, NULL);
586 _gpgme_io_close (iocb_data->fd);
588 if (iocb_data->server_fd != -1)
590 _gpgme_io_close (iocb_data->server_fd);
591 iocb_data->server_fd = -1;
600 map_data_enc (gpgme_data_t d)
602 switch (gpgme_data_get_encoding (d))
604 case GPGME_DATA_ENCODING_NONE:
606 case GPGME_DATA_ENCODING_BINARY:
608 case GPGME_DATA_ENCODING_BASE64:
610 case GPGME_DATA_ENCODING_ARMOR:
620 status_cmp (const void *ap, const void *bp)
622 const struct status_table_s *a = ap;
623 const struct status_table_s *b = bp;
625 return strcmp (a->name, b->name);
629 static gpgme_status_code_t
630 parse_status (const char *name)
632 struct status_table_s t, *r;
634 r = bsearch (&t, status_table, DIM(status_table) - 1,
635 sizeof t, status_cmp);
636 return r ? r->code : -1;
641 status_handler (void *opaque, int fd)
643 struct io_cb_data *data = (struct io_cb_data *) opaque;
644 engine_uiserver_t uiserver = (engine_uiserver_t) data->handler_value;
645 gpgme_error_t err = 0;
651 err = assuan_read_line (uiserver->assuan_ctx, &line, &linelen);
654 /* Try our best to terminate the connection friendly. */
655 /* assuan_write_line (uiserver->assuan_ctx, "BYE"); */
656 TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
657 "fd 0x%x: error from assuan (%d) getting status line : %s",
658 fd, err, gpg_strerror (err));
660 else if (linelen >= 3
661 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
662 && (line[3] == '\0' || line[3] == ' '))
665 err = atoi (&line[4]);
667 err = gpg_error (GPG_ERR_GENERAL);
668 TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
669 "fd 0x%x: ERR line - mapped to: %s",
670 fd, err ? gpg_strerror (err) : "ok");
671 /* Try our best to terminate the connection friendly. */
672 /* assuan_write_line (uiserver->assuan_ctx, "BYE"); */
674 else if (linelen >= 2
675 && line[0] == 'O' && line[1] == 'K'
676 && (line[2] == '\0' || line[2] == ' '))
678 if (uiserver->status.fnc)
679 err = uiserver->status.fnc (uiserver->status.fnc_value,
680 GPGME_STATUS_EOF, "");
682 if (!err && uiserver->colon.fnc && uiserver->colon.any)
684 /* We must tell a colon function about the EOF. We do
685 this only when we have seen any data lines. Note
686 that this inlined use of colon data lines will
687 eventually be changed into using a regular data
689 uiserver->colon.any = 0;
690 err = uiserver->colon.fnc (uiserver->colon.fnc_value, NULL);
692 TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
693 "fd 0x%x: OK line - final status: %s",
694 fd, err ? gpg_strerror (err) : "ok");
695 _gpgme_io_close (uiserver->status_cb.fd);
699 && line[0] == 'D' && line[1] == ' '
700 && uiserver->colon.fnc)
702 /* We are using the colon handler even for plain inline data
703 - strange name for that function but for historic reasons
705 /* FIXME We can't use this for binary data because we
706 assume this is a string. For the current usage of colon
707 output it is correct. */
708 char *src = line + 2;
709 char *end = line + linelen;
711 char **aline = &uiserver->colon.attic.line;
712 int *alinelen = &uiserver->colon.attic.linelen;
714 if (uiserver->colon.attic.linesize < *alinelen + linelen + 1)
716 char *newline = realloc (*aline, *alinelen + linelen + 1);
718 err = gpg_error_from_errno (errno);
722 uiserver->colon.attic.linesize += linelen + 1;
727 dst = *aline + *alinelen;
729 while (!err && src < end)
731 if (*src == '%' && src + 2 < end)
733 /* Handle escaped characters. */
735 *dst = _gpgme_hextobyte (src);
747 /* Terminate the pending line, pass it to the colon
748 handler and reset it. */
750 uiserver->colon.any = 1;
751 if (*alinelen > 1 && *(dst - 1) == '\r')
755 /* FIXME How should we handle the return code? */
756 err = uiserver->colon.fnc (uiserver->colon.fnc_value, *aline);
767 TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
768 "fd 0x%x: D line; final status: %s",
769 fd, err? gpg_strerror (err):"ok");
772 && line[0] == 'D' && line[1] == ' '
773 && uiserver->inline_data)
775 char *src = line + 2;
776 char *end = line + linelen;
783 if (*src == '%' && src + 2 < end)
785 /* Handle escaped characters. */
787 *dst++ = _gpgme_hextobyte (src);
799 nwritten = gpgme_data_write (uiserver->inline_data, src, linelen);
800 if (!nwritten || (nwritten < 0 && errno != EINTR)
801 || nwritten > linelen)
803 err = gpg_error_from_errno (errno);
810 TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
811 "fd 0x%x: D inlinedata; final status: %s",
812 fd, err? gpg_strerror (err):"ok");
815 && line[0] == 'S' && line[1] == ' ')
818 gpgme_status_code_t r;
820 rest = strchr (line + 2, ' ');
822 rest = line + linelen; /* set to an empty string */
826 r = parse_status (line + 2);
830 if (uiserver->status.fnc)
831 err = uiserver->status.fnc (uiserver->status.fnc_value, r, rest);
834 fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
835 TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
836 "fd 0x%x: S line (%s) - final status: %s",
837 fd, line+2, err? gpg_strerror (err):"ok");
839 else if (linelen >= 7
840 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
841 && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
843 && (line[7] == '\0' || line[7] == ' '))
845 char *keyword = line+7;
847 while (*keyword == ' ')
849 default_inq_cb (uiserver, keyword);
850 assuan_write_line (uiserver->assuan_ctx, "END");
854 while (!err && assuan_pending_line (uiserver->assuan_ctx));
861 add_io_cb (engine_uiserver_t uiserver, iocb_data_t *iocbd, gpgme_io_cb_t handler)
865 TRACE_BEG2 (DEBUG_ENGINE, "engine-uiserver:add_io_cb", uiserver,
866 "fd %d, dir %d", iocbd->fd, iocbd->dir);
867 err = (*uiserver->io_cbs.add) (uiserver->io_cbs.add_priv,
868 iocbd->fd, iocbd->dir,
869 handler, iocbd->data, &iocbd->tag);
871 return TRACE_ERR (err);
873 /* FIXME Kludge around poll() problem. */
874 err = _gpgme_io_set_nonblocking (iocbd->fd);
875 return TRACE_ERR (err);
880 start (engine_uiserver_t uiserver, const char *command)
886 /* We need to know the fd used by assuan for reads. We do this by
887 using the assumption that the first returned fd from
888 assuan_get_active_fds() is always this one. */
889 nfds = assuan_get_active_fds (uiserver->assuan_ctx, 0 /* read fds */,
890 fdlist, DIM (fdlist));
892 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
894 /* We "duplicate" the file descriptor, so we can close it here (we
895 can't close fdlist[0], as that is closed by libassuan, and
896 closing it here might cause libassuan to close some unrelated FD
897 later). Alternatively, we could special case status_fd and
898 register/unregister it manually as needed, but this increases
899 code duplication and is more complicated as we can not use the
900 close notifications etc. A third alternative would be to let
901 Assuan know that we closed the FD, but that complicates the
904 uiserver->status_cb.fd = _gpgme_io_dup (fdlist[0]);
905 if (uiserver->status_cb.fd < 0)
906 return gpg_error_from_syserror ();
908 if (_gpgme_io_set_close_notify (uiserver->status_cb.fd,
909 close_notify_handler, uiserver))
911 _gpgme_io_close (uiserver->status_cb.fd);
912 uiserver->status_cb.fd = -1;
913 return gpg_error (GPG_ERR_GENERAL);
916 err = add_io_cb (uiserver, &uiserver->status_cb, status_handler);
917 if (!err && uiserver->input_cb.fd != -1)
918 err = add_io_cb (uiserver, &uiserver->input_cb, _gpgme_data_outbound_handler);
919 if (!err && uiserver->output_cb.fd != -1)
920 err = add_io_cb (uiserver, &uiserver->output_cb, _gpgme_data_inbound_handler);
921 if (!err && uiserver->message_cb.fd != -1)
922 err = add_io_cb (uiserver, &uiserver->message_cb, _gpgme_data_outbound_handler);
925 err = assuan_write_line (uiserver->assuan_ctx, command);
928 uiserver_io_event (uiserver, GPGME_EVENT_START, NULL);
935 uiserver_reset (void *engine)
937 engine_uiserver_t uiserver = engine;
939 /* We must send a reset because we need to reset the list of
940 signers. Note that RESET does not reset OPTION commands. */
941 return uiserver_assuan_simple_command (uiserver->assuan_ctx, "RESET", NULL, NULL);
946 _uiserver_decrypt (void *engine, int verify,
947 gpgme_data_t ciph, gpgme_data_t plain)
949 engine_uiserver_t uiserver = engine;
951 const char *protocol;
955 return gpg_error (GPG_ERR_INV_VALUE);
956 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
958 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
959 protocol = " --protocol=OpenPGP";
960 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
961 protocol = " --protocol=CMS";
963 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
965 if (asprintf (&cmd, "DECRYPT%s%s", protocol,
966 verify ? "" : " --no-verify") < 0)
967 return gpg_error_from_errno (errno);
969 uiserver->input_cb.data = ciph;
970 err = uiserver_set_fd (uiserver, INPUT_FD,
971 map_data_enc (uiserver->input_cb.data));
975 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
977 uiserver->output_cb.data = plain;
978 err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
982 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
984 uiserver->inline_data = NULL;
986 err = start (engine, cmd);
993 uiserver_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
995 return _uiserver_decrypt (engine, 0, ciph, plain);
1000 uiserver_decrypt_verify (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
1002 return _uiserver_decrypt (engine, 1, ciph, plain);
1006 static gpgme_error_t
1007 set_recipients (engine_uiserver_t uiserver, gpgme_key_t recp[])
1009 gpgme_error_t err = 0;
1010 assuan_context_t ctx = uiserver->assuan_ctx;
1013 int invalid_recipients = 0;
1016 linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */
1017 line = malloc (10 + 40 + 1);
1019 return gpg_error_from_errno (errno);
1020 strcpy (line, "RECIPIENT ");
1021 while (!err && recp[i])
1026 if (!recp[i]->subkeys || !recp[i]->subkeys->fpr)
1028 invalid_recipients++;
1031 fpr = recp[i]->subkeys->fpr;
1033 newlen = 11 + strlen (fpr);
1034 if (linelen < newlen)
1036 char *newline = realloc (line, newlen);
1039 int saved_errno = errno;
1041 return gpg_error_from_errno (saved_errno);
1046 strcpy (&line[10], fpr);
1048 err = uiserver_assuan_simple_command (ctx, line, uiserver->status.fnc,
1049 uiserver->status.fnc_value);
1050 /* FIXME: This requires more work. */
1051 if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1052 invalid_recipients++;
1061 return gpg_error (invalid_recipients
1062 ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
1066 static gpgme_error_t
1067 uiserver_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
1068 gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
1070 engine_uiserver_t uiserver = engine;
1072 const char *protocol;
1076 return gpg_error (GPG_ERR_INV_VALUE);
1077 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1079 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1080 protocol = " --protocol=OpenPGP";
1081 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1082 protocol = " --protocol=CMS";
1084 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1086 if (flags & GPGME_ENCRYPT_PREPARE)
1088 if (!recp || plain || ciph)
1089 return gpg_error (GPG_ERR_INV_VALUE);
1091 if (asprintf (&cmd, "PREP_ENCRYPT%s%s", protocol,
1092 (flags & GPGME_ENCRYPT_EXPECT_SIGN)
1093 ? " --expect-sign" : "") < 0)
1094 return gpg_error_from_errno (errno);
1098 if (!plain || !ciph)
1099 return gpg_error (GPG_ERR_INV_VALUE);
1101 if (asprintf (&cmd, "ENCRYPT%s", protocol) < 0)
1102 return gpg_error_from_errno (errno);
1107 uiserver->input_cb.data = plain;
1108 err = uiserver_set_fd (uiserver, INPUT_FD,
1109 map_data_enc (uiserver->input_cb.data));
1119 uiserver->output_cb.data = ciph;
1120 err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
1121 : map_data_enc (uiserver->output_cb.data));
1129 uiserver->inline_data = NULL;
1133 err = set_recipients (uiserver, recp);
1141 err = start (uiserver, cmd);
1147 static gpgme_error_t
1148 uiserver_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
1149 gpgme_sig_mode_t mode, int use_armor, int use_textmode,
1150 int include_certs, gpgme_ctx_t ctx /* FIXME */)
1152 engine_uiserver_t uiserver = engine;
1153 gpgme_error_t err = 0;
1154 const char *protocol;
1157 if (!uiserver || !in || !out)
1158 return gpg_error (GPG_ERR_INV_VALUE);
1159 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1161 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1162 protocol = " --protocol=OpenPGP";
1163 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1164 protocol = " --protocol=CMS";
1166 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1168 if (asprintf (&cmd, "SIGN%s%s", protocol,
1169 (mode == GPGME_SIG_MODE_DETACH) ? " --detached" : "") < 0)
1170 return gpg_error_from_errno (errno);
1173 gpgme_key_t key = gpgme_signers_enum (ctx, 0);
1174 const char *s = NULL;
1176 if (key && key->uids)
1177 s = key->uids->email;
1179 if (s && strlen (s) < 80)
1183 strcpy (stpcpy (buf, "SENDER --info "), s);
1184 err = uiserver_assuan_simple_command (uiserver->assuan_ctx, buf,
1185 uiserver->status.fnc,
1186 uiserver->status.fnc_value);
1189 err = gpg_error (GPG_ERR_INV_VALUE);
1190 gpgme_key_unref (key);
1198 uiserver->input_cb.data = in;
1199 err = uiserver_set_fd (uiserver, INPUT_FD,
1200 map_data_enc (uiserver->input_cb.data));
1206 uiserver->output_cb.data = out;
1207 err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
1208 : map_data_enc (uiserver->output_cb.data));
1214 uiserver->inline_data = NULL;
1216 err = start (uiserver, cmd);
1222 /* FIXME: Missing a way to specify --silent. */
1223 static gpgme_error_t
1224 uiserver_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
1225 gpgme_data_t plaintext)
1227 engine_uiserver_t uiserver = engine;
1229 const char *protocol;
1233 return gpg_error (GPG_ERR_INV_VALUE);
1234 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1236 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1237 protocol = " --protocol=OpenPGP";
1238 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1239 protocol = " --protocol=CMS";
1241 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1243 if (asprintf (&cmd, "VERIFY%s", protocol) < 0)
1244 return gpg_error_from_errno (errno);
1246 uiserver->input_cb.data = sig;
1247 err = uiserver_set_fd (uiserver, INPUT_FD,
1248 map_data_enc (uiserver->input_cb.data));
1256 /* Normal or cleartext signature. */
1257 uiserver->output_cb.data = plaintext;
1258 err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
1262 /* Detached signature. */
1263 uiserver->message_cb.data = signed_text;
1264 err = uiserver_set_fd (uiserver, MESSAGE_FD, 0);
1266 uiserver->inline_data = NULL;
1269 err = start (uiserver, cmd);
1277 uiserver_set_status_handler (void *engine, engine_status_handler_t fnc,
1280 engine_uiserver_t uiserver = engine;
1282 uiserver->status.fnc = fnc;
1283 uiserver->status.fnc_value = fnc_value;
1287 static gpgme_error_t
1288 uiserver_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
1291 engine_uiserver_t uiserver = engine;
1293 uiserver->colon.fnc = fnc;
1294 uiserver->colon.fnc_value = fnc_value;
1295 uiserver->colon.any = 0;
1301 uiserver_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1303 engine_uiserver_t uiserver = engine;
1304 uiserver->io_cbs = *io_cbs;
1309 uiserver_io_event (void *engine, gpgme_event_io_t type, void *type_data)
1311 engine_uiserver_t uiserver = engine;
1313 TRACE3 (DEBUG_ENGINE, "gpgme:uiserver_io_event", uiserver,
1314 "event %p, type %d, type_data %p",
1315 uiserver->io_cbs.event, type, type_data);
1316 if (uiserver->io_cbs.event)
1317 (*uiserver->io_cbs.event) (uiserver->io_cbs.event_priv, type, type_data);
1321 struct engine_ops _gpgme_engine_ops_uiserver =
1323 /* Static functions. */
1324 _gpgme_get_uiserver_socket_path,
1326 uiserver_get_version,
1327 uiserver_get_req_version,
1330 /* Member functions. */
1333 uiserver_set_status_handler,
1334 NULL, /* set_command_handler */
1335 uiserver_set_colon_line_handler,
1336 uiserver_set_locale,
1337 uiserver_set_protocol,
1339 uiserver_decrypt_verify,
1343 NULL, /* encrypt_sign */
1345 NULL, /* export_ext */
1349 NULL, /* keylist_ext */
1351 NULL, /* trustlist */
1353 NULL, /* getauditlog */
1354 NULL, /* opassuan_transact */
1355 NULL, /* conf_load */
1356 NULL, /* conf_save */
1357 uiserver_set_io_cbs,
1360 NULL /* cancel_op */