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>
32 #include <fcntl.h> /* FIXME */
45 #include "engine-backend.h"
50 int fd; /* FD we talk about. */
51 int server_fd;/* Server FD for this connection. */
52 int dir; /* Inbound/Outbound, maybe given implicit? */
53 void *data; /* Handler-specific data. */
54 void *tag; /* ID from the user for gpgme_remove_io_callback. */
55 char server_fd_str[15]; /* Same as SERVER_FD but as a string. We
56 need this because _gpgme_io_fd2str can't
57 be used on a closed descriptor. */
63 assuan_context_t assuan_ctx;
68 iocb_data_t status_cb;
70 struct gpgme_io_cbs io_cbs;
72 /* User provided callbacks. */
74 gpgme_assuan_data_cb_t data_cb;
77 gpgme_assuan_inquire_cb_t inq_cb;
80 gpgme_assuan_status_cb_t status_cb;
81 void *status_cb_value;
85 typedef struct engine_g13 *engine_g13_t;
88 static void g13_io_event (void *engine,
89 gpgme_event_io_t type, void *type_data);
94 g13_get_version (const char *file_name)
96 return _gpgme_get_program_version (file_name ? file_name
97 : _gpgme_get_g13_path ());
102 g13_get_req_version (void)
104 return NEED_G13_VERSION;
109 close_notify_handler (int fd, void *opaque)
111 engine_g13_t g13 = opaque;
114 if (g13->status_cb.fd == fd)
116 if (g13->status_cb.tag)
117 (*g13->io_cbs.remove) (g13->status_cb.tag);
118 g13->status_cb.fd = -1;
119 g13->status_cb.tag = NULL;
124 /* This is the default inquiry callback. We use it to handle the
125 Pinentry notifications. */
127 default_inq_cb (engine_g13_t g13, const char *keyword, const char *args)
131 if (!strcmp (keyword, "PINENTRY_LAUNCHED"))
133 _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10));
136 if (g13->user.inq_cb)
138 gpgme_data_t data = NULL;
140 err = g13->user.inq_cb (g13->user.inq_cb_value,
141 keyword, args, &data);
144 /* FIXME: Returning data is not yet implemented. However we
145 need to allow the caller to cleanup his data object.
146 Thus we run the callback in finish mode immediately. */
147 err = g13->user.inq_cb (g13->user.inq_cb_value,
159 g13_cancel (void *engine)
161 engine_g13_t g13 = engine;
164 return gpg_error (GPG_ERR_INV_VALUE);
166 if (g13->status_cb.fd != -1)
167 _gpgme_io_close (g13->status_cb.fd);
171 assuan_release (g13->assuan_ctx);
172 g13->assuan_ctx = NULL;
180 g13_cancel_op (void *engine)
182 engine_g13_t g13 = engine;
185 return gpg_error (GPG_ERR_INV_VALUE);
187 if (g13->status_cb.fd != -1)
188 _gpgme_io_close (g13->status_cb.fd);
195 g13_release (void *engine)
197 engine_g13_t g13 = engine;
209 g13_new (void **engine, const char *file_name, const char *home_dir)
211 gpgme_error_t err = 0;
215 char *dft_display = NULL;
216 char dft_ttyname[64];
217 char *dft_ttytype = NULL;
220 g13 = calloc (1, sizeof *g13);
222 return gpg_error_from_errno (errno);
224 g13->status_cb.fd = -1;
225 g13->status_cb.dir = 1;
226 g13->status_cb.tag = 0;
227 g13->status_cb.data = g13;
230 argv[argc++] = "g13";
233 argv[argc++] = "--homedir";
234 argv[argc++] = home_dir;
236 argv[argc++] = "--server";
239 err = assuan_new_ext (&g13->assuan_ctx, GPG_ERR_SOURCE_GPGME,
240 &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
244 assuan_ctx_set_system_hooks (g13->assuan_ctx, &_gpgme_assuan_system_hooks);
246 #if USE_DESCRIPTOR_PASSING
247 err = assuan_pipe_connect
248 (g13->assuan_ctx, file_name ? file_name : _gpgme_get_g13_path (),
249 argv, NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
251 err = assuan_pipe_connect
252 (g13->assuan_ctx, file_name ? file_name : _gpgme_get_g13_path (),
253 argv, NULL, NULL, NULL, 0);
258 err = _gpgme_getenv ("DISPLAY", &dft_display);
263 if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
266 err = gpg_error_from_errno (errno);
271 err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
282 rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
285 err = gpg_error_from_errno (rc);
290 if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
292 err = gpg_error_from_errno (errno);
295 err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
301 err = _gpgme_getenv ("TERM", &dft_ttytype);
306 if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
309 err = gpg_error_from_errno (errno);
314 err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
315 NULL, NULL, NULL, NULL);
323 #ifdef HAVE_W32_SYSTEM
324 /* Under Windows we need to use AllowSetForegroundWindow. Tell
325 g13 to tell us when it needs it. */
328 err = assuan_transact (g13->assuan_ctx, "OPTION allow-pinentry-notify",
329 NULL, NULL, NULL, NULL, NULL, NULL);
330 if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
331 err = 0; /* This is a new feature of g13. */
333 #endif /*HAVE_W32_SYSTEM*/
347 g13_set_locale (void *engine, int category, const char *value)
349 engine_g13_t g13 = engine;
354 /* FIXME: If value is NULL, we need to reset the option to default.
355 But we can't do this. So we error out here. G13 needs support
357 if (category == LC_CTYPE)
360 if (!value && g13->lc_ctype_set)
361 return gpg_error (GPG_ERR_INV_VALUE);
363 g13->lc_ctype_set = 1;
366 else if (category == LC_MESSAGES)
368 catstr = "lc-messages";
369 if (!value && g13->lc_messages_set)
370 return gpg_error (GPG_ERR_INV_VALUE);
372 g13->lc_messages_set = 1;
374 #endif /* LC_MESSAGES */
376 return gpg_error (GPG_ERR_INV_VALUE);
378 /* FIXME: Reset value to default. */
382 if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
383 err = gpg_error_from_errno (errno);
386 err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
387 NULL, NULL, NULL, NULL);
395 #if USE_DESCRIPTOR_PASSING
397 g13_assuan_simple_command (assuan_context_t ctx, char *cmd,
398 engine_status_handler_t status_fnc,
399 void *status_fnc_value)
405 err = assuan_write_line (ctx, cmd);
411 err = assuan_read_line (ctx, &line, &linelen);
415 if (*line == '#' || !linelen)
419 && line[0] == 'O' && line[1] == 'K'
420 && (line[2] == '\0' || line[2] == ' '))
422 else if (linelen >= 4
423 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
425 err = atoi (&line[4]);
426 else if (linelen >= 2
427 && line[0] == 'S' && line[1] == ' ')
431 rest = strchr (line + 2, ' ');
433 rest = line + linelen; /* set to an empty string */
437 /* Nothing to do with status lines. */
440 err = gpg_error (GPG_ERR_GENERAL);
450 status_handler (void *opaque, int fd)
452 struct io_cb_data *data = (struct io_cb_data *) opaque;
453 engine_g13_t g13 = (engine_g13_t) data->handler_value;
454 gpgme_error_t err = 0;
460 err = assuan_read_line (g13->assuan_ctx, &line, &linelen);
463 /* Try our best to terminate the connection friendly. */
464 /* assuan_write_line (g13->assuan_ctx, "BYE"); */
465 TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
466 "fd 0x%x: error reading assuan line: %s",
467 fd, gpg_strerror (err));
469 else if (linelen >= 3
470 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
471 && (line[3] == '\0' || line[3] == ' '))
474 err = atoi (&line[4]);
476 err = gpg_error (GPG_ERR_GENERAL);
477 TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
478 "fd 0x%x: ERR line: %s",
479 fd, err ? gpg_strerror (err) : "ok");
481 /* Command execution errors are not fatal, as we use
482 a session based protocol. */
485 /* The caller will do the rest (namely, call cancel_op,
486 which closes status_fd). */
489 else if (linelen >= 2
490 && line[0] == 'O' && line[1] == 'K'
491 && (line[2] == '\0' || line[2] == ' '))
493 TRACE1 (DEBUG_CTX, "gpgme:status_handler", g13,
494 "fd 0x%x: OK line", fd);
496 _gpgme_io_close (g13->status_cb.fd);
500 && line[0] == 'D' && line[1] == ' ')
502 /* We are using the colon handler even for plain inline data
503 - strange name for that function but for historic reasons
505 /* FIXME We can't use this for binary data because we
506 assume this is a string. For the current usage of colon
507 output it is correct. */
508 char *src = line + 2;
509 char *end = line + linelen;
515 if (*src == '%' && src + 2 < end)
517 /* Handle escaped characters. */
519 *dst++ = _gpgme_hextobyte (src);
529 if (linelen && g13->user.data_cb)
530 err = g13->user.data_cb (g13->user.data_cb_value,
535 TRACE2 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
536 "fd 0x%x: D inlinedata; status from cb: %s",
537 fd, (g13->user.data_cb ?
538 (err? gpg_strerror (err):"ok"):"no callback"));
542 && line[0] == 'S' && line[1] == ' ')
551 args = strchr (line + 2, ' ');
553 args = line + linelen; /* set to an empty string */
560 if (g13->user.status_cb)
561 err = g13->user.status_cb (g13->user.status_cb_value,
566 TRACE3 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
567 "fd 0x%x: S line (%s) - status from cb: %s",
568 fd, line+2, (g13->user.status_cb ?
569 (err? gpg_strerror (err):"ok"):"no callback"));
571 else if (linelen >= 7
572 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
573 && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
575 && (line[7] == '\0' || line[7] == ' '))
580 for (src=line+7; *src == ' '; src++)
583 args = strchr (src, ' ');
585 args = line + linelen; /* Let it point to an empty string. */
592 err = default_inq_cb (g13, src, args);
595 /* Flush and send END. */
596 err = assuan_send_data (g13->assuan_ctx, NULL, 0);
598 else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
600 /* Flush and send CANcel. */
601 err = assuan_send_data (g13->assuan_ctx, NULL, 1);
603 assuan_write_line (g13->assuan_ctx, "END");
606 while (!err && assuan_pending_line (g13->assuan_ctx));
613 add_io_cb (engine_g13_t g13, iocb_data_t *iocbd, gpgme_io_cb_t handler)
617 TRACE_BEG2 (DEBUG_ENGINE, "engine-g13:add_io_cb", g13,
618 "fd %d, dir %d", iocbd->fd, iocbd->dir);
619 err = (*g13->io_cbs.add) (g13->io_cbs.add_priv,
620 iocbd->fd, iocbd->dir,
621 handler, iocbd->data, &iocbd->tag);
623 return TRACE_ERR (err);
625 /* FIXME Kludge around poll() problem. */
626 err = _gpgme_io_set_nonblocking (iocbd->fd);
627 return TRACE_ERR (err);
632 start (engine_g13_t g13, const char *command)
638 /* We need to know the fd used by assuan for reads. We do this by
639 using the assumption that the first returned fd from
640 assuan_get_active_fds() is always this one. */
641 nfds = assuan_get_active_fds (g13->assuan_ctx, 0 /* read fds */,
642 fdlist, DIM (fdlist));
644 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
646 /* We "duplicate" the file descriptor, so we can close it here (we
647 can't close fdlist[0], as that is closed by libassuan, and
648 closing it here might cause libassuan to close some unrelated FD
649 later). Alternatively, we could special case status_fd and
650 register/unregister it manually as needed, but this increases
651 code duplication and is more complicated as we can not use the
652 close notifications etc. A third alternative would be to let
653 Assuan know that we closed the FD, but that complicates the
656 g13->status_cb.fd = _gpgme_io_dup (fdlist[0]);
657 if (g13->status_cb.fd < 0)
658 return gpg_error_from_syserror ();
660 if (_gpgme_io_set_close_notify (g13->status_cb.fd,
661 close_notify_handler, g13))
663 _gpgme_io_close (g13->status_cb.fd);
664 g13->status_cb.fd = -1;
665 return gpg_error (GPG_ERR_GENERAL);
668 err = add_io_cb (g13, &g13->status_cb, status_handler);
670 err = assuan_write_line (g13->assuan_ctx, command);
673 g13_io_event (g13, GPGME_EVENT_START, NULL);
679 #if USE_DESCRIPTOR_PASSING
681 g13_reset (void *engine)
683 engine_g13_t g13 = engine;
685 /* We must send a reset because we need to reset the list of
686 signers. Note that RESET does not reset OPTION commands. */
687 return g13_assuan_simple_command (g13->assuan_ctx, "RESET", NULL, NULL);
693 g13_transact (void *engine,
695 gpgme_assuan_data_cb_t data_cb,
697 gpgme_assuan_inquire_cb_t inq_cb,
699 gpgme_assuan_status_cb_t status_cb,
700 void *status_cb_value)
702 engine_g13_t g13 = engine;
705 if (!g13 || !command || !*command)
706 return gpg_error (GPG_ERR_INV_VALUE);
708 g13->user.data_cb = data_cb;
709 g13->user.data_cb_value = data_cb_value;
710 g13->user.inq_cb = inq_cb;
711 g13->user.inq_cb_value = inq_cb_value;
712 g13->user.status_cb = status_cb;
713 g13->user.status_cb_value = status_cb_value;
715 err = start (g13, command);
722 g13_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
724 engine_g13_t g13 = engine;
725 g13->io_cbs = *io_cbs;
730 g13_io_event (void *engine, gpgme_event_io_t type, void *type_data)
732 engine_g13_t g13 = engine;
734 TRACE3 (DEBUG_ENGINE, "gpgme:g13_io_event", g13,
735 "event %p, type %d, type_data %p",
736 g13->io_cbs.event, type, type_data);
737 if (g13->io_cbs.event)
738 (*g13->io_cbs.event) (g13->io_cbs.event_priv, type, type_data);
742 struct engine_ops _gpgme_engine_ops_g13 =
744 /* Static functions. */
751 /* Member functions. */
753 #if USE_DESCRIPTOR_PASSING
758 NULL, /* set_status_handler */
759 NULL, /* set_command_handler */
760 NULL, /* set_colon_line_handler */
762 NULL, /* set_protocol */
764 NULL, /* decrypt_verify */
768 NULL, /* encrypt_sign */
770 NULL, /* export_ext */
774 NULL, /* keylist_ext */
776 NULL, /* trustlist */
778 NULL, /* getauditlog */
780 NULL, /* conf_load */
781 NULL, /* conf_save */