Blame src/engine-g13.c

Packit d7e8d0
/* engine-g13.c - G13 engine.
Packit d7e8d0
   Copyright (C) 2000 Werner Koch (dd9jn)
Packit d7e8d0
   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH
Packit d7e8d0
Packit d7e8d0
   This file is part of GPGME.
Packit d7e8d0
Packit d7e8d0
   GPGME is free software; you can redistribute it and/or modify it
Packit d7e8d0
   under the terms of the GNU Lesser General Public License as
Packit d7e8d0
   published by the Free Software Foundation; either version 2.1 of
Packit d7e8d0
   the License, or (at your option) any later version.
Packit d7e8d0
Packit d7e8d0
   GPGME is distributed in the hope that it will be useful, but
Packit d7e8d0
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit d7e8d0
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit d7e8d0
   Lesser General Public License for more details.
Packit d7e8d0
Packit d7e8d0
   You should have received a copy of the GNU Lesser General Public
Packit d7e8d0
   License along with this program; if not, write to the Free Software
Packit d7e8d0
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
Packit d7e8d0
   02111-1307, USA.  */
Packit d7e8d0
Packit d7e8d0
#if HAVE_CONFIG_H
Packit d7e8d0
#include <config.h>
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
#include <stdlib.h>
Packit d7e8d0
#include <string.h>
Packit d7e8d0
#ifdef HAVE_SYS_TYPES_H
Packit d7e8d0
# include <sys/types.h>
Packit d7e8d0
#endif
Packit d7e8d0
#include <assert.h>
Packit d7e8d0
#ifdef HAVE_UNISTD_H
Packit d7e8d0
# include <unistd.h>
Packit d7e8d0
#endif
Packit d7e8d0
#ifdef HAVE_LOCALE_H
Packit d7e8d0
#include <locale.h>
Packit d7e8d0
#endif
Packit d7e8d0
#include <fcntl.h> /* FIXME */
Packit d7e8d0
#include <errno.h>
Packit d7e8d0
Packit d7e8d0
#include "gpgme.h"
Packit d7e8d0
#include "util.h"
Packit d7e8d0
#include "ops.h"
Packit d7e8d0
#include "wait.h"
Packit d7e8d0
#include "priv-io.h"
Packit d7e8d0
#include "sema.h"
Packit d7e8d0
Packit d7e8d0
#include "assuan.h"
Packit d7e8d0
#include "debug.h"
Packit d7e8d0
Packit d7e8d0
#include "engine-backend.h"
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
typedef struct
Packit d7e8d0
{
Packit d7e8d0
  int fd;	/* FD we talk about.  */
Packit d7e8d0
  int server_fd;/* Server FD for this connection.  */
Packit d7e8d0
  int dir;	/* Inbound/Outbound, maybe given implicit?  */
Packit d7e8d0
  void *data;	/* Handler-specific data.  */
Packit d7e8d0
  void *tag;	/* ID from the user for gpgme_remove_io_callback.  */
Packit d7e8d0
  char server_fd_str[15]; /* Same as SERVER_FD but as a string.  We
Packit d7e8d0
                             need this because _gpgme_io_fd2str can't
Packit d7e8d0
                             be used on a closed descriptor.  */
Packit d7e8d0
} iocb_data_t;
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
struct engine_g13
Packit d7e8d0
{
Packit d7e8d0
  assuan_context_t assuan_ctx;
Packit d7e8d0
Packit d7e8d0
  int lc_ctype_set;
Packit d7e8d0
  int lc_messages_set;
Packit d7e8d0
Packit d7e8d0
  iocb_data_t status_cb;
Packit d7e8d0
Packit d7e8d0
  struct gpgme_io_cbs io_cbs;
Packit d7e8d0
Packit d7e8d0
  /* User provided callbacks.  */
Packit d7e8d0
  struct {
Packit d7e8d0
    gpgme_assuan_data_cb_t data_cb;
Packit d7e8d0
    void *data_cb_value;
Packit d7e8d0
Packit d7e8d0
    gpgme_assuan_inquire_cb_t inq_cb;
Packit d7e8d0
    void *inq_cb_value;
Packit d7e8d0
Packit d7e8d0
    gpgme_assuan_status_cb_t status_cb;
Packit d7e8d0
    void *status_cb_value;
Packit d7e8d0
  } user;
Packit d7e8d0
};
Packit d7e8d0
Packit d7e8d0
typedef struct engine_g13 *engine_g13_t;
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static void g13_io_event (void *engine,
Packit d7e8d0
                            gpgme_event_io_t type, void *type_data);
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
static char *
Packit d7e8d0
g13_get_version (const char *file_name)
Packit d7e8d0
{
Packit d7e8d0
  return _gpgme_get_program_version (file_name ? file_name
Packit d7e8d0
				     : _gpgme_get_default_g13_name ());
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static const char *
Packit d7e8d0
g13_get_req_version (void)
Packit d7e8d0
{
Packit d7e8d0
  return "2.1.0";
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0

Packit d7e8d0
static void
Packit d7e8d0
close_notify_handler (int fd, void *opaque)
Packit d7e8d0
{
Packit d7e8d0
  engine_g13_t g13 = opaque;
Packit d7e8d0
Packit d7e8d0
  assert (fd != -1);
Packit d7e8d0
  if (g13->status_cb.fd == fd)
Packit d7e8d0
    {
Packit d7e8d0
      if (g13->status_cb.tag)
Packit d7e8d0
	(*g13->io_cbs.remove) (g13->status_cb.tag);
Packit d7e8d0
      g13->status_cb.fd = -1;
Packit d7e8d0
      g13->status_cb.tag = NULL;
Packit d7e8d0
    }
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* This is the default inquiry callback.  We use it to handle the
Packit d7e8d0
   Pinentry notifications.  */
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
default_inq_cb (engine_g13_t g13, const char *keyword, const char *args)
Packit d7e8d0
{
Packit d7e8d0
  gpg_error_t err;
Packit d7e8d0
Packit d7e8d0
  if (!strcmp (keyword, "PINENTRY_LAUNCHED"))
Packit d7e8d0
    {
Packit d7e8d0
      _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10));
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (g13->user.inq_cb)
Packit d7e8d0
    {
Packit d7e8d0
      gpgme_data_t data = NULL;
Packit d7e8d0
Packit d7e8d0
      err = g13->user.inq_cb (g13->user.inq_cb_value,
Packit d7e8d0
                                keyword, args, &data);
Packit d7e8d0
      if (!err && data)
Packit d7e8d0
        {
Packit d7e8d0
          /* FIXME: Returning data is not yet implemented.  However we
Packit d7e8d0
             need to allow the caller to cleanup his data object.
Packit d7e8d0
             Thus we run the callback in finish mode immediately.  */
Packit d7e8d0
          err = g13->user.inq_cb (g13->user.inq_cb_value,
Packit d7e8d0
				  NULL, NULL, &data);
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
  else
Packit d7e8d0
    err = 0;
Packit d7e8d0
Packit d7e8d0
  return err;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
g13_cancel (void *engine)
Packit d7e8d0
{
Packit d7e8d0
  engine_g13_t g13 = engine;
Packit d7e8d0
Packit d7e8d0
  if (!g13)
Packit d7e8d0
    return gpg_error (GPG_ERR_INV_VALUE);
Packit d7e8d0
Packit d7e8d0
  if (g13->status_cb.fd != -1)
Packit d7e8d0
    _gpgme_io_close (g13->status_cb.fd);
Packit d7e8d0
Packit d7e8d0
  if (g13->assuan_ctx)
Packit d7e8d0
    {
Packit d7e8d0
      assuan_release (g13->assuan_ctx);
Packit d7e8d0
      g13->assuan_ctx = NULL;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
g13_cancel_op (void *engine)
Packit d7e8d0
{
Packit d7e8d0
  engine_g13_t g13 = engine;
Packit d7e8d0
Packit d7e8d0
  if (!g13)
Packit d7e8d0
    return gpg_error (GPG_ERR_INV_VALUE);
Packit d7e8d0
Packit d7e8d0
  if (g13->status_cb.fd != -1)
Packit d7e8d0
    _gpgme_io_close (g13->status_cb.fd);
Packit d7e8d0
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static void
Packit d7e8d0
g13_release (void *engine)
Packit d7e8d0
{
Packit d7e8d0
  engine_g13_t g13 = engine;
Packit d7e8d0
Packit d7e8d0
  if (!g13)
Packit d7e8d0
    return;
Packit d7e8d0
Packit d7e8d0
  g13_cancel (engine);
Packit d7e8d0
Packit d7e8d0
  free (g13);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
g13_new (void **engine, const char *file_name, const char *home_dir,
Packit d7e8d0
         const char *version)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err = 0;
Packit d7e8d0
  engine_g13_t g13;
Packit d7e8d0
  const char *pgmname;
Packit d7e8d0
  int argc;
Packit d7e8d0
  const char *argv[5];
Packit d7e8d0
  char *dft_display = NULL;
Packit d7e8d0
  char dft_ttyname[64];
Packit d7e8d0
  char *env_tty = NULL;
Packit d7e8d0
  char *dft_ttytype = NULL;
Packit d7e8d0
  char *optstr;
Packit d7e8d0
Packit d7e8d0
  (void)version; /* Not yet used.  */
Packit d7e8d0
Packit d7e8d0
  g13 = calloc (1, sizeof *g13);
Packit d7e8d0
  if (!g13)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  g13->status_cb.fd = -1;
Packit d7e8d0
  g13->status_cb.dir = 1;
Packit d7e8d0
  g13->status_cb.tag = 0;
Packit d7e8d0
  g13->status_cb.data = g13;
Packit d7e8d0
Packit d7e8d0
  pgmname = file_name ? file_name : _gpgme_get_default_g13_name ();
Packit d7e8d0
  argc = 0;
Packit d7e8d0
  argv[argc++] = _gpgme_get_basename (pgmname);
Packit d7e8d0
  if (home_dir)
Packit d7e8d0
    {
Packit d7e8d0
      argv[argc++] = "--homedir";
Packit d7e8d0
      argv[argc++] = home_dir;
Packit d7e8d0
    }
Packit d7e8d0
  argv[argc++] = "--server";
Packit d7e8d0
  argv[argc++] = NULL;
Packit d7e8d0
Packit d7e8d0
  err = assuan_new_ext (&g13->assuan_ctx, GPG_ERR_SOURCE_GPGME,
Packit d7e8d0
			&_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
Packit d7e8d0
			NULL);
Packit d7e8d0
  if (err)
Packit d7e8d0
    goto leave;
Packit d7e8d0
  assuan_ctx_set_system_hooks (g13->assuan_ctx, &_gpgme_assuan_system_hooks);
Packit d7e8d0
Packit d7e8d0
#if USE_DESCRIPTOR_PASSING
Packit d7e8d0
  err = assuan_pipe_connect (g13->assuan_ctx, pgmname, argv,
Packit d7e8d0
                             NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
Packit d7e8d0
#else
Packit d7e8d0
  err = assuan_pipe_connect (g13->assuan_ctx, pgmname, argv,
Packit d7e8d0
                             NULL, NULL, NULL, 0);
Packit d7e8d0
#endif
Packit d7e8d0
  if (err)
Packit d7e8d0
    goto leave;
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_getenv ("DISPLAY", &dft_display);
Packit d7e8d0
  if (err)
Packit d7e8d0
    goto leave;
Packit d7e8d0
  if (dft_display)
Packit d7e8d0
    {
Packit d7e8d0
      if (gpgrt_asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
Packit d7e8d0
        {
Packit d7e8d0
	  free (dft_display);
Packit d7e8d0
	  err = gpg_error_from_syserror ();
Packit d7e8d0
	  goto leave;
Packit d7e8d0
	}
Packit d7e8d0
      free (dft_display);
Packit d7e8d0
Packit d7e8d0
      err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
Packit d7e8d0
			     NULL, NULL, NULL);
Packit d7e8d0
      gpgrt_free (optstr);
Packit d7e8d0
      if (err)
Packit d7e8d0
	goto leave;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  err = _gpgme_getenv ("GPG_TTY", &env_tty);
Packit d7e8d0
  if (isatty (1) || env_tty || err)
Packit d7e8d0
    {
Packit d7e8d0
      int rc = 0;
Packit d7e8d0
Packit d7e8d0
      if (err)
Packit d7e8d0
        goto leave;
Packit d7e8d0
      else if (env_tty)
Packit d7e8d0
        {
Packit d7e8d0
          snprintf (dft_ttyname, sizeof (dft_ttyname), "%s", env_tty);
Packit d7e8d0
          free (env_tty);
Packit d7e8d0
        }
Packit d7e8d0
      else
Packit d7e8d0
        rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
Packit d7e8d0
Packit d7e8d0
      /* Even though isatty() returns 1, ttyname_r() may fail in many
Packit d7e8d0
	 ways, e.g., when /dev/pts is not accessible under chroot.  */
Packit d7e8d0
      if (!rc)
Packit d7e8d0
	{
Packit d7e8d0
	  if (gpgrt_asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
Packit d7e8d0
	    {
Packit d7e8d0
	      err = gpg_error_from_syserror ();
Packit d7e8d0
	      goto leave;
Packit d7e8d0
	    }
Packit d7e8d0
	  err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
Packit d7e8d0
				 NULL, NULL, NULL);
Packit d7e8d0
	  gpgrt_free (optstr);
Packit d7e8d0
	  if (err)
Packit d7e8d0
	    goto leave;
Packit d7e8d0
Packit d7e8d0
	  err = _gpgme_getenv ("TERM", &dft_ttytype);
Packit d7e8d0
	  if (err)
Packit d7e8d0
	    goto leave;
Packit d7e8d0
	  if (dft_ttytype)
Packit d7e8d0
	    {
Packit d7e8d0
	      if (gpgrt_asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype)< 0)
Packit d7e8d0
		{
Packit d7e8d0
		  free (dft_ttytype);
Packit d7e8d0
		  err = gpg_error_from_syserror ();
Packit d7e8d0
		  goto leave;
Packit d7e8d0
		}
Packit d7e8d0
	      free (dft_ttytype);
Packit d7e8d0
Packit d7e8d0
	      err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
Packit d7e8d0
				     NULL, NULL, NULL, NULL);
Packit d7e8d0
	      gpgrt_free (optstr);
Packit d7e8d0
	      if (err)
Packit d7e8d0
		goto leave;
Packit d7e8d0
	    }
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
#ifdef HAVE_W32_SYSTEM
Packit d7e8d0
  /* Under Windows we need to use AllowSetForegroundWindow.  Tell
Packit d7e8d0
     g13 to tell us when it needs it.  */
Packit d7e8d0
  if (!err)
Packit d7e8d0
    {
Packit d7e8d0
      err = assuan_transact (g13->assuan_ctx, "OPTION allow-pinentry-notify",
Packit d7e8d0
                             NULL, NULL, NULL, NULL, NULL, NULL);
Packit d7e8d0
      if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
Packit d7e8d0
        err = 0; /* This is a new feature of g13.  */
Packit d7e8d0
    }
Packit d7e8d0
#endif /*HAVE_W32_SYSTEM*/
Packit d7e8d0
Packit d7e8d0
 leave:
Packit d7e8d0
Packit d7e8d0
  if (err)
Packit d7e8d0
    g13_release (g13);
Packit d7e8d0
  else
Packit d7e8d0
    *engine = g13;
Packit d7e8d0
Packit d7e8d0
  return err;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
g13_set_locale (void *engine, int category, const char *value)
Packit d7e8d0
{
Packit d7e8d0
  engine_g13_t g13 = engine;
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  char *optstr;
Packit d7e8d0
  const char *catstr;
Packit d7e8d0
Packit d7e8d0
  /* FIXME: If value is NULL, we need to reset the option to default.
Packit d7e8d0
     But we can't do this.  So we error out here.  G13 needs support
Packit d7e8d0
     for this.  */
Packit d7e8d0
  if (0)
Packit d7e8d0
    ;
Packit d7e8d0
#ifdef LC_CTYPE
Packit d7e8d0
  else if (category == LC_CTYPE)
Packit d7e8d0
    {
Packit d7e8d0
      catstr = "lc-ctype";
Packit d7e8d0
      if (!value && g13->lc_ctype_set)
Packit d7e8d0
	return gpg_error (GPG_ERR_INV_VALUE);
Packit d7e8d0
      if (value)
Packit d7e8d0
	g13->lc_ctype_set = 1;
Packit d7e8d0
    }
Packit d7e8d0
#endif
Packit d7e8d0
#ifdef LC_MESSAGES
Packit d7e8d0
  else if (category == LC_MESSAGES)
Packit d7e8d0
    {
Packit d7e8d0
      catstr = "lc-messages";
Packit d7e8d0
      if (!value && g13->lc_messages_set)
Packit d7e8d0
	return gpg_error (GPG_ERR_INV_VALUE);
Packit d7e8d0
      if (value)
Packit d7e8d0
	g13->lc_messages_set = 1;
Packit d7e8d0
    }
Packit d7e8d0
#endif /* LC_MESSAGES */
Packit d7e8d0
  else
Packit d7e8d0
    return gpg_error (GPG_ERR_INV_VALUE);
Packit d7e8d0
Packit d7e8d0
  /* FIXME: Reset value to default.  */
Packit d7e8d0
  if (!value)
Packit d7e8d0
    return 0;
Packit d7e8d0
Packit d7e8d0
  if (gpgrt_asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
Packit d7e8d0
    err = gpg_error_from_syserror ();
Packit d7e8d0
  else
Packit d7e8d0
    {
Packit d7e8d0
      err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
Packit d7e8d0
			     NULL, NULL, NULL, NULL);
Packit d7e8d0
      gpgrt_free (optstr);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  return err;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
#if USE_DESCRIPTOR_PASSING
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
g13_assuan_simple_command (assuan_context_t ctx, const char *cmd,
Packit d7e8d0
			   engine_status_handler_t status_fnc,
Packit d7e8d0
			   void *status_fnc_value)
Packit d7e8d0
{
Packit d7e8d0
  gpg_error_t err;
Packit d7e8d0
  char *line;
Packit d7e8d0
  size_t linelen;
Packit d7e8d0
Packit d7e8d0
  (void)status_fnc;
Packit d7e8d0
  (void)status_fnc_value;
Packit d7e8d0
Packit d7e8d0
  err = assuan_write_line (ctx, cmd);
Packit d7e8d0
  if (err)
Packit d7e8d0
    return err;
Packit d7e8d0
Packit d7e8d0
  do
Packit d7e8d0
    {
Packit d7e8d0
      err = assuan_read_line (ctx, &line, &linelen);
Packit d7e8d0
      if (err)
Packit d7e8d0
	return err;
Packit d7e8d0
Packit d7e8d0
      if (*line == '#' || !linelen)
Packit d7e8d0
	continue;
Packit d7e8d0
Packit d7e8d0
      if (linelen >= 2
Packit d7e8d0
	  && line[0] == 'O' && line[1] == 'K'
Packit d7e8d0
	  && (line[2] == '\0' || line[2] == ' '))
Packit d7e8d0
	return 0;
Packit d7e8d0
      else if (linelen >= 4
Packit d7e8d0
	  && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
Packit d7e8d0
	  && line[3] == ' ')
Packit d7e8d0
	err = atoi (&line[4]);
Packit d7e8d0
      else if (linelen >= 2
Packit d7e8d0
	       && line[0] == 'S' && line[1] == ' ')
Packit d7e8d0
	{
Packit d7e8d0
	  char *rest;
Packit d7e8d0
Packit d7e8d0
	  rest = strchr (line + 2, ' ');
Packit d7e8d0
	  if (!rest)
Packit d7e8d0
	    rest = line + linelen; /* set to an empty string */
Packit d7e8d0
	  else
Packit d7e8d0
	    *(rest++) = 0;
Packit d7e8d0
Packit d7e8d0
	  /* Nothing to do with status lines.  */
Packit d7e8d0
	}
Packit d7e8d0
      else
Packit d7e8d0
	err = gpg_error (GPG_ERR_GENERAL);
Packit d7e8d0
    }
Packit d7e8d0
  while (!err);
Packit d7e8d0
Packit d7e8d0
  return err;
Packit d7e8d0
}
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
status_handler (void *opaque, int fd)
Packit d7e8d0
{
Packit d7e8d0
  struct io_cb_data *data = (struct io_cb_data *) opaque;
Packit d7e8d0
  engine_g13_t g13 = (engine_g13_t) data->handler_value;
Packit d7e8d0
  gpgme_error_t err = 0;
Packit d7e8d0
  char *line;
Packit d7e8d0
  size_t linelen;
Packit d7e8d0
Packit d7e8d0
  do
Packit d7e8d0
    {
Packit d7e8d0
      err = assuan_read_line (g13->assuan_ctx, &line, &linelen);
Packit d7e8d0
      if (err)
Packit d7e8d0
	{
Packit d7e8d0
	  /* Try our best to terminate the connection friendly.  */
Packit d7e8d0
	  /*	  assuan_write_line (g13->assuan_ctx, "BYE"); */
Packit d7e8d0
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
Packit d7e8d0
		  "fd 0x%x: error reading assuan line: %s",
Packit d7e8d0
                  fd, gpg_strerror (err));
Packit d7e8d0
	}
Packit d7e8d0
      else if (linelen >= 3
Packit d7e8d0
	       && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
Packit d7e8d0
	       && (line[3] == '\0' || line[3] == ' '))
Packit d7e8d0
	{
Packit d7e8d0
	  if (line[3] == ' ')
Packit d7e8d0
	    err = atoi (&line[4]);
Packit d7e8d0
	  if (! err)
Packit d7e8d0
	    err = gpg_error (GPG_ERR_GENERAL);
Packit d7e8d0
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
Packit d7e8d0
		  "fd 0x%x: ERR line: %s",
Packit d7e8d0
                  fd, err ? gpg_strerror (err) : "ok");
Packit d7e8d0
Packit d7e8d0
	  /* Command execution errors are not fatal, as we use
Packit d7e8d0
	     a session based protocol.  */
Packit d7e8d0
	  data->op_err = err;
Packit d7e8d0
Packit d7e8d0
	  /* The caller will do the rest (namely, call cancel_op,
Packit d7e8d0
	     which closes status_fd).  */
Packit d7e8d0
	  return 0;
Packit d7e8d0
	}
Packit d7e8d0
      else if (linelen >= 2
Packit d7e8d0
	       && line[0] == 'O' && line[1] == 'K'
Packit d7e8d0
	       && (line[2] == '\0' || line[2] == ' '))
Packit d7e8d0
	{
Packit d7e8d0
          TRACE1 (DEBUG_CTX, "gpgme:status_handler", g13,
Packit d7e8d0
		  "fd 0x%x: OK line", fd);
Packit d7e8d0
Packit d7e8d0
	  _gpgme_io_close (g13->status_cb.fd);
Packit d7e8d0
	  return 0;
Packit d7e8d0
	}
Packit d7e8d0
      else if (linelen > 2
Packit d7e8d0
	       && line[0] == 'D' && line[1] == ' ')
Packit d7e8d0
        {
Packit d7e8d0
	  /* We are using the colon handler even for plain inline data
Packit d7e8d0
             - strange name for that function but for historic reasons
Packit d7e8d0
             we keep it.  */
Packit d7e8d0
          /* FIXME We can't use this for binary data because we
Packit d7e8d0
             assume this is a string.  For the current usage of colon
Packit d7e8d0
             output it is correct.  */
Packit d7e8d0
          char *src = line + 2;
Packit d7e8d0
	  char *end = line + linelen;
Packit d7e8d0
	  char *dst = src;
Packit d7e8d0
Packit d7e8d0
          linelen = 0;
Packit d7e8d0
          while (src < end)
Packit d7e8d0
            {
Packit d7e8d0
              if (*src == '%' && src + 2 < end)
Packit d7e8d0
                {
Packit d7e8d0
                  /* Handle escaped characters.  */
Packit d7e8d0
                  ++src;
Packit d7e8d0
                  *dst++ = _gpgme_hextobyte (src);
Packit d7e8d0
                  src += 2;
Packit d7e8d0
                }
Packit d7e8d0
              else
Packit d7e8d0
                *dst++ = *src++;
Packit d7e8d0
Packit d7e8d0
              linelen++;
Packit d7e8d0
            }
Packit d7e8d0
Packit d7e8d0
          src = line + 2;
Packit d7e8d0
          if (linelen && g13->user.data_cb)
Packit d7e8d0
            err = g13->user.data_cb (g13->user.data_cb_value,
Packit d7e8d0
                                       src, linelen);
Packit d7e8d0
          else
Packit d7e8d0
            err = 0;
Packit d7e8d0
Packit d7e8d0
          TRACE2 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
Packit d7e8d0
		  "fd 0x%x: D inlinedata; status from cb: %s",
Packit d7e8d0
                  fd, (g13->user.data_cb ?
Packit d7e8d0
                       (err? gpg_strerror (err):"ok"):"no callback"));
Packit d7e8d0
Packit d7e8d0
        }
Packit d7e8d0
      else if (linelen > 2
Packit d7e8d0
	       && line[0] == 'S' && line[1] == ' ')
Packit d7e8d0
	{
Packit d7e8d0
	  char *src;
Packit d7e8d0
	  char *args;
Packit d7e8d0
Packit d7e8d0
	  src = line + 2;
Packit d7e8d0
          while (*src == ' ')
Packit d7e8d0
            src++;
Packit d7e8d0
Packit d7e8d0
	  args = strchr (line + 2, ' ');
Packit d7e8d0
	  if (!args)
Packit d7e8d0
	    args = line + linelen; /* set to an empty string */
Packit d7e8d0
	  else
Packit d7e8d0
	    *(args++) = 0;
Packit d7e8d0
Packit d7e8d0
          while (*args == ' ')
Packit d7e8d0
            args++;
Packit d7e8d0
Packit d7e8d0
          if (g13->user.status_cb)
Packit d7e8d0
            err = g13->user.status_cb (g13->user.status_cb_value,
Packit d7e8d0
				       src, args);
Packit d7e8d0
          else
Packit d7e8d0
            err = 0;
Packit d7e8d0
Packit d7e8d0
          TRACE3 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
Packit d7e8d0
		  "fd 0x%x: S line (%s) - status from cb: %s",
Packit d7e8d0
                  fd, line+2, (g13->user.status_cb ?
Packit d7e8d0
                               (err? gpg_strerror (err):"ok"):"no callback"));
Packit d7e8d0
	}
Packit d7e8d0
      else if (linelen >= 7
Packit d7e8d0
               && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
Packit d7e8d0
               && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
Packit d7e8d0
               && line[6] == 'E'
Packit d7e8d0
               && (line[7] == '\0' || line[7] == ' '))
Packit d7e8d0
        {
Packit d7e8d0
          char *src;
Packit d7e8d0
	  char *args;
Packit d7e8d0
Packit d7e8d0
          for (src=line+7; *src == ' '; src++)
Packit d7e8d0
            ;
Packit d7e8d0
Packit d7e8d0
	  args = strchr (src, ' ');
Packit d7e8d0
	  if (!args)
Packit d7e8d0
	    args = line + linelen; /* Let it point to an empty string.  */
Packit d7e8d0
	  else
Packit d7e8d0
	    *(args++) = 0;
Packit d7e8d0
Packit d7e8d0
          while (*args == ' ')
Packit d7e8d0
            args++;
Packit d7e8d0
Packit d7e8d0
          err = default_inq_cb (g13, src, args);
Packit d7e8d0
          if (!err)
Packit d7e8d0
            {
Packit d7e8d0
              /* Flush and send END.  */
Packit d7e8d0
              err = assuan_send_data (g13->assuan_ctx, NULL, 0);
Packit d7e8d0
            }
Packit d7e8d0
          else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
Packit d7e8d0
            {
Packit d7e8d0
              /* Flush and send CANcel.  */
Packit d7e8d0
              err = assuan_send_data (g13->assuan_ctx, NULL, 1);
Packit d7e8d0
            }
Packit d7e8d0
          assuan_write_line (g13->assuan_ctx, "END");
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
  while (!err && assuan_pending_line (g13->assuan_ctx));
Packit d7e8d0
Packit d7e8d0
  return err;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
add_io_cb (engine_g13_t g13, iocb_data_t *iocbd, gpgme_io_cb_t handler)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
Packit d7e8d0
  TRACE_BEG2 (DEBUG_ENGINE, "engine-g13:add_io_cb", g13,
Packit d7e8d0
              "fd %d, dir %d", iocbd->fd, iocbd->dir);
Packit d7e8d0
  err = (*g13->io_cbs.add) (g13->io_cbs.add_priv,
Packit d7e8d0
			      iocbd->fd, iocbd->dir,
Packit d7e8d0
			      handler, iocbd->data, &iocbd->tag);
Packit d7e8d0
  if (err)
Packit d7e8d0
    return TRACE_ERR (err);
Packit d7e8d0
  if (!iocbd->dir)
Packit d7e8d0
    /* FIXME Kludge around poll() problem.  */
Packit d7e8d0
    err = _gpgme_io_set_nonblocking (iocbd->fd);
Packit d7e8d0
  return TRACE_ERR (err);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
start (engine_g13_t g13, const char *command)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
  assuan_fd_t afdlist[5];
Packit d7e8d0
  int fdlist[5];
Packit d7e8d0
  int nfds;
Packit d7e8d0
  int i;
Packit d7e8d0
Packit d7e8d0
  /* We need to know the fd used by assuan for reads.  We do this by
Packit d7e8d0
     using the assumption that the first returned fd from
Packit d7e8d0
     assuan_get_active_fds() is always this one.  */
Packit d7e8d0
  nfds = assuan_get_active_fds (g13->assuan_ctx, 0 /* read fds */,
Packit d7e8d0
                                afdlist, DIM (afdlist));
Packit d7e8d0
  if (nfds < 1)
Packit d7e8d0
    return gpg_error (GPG_ERR_GENERAL);	/* FIXME */
Packit d7e8d0
  /* For now... */
Packit d7e8d0
  for (i = 0; i < nfds; i++)
Packit d7e8d0
    fdlist[i] = (int) afdlist[i];
Packit d7e8d0
Packit d7e8d0
  /* We "duplicate" the file descriptor, so we can close it here (we
Packit d7e8d0
     can't close fdlist[0], as that is closed by libassuan, and
Packit d7e8d0
     closing it here might cause libassuan to close some unrelated FD
Packit d7e8d0
     later).  Alternatively, we could special case status_fd and
Packit d7e8d0
     register/unregister it manually as needed, but this increases
Packit d7e8d0
     code duplication and is more complicated as we can not use the
Packit d7e8d0
     close notifications etc.  A third alternative would be to let
Packit d7e8d0
     Assuan know that we closed the FD, but that complicates the
Packit d7e8d0
     Assuan interface.  */
Packit d7e8d0
Packit d7e8d0
  g13->status_cb.fd = _gpgme_io_dup (fdlist[0]);
Packit d7e8d0
  if (g13->status_cb.fd < 0)
Packit d7e8d0
    return gpg_error_from_syserror ();
Packit d7e8d0
Packit d7e8d0
  if (_gpgme_io_set_close_notify (g13->status_cb.fd,
Packit d7e8d0
				  close_notify_handler, g13))
Packit d7e8d0
    {
Packit d7e8d0
      _gpgme_io_close (g13->status_cb.fd);
Packit d7e8d0
      g13->status_cb.fd = -1;
Packit d7e8d0
      return gpg_error (GPG_ERR_GENERAL);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  err = add_io_cb (g13, &g13->status_cb, status_handler);
Packit d7e8d0
  if (!err)
Packit d7e8d0
    err = assuan_write_line (g13->assuan_ctx, command);
Packit d7e8d0
Packit d7e8d0
  if (!err)
Packit d7e8d0
    g13_io_event (g13, GPGME_EVENT_START, NULL);
Packit d7e8d0
Packit d7e8d0
  return err;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
#if USE_DESCRIPTOR_PASSING
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
g13_reset (void *engine)
Packit d7e8d0
{
Packit d7e8d0
  engine_g13_t g13 = engine;
Packit d7e8d0
Packit d7e8d0
  /* We must send a reset because we need to reset the list of
Packit d7e8d0
     signers.  Note that RESET does not reset OPTION commands. */
Packit d7e8d0
  return g13_assuan_simple_command (g13->assuan_ctx, "RESET", NULL, NULL);
Packit d7e8d0
}
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static gpgme_error_t
Packit d7e8d0
g13_transact (void *engine,
Packit d7e8d0
                const char *command,
Packit d7e8d0
                gpgme_assuan_data_cb_t data_cb,
Packit d7e8d0
                void *data_cb_value,
Packit d7e8d0
                gpgme_assuan_inquire_cb_t inq_cb,
Packit d7e8d0
                void *inq_cb_value,
Packit d7e8d0
                gpgme_assuan_status_cb_t status_cb,
Packit d7e8d0
                void *status_cb_value)
Packit d7e8d0
{
Packit d7e8d0
  engine_g13_t g13 = engine;
Packit d7e8d0
  gpgme_error_t err;
Packit d7e8d0
Packit d7e8d0
  if (!g13 || !command || !*command)
Packit d7e8d0
    return gpg_error (GPG_ERR_INV_VALUE);
Packit d7e8d0
Packit d7e8d0
  g13->user.data_cb = data_cb;
Packit d7e8d0
  g13->user.data_cb_value = data_cb_value;
Packit d7e8d0
  g13->user.inq_cb = inq_cb;
Packit d7e8d0
  g13->user.inq_cb_value = inq_cb_value;
Packit d7e8d0
  g13->user.status_cb = status_cb;
Packit d7e8d0
  g13->user.status_cb_value = status_cb_value;
Packit d7e8d0
Packit d7e8d0
  err = start (g13, command);
Packit d7e8d0
  return err;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static void
Packit d7e8d0
g13_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
Packit d7e8d0
{
Packit d7e8d0
  engine_g13_t g13 = engine;
Packit d7e8d0
  g13->io_cbs = *io_cbs;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static void
Packit d7e8d0
g13_io_event (void *engine, gpgme_event_io_t type, void *type_data)
Packit d7e8d0
{
Packit d7e8d0
  engine_g13_t g13 = engine;
Packit d7e8d0
Packit d7e8d0
  TRACE3 (DEBUG_ENGINE, "gpgme:g13_io_event", g13,
Packit d7e8d0
          "event %p, type %d, type_data %p",
Packit d7e8d0
          g13->io_cbs.event, type, type_data);
Packit d7e8d0
  if (g13->io_cbs.event)
Packit d7e8d0
    (*g13->io_cbs.event) (g13->io_cbs.event_priv, type, type_data);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
struct engine_ops _gpgme_engine_ops_g13 =
Packit d7e8d0
  {
Packit d7e8d0
    /* Static functions.  */
Packit d7e8d0
    _gpgme_get_default_g13_name,
Packit d7e8d0
    NULL,
Packit d7e8d0
    g13_get_version,
Packit d7e8d0
    g13_get_req_version,
Packit d7e8d0
    g13_new,
Packit d7e8d0
Packit d7e8d0
    /* Member functions.  */
Packit d7e8d0
    g13_release,
Packit d7e8d0
#if USE_DESCRIPTOR_PASSING
Packit d7e8d0
    g13_reset,
Packit d7e8d0
#else
Packit d7e8d0
    NULL,			/* reset */
Packit d7e8d0
#endif
Packit d7e8d0
    NULL,               /* set_status_cb */
Packit d7e8d0
    NULL,               /* set_status_handler */
Packit d7e8d0
    NULL,		/* set_command_handler */
Packit d7e8d0
    NULL,               /* set_colon_line_handler */
Packit d7e8d0
    g13_set_locale,
Packit d7e8d0
    NULL,		/* set_protocol */
Packit d7e8d0
    NULL,               /* decrypt */
Packit d7e8d0
    NULL,               /* delete */
Packit d7e8d0
    NULL,		/* edit */
Packit d7e8d0
    NULL,               /* encrypt */
Packit d7e8d0
    NULL,		/* encrypt_sign */
Packit d7e8d0
    NULL,               /* export */
Packit d7e8d0
    NULL,               /* export_ext */
Packit d7e8d0
    NULL,               /* genkey */
Packit d7e8d0
    NULL,               /* import */
Packit d7e8d0
    NULL,               /* keylist */
Packit d7e8d0
    NULL,               /* keylist_ext */
Packit d7e8d0
    NULL,               /* keylist_data */
Packit d7e8d0
    NULL,               /* keysign */
Packit d7e8d0
    NULL,               /* tofu_policy */
Packit d7e8d0
    NULL,               /* sign */
Packit d7e8d0
    NULL,		/* trustlist */
Packit d7e8d0
    NULL,               /* verify */
Packit d7e8d0
    NULL,               /* getauditlog */
Packit d7e8d0
    g13_transact,
Packit d7e8d0
    NULL,		/* conf_load */
Packit d7e8d0
    NULL,		/* conf_save */
Packit d7e8d0
    NULL,		/* conf_dir */
Packit d7e8d0
    NULL,               /* query_swdb */
Packit d7e8d0
    g13_set_io_cbs,
Packit d7e8d0
    g13_io_event,
Packit d7e8d0
    g13_cancel,
Packit d7e8d0
    g13_cancel_op,
Packit d7e8d0
    NULL,               /* passwd */
Packit d7e8d0
    NULL,               /* set_pinentry_mode */
Packit d7e8d0
    NULL                /* opspawn */
Packit d7e8d0
  };