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>
36 #include <fcntl.h> /* FIXME */
49 #include "engine-backend.h"
54 int fd; /* FD we talk about. */
55 int server_fd;/* Server FD for this connection. */
56 int dir; /* Inbound/Outbound, maybe given implicit? */
57 void *data; /* Handler-specific data. */
58 void *tag; /* ID from the user for gpgme_remove_io_callback. */
59 char server_fd_str[15]; /* Same as SERVER_FD but as a string. We
60 need this because _gpgme_io_fd2str can't
61 be used on a closed descriptor. */
67 assuan_context_t assuan_ctx;
72 iocb_data_t status_cb;
74 struct gpgme_io_cbs io_cbs;
76 /* User provided callbacks. */
78 gpgme_assuan_data_cb_t data_cb;
81 gpgme_assuan_inquire_cb_t inq_cb;
84 gpgme_assuan_status_cb_t status_cb;
85 void *status_cb_value;
89 typedef struct engine_g13 *engine_g13_t;
92 static void g13_io_event (void *engine,
93 gpgme_event_io_t type, void *type_data);
98 g13_get_version (const char *file_name)
100 return _gpgme_get_program_version (file_name ? file_name
101 : _gpgme_get_g13_path ());
106 g13_get_req_version (void)
108 return NEED_G13_VERSION;
113 close_notify_handler (int fd, void *opaque)
115 engine_g13_t g13 = opaque;
118 if (g13->status_cb.fd == fd)
120 if (g13->status_cb.tag)
121 (*g13->io_cbs.remove) (g13->status_cb.tag);
122 g13->status_cb.fd = -1;
123 g13->status_cb.tag = NULL;
128 /* This is the default inquiry callback. We use it to handle the
129 Pinentry notifications. */
131 default_inq_cb (engine_g13_t g13, const char *keyword, const char *args)
135 if (!strcmp (keyword, "PINENTRY_LAUNCHED"))
137 _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10));
140 if (g13->user.inq_cb)
142 gpgme_data_t data = NULL;
144 err = g13->user.inq_cb (g13->user.inq_cb_value,
145 keyword, args, &data);
148 /* FIXME: Returning data is not yet implemented. However we
149 need to allow the caller to cleanup his data object.
150 Thus we run the callback in finish mode immediately. */
151 err = g13->user.inq_cb (g13->user.inq_cb_value,
163 g13_cancel (void *engine)
165 engine_g13_t g13 = engine;
168 return gpg_error (GPG_ERR_INV_VALUE);
170 if (g13->status_cb.fd != -1)
171 _gpgme_io_close (g13->status_cb.fd);
175 assuan_release (g13->assuan_ctx);
176 g13->assuan_ctx = NULL;
184 g13_cancel_op (void *engine)
186 engine_g13_t g13 = engine;
189 return gpg_error (GPG_ERR_INV_VALUE);
191 if (g13->status_cb.fd != -1)
192 _gpgme_io_close (g13->status_cb.fd);
199 g13_release (void *engine)
201 engine_g13_t g13 = engine;
213 g13_new (void **engine, const char *file_name, const char *home_dir)
215 gpgme_error_t err = 0;
219 char *dft_display = NULL;
220 char dft_ttyname[64];
221 char *dft_ttytype = NULL;
224 g13 = calloc (1, sizeof *g13);
226 return gpg_error_from_errno (errno);
228 g13->status_cb.fd = -1;
229 g13->status_cb.dir = 1;
230 g13->status_cb.tag = 0;
231 g13->status_cb.data = g13;
234 argv[argc++] = "g13";
237 argv[argc++] = "--homedir";
238 argv[argc++] = home_dir;
240 argv[argc++] = "--server";
243 err = assuan_new_ext (&g13->assuan_ctx, GPG_ERR_SOURCE_GPGME,
244 &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
248 assuan_ctx_set_system_hooks (g13->assuan_ctx, &_gpgme_assuan_system_hooks);
250 #if USE_DESCRIPTOR_PASSING
251 err = assuan_pipe_connect
252 (g13->assuan_ctx, file_name ? file_name : _gpgme_get_g13_path (),
253 argv, NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
255 err = assuan_pipe_connect
256 (g13->assuan_ctx, file_name ? file_name : _gpgme_get_g13_path (),
257 argv, NULL, NULL, NULL, 0);
262 err = _gpgme_getenv ("DISPLAY", &dft_display);
267 if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
270 err = gpg_error_from_errno (errno);
275 err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
286 rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
289 err = gpg_error_from_errno (rc);
294 if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
296 err = gpg_error_from_errno (errno);
299 err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
305 err = _gpgme_getenv ("TERM", &dft_ttytype);
310 if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
313 err = gpg_error_from_errno (errno);
318 err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
319 NULL, NULL, NULL, NULL);
327 #ifdef HAVE_W32_SYSTEM
328 /* Under Windows we need to use AllowSetForegroundWindow. Tell
329 g13 to tell us when it needs it. */
332 err = assuan_transact (g13->assuan_ctx, "OPTION allow-pinentry-notify",
333 NULL, NULL, NULL, NULL, NULL, NULL);
334 if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
335 err = 0; /* This is a new feature of g13. */
337 #endif /*HAVE_W32_SYSTEM*/
351 g13_set_locale (void *engine, int category, const char *value)
353 engine_g13_t g13 = engine;
358 /* FIXME: If value is NULL, we need to reset the option to default.
359 But we can't do this. So we error out here. G13 needs support
364 else if (category == LC_CTYPE)
367 if (!value && g13->lc_ctype_set)
368 return gpg_error (GPG_ERR_INV_VALUE);
370 g13->lc_ctype_set = 1;
374 else if (category == LC_MESSAGES)
376 catstr = "lc-messages";
377 if (!value && g13->lc_messages_set)
378 return gpg_error (GPG_ERR_INV_VALUE);
380 g13->lc_messages_set = 1;
382 #endif /* LC_MESSAGES */
384 return gpg_error (GPG_ERR_INV_VALUE);
386 /* FIXME: Reset value to default. */
390 if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
391 err = gpg_error_from_errno (errno);
394 err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
395 NULL, NULL, NULL, NULL);
403 #if USE_DESCRIPTOR_PASSING
405 g13_assuan_simple_command (assuan_context_t ctx, char *cmd,
406 engine_status_handler_t status_fnc,
407 void *status_fnc_value)
413 err = assuan_write_line (ctx, cmd);
419 err = assuan_read_line (ctx, &line, &linelen);
423 if (*line == '#' || !linelen)
427 && line[0] == 'O' && line[1] == 'K'
428 && (line[2] == '\0' || line[2] == ' '))
430 else if (linelen >= 4
431 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
433 err = atoi (&line[4]);
434 else if (linelen >= 2
435 && line[0] == 'S' && line[1] == ' ')
439 rest = strchr (line + 2, ' ');
441 rest = line + linelen; /* set to an empty string */
445 /* Nothing to do with status lines. */
448 err = gpg_error (GPG_ERR_GENERAL);
458 status_handler (void *opaque, int fd)
460 struct io_cb_data *data = (struct io_cb_data *) opaque;
461 engine_g13_t g13 = (engine_g13_t) data->handler_value;
462 gpgme_error_t err = 0;
468 err = assuan_read_line (g13->assuan_ctx, &line, &linelen);
471 /* Try our best to terminate the connection friendly. */
472 /* assuan_write_line (g13->assuan_ctx, "BYE"); */
473 TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
474 "fd 0x%x: error reading assuan line: %s",
475 fd, gpg_strerror (err));
477 else if (linelen >= 3
478 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
479 && (line[3] == '\0' || line[3] == ' '))
482 err = atoi (&line[4]);
484 err = gpg_error (GPG_ERR_GENERAL);
485 TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
486 "fd 0x%x: ERR line: %s",
487 fd, err ? gpg_strerror (err) : "ok");
489 /* Command execution errors are not fatal, as we use
490 a session based protocol. */
493 /* The caller will do the rest (namely, call cancel_op,
494 which closes status_fd). */
497 else if (linelen >= 2
498 && line[0] == 'O' && line[1] == 'K'
499 && (line[2] == '\0' || line[2] == ' '))
501 TRACE1 (DEBUG_CTX, "gpgme:status_handler", g13,
502 "fd 0x%x: OK line", fd);
504 _gpgme_io_close (g13->status_cb.fd);
508 && line[0] == 'D' && line[1] == ' ')
510 /* We are using the colon handler even for plain inline data
511 - strange name for that function but for historic reasons
513 /* FIXME We can't use this for binary data because we
514 assume this is a string. For the current usage of colon
515 output it is correct. */
516 char *src = line + 2;
517 char *end = line + linelen;
523 if (*src == '%' && src + 2 < end)
525 /* Handle escaped characters. */
527 *dst++ = _gpgme_hextobyte (src);
537 if (linelen && g13->user.data_cb)
538 err = g13->user.data_cb (g13->user.data_cb_value,
543 TRACE2 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
544 "fd 0x%x: D inlinedata; status from cb: %s",
545 fd, (g13->user.data_cb ?
546 (err? gpg_strerror (err):"ok"):"no callback"));
550 && line[0] == 'S' && line[1] == ' ')
559 args = strchr (line + 2, ' ');
561 args = line + linelen; /* set to an empty string */
568 if (g13->user.status_cb)
569 err = g13->user.status_cb (g13->user.status_cb_value,
574 TRACE3 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
575 "fd 0x%x: S line (%s) - status from cb: %s",
576 fd, line+2, (g13->user.status_cb ?
577 (err? gpg_strerror (err):"ok"):"no callback"));
579 else if (linelen >= 7
580 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
581 && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
583 && (line[7] == '\0' || line[7] == ' '))
588 for (src=line+7; *src == ' '; src++)
591 args = strchr (src, ' ');
593 args = line + linelen; /* Let it point to an empty string. */
600 err = default_inq_cb (g13, src, args);
603 /* Flush and send END. */
604 err = assuan_send_data (g13->assuan_ctx, NULL, 0);
606 else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
608 /* Flush and send CANcel. */
609 err = assuan_send_data (g13->assuan_ctx, NULL, 1);
611 assuan_write_line (g13->assuan_ctx, "END");
614 while (!err && assuan_pending_line (g13->assuan_ctx));
621 add_io_cb (engine_g13_t g13, iocb_data_t *iocbd, gpgme_io_cb_t handler)
625 TRACE_BEG2 (DEBUG_ENGINE, "engine-g13:add_io_cb", g13,
626 "fd %d, dir %d", iocbd->fd, iocbd->dir);
627 err = (*g13->io_cbs.add) (g13->io_cbs.add_priv,
628 iocbd->fd, iocbd->dir,
629 handler, iocbd->data, &iocbd->tag);
631 return TRACE_ERR (err);
633 /* FIXME Kludge around poll() problem. */
634 err = _gpgme_io_set_nonblocking (iocbd->fd);
635 return TRACE_ERR (err);
640 start (engine_g13_t g13, const char *command)
643 assuan_fd_t afdlist[5];
648 /* We need to know the fd used by assuan for reads. We do this by
649 using the assumption that the first returned fd from
650 assuan_get_active_fds() is always this one. */
651 nfds = assuan_get_active_fds (g13->assuan_ctx, 0 /* read fds */,
652 afdlist, DIM (afdlist));
654 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
656 for (i = 0; i < nfds; i++)
657 fdlist[i] = (int) afdlist[i];
659 /* We "duplicate" the file descriptor, so we can close it here (we
660 can't close fdlist[0], as that is closed by libassuan, and
661 closing it here might cause libassuan to close some unrelated FD
662 later). Alternatively, we could special case status_fd and
663 register/unregister it manually as needed, but this increases
664 code duplication and is more complicated as we can not use the
665 close notifications etc. A third alternative would be to let
666 Assuan know that we closed the FD, but that complicates the
669 g13->status_cb.fd = _gpgme_io_dup (fdlist[0]);
670 if (g13->status_cb.fd < 0)
671 return gpg_error_from_syserror ();
673 if (_gpgme_io_set_close_notify (g13->status_cb.fd,
674 close_notify_handler, g13))
676 _gpgme_io_close (g13->status_cb.fd);
677 g13->status_cb.fd = -1;
678 return gpg_error (GPG_ERR_GENERAL);
681 err = add_io_cb (g13, &g13->status_cb, status_handler);
683 err = assuan_write_line (g13->assuan_ctx, command);
686 g13_io_event (g13, GPGME_EVENT_START, NULL);
692 #if USE_DESCRIPTOR_PASSING
694 g13_reset (void *engine)
696 engine_g13_t g13 = engine;
698 /* We must send a reset because we need to reset the list of
699 signers. Note that RESET does not reset OPTION commands. */
700 return g13_assuan_simple_command (g13->assuan_ctx, "RESET", NULL, NULL);
706 g13_transact (void *engine,
708 gpgme_assuan_data_cb_t data_cb,
710 gpgme_assuan_inquire_cb_t inq_cb,
712 gpgme_assuan_status_cb_t status_cb,
713 void *status_cb_value)
715 engine_g13_t g13 = engine;
718 if (!g13 || !command || !*command)
719 return gpg_error (GPG_ERR_INV_VALUE);
721 g13->user.data_cb = data_cb;
722 g13->user.data_cb_value = data_cb_value;
723 g13->user.inq_cb = inq_cb;
724 g13->user.inq_cb_value = inq_cb_value;
725 g13->user.status_cb = status_cb;
726 g13->user.status_cb_value = status_cb_value;
728 err = start (g13, command);
735 g13_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
737 engine_g13_t g13 = engine;
738 g13->io_cbs = *io_cbs;
743 g13_io_event (void *engine, gpgme_event_io_t type, void *type_data)
745 engine_g13_t g13 = engine;
747 TRACE3 (DEBUG_ENGINE, "gpgme:g13_io_event", g13,
748 "event %p, type %d, type_data %p",
749 g13->io_cbs.event, type, type_data);
750 if (g13->io_cbs.event)
751 (*g13->io_cbs.event) (g13->io_cbs.event_priv, type, type_data);
755 struct engine_ops _gpgme_engine_ops_g13 =
757 /* Static functions. */
764 /* Member functions. */
766 #if USE_DESCRIPTOR_PASSING
771 NULL, /* set_status_handler */
772 NULL, /* set_command_handler */
773 NULL, /* set_colon_line_handler */
775 NULL, /* set_protocol */
777 NULL, /* decrypt_verify */
781 NULL, /* encrypt_sign */
783 NULL, /* export_ext */
787 NULL, /* keylist_ext */
789 NULL, /* trustlist */
791 NULL, /* getauditlog */
793 NULL, /* conf_load */
794 NULL, /* conf_save */