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);
396 g13_assuan_simple_command (assuan_context_t ctx, char *cmd,
397 engine_status_handler_t status_fnc,
398 void *status_fnc_value)
404 err = assuan_write_line (ctx, cmd);
410 err = assuan_read_line (ctx, &line, &linelen);
414 if (*line == '#' || !linelen)
418 && line[0] == 'O' && line[1] == 'K'
419 && (line[2] == '\0' || line[2] == ' '))
421 else if (linelen >= 4
422 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
424 err = atoi (&line[4]);
425 else if (linelen >= 2
426 && line[0] == 'S' && line[1] == ' ')
430 rest = strchr (line + 2, ' ');
432 rest = line + linelen; /* set to an empty string */
436 /* Nothing to do with status lines. */
439 err = gpg_error (GPG_ERR_GENERAL);
448 status_handler (void *opaque, int fd)
450 struct io_cb_data *data = (struct io_cb_data *) opaque;
451 engine_g13_t g13 = (engine_g13_t) data->handler_value;
452 gpgme_error_t err = 0;
458 err = assuan_read_line (g13->assuan_ctx, &line, &linelen);
461 /* Try our best to terminate the connection friendly. */
462 /* assuan_write_line (g13->assuan_ctx, "BYE"); */
463 TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
464 "fd 0x%x: error reading assuan line: %s",
465 fd, gpg_strerror (err));
467 else if (linelen >= 3
468 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
469 && (line[3] == '\0' || line[3] == ' '))
472 err = atoi (&line[4]);
474 err = gpg_error (GPG_ERR_GENERAL);
475 TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
476 "fd 0x%x: ERR line: %s",
477 fd, err ? gpg_strerror (err) : "ok");
479 /* Command execution errors are not fatal, as we use
480 a session based protocol. */
483 /* The caller will do the rest (namely, call cancel_op,
484 which closes status_fd). */
487 else if (linelen >= 2
488 && line[0] == 'O' && line[1] == 'K'
489 && (line[2] == '\0' || line[2] == ' '))
491 TRACE1 (DEBUG_CTX, "gpgme:status_handler", g13,
492 "fd 0x%x: OK line", fd);
494 _gpgme_io_close (g13->status_cb.fd);
498 && line[0] == 'D' && line[1] == ' ')
500 /* We are using the colon handler even for plain inline data
501 - strange name for that function but for historic reasons
503 /* FIXME We can't use this for binary data because we
504 assume this is a string. For the current usage of colon
505 output it is correct. */
506 char *src = line + 2;
507 char *end = line + linelen;
513 if (*src == '%' && src + 2 < end)
515 /* Handle escaped characters. */
517 *dst++ = _gpgme_hextobyte (src);
527 if (linelen && g13->user.data_cb)
528 err = g13->user.data_cb (g13->user.data_cb_value,
533 TRACE2 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
534 "fd 0x%x: D inlinedata; status from cb: %s",
535 fd, (g13->user.data_cb ?
536 (err? gpg_strerror (err):"ok"):"no callback"));
540 && line[0] == 'S' && line[1] == ' ')
549 args = strchr (line + 2, ' ');
551 args = line + linelen; /* set to an empty string */
558 if (g13->user.status_cb)
559 err = g13->user.status_cb (g13->user.status_cb_value,
564 TRACE3 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
565 "fd 0x%x: S line (%s) - status from cb: %s",
566 fd, line+2, (g13->user.status_cb ?
567 (err? gpg_strerror (err):"ok"):"no callback"));
569 else if (linelen >= 7
570 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
571 && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
573 && (line[7] == '\0' || line[7] == ' '))
578 for (src=line+7; *src == ' '; src++)
581 args = strchr (src, ' ');
583 args = line + linelen; /* Let it point to an empty string. */
590 err = default_inq_cb (g13, src, args);
593 /* Flush and send END. */
594 err = assuan_send_data (g13->assuan_ctx, NULL, 0);
596 else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
598 /* Flush and send CANcel. */
599 err = assuan_send_data (g13->assuan_ctx, NULL, 1);
601 assuan_write_line (g13->assuan_ctx, "END");
604 while (!err && assuan_pending_line (g13->assuan_ctx));
611 add_io_cb (engine_g13_t g13, iocb_data_t *iocbd, gpgme_io_cb_t handler)
615 TRACE_BEG2 (DEBUG_ENGINE, "engine-g13:add_io_cb", g13,
616 "fd %d, dir %d", iocbd->fd, iocbd->dir);
617 err = (*g13->io_cbs.add) (g13->io_cbs.add_priv,
618 iocbd->fd, iocbd->dir,
619 handler, iocbd->data, &iocbd->tag);
621 return TRACE_ERR (err);
623 /* FIXME Kludge around poll() problem. */
624 err = _gpgme_io_set_nonblocking (iocbd->fd);
625 return TRACE_ERR (err);
630 start (engine_g13_t g13, const char *command)
636 /* We need to know the fd used by assuan for reads. We do this by
637 using the assumption that the first returned fd from
638 assuan_get_active_fds() is always this one. */
639 nfds = assuan_get_active_fds (g13->assuan_ctx, 0 /* read fds */,
640 fdlist, DIM (fdlist));
642 return gpg_error (GPG_ERR_GENERAL); /* FIXME */
644 /* We "duplicate" the file descriptor, so we can close it here (we
645 can't close fdlist[0], as that is closed by libassuan, and
646 closing it here might cause libassuan to close some unrelated FD
647 later). Alternatively, we could special case status_fd and
648 register/unregister it manually as needed, but this increases
649 code duplication and is more complicated as we can not use the
650 close notifications etc. A third alternative would be to let
651 Assuan know that we closed the FD, but that complicates the
654 g13->status_cb.fd = _gpgme_io_dup (fdlist[0]);
655 if (g13->status_cb.fd < 0)
656 return gpg_error_from_syserror ();
658 if (_gpgme_io_set_close_notify (g13->status_cb.fd,
659 close_notify_handler, g13))
661 _gpgme_io_close (g13->status_cb.fd);
662 g13->status_cb.fd = -1;
663 return gpg_error (GPG_ERR_GENERAL);
666 err = add_io_cb (g13, &g13->status_cb, status_handler);
668 err = assuan_write_line (g13->assuan_ctx, command);
671 g13_io_event (g13, GPGME_EVENT_START, NULL);
677 #if USE_DESCRIPTOR_PASSING
679 g13_reset (void *engine)
681 engine_g13_t g13 = engine;
683 /* We must send a reset because we need to reset the list of
684 signers. Note that RESET does not reset OPTION commands. */
685 return g13_assuan_simple_command (g13->assuan_ctx, "RESET", NULL, NULL);
691 g13_transact (void *engine,
693 gpgme_assuan_data_cb_t data_cb,
695 gpgme_assuan_inquire_cb_t inq_cb,
697 gpgme_assuan_status_cb_t status_cb,
698 void *status_cb_value)
700 engine_g13_t g13 = engine;
703 if (!g13 || !command || !*command)
704 return gpg_error (GPG_ERR_INV_VALUE);
706 g13->user.data_cb = data_cb;
707 g13->user.data_cb_value = data_cb_value;
708 g13->user.inq_cb = inq_cb;
709 g13->user.inq_cb_value = inq_cb_value;
710 g13->user.status_cb = status_cb;
711 g13->user.status_cb_value = status_cb_value;
713 err = start (g13, command);
720 g13_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
722 engine_g13_t g13 = engine;
723 g13->io_cbs = *io_cbs;
728 g13_io_event (void *engine, gpgme_event_io_t type, void *type_data)
730 engine_g13_t g13 = engine;
732 TRACE3 (DEBUG_ENGINE, "gpgme:g13_io_event", g13,
733 "event %p, type %d, type_data %p",
734 g13->io_cbs.event, type, type_data);
735 if (g13->io_cbs.event)
736 (*g13->io_cbs.event) (g13->io_cbs.event_priv, type, type_data);
740 struct engine_ops _gpgme_engine_ops_g13 =
742 /* Static functions. */
749 /* Member functions. */
751 #if USE_DESCRIPTOR_PASSING
756 NULL, /* set_status_handler */
757 NULL, /* set_command_handler */
758 NULL, /* set_colon_line_handler */
760 NULL, /* set_protocol */
762 NULL, /* decrypt_verify */
766 NULL, /* encrypt_sign */
768 NULL, /* export_ext */
772 NULL, /* keylist_ext */
774 NULL, /* trustlist */
776 NULL, /* getauditlog */
778 NULL, /* conf_load */
779 NULL, /* conf_save */