1 /* engine-g13.c - G13 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
28 #include <sys/types.h>
34 #include <fcntl.h> /* FIXME */
47 #include "engine-backend.h"
52 int fd; /* FD we talk about. */
53 int server_fd;/* Server FD for this connection. */
54 int dir; /* Inbound/Outbound, maybe given implicit? */
55 void *data; /* Handler-specific data. */
56 void *tag; /* ID from the user for gpgme_remove_io_callback. */
57 char server_fd_str[15]; /* Same as SERVER_FD but as a string. We
58 need this because _gpgme_io_fd2str can't
59 be used on a closed descriptor. */
65 assuan_context_t assuan_ctx;
70 iocb_data_t status_cb;
72 struct gpgme_io_cbs io_cbs;
74 /* User provided callbacks. */
76 gpgme_assuan_data_cb_t data_cb;
79 gpgme_assuan_inquire_cb_t inq_cb;
82 gpgme_assuan_status_cb_t status_cb;
83 void *status_cb_value;
87 typedef struct engine_g13 *engine_g13_t;
90 static void g13_io_event (void *engine,
91 gpgme_event_io_t type, void *type_data);
96 g13_get_version (const char *file_name)
98 return _gpgme_get_program_version (file_name ? file_name
99 : _gpgme_get_g13_path ());
104 g13_get_req_version (void)
106 return NEED_G13_VERSION;
111 close_notify_handler (int fd, void *opaque)
113 engine_g13_t g13 = opaque;
116 if (g13->status_cb.fd == fd)
118 if (g13->status_cb.tag)
119 (*g13->io_cbs.remove) (g13->status_cb.tag);
120 g13->status_cb.fd = -1;
121 g13->status_cb.tag = NULL;
126 /* This is the default inquiry callback. We use it to handle the
127 Pinentry notifications. */
129 default_inq_cb (engine_g13_t g13, const char *keyword, const char *args)
133 if (!strcmp (keyword, "PINENTRY_LAUNCHED"))
135 _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10));
138 if (g13->user.inq_cb)
140 gpgme_data_t data = NULL;
142 err = g13->user.inq_cb (g13->user.inq_cb_value,
143 keyword, args, &data);
146 /* FIXME: Returning data is not yet implemented. However we
147 need to allow the caller to cleanup his data object.
148 Thus we run the callback in finish mode immediately. */
149 err = g13->user.inq_cb (g13->user.inq_cb_value,
161 g13_cancel (void *engine)
163 engine_g13_t g13 = engine;
166 return gpg_error (GPG_ERR_INV_VALUE);
168 if (g13->status_cb.fd != -1)
169 _gpgme_io_close (g13->status_cb.fd);
173 assuan_release (g13->assuan_ctx);
174 g13->assuan_ctx = NULL;
182 g13_cancel_op (void *engine)
184 engine_g13_t g13 = engine;
187 return gpg_error (GPG_ERR_INV_VALUE);
189 if (g13->status_cb.fd != -1)
190 _gpgme_io_close (g13->status_cb.fd);
197 g13_release (void *engine)
199 engine_g13_t g13 = engine;
211 g13_new (void **engine, const char *file_name, const char *home_dir)
213 gpgme_error_t err = 0;
217 char *dft_display = NULL;
218 char dft_ttyname[64];
219 char *dft_ttytype = NULL;
222 g13 = calloc (1, sizeof *g13);
224 return gpg_error_from_errno (errno);
226 g13->status_cb.fd = -1;
227 g13->status_cb.dir = 1;
228 g13->status_cb.tag = 0;
229 g13->status_cb.data = g13;
232 argv[argc++] = "g13";
235 argv[argc++] = "--homedir";
236 argv[argc++] = home_dir;
238 argv[argc++] = "--server";
241 err = assuan_new_ext (&g13->assuan_ctx, GPG_ERR_SOURCE_GPGME,
242 &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
246 assuan_ctx_set_system_hooks (g13->assuan_ctx, &_gpgme_assuan_system_hooks);
248 #if USE_DESCRIPTOR_PASSING
249 err = assuan_pipe_connect
250 (g13->assuan_ctx, file_name ? file_name : _gpgme_get_g13_path (),
251 argv, NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
253 err = assuan_pipe_connect
254 (g13->assuan_ctx, file_name ? file_name : _gpgme_get_g13_path (),
255 argv, NULL, NULL, NULL, 0);
260 err = _gpgme_getenv ("DISPLAY", &dft_display);
265 if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
268 err = gpg_error_from_errno (errno);
273 err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
284 rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
287 err = gpg_error_from_errno (rc);
292 if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
294 err = gpg_error_from_errno (errno);
297 err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
303 err = _gpgme_getenv ("TERM", &dft_ttytype);
308 if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
311 err = gpg_error_from_errno (errno);
316 err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
317 NULL, NULL, NULL, NULL);
325 #ifdef HAVE_W32_SYSTEM
326 /* Under Windows we need to use AllowSetForegroundWindow. Tell
327 g13 to tell us when it needs it. */
330 err = assuan_transact (g13->assuan_ctx, "OPTION allow-pinentry-notify",
331 NULL, NULL, NULL, NULL, NULL, NULL);
332 if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
333 err = 0; /* This is a new feature of g13. */
335 #endif /*HAVE_W32_SYSTEM*/
349 g13_set_locale (void *engine, int category, const char *value)
351 engine_g13_t g13 = engine;
356 /* FIXME: If value is NULL, we need to reset the option to default.
357 But we can't do this. So we error out here. G13 needs support
362 else if (category == LC_CTYPE)
365 if (!value && g13->lc_ctype_set)
366 return gpg_error (GPG_ERR_INV_VALUE);
368 g13->lc_ctype_set = 1;
372 else if (category == LC_MESSAGES)
374 catstr = "lc-messages";
375 if (!value && g13->lc_messages_set)
376 return gpg_error (GPG_ERR_INV_VALUE);
378 g13->lc_messages_set = 1;
380 #endif /* LC_MESSAGES */
382 return gpg_error (GPG_ERR_INV_VALUE);
384 /* FIXME: Reset value to default. */
388 if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
389 err = gpg_error_from_errno (errno);
392 err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
393 NULL, NULL, NULL, NULL);
401 #if USE_DESCRIPTOR_PASSING
403 g13_assuan_simple_command (assuan_context_t ctx, char *cmd,
404 engine_status_handler_t status_fnc,
405 void *status_fnc_value)
411 err = assuan_write_line (ctx, cmd);
417 err = assuan_read_line (ctx, &line, &linelen);
421 if (*line == '#' || !linelen)
425 && line[0] == 'O' && line[1] == 'K'
426 && (line[2] == '\0' || line[2] == ' '))
428 else if (linelen >= 4
429 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
431 err = atoi (&line[4]);
432 else if (linelen >= 2
433 && line[0] == 'S' && line[1] == ' ')
437 rest = strchr (line + 2, ' ');
439 rest = line + linelen; /* set to an empty string */
443 /* Nothing to do with status lines. */
446 err = gpg_error (GPG_ERR_GENERAL);
456 status_handler (void *opaque, int fd)
458 struct io_cb_data *data = (struct io_cb_data *) opaque;
459 engine_g13_t g13 = (engine_g13_t) data->handler_value;
460 gpgme_error_t err = 0;
466 err = assuan_read_line (g13->assuan_ctx, &line, &linelen);
469 /* Try our best to terminate the connection friendly. */
470 /* assuan_write_line (g13->assuan_ctx, "BYE"); */
471 TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
472 "fd 0x%x: error reading assuan line: %s",
473 fd, gpg_strerror (err));
475 else if (linelen >= 3
476 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
477 && (line[3] == '\0' || line[3] == ' '))
480 err = atoi (&line[4]);
482 err = gpg_error (GPG_ERR_GENERAL);
483 TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
484 "fd 0x%x: ERR line: %s",
485 fd, err ? gpg_strerror (err) : "ok");
487 /* Command execution errors are not fatal, as we use
488 a session based protocol. */
491 /* The caller will do the rest (namely, call cancel_op,
492 which closes status_fd). */
495 else if (linelen >= 2
496 && line[0] == 'O' && line[1] == 'K'
497 && (line[2] == '\0' || line[2] == ' '))
499 TRACE1 (DEBUG_CTX, "gpgme:status_handler", g13,
500 "fd 0x%x: OK line", fd);
502 _gpgme_io_close (g13->status_cb.fd);
506 && line[0] == 'D' && line[1] == ' ')
508 /* We are using the colon handler even for plain inline data
509 - strange name for that function but for historic reasons
511 /* FIXME We can't use this for binary data because we
512 assume this is a string. For the current usage of colon
513 output it is correct. */
514 char *src = line + 2;
515 char *end = line + linelen;
521 if (*src == '%' && src + 2 < end)
523 /* Handle escaped characters. */
525 *dst++ = _gpgme_hextobyte (src);
535 if (linelen && g13->user.data_cb)
536 err = g13->user.data_cb (g13->user.data_cb_value,
541 TRACE2 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
542 "fd 0x%x: D inlinedata; status from cb: %s",
543 fd, (g13->user.data_cb ?
544 (err? gpg_strerror (err):"ok"):"no callback"));
548 && line[0] == 'S' && line[1] == ' ')
557 args = strchr (line + 2, ' ');
559 args = line + linelen; /* set to an empty string */
566 if (g13->user.status_cb)
567 err = g13->user.status_cb (g13->user.status_cb_value,
572 TRACE3 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
573 "fd 0x%x: S line (%s) - status from cb: %s",
574 fd, line+2, (g13->user.status_cb ?
575 (err? gpg_strerror (err):"ok"):"no callback"));
577 else if (linelen >= 7
578 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
579 && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
581 && (line[7] == '\0' || line[7] == ' '))
586 for (src=line+7; *src == ' '; src++)
589 args = strchr (src, ' ');
591 args = line + linelen; /* Let it point to an empty string. */
598 err = default_inq_cb (g13, src, args);
601 /* Flush and send END. */
602 err = assuan_send_data (g13->assuan_ctx, NULL, 0);
604 else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
606 /* Flush and send CANcel. */
607 err = assuan_send_data (g13->assuan_ctx, NULL, 1);
609 assuan_write_line (g13->assuan_ctx, "END");
612 while (!err && assuan_pending_line (g13->assuan_ctx));
619 add_io_cb (engine_g13_t g13, iocb_data_t *iocbd, gpgme_io_cb_t handler)
623 TRACE_BEG2 (DEBUG_ENGINE, "engine-g13:add_io_cb", g13,
624 "fd %d, dir %d", iocbd->fd, iocbd->dir);
625 err = (*g13->io_cbs.add) (g13->io_cbs.add_priv,
626 iocbd->fd, iocbd->dir,
627 handler, iocbd->data, &iocbd->tag);
629 return TRACE_ERR (err);
631 /* FIXME Kludge around poll() problem. */
632 err = _gpgme_io_set_nonblocking (iocbd->fd);
633 return TRACE_ERR (err);
638 start (engine_g13_t g13, const char *command)
641 assuan_fd_t afdlist[5];
646 /* We need to know the fd used by assuan for reads. We do this by
647 using the assumption that the first returned fd from
648 assuan_get_active_fds() is always this one. */
649 nfds = assuan_get_active_fds (g13->assuan_ctx, 0 /* read fds */,
650 afdlist, DIM (afdlist));
652 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
654 for (i = 0; i < nfds; i++)
655 fdlist[i] = (int) afdlist[i];
657 /* We "duplicate" the file descriptor, so we can close it here (we
658 can't close fdlist[0], as that is closed by libassuan, and
659 closing it here might cause libassuan to close some unrelated FD
660 later). Alternatively, we could special case status_fd and
661 register/unregister it manually as needed, but this increases
662 code duplication and is more complicated as we can not use the
663 close notifications etc. A third alternative would be to let
664 Assuan know that we closed the FD, but that complicates the
667 g13->status_cb.fd = _gpgme_io_dup (fdlist[0]);
668 if (g13->status_cb.fd < 0)
669 return gpg_error_from_syserror ();
671 if (_gpgme_io_set_close_notify (g13->status_cb.fd,
672 close_notify_handler, g13))
674 _gpgme_io_close (g13->status_cb.fd);
675 g13->status_cb.fd = -1;
676 return gpg_error (GPG_ERR_GENERAL);
679 err = add_io_cb (g13, &g13->status_cb, status_handler);
681 err = assuan_write_line (g13->assuan_ctx, command);
684 g13_io_event (g13, GPGME_EVENT_START, NULL);
690 #if USE_DESCRIPTOR_PASSING
692 g13_reset (void *engine)
694 engine_g13_t g13 = engine;
696 /* We must send a reset because we need to reset the list of
697 signers. Note that RESET does not reset OPTION commands. */
698 return g13_assuan_simple_command (g13->assuan_ctx, "RESET", NULL, NULL);
704 g13_transact (void *engine,
706 gpgme_assuan_data_cb_t data_cb,
708 gpgme_assuan_inquire_cb_t inq_cb,
710 gpgme_assuan_status_cb_t status_cb,
711 void *status_cb_value)
713 engine_g13_t g13 = engine;
716 if (!g13 || !command || !*command)
717 return gpg_error (GPG_ERR_INV_VALUE);
719 g13->user.data_cb = data_cb;
720 g13->user.data_cb_value = data_cb_value;
721 g13->user.inq_cb = inq_cb;
722 g13->user.inq_cb_value = inq_cb_value;
723 g13->user.status_cb = status_cb;
724 g13->user.status_cb_value = status_cb_value;
726 err = start (g13, command);
733 g13_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
735 engine_g13_t g13 = engine;
736 g13->io_cbs = *io_cbs;
741 g13_io_event (void *engine, gpgme_event_io_t type, void *type_data)
743 engine_g13_t g13 = engine;
745 TRACE3 (DEBUG_ENGINE, "gpgme:g13_io_event", g13,
746 "event %p, type %d, type_data %p",
747 g13->io_cbs.event, type, type_data);
748 if (g13->io_cbs.event)
749 (*g13->io_cbs.event) (g13->io_cbs.event_priv, type, type_data);
753 struct engine_ops _gpgme_engine_ops_g13 =
755 /* Static functions. */
762 /* Member functions. */
764 #if USE_DESCRIPTOR_PASSING
769 NULL, /* set_status_handler */
770 NULL, /* set_command_handler */
771 NULL, /* set_colon_line_handler */
773 NULL, /* set_protocol */
775 NULL, /* decrypt_verify */
779 NULL, /* encrypt_sign */
781 NULL, /* export_ext */
785 NULL, /* keylist_ext */
787 NULL, /* trustlist */
789 NULL, /* getauditlog */
791 NULL, /* conf_load */
792 NULL, /* conf_save */