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>
38 #include <fcntl.h> /* FIXME */
50 #include "status-table.h"
53 #include "engine-backend.h"
58 int fd; /* FD we talk about. */
59 int server_fd;/* Server FD for this connection. */
60 int dir; /* Inbound/Outbound, maybe given implicit? */
61 void *data; /* Handler-specific data. */
62 void *tag; /* ID from the user for gpgme_remove_io_callback. */
63 char server_fd_str[15]; /* Same as SERVER_FD but as a string. We
64 need this because _gpgme_io_fd2str can't
65 be used on a closed descriptor. */
69 struct engine_uiserver
71 assuan_context_t assuan_ctx;
75 gpgme_protocol_t protocol;
77 iocb_data_t status_cb;
79 /* Input, output etc are from the servers perspective. */
81 gpgme_data_t input_helper_data; /* Input helper data object. */
82 void *input_helper_memory; /* Input helper memory block. */
84 iocb_data_t output_cb;
86 iocb_data_t message_cb;
90 engine_status_handler_t fnc;
96 engine_colon_line_handler_t fnc;
104 int any; /* any data line seen */
107 gpgme_data_t inline_data; /* Used to collect D lines. */
109 struct gpgme_io_cbs io_cbs;
112 typedef struct engine_uiserver *engine_uiserver_t;
115 static void uiserver_io_event (void *engine,
116 gpgme_event_io_t type, void *type_data);
121 uiserver_get_version (const char *file_name)
123 return strdup ("1.0");
128 uiserver_get_req_version (void)
135 close_notify_handler (int fd, void *opaque)
137 engine_uiserver_t uiserver = opaque;
140 if (uiserver->status_cb.fd == fd)
142 if (uiserver->status_cb.tag)
143 (*uiserver->io_cbs.remove) (uiserver->status_cb.tag);
144 uiserver->status_cb.fd = -1;
145 uiserver->status_cb.tag = NULL;
147 else if (uiserver->input_cb.fd == fd)
149 if (uiserver->input_cb.tag)
150 (*uiserver->io_cbs.remove) (uiserver->input_cb.tag);
151 uiserver->input_cb.fd = -1;
152 uiserver->input_cb.tag = NULL;
153 if (uiserver->input_helper_data)
155 gpgme_data_release (uiserver->input_helper_data);
156 uiserver->input_helper_data = NULL;
158 if (uiserver->input_helper_memory)
160 free (uiserver->input_helper_memory);
161 uiserver->input_helper_memory = NULL;
164 else if (uiserver->output_cb.fd == fd)
166 if (uiserver->output_cb.tag)
167 (*uiserver->io_cbs.remove) (uiserver->output_cb.tag);
168 uiserver->output_cb.fd = -1;
169 uiserver->output_cb.tag = NULL;
171 else if (uiserver->message_cb.fd == fd)
173 if (uiserver->message_cb.tag)
174 (*uiserver->io_cbs.remove) (uiserver->message_cb.tag);
175 uiserver->message_cb.fd = -1;
176 uiserver->message_cb.tag = NULL;
181 /* This is the default inquiry callback. We use it to handle the
182 Pinentry notifications. */
184 default_inq_cb (engine_uiserver_t uiserver, const char *line)
186 if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
188 _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
196 uiserver_cancel (void *engine)
198 engine_uiserver_t uiserver = engine;
201 return gpg_error (GPG_ERR_INV_VALUE);
203 if (uiserver->status_cb.fd != -1)
204 _gpgme_io_close (uiserver->status_cb.fd);
205 if (uiserver->input_cb.fd != -1)
206 _gpgme_io_close (uiserver->input_cb.fd);
207 if (uiserver->output_cb.fd != -1)
208 _gpgme_io_close (uiserver->output_cb.fd);
209 if (uiserver->message_cb.fd != -1)
210 _gpgme_io_close (uiserver->message_cb.fd);
212 if (uiserver->assuan_ctx)
214 assuan_release (uiserver->assuan_ctx);
215 uiserver->assuan_ctx = NULL;
223 uiserver_release (void *engine)
225 engine_uiserver_t uiserver = engine;
230 uiserver_cancel (engine);
232 free (uiserver->colon.attic.line);
238 uiserver_new (void **engine, const char *file_name, const char *home_dir)
240 gpgme_error_t err = 0;
241 engine_uiserver_t uiserver;
242 char *dft_display = NULL;
243 char dft_ttyname[64];
244 char *dft_ttytype = NULL;
247 uiserver = calloc (1, sizeof *uiserver);
249 return gpg_error_from_syserror ();
251 uiserver->protocol = GPGME_PROTOCOL_DEFAULT;
252 uiserver->status_cb.fd = -1;
253 uiserver->status_cb.dir = 1;
254 uiserver->status_cb.tag = 0;
255 uiserver->status_cb.data = uiserver;
257 uiserver->input_cb.fd = -1;
258 uiserver->input_cb.dir = 0;
259 uiserver->input_cb.tag = 0;
260 uiserver->input_cb.server_fd = -1;
261 *uiserver->input_cb.server_fd_str = 0;
262 uiserver->output_cb.fd = -1;
263 uiserver->output_cb.dir = 1;
264 uiserver->output_cb.tag = 0;
265 uiserver->output_cb.server_fd = -1;
266 *uiserver->output_cb.server_fd_str = 0;
267 uiserver->message_cb.fd = -1;
268 uiserver->message_cb.dir = 0;
269 uiserver->message_cb.tag = 0;
270 uiserver->message_cb.server_fd = -1;
271 *uiserver->message_cb.server_fd_str = 0;
273 uiserver->status.fnc = 0;
274 uiserver->colon.fnc = 0;
275 uiserver->colon.attic.line = 0;
276 uiserver->colon.attic.linesize = 0;
277 uiserver->colon.attic.linelen = 0;
278 uiserver->colon.any = 0;
280 uiserver->inline_data = NULL;
282 uiserver->io_cbs.add = NULL;
283 uiserver->io_cbs.add_priv = NULL;
284 uiserver->io_cbs.remove = NULL;
285 uiserver->io_cbs.event = NULL;
286 uiserver->io_cbs.event_priv = NULL;
288 err = assuan_new_ext (&uiserver->assuan_ctx, GPG_ERR_SOURCE_GPGME,
289 &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
293 assuan_ctx_set_system_hooks (uiserver->assuan_ctx,
294 &_gpgme_assuan_system_hooks);
296 err = assuan_socket_connect (uiserver->assuan_ctx,
298 file_name : _gpgme_get_uiserver_socket_path (),
299 0, ASSUAN_SOCKET_SERVER_FDPASSING);
303 err = _gpgme_getenv ("DISPLAY", &dft_display);
308 if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
311 err = gpg_error_from_errno (errno);
316 err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
327 rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
330 err = gpg_error_from_errno (rc);
335 if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
337 err = gpg_error_from_errno (errno);
340 err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
346 err = _gpgme_getenv ("TERM", &dft_ttytype);
351 if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
354 err = gpg_error_from_errno (errno);
359 err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
360 NULL, NULL, NULL, NULL);
368 #ifdef HAVE_W32_SYSTEM
369 /* Under Windows we need to use AllowSetForegroundWindow. Tell
370 uiserver to tell us when it needs it. */
373 err = assuan_transact (uiserver->assuan_ctx, "OPTION allow-pinentry-notify",
374 NULL, NULL, NULL, NULL, NULL, NULL);
375 if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
376 err = 0; /* This is a new feature of uiserver. */
378 #endif /*HAVE_W32_SYSTEM*/
382 uiserver_release (uiserver);
391 uiserver_set_locale (void *engine, int category, const char *value)
393 engine_uiserver_t uiserver = engine;
398 /* FIXME: If value is NULL, we need to reset the option to default.
399 But we can't do this. So we error out here. UISERVER needs support
401 if (category == LC_CTYPE)
404 if (!value && uiserver->lc_ctype_set)
405 return gpg_error (GPG_ERR_INV_VALUE);
407 uiserver->lc_ctype_set = 1;
410 else if (category == LC_MESSAGES)
412 catstr = "lc-messages";
413 if (!value && uiserver->lc_messages_set)
414 return gpg_error (GPG_ERR_INV_VALUE);
416 uiserver->lc_messages_set = 1;
418 #endif /* LC_MESSAGES */
420 return gpg_error (GPG_ERR_INV_VALUE);
422 /* FIXME: Reset value to default. */
426 if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
427 err = gpg_error_from_errno (errno);
430 err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
431 NULL, NULL, NULL, NULL);
440 uiserver_set_protocol (void *engine, gpgme_protocol_t protocol)
442 engine_uiserver_t uiserver = engine;
444 if (protocol != GPGME_PROTOCOL_OpenPGP
445 && protocol != GPGME_PROTOCOL_CMS
446 && protocol != GPGME_PROTOCOL_DEFAULT)
447 return gpg_error (GPG_ERR_INV_VALUE);
449 uiserver->protocol = protocol;
454 /* Forward declaration. */
455 static gpgme_status_code_t parse_status (const char *name);
458 uiserver_assuan_simple_command (assuan_context_t ctx, char *cmd,
459 engine_status_handler_t status_fnc,
460 void *status_fnc_value)
466 err = assuan_write_line (ctx, cmd);
472 err = assuan_read_line (ctx, &line, &linelen);
476 if (*line == '#' || !linelen)
480 && line[0] == 'O' && line[1] == 'K'
481 && (line[2] == '\0' || line[2] == ' '))
483 else if (linelen >= 4
484 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
486 err = atoi (&line[4]);
487 else if (linelen >= 2
488 && line[0] == 'S' && line[1] == ' ')
491 gpgme_status_code_t r;
493 rest = strchr (line + 2, ' ');
495 rest = line + linelen; /* set to an empty string */
499 r = parse_status (line + 2);
501 if (r >= 0 && status_fnc)
502 err = status_fnc (status_fnc_value, r, rest);
504 err = gpg_error (GPG_ERR_GENERAL);
507 err = gpg_error (GPG_ERR_GENERAL);
515 typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
517 #define COMMANDLINELEN 40
519 uiserver_set_fd (engine_uiserver_t uiserver, fd_type_t fd_type, const char *opt)
522 char line[COMMANDLINELEN];
524 iocb_data_t *iocb_data;
531 iocb_data = &uiserver->input_cb;
536 iocb_data = &uiserver->output_cb;
541 iocb_data = &uiserver->message_cb;
545 return gpg_error (GPG_ERR_INV_VALUE);
548 dir = iocb_data->dir;
550 /* We try to short-cut the communication by giving UISERVER direct
551 access to the file descriptor, rather than using a pipe. */
552 iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
553 if (iocb_data->server_fd < 0)
557 if (_gpgme_io_pipe (fds, 0) < 0)
558 return gpg_error_from_errno (errno);
560 iocb_data->fd = dir ? fds[0] : fds[1];
561 iocb_data->server_fd = dir ? fds[1] : fds[0];
563 if (_gpgme_io_set_close_notify (iocb_data->fd,
564 close_notify_handler, uiserver))
566 err = gpg_error (GPG_ERR_GENERAL);
571 err = assuan_sendfd (uiserver->assuan_ctx, iocb_data->server_fd);
575 _gpgme_io_close (iocb_data->server_fd);
576 iocb_data->server_fd = -1;
579 snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
581 snprintf (line, COMMANDLINELEN, "%s FD", which);
583 err = uiserver_assuan_simple_command (uiserver->assuan_ctx, line, NULL, NULL);
588 _gpgme_io_close (iocb_data->fd);
590 if (iocb_data->server_fd != -1)
592 _gpgme_io_close (iocb_data->server_fd);
593 iocb_data->server_fd = -1;
602 map_data_enc (gpgme_data_t d)
604 switch (gpgme_data_get_encoding (d))
606 case GPGME_DATA_ENCODING_NONE:
608 case GPGME_DATA_ENCODING_BINARY:
610 case GPGME_DATA_ENCODING_BASE64:
612 case GPGME_DATA_ENCODING_ARMOR:
622 status_cmp (const void *ap, const void *bp)
624 const struct status_table_s *a = ap;
625 const struct status_table_s *b = bp;
627 return strcmp (a->name, b->name);
631 static gpgme_status_code_t
632 parse_status (const char *name)
634 struct status_table_s t, *r;
636 r = bsearch (&t, status_table, DIM(status_table) - 1,
637 sizeof t, status_cmp);
638 return r ? r->code : -1;
643 status_handler (void *opaque, int fd)
645 struct io_cb_data *data = (struct io_cb_data *) opaque;
646 engine_uiserver_t uiserver = (engine_uiserver_t) data->handler_value;
647 gpgme_error_t err = 0;
653 err = assuan_read_line (uiserver->assuan_ctx, &line, &linelen);
656 /* Try our best to terminate the connection friendly. */
657 /* assuan_write_line (uiserver->assuan_ctx, "BYE"); */
658 TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
659 "fd 0x%x: error from assuan (%d) getting status line : %s",
660 fd, err, gpg_strerror (err));
662 else if (linelen >= 3
663 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
664 && (line[3] == '\0' || line[3] == ' '))
667 err = atoi (&line[4]);
669 err = gpg_error (GPG_ERR_GENERAL);
670 TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
671 "fd 0x%x: ERR line - mapped to: %s",
672 fd, err ? gpg_strerror (err) : "ok");
673 /* Try our best to terminate the connection friendly. */
674 /* assuan_write_line (uiserver->assuan_ctx, "BYE"); */
676 else if (linelen >= 2
677 && line[0] == 'O' && line[1] == 'K'
678 && (line[2] == '\0' || line[2] == ' '))
680 if (uiserver->status.fnc)
681 err = uiserver->status.fnc (uiserver->status.fnc_value,
682 GPGME_STATUS_EOF, "");
684 if (!err && uiserver->colon.fnc && uiserver->colon.any)
686 /* We must tell a colon function about the EOF. We do
687 this only when we have seen any data lines. Note
688 that this inlined use of colon data lines will
689 eventually be changed into using a regular data
691 uiserver->colon.any = 0;
692 err = uiserver->colon.fnc (uiserver->colon.fnc_value, NULL);
694 TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
695 "fd 0x%x: OK line - final status: %s",
696 fd, err ? gpg_strerror (err) : "ok");
697 _gpgme_io_close (uiserver->status_cb.fd);
701 && line[0] == 'D' && line[1] == ' '
702 && uiserver->colon.fnc)
704 /* We are using the colon handler even for plain inline data
705 - strange name for that function but for historic reasons
707 /* FIXME We can't use this for binary data because we
708 assume this is a string. For the current usage of colon
709 output it is correct. */
710 char *src = line + 2;
711 char *end = line + linelen;
713 char **aline = &uiserver->colon.attic.line;
714 int *alinelen = &uiserver->colon.attic.linelen;
716 if (uiserver->colon.attic.linesize < *alinelen + linelen + 1)
718 char *newline = realloc (*aline, *alinelen + linelen + 1);
720 err = gpg_error_from_errno (errno);
724 uiserver->colon.attic.linesize += linelen + 1;
729 dst = *aline + *alinelen;
731 while (!err && src < end)
733 if (*src == '%' && src + 2 < end)
735 /* Handle escaped characters. */
737 *dst = _gpgme_hextobyte (src);
749 /* Terminate the pending line, pass it to the colon
750 handler and reset it. */
752 uiserver->colon.any = 1;
753 if (*alinelen > 1 && *(dst - 1) == '\r')
757 /* FIXME How should we handle the return code? */
758 err = uiserver->colon.fnc (uiserver->colon.fnc_value, *aline);
769 TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
770 "fd 0x%x: D line; final status: %s",
771 fd, err? gpg_strerror (err):"ok");
774 && line[0] == 'D' && line[1] == ' '
775 && uiserver->inline_data)
777 char *src = line + 2;
778 char *end = line + linelen;
785 if (*src == '%' && src + 2 < end)
787 /* Handle escaped characters. */
789 *dst++ = _gpgme_hextobyte (src);
801 nwritten = gpgme_data_write (uiserver->inline_data, src, linelen);
802 if (!nwritten || (nwritten < 0 && errno != EINTR)
803 || nwritten > linelen)
805 err = gpg_error_from_errno (errno);
812 TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
813 "fd 0x%x: D inlinedata; final status: %s",
814 fd, err? gpg_strerror (err):"ok");
817 && line[0] == 'S' && line[1] == ' ')
820 gpgme_status_code_t r;
822 rest = strchr (line + 2, ' ');
824 rest = line + linelen; /* set to an empty string */
828 r = parse_status (line + 2);
832 if (uiserver->status.fnc)
833 err = uiserver->status.fnc (uiserver->status.fnc_value, r, rest);
836 fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
837 TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
838 "fd 0x%x: S line (%s) - final status: %s",
839 fd, line+2, err? gpg_strerror (err):"ok");
841 else if (linelen >= 7
842 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
843 && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
845 && (line[7] == '\0' || line[7] == ' '))
847 char *keyword = line+7;
849 while (*keyword == ' ')
851 default_inq_cb (uiserver, keyword);
852 assuan_write_line (uiserver->assuan_ctx, "END");
856 while (!err && assuan_pending_line (uiserver->assuan_ctx));
863 add_io_cb (engine_uiserver_t uiserver, iocb_data_t *iocbd, gpgme_io_cb_t handler)
867 TRACE_BEG2 (DEBUG_ENGINE, "engine-uiserver:add_io_cb", uiserver,
868 "fd %d, dir %d", iocbd->fd, iocbd->dir);
869 err = (*uiserver->io_cbs.add) (uiserver->io_cbs.add_priv,
870 iocbd->fd, iocbd->dir,
871 handler, iocbd->data, &iocbd->tag);
873 return TRACE_ERR (err);
875 /* FIXME Kludge around poll() problem. */
876 err = _gpgme_io_set_nonblocking (iocbd->fd);
877 return TRACE_ERR (err);
882 start (engine_uiserver_t uiserver, const char *command)
888 /* We need to know the fd used by assuan for reads. We do this by
889 using the assumption that the first returned fd from
890 assuan_get_active_fds() is always this one. */
891 nfds = assuan_get_active_fds (uiserver->assuan_ctx, 0 /* read fds */,
892 fdlist, DIM (fdlist));
894 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
896 /* We "duplicate" the file descriptor, so we can close it here (we
897 can't close fdlist[0], as that is closed by libassuan, and
898 closing it here might cause libassuan to close some unrelated FD
899 later). Alternatively, we could special case status_fd and
900 register/unregister it manually as needed, but this increases
901 code duplication and is more complicated as we can not use the
902 close notifications etc. A third alternative would be to let
903 Assuan know that we closed the FD, but that complicates the
906 uiserver->status_cb.fd = _gpgme_io_dup (fdlist[0]);
907 if (uiserver->status_cb.fd < 0)
908 return gpg_error_from_syserror ();
910 if (_gpgme_io_set_close_notify (uiserver->status_cb.fd,
911 close_notify_handler, uiserver))
913 _gpgme_io_close (uiserver->status_cb.fd);
914 uiserver->status_cb.fd = -1;
915 return gpg_error (GPG_ERR_GENERAL);
918 err = add_io_cb (uiserver, &uiserver->status_cb, status_handler);
919 if (!err && uiserver->input_cb.fd != -1)
920 err = add_io_cb (uiserver, &uiserver->input_cb, _gpgme_data_outbound_handler);
921 if (!err && uiserver->output_cb.fd != -1)
922 err = add_io_cb (uiserver, &uiserver->output_cb, _gpgme_data_inbound_handler);
923 if (!err && uiserver->message_cb.fd != -1)
924 err = add_io_cb (uiserver, &uiserver->message_cb, _gpgme_data_outbound_handler);
927 err = assuan_write_line (uiserver->assuan_ctx, command);
930 uiserver_io_event (uiserver, GPGME_EVENT_START, NULL);
937 uiserver_reset (void *engine)
939 engine_uiserver_t uiserver = engine;
941 /* We must send a reset because we need to reset the list of
942 signers. Note that RESET does not reset OPTION commands. */
943 return uiserver_assuan_simple_command (uiserver->assuan_ctx, "RESET", NULL, NULL);
948 _uiserver_decrypt (void *engine, int verify,
949 gpgme_data_t ciph, gpgme_data_t plain)
951 engine_uiserver_t uiserver = engine;
953 const char *protocol;
957 return gpg_error (GPG_ERR_INV_VALUE);
958 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
960 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
961 protocol = " --protocol=OpenPGP";
962 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
963 protocol = " --protocol=CMS";
965 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
967 if (asprintf (&cmd, "DECRYPT%s%s", protocol,
968 verify ? "" : " --no-verify") < 0)
969 return gpg_error_from_errno (errno);
971 uiserver->input_cb.data = ciph;
972 err = uiserver_set_fd (uiserver, INPUT_FD,
973 map_data_enc (uiserver->input_cb.data));
977 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
979 uiserver->output_cb.data = plain;
980 err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
984 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
986 uiserver->inline_data = NULL;
988 err = start (engine, cmd);
995 uiserver_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
997 return _uiserver_decrypt (engine, 0, ciph, plain);
1001 static gpgme_error_t
1002 uiserver_decrypt_verify (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
1004 return _uiserver_decrypt (engine, 1, ciph, plain);
1008 static gpgme_error_t
1009 set_recipients (engine_uiserver_t uiserver, gpgme_key_t recp[])
1011 gpgme_error_t err = 0;
1012 assuan_context_t ctx = uiserver->assuan_ctx;
1015 int invalid_recipients = 0;
1018 linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */
1019 line = malloc (10 + 40 + 1);
1021 return gpg_error_from_errno (errno);
1022 strcpy (line, "RECIPIENT ");
1023 for (i=0; !err && recp[i]; i++)
1028 /* We use only the first user ID of the key. */
1029 if (!recp[i]->uids || !(uid=recp[i]->uids->uid) || !*uid)
1031 invalid_recipients++;
1035 newlen = 11 + strlen (uid);
1036 if (linelen < newlen)
1038 char *newline = realloc (line, newlen);
1041 int saved_errno = errno;
1043 return gpg_error_from_errno (saved_errno);
1048 /* FIXME: need to do proper escaping */
1049 strcpy (&line[10], uid);
1051 err = uiserver_assuan_simple_command (ctx, line, uiserver->status.fnc,
1052 uiserver->status.fnc_value);
1053 /* FIXME: This might requires more work. */
1054 if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1055 invalid_recipients++;
1063 return gpg_error (invalid_recipients
1064 ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
1068 static gpgme_error_t
1069 uiserver_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
1070 gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
1072 engine_uiserver_t uiserver = engine;
1074 const char *protocol;
1078 return gpg_error (GPG_ERR_INV_VALUE);
1079 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1081 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1082 protocol = " --protocol=OpenPGP";
1083 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1084 protocol = " --protocol=CMS";
1086 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1088 if (flags & GPGME_ENCRYPT_PREPARE)
1090 if (!recp || plain || ciph)
1091 return gpg_error (GPG_ERR_INV_VALUE);
1093 if (asprintf (&cmd, "PREP_ENCRYPT%s%s", protocol,
1094 (flags & GPGME_ENCRYPT_EXPECT_SIGN)
1095 ? " --expect-sign" : "") < 0)
1096 return gpg_error_from_errno (errno);
1100 if (!plain || !ciph)
1101 return gpg_error (GPG_ERR_INV_VALUE);
1103 if (asprintf (&cmd, "ENCRYPT%s", protocol) < 0)
1104 return gpg_error_from_errno (errno);
1109 uiserver->input_cb.data = plain;
1110 err = uiserver_set_fd (uiserver, INPUT_FD,
1111 map_data_enc (uiserver->input_cb.data));
1121 uiserver->output_cb.data = ciph;
1122 err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
1123 : map_data_enc (uiserver->output_cb.data));
1131 uiserver->inline_data = NULL;
1135 err = set_recipients (uiserver, recp);
1143 err = start (uiserver, cmd);
1149 static gpgme_error_t
1150 uiserver_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
1151 gpgme_sig_mode_t mode, int use_armor, int use_textmode,
1152 int include_certs, gpgme_ctx_t ctx /* FIXME */)
1154 engine_uiserver_t uiserver = engine;
1155 gpgme_error_t err = 0;
1156 const char *protocol;
1160 if (!uiserver || !in || !out)
1161 return gpg_error (GPG_ERR_INV_VALUE);
1162 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1164 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1165 protocol = " --protocol=OpenPGP";
1166 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1167 protocol = " --protocol=CMS";
1169 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1171 if (asprintf (&cmd, "SIGN%s%s", protocol,
1172 (mode == GPGME_SIG_MODE_DETACH) ? " --detached" : "") < 0)
1173 return gpg_error_from_errno (errno);
1175 key = gpgme_signers_enum (ctx, 0);
1178 const char *s = NULL;
1180 if (key && key->uids)
1181 s = key->uids->email;
1183 if (s && strlen (s) < 80)
1187 strcpy (stpcpy (buf, "SENDER --info "), s);
1188 err = uiserver_assuan_simple_command (uiserver->assuan_ctx, buf,
1189 uiserver->status.fnc,
1190 uiserver->status.fnc_value);
1193 err = gpg_error (GPG_ERR_INV_VALUE);
1194 gpgme_key_unref (key);
1202 uiserver->input_cb.data = in;
1203 err = uiserver_set_fd (uiserver, INPUT_FD,
1204 map_data_enc (uiserver->input_cb.data));
1210 uiserver->output_cb.data = out;
1211 err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
1212 : map_data_enc (uiserver->output_cb.data));
1218 uiserver->inline_data = NULL;
1220 err = start (uiserver, cmd);
1226 /* FIXME: Missing a way to specify --silent. */
1227 static gpgme_error_t
1228 uiserver_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
1229 gpgme_data_t plaintext)
1231 engine_uiserver_t uiserver = engine;
1233 const char *protocol;
1237 return gpg_error (GPG_ERR_INV_VALUE);
1238 if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1240 else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1241 protocol = " --protocol=OpenPGP";
1242 else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1243 protocol = " --protocol=CMS";
1245 return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1247 if (asprintf (&cmd, "VERIFY%s", protocol) < 0)
1248 return gpg_error_from_errno (errno);
1250 uiserver->input_cb.data = sig;
1251 err = uiserver_set_fd (uiserver, INPUT_FD,
1252 map_data_enc (uiserver->input_cb.data));
1260 /* Normal or cleartext signature. */
1261 uiserver->output_cb.data = plaintext;
1262 err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
1266 /* Detached signature. */
1267 uiserver->message_cb.data = signed_text;
1268 err = uiserver_set_fd (uiserver, MESSAGE_FD, 0);
1270 uiserver->inline_data = NULL;
1273 err = start (uiserver, cmd);
1281 uiserver_set_status_handler (void *engine, engine_status_handler_t fnc,
1284 engine_uiserver_t uiserver = engine;
1286 uiserver->status.fnc = fnc;
1287 uiserver->status.fnc_value = fnc_value;
1291 static gpgme_error_t
1292 uiserver_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
1295 engine_uiserver_t uiserver = engine;
1297 uiserver->colon.fnc = fnc;
1298 uiserver->colon.fnc_value = fnc_value;
1299 uiserver->colon.any = 0;
1305 uiserver_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1307 engine_uiserver_t uiserver = engine;
1308 uiserver->io_cbs = *io_cbs;
1313 uiserver_io_event (void *engine, gpgme_event_io_t type, void *type_data)
1315 engine_uiserver_t uiserver = engine;
1317 TRACE3 (DEBUG_ENGINE, "gpgme:uiserver_io_event", uiserver,
1318 "event %p, type %d, type_data %p",
1319 uiserver->io_cbs.event, type, type_data);
1320 if (uiserver->io_cbs.event)
1321 (*uiserver->io_cbs.event) (uiserver->io_cbs.event_priv, type, type_data);
1325 struct engine_ops _gpgme_engine_ops_uiserver =
1327 /* Static functions. */
1328 _gpgme_get_uiserver_socket_path,
1330 uiserver_get_version,
1331 uiserver_get_req_version,
1334 /* Member functions. */
1337 uiserver_set_status_handler,
1338 NULL, /* set_command_handler */
1339 uiserver_set_colon_line_handler,
1340 uiserver_set_locale,
1341 uiserver_set_protocol,
1343 uiserver_decrypt_verify,
1347 NULL, /* encrypt_sign */
1349 NULL, /* export_ext */
1353 NULL, /* keylist_ext */
1355 NULL, /* trustlist */
1357 NULL, /* getauditlog */
1358 NULL, /* opassuan_transact */
1359 NULL, /* conf_load */
1360 NULL, /* conf_save */
1361 uiserver_set_io_cbs,
1364 NULL /* cancel_op */