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 #ifdef HAVE_SYS_TYPES_H
29 # include <sys/types.h>
38 #include <fcntl.h> /* FIXME */
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. */
69 assuan_context_t assuan_ctx;
74 iocb_data_t status_cb;
76 struct gpgme_io_cbs io_cbs;
78 /* User provided callbacks. */
80 gpgme_assuan_data_cb_t data_cb;
83 gpgme_assuan_inquire_cb_t inq_cb;
86 gpgme_assuan_status_cb_t status_cb;
87 void *status_cb_value;
91 typedef struct engine_g13 *engine_g13_t;
94 static void g13_io_event (void *engine,
95 gpgme_event_io_t type, void *type_data);
100 g13_get_version (const char *file_name)
102 return _gpgme_get_program_version (file_name ? file_name
103 : _gpgme_get_g13_path ());
108 g13_get_req_version (void)
110 return NEED_G13_VERSION;
115 close_notify_handler (int fd, void *opaque)
117 engine_g13_t g13 = opaque;
120 if (g13->status_cb.fd == fd)
122 if (g13->status_cb.tag)
123 (*g13->io_cbs.remove) (g13->status_cb.tag);
124 g13->status_cb.fd = -1;
125 g13->status_cb.tag = NULL;
130 /* This is the default inquiry callback. We use it to handle the
131 Pinentry notifications. */
133 default_inq_cb (engine_g13_t g13, const char *keyword, const char *args)
137 if (!strcmp (keyword, "PINENTRY_LAUNCHED"))
139 _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10));
142 if (g13->user.inq_cb)
144 gpgme_data_t data = NULL;
146 err = g13->user.inq_cb (g13->user.inq_cb_value,
147 keyword, args, &data);
150 /* FIXME: Returning data is not yet implemented. However we
151 need to allow the caller to cleanup his data object.
152 Thus we run the callback in finish mode immediately. */
153 err = g13->user.inq_cb (g13->user.inq_cb_value,
165 g13_cancel (void *engine)
167 engine_g13_t g13 = engine;
170 return gpg_error (GPG_ERR_INV_VALUE);
172 if (g13->status_cb.fd != -1)
173 _gpgme_io_close (g13->status_cb.fd);
177 assuan_release (g13->assuan_ctx);
178 g13->assuan_ctx = NULL;
186 g13_cancel_op (void *engine)
188 engine_g13_t g13 = engine;
191 return gpg_error (GPG_ERR_INV_VALUE);
193 if (g13->status_cb.fd != -1)
194 _gpgme_io_close (g13->status_cb.fd);
201 g13_release (void *engine)
203 engine_g13_t g13 = engine;
215 g13_new (void **engine, const char *file_name, const char *home_dir)
217 gpgme_error_t err = 0;
221 char *dft_display = NULL;
222 char dft_ttyname[64];
223 char *dft_ttytype = NULL;
226 g13 = calloc (1, sizeof *g13);
228 return gpg_error_from_errno (errno);
230 g13->status_cb.fd = -1;
231 g13->status_cb.dir = 1;
232 g13->status_cb.tag = 0;
233 g13->status_cb.data = g13;
236 argv[argc++] = "g13";
239 argv[argc++] = "--homedir";
240 argv[argc++] = home_dir;
242 argv[argc++] = "--server";
245 err = assuan_new_ext (&g13->assuan_ctx, GPG_ERR_SOURCE_GPGME,
246 &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
250 assuan_ctx_set_system_hooks (g13->assuan_ctx, &_gpgme_assuan_system_hooks);
252 #if USE_DESCRIPTOR_PASSING
253 err = assuan_pipe_connect
254 (g13->assuan_ctx, file_name ? file_name : _gpgme_get_g13_path (),
255 argv, NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
257 err = assuan_pipe_connect
258 (g13->assuan_ctx, file_name ? file_name : _gpgme_get_g13_path (),
259 argv, NULL, NULL, NULL, 0);
264 err = _gpgme_getenv ("DISPLAY", &dft_display);
269 if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
272 err = gpg_error_from_errno (errno);
277 err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
288 rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
291 err = gpg_error_from_errno (rc);
296 if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
298 err = gpg_error_from_errno (errno);
301 err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
307 err = _gpgme_getenv ("TERM", &dft_ttytype);
312 if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
315 err = gpg_error_from_errno (errno);
320 err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
321 NULL, NULL, NULL, NULL);
329 #ifdef HAVE_W32_SYSTEM
330 /* Under Windows we need to use AllowSetForegroundWindow. Tell
331 g13 to tell us when it needs it. */
334 err = assuan_transact (g13->assuan_ctx, "OPTION allow-pinentry-notify",
335 NULL, NULL, NULL, NULL, NULL, NULL);
336 if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
337 err = 0; /* This is a new feature of g13. */
339 #endif /*HAVE_W32_SYSTEM*/
353 g13_set_locale (void *engine, int category, const char *value)
355 engine_g13_t g13 = engine;
360 /* FIXME: If value is NULL, we need to reset the option to default.
361 But we can't do this. So we error out here. G13 needs support
366 else if (category == LC_CTYPE)
369 if (!value && g13->lc_ctype_set)
370 return gpg_error (GPG_ERR_INV_VALUE);
372 g13->lc_ctype_set = 1;
376 else if (category == LC_MESSAGES)
378 catstr = "lc-messages";
379 if (!value && g13->lc_messages_set)
380 return gpg_error (GPG_ERR_INV_VALUE);
382 g13->lc_messages_set = 1;
384 #endif /* LC_MESSAGES */
386 return gpg_error (GPG_ERR_INV_VALUE);
388 /* FIXME: Reset value to default. */
392 if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
393 err = gpg_error_from_errno (errno);
396 err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
397 NULL, NULL, NULL, NULL);
405 #if USE_DESCRIPTOR_PASSING
407 g13_assuan_simple_command (assuan_context_t ctx, char *cmd,
408 engine_status_handler_t status_fnc,
409 void *status_fnc_value)
415 err = assuan_write_line (ctx, cmd);
421 err = assuan_read_line (ctx, &line, &linelen);
425 if (*line == '#' || !linelen)
429 && line[0] == 'O' && line[1] == 'K'
430 && (line[2] == '\0' || line[2] == ' '))
432 else if (linelen >= 4
433 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
435 err = atoi (&line[4]);
436 else if (linelen >= 2
437 && line[0] == 'S' && line[1] == ' ')
441 rest = strchr (line + 2, ' ');
443 rest = line + linelen; /* set to an empty string */
447 /* Nothing to do with status lines. */
450 err = gpg_error (GPG_ERR_GENERAL);
460 status_handler (void *opaque, int fd)
462 struct io_cb_data *data = (struct io_cb_data *) opaque;
463 engine_g13_t g13 = (engine_g13_t) data->handler_value;
464 gpgme_error_t err = 0;
470 err = assuan_read_line (g13->assuan_ctx, &line, &linelen);
473 /* Try our best to terminate the connection friendly. */
474 /* assuan_write_line (g13->assuan_ctx, "BYE"); */
475 TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
476 "fd 0x%x: error reading assuan line: %s",
477 fd, gpg_strerror (err));
479 else if (linelen >= 3
480 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
481 && (line[3] == '\0' || line[3] == ' '))
484 err = atoi (&line[4]);
486 err = gpg_error (GPG_ERR_GENERAL);
487 TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
488 "fd 0x%x: ERR line: %s",
489 fd, err ? gpg_strerror (err) : "ok");
491 /* Command execution errors are not fatal, as we use
492 a session based protocol. */
495 /* The caller will do the rest (namely, call cancel_op,
496 which closes status_fd). */
499 else if (linelen >= 2
500 && line[0] == 'O' && line[1] == 'K'
501 && (line[2] == '\0' || line[2] == ' '))
503 TRACE1 (DEBUG_CTX, "gpgme:status_handler", g13,
504 "fd 0x%x: OK line", fd);
506 _gpgme_io_close (g13->status_cb.fd);
510 && line[0] == 'D' && line[1] == ' ')
512 /* We are using the colon handler even for plain inline data
513 - strange name for that function but for historic reasons
515 /* FIXME We can't use this for binary data because we
516 assume this is a string. For the current usage of colon
517 output it is correct. */
518 char *src = line + 2;
519 char *end = line + linelen;
525 if (*src == '%' && src + 2 < end)
527 /* Handle escaped characters. */
529 *dst++ = _gpgme_hextobyte (src);
539 if (linelen && g13->user.data_cb)
540 err = g13->user.data_cb (g13->user.data_cb_value,
545 TRACE2 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
546 "fd 0x%x: D inlinedata; status from cb: %s",
547 fd, (g13->user.data_cb ?
548 (err? gpg_strerror (err):"ok"):"no callback"));
552 && line[0] == 'S' && line[1] == ' ')
561 args = strchr (line + 2, ' ');
563 args = line + linelen; /* set to an empty string */
570 if (g13->user.status_cb)
571 err = g13->user.status_cb (g13->user.status_cb_value,
576 TRACE3 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
577 "fd 0x%x: S line (%s) - status from cb: %s",
578 fd, line+2, (g13->user.status_cb ?
579 (err? gpg_strerror (err):"ok"):"no callback"));
581 else if (linelen >= 7
582 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
583 && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
585 && (line[7] == '\0' || line[7] == ' '))
590 for (src=line+7; *src == ' '; src++)
593 args = strchr (src, ' ');
595 args = line + linelen; /* Let it point to an empty string. */
602 err = default_inq_cb (g13, src, args);
605 /* Flush and send END. */
606 err = assuan_send_data (g13->assuan_ctx, NULL, 0);
608 else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
610 /* Flush and send CANcel. */
611 err = assuan_send_data (g13->assuan_ctx, NULL, 1);
613 assuan_write_line (g13->assuan_ctx, "END");
616 while (!err && assuan_pending_line (g13->assuan_ctx));
623 add_io_cb (engine_g13_t g13, iocb_data_t *iocbd, gpgme_io_cb_t handler)
627 TRACE_BEG2 (DEBUG_ENGINE, "engine-g13:add_io_cb", g13,
628 "fd %d, dir %d", iocbd->fd, iocbd->dir);
629 err = (*g13->io_cbs.add) (g13->io_cbs.add_priv,
630 iocbd->fd, iocbd->dir,
631 handler, iocbd->data, &iocbd->tag);
633 return TRACE_ERR (err);
635 /* FIXME Kludge around poll() problem. */
636 err = _gpgme_io_set_nonblocking (iocbd->fd);
637 return TRACE_ERR (err);
642 start (engine_g13_t g13, const char *command)
645 assuan_fd_t afdlist[5];
650 /* We need to know the fd used by assuan for reads. We do this by
651 using the assumption that the first returned fd from
652 assuan_get_active_fds() is always this one. */
653 nfds = assuan_get_active_fds (g13->assuan_ctx, 0 /* read fds */,
654 afdlist, DIM (afdlist));
656 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
658 for (i = 0; i < nfds; i++)
659 fdlist[i] = (int) afdlist[i];
661 /* We "duplicate" the file descriptor, so we can close it here (we
662 can't close fdlist[0], as that is closed by libassuan, and
663 closing it here might cause libassuan to close some unrelated FD
664 later). Alternatively, we could special case status_fd and
665 register/unregister it manually as needed, but this increases
666 code duplication and is more complicated as we can not use the
667 close notifications etc. A third alternative would be to let
668 Assuan know that we closed the FD, but that complicates the
671 g13->status_cb.fd = _gpgme_io_dup (fdlist[0]);
672 if (g13->status_cb.fd < 0)
673 return gpg_error_from_syserror ();
675 if (_gpgme_io_set_close_notify (g13->status_cb.fd,
676 close_notify_handler, g13))
678 _gpgme_io_close (g13->status_cb.fd);
679 g13->status_cb.fd = -1;
680 return gpg_error (GPG_ERR_GENERAL);
683 err = add_io_cb (g13, &g13->status_cb, status_handler);
685 err = assuan_write_line (g13->assuan_ctx, command);
688 g13_io_event (g13, GPGME_EVENT_START, NULL);
694 #if USE_DESCRIPTOR_PASSING
696 g13_reset (void *engine)
698 engine_g13_t g13 = engine;
700 /* We must send a reset because we need to reset the list of
701 signers. Note that RESET does not reset OPTION commands. */
702 return g13_assuan_simple_command (g13->assuan_ctx, "RESET", NULL, NULL);
708 g13_transact (void *engine,
710 gpgme_assuan_data_cb_t data_cb,
712 gpgme_assuan_inquire_cb_t inq_cb,
714 gpgme_assuan_status_cb_t status_cb,
715 void *status_cb_value)
717 engine_g13_t g13 = engine;
720 if (!g13 || !command || !*command)
721 return gpg_error (GPG_ERR_INV_VALUE);
723 g13->user.data_cb = data_cb;
724 g13->user.data_cb_value = data_cb_value;
725 g13->user.inq_cb = inq_cb;
726 g13->user.inq_cb_value = inq_cb_value;
727 g13->user.status_cb = status_cb;
728 g13->user.status_cb_value = status_cb_value;
730 err = start (g13, command);
737 g13_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
739 engine_g13_t g13 = engine;
740 g13->io_cbs = *io_cbs;
745 g13_io_event (void *engine, gpgme_event_io_t type, void *type_data)
747 engine_g13_t g13 = engine;
749 TRACE3 (DEBUG_ENGINE, "gpgme:g13_io_event", g13,
750 "event %p, type %d, type_data %p",
751 g13->io_cbs.event, type, type_data);
752 if (g13->io_cbs.event)
753 (*g13->io_cbs.event) (g13->io_cbs.event_priv, type, type_data);
757 struct engine_ops _gpgme_engine_ops_g13 =
759 /* Static functions. */
766 /* Member functions. */
768 #if USE_DESCRIPTOR_PASSING
773 NULL, /* set_status_handler */
774 NULL, /* set_command_handler */
775 NULL, /* set_colon_line_handler */
777 NULL, /* set_protocol */
779 NULL, /* decrypt_verify */
783 NULL, /* encrypt_sign */
785 NULL, /* export_ext */
789 NULL, /* keylist_ext */
791 NULL, /* trustlist */
793 NULL, /* getauditlog */
795 NULL, /* conf_load */
796 NULL, /* conf_save */