Exemplo n.º 1
0
static gpgme_error_t
gpgsm_cancel (void *engine)
{
  engine_gpgsm_t gpgsm = engine;

  if (!gpgsm)
    return gpg_error (GPG_ERR_INV_VALUE);

  if (gpgsm->status_cb.fd != -1)
    _gpgme_io_close (gpgsm->status_cb.fd);
  if (gpgsm->input_cb.fd != -1)
    _gpgme_io_close (gpgsm->input_cb.fd);
  if (gpgsm->output_cb.fd != -1)
    _gpgme_io_close (gpgsm->output_cb.fd);
  if (gpgsm->message_cb.fd != -1)
    _gpgme_io_close (gpgsm->message_cb.fd);

  if (gpgsm->assuan_ctx)
    {
      assuan_release (gpgsm->assuan_ctx);
      gpgsm->assuan_ctx = NULL;
    }

  return 0;
}
Exemplo n.º 2
0
static gpgme_error_t
uiserver_cancel (void *engine)
{
  engine_uiserver_t uiserver = engine;

  if (!uiserver)
    return gpg_error (GPG_ERR_INV_VALUE);

  if (uiserver->status_cb.fd != -1)
    _gpgme_io_close (uiserver->status_cb.fd);
  if (uiserver->input_cb.fd != -1)
    _gpgme_io_close (uiserver->input_cb.fd);
  if (uiserver->output_cb.fd != -1)
    _gpgme_io_close (uiserver->output_cb.fd);
  if (uiserver->message_cb.fd != -1)
    _gpgme_io_close (uiserver->message_cb.fd);

  if (uiserver->assuan_ctx)
    {
      assuan_release (uiserver->assuan_ctx);
      uiserver->assuan_ctx = NULL;
    }

  return 0;
}
Exemplo n.º 3
0
gpgme_error_t
_gpgme_data_inbound_handler(void *opaque, int fd)
{
    gpgme_data_t dh = (gpgme_data_t) opaque;
    char buffer[BUFFER_SIZE];
    char *bufp = buffer;
    ssize_t buflen;

    buflen = _gpgme_io_read(fd, buffer, BUFFER_SIZE);
    if(buflen < 0)
        return gpg_error_from_errno(errno);
    if(buflen == 0)
    {
        _gpgme_io_close(fd);
        return 0;
    }

    do
    {
        ssize_t amt = gpgme_data_write(dh, bufp, buflen);
        if(amt == 0 || (amt < 0 && errno != EINTR))
            return gpg_error_from_errno(errno);
        bufp += amt;
        buflen -= amt;
    }
    while(buflen > 0);
    return 0;
}
Exemplo n.º 4
0
gpgme_error_t
_gpgme_data_inbound_handler (void *opaque, int fd)
{
  struct io_cb_data *data = (struct io_cb_data *) opaque;
  gpgme_data_t dh = (gpgme_data_t) data->handler_value;
  char buffer[BUFFER_SIZE];
  char *bufp = buffer;
  gpgme_ssize_t buflen;
  TRACE_BEG1 (DEBUG_CTX, "_gpgme_data_inbound_handler", dh,
	      "fd=0x%x", fd);

  buflen = _gpgme_io_read (fd, buffer, BUFFER_SIZE);
  if (buflen < 0)
    return gpg_error_from_syserror ();
  if (buflen == 0)
    {
      _gpgme_io_close (fd);
      return TRACE_ERR (0);
    }

  do
    {
      gpgme_ssize_t amt = gpgme_data_write (dh, bufp, buflen);
      if (amt == 0 || (amt < 0 && errno != EINTR))
	return TRACE_ERR (gpg_error_from_syserror ());
      bufp += amt;
      buflen -= amt;
    }
  while (buflen > 0);
  return TRACE_ERR (0);
}
Exemplo n.º 5
0
static gpgme_error_t
start (engine_gpgsm_t gpgsm, const char *command)
{
  gpgme_error_t err;
  assuan_fd_t afdlist[5];
  int fdlist[5];
  int nfds;
  int i;

  /* We need to know the fd used by assuan for reads.  We do this by
     using the assumption that the first returned fd from
     assuan_get_active_fds() is always this one.  */
  nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
                                afdlist, DIM (afdlist));
  if (nfds < 1)
    return gpg_error (GPG_ERR_GENERAL);	/* FIXME */
  /* For now... */
  for (i = 0; i < nfds; i++)
    fdlist[i] = (int) afdlist[i];

  /* We "duplicate" the file descriptor, so we can close it here (we
     can't close fdlist[0], as that is closed by libassuan, and
     closing it here might cause libassuan to close some unrelated FD
     later).  Alternatively, we could special case status_fd and
     register/unregister it manually as needed, but this increases
     code duplication and is more complicated as we can not use the
     close notifications etc.  A third alternative would be to let
     Assuan know that we closed the FD, but that complicates the
     Assuan interface.  */

  gpgsm->status_cb.fd = _gpgme_io_dup (fdlist[0]);
  if (gpgsm->status_cb.fd < 0)
    return gpg_error_from_syserror ();

  if (_gpgme_io_set_close_notify (gpgsm->status_cb.fd,
				  close_notify_handler, gpgsm))
    {
      _gpgme_io_close (gpgsm->status_cb.fd);
      gpgsm->status_cb.fd = -1;
      return gpg_error (GPG_ERR_GENERAL);
    }

  err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler);
  if (!err && gpgsm->input_cb.fd != -1)
    err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler);
  if (!err && gpgsm->output_cb.fd != -1)
    err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler);
  if (!err && gpgsm->message_cb.fd != -1)
    err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);

  if (!err)
    err = assuan_write_line (gpgsm->assuan_ctx, command);

  if (!err)
    gpgsm_io_event (gpgsm, GPGME_EVENT_START, NULL);

  return err;
}
Exemplo n.º 6
0
static void
free_fd_data_map (struct fd_data_map_s *fd_data_map)
{
  int i;

  if (!fd_data_map)
    return;

  for (i = 0; fd_data_map[i].data; i++)
    {
      if (fd_data_map[i].fd != -1)
	_gpgme_io_close (fd_data_map[i].fd);
      if (fd_data_map[i].peer_fd != -1)
	_gpgme_io_close (fd_data_map[i].peer_fd);
      /* Don't release data because this is only a reference.  */
    }
  free (fd_data_map);
}
Exemplo n.º 7
0
static void
gpgsm_clear_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type)
{
#if !USE_DESCRIPTOR_PASSING
  switch (fd_type)
    {
    case INPUT_FD:
      _gpgme_io_close (gpgsm->input_cb.fd);
      break;
    case OUTPUT_FD:
      _gpgme_io_close (gpgsm->output_cb.fd);
      break;
    case MESSAGE_FD:
      _gpgme_io_close (gpgsm->message_cb.fd);
      break;
    }
#endif
}
Exemplo n.º 8
0
gpgme_error_t
_gpgme_data_outbound_handler (void *opaque, int fd)
{
  struct io_cb_data *data = (struct io_cb_data *) opaque;
  gpgme_data_t dh = (gpgme_data_t) data->handler_value;
  gpgme_ssize_t nwritten;
  TRACE_BEG1 (DEBUG_CTX, "_gpgme_data_outbound_handler", dh,
	      "fd=0x%x", fd);

  if (!dh->pending_len)
    {
      gpgme_ssize_t amt = gpgme_data_read (dh, dh->pending, BUFFER_SIZE);
      if (amt < 0)
	return TRACE_ERR (gpg_error_from_syserror ());
      if (amt == 0)
	{
	  _gpgme_io_close (fd);
	  return TRACE_ERR (0);
	}
      dh->pending_len = amt;
    }

  nwritten = _gpgme_io_write (fd, dh->pending, dh->pending_len);
  if (nwritten == -1 && errno == EAGAIN)
    return TRACE_ERR (0);

  if (nwritten == -1 && errno == EPIPE)
    {
      /* Not much we can do.  The other end closed the pipe, but we
	 still have data.  This should only ever happen if the other
	 end is going to tell us what happened on some other channel.
	 Silently close our end.  */
      _gpgme_io_close (fd);
      return TRACE_ERR (0);
    }

  if (nwritten <= 0)
    return TRACE_ERR (gpg_error_from_syserror ());

  if (nwritten < dh->pending_len)
    memmove (dh->pending, dh->pending + nwritten, dh->pending_len - nwritten);
  dh->pending_len -= nwritten;
  return TRACE_ERR (0);
}
Exemplo n.º 9
0
gpgme_error_t
_gpgme_data_outbound_handler(void *opaque, int fd)
{
    gpgme_data_t dh = (gpgme_data_t) opaque;
    ssize_t nwritten;

    if(!dh->pending_len)
    {
        ssize_t amt = gpgme_data_read(dh, dh->pending, BUFFER_SIZE);
        if(amt < 0)
            return gpg_error_from_errno(errno);
        if(amt == 0)
        {
            _gpgme_io_close(fd);
            return 0;
        }
        dh->pending_len = amt;
    }

    nwritten = _gpgme_io_write(fd, dh->pending, dh->pending_len);
    if(nwritten == -1 && errno == EAGAIN)
        return 0;

    if(nwritten == -1 && errno == EPIPE)
    {
        /* Not much we can do.  The other end closed the pipe, but we
        still have data.  This should only ever happen if the other
         end is going to tell us what happened on some other channel.
         Silently close our end.  */
        _gpgme_io_close(fd);
        return 0;
    }

    if(nwritten <= 0)
        return gpg_error_from_errno(errno);

    if(nwritten < dh->pending_len)
        memmove(dh->pending, dh->pending + nwritten, dh->pending_len - nwritten);
    dh->pending_len -= nwritten;
    return 0;
}
Exemplo n.º 10
0
static gpgme_error_t
g13_cancel_op (void *engine)
{
  engine_g13_t g13 = engine;

  if (!g13)
    return gpg_error (GPG_ERR_INV_VALUE);

  if (g13->status_cb.fd != -1)
    _gpgme_io_close (g13->status_cb.fd);

  return 0;
}
Exemplo n.º 11
0
void
_gpgme_wait_global_event_cb (void *data, gpgme_event_io_t type,
			     void *type_data)
{
  gpgme_ctx_t ctx = (gpgme_ctx_t) data;

  assert (ctx);

  switch (type)
    {
    case GPGME_EVENT_START:
      {
	gpgme_error_t err = ctx_active (ctx);

	if (err)
	  {
	    /* An error occured.  Close all fds in this context, and
	       send the error in a done event.  */
	    unsigned int idx;
	    
	    for (idx = 0; idx <= ctx->fdt.size; idx++)
	      if (ctx->fdt.fds[idx].fd != -1)
		_gpgme_io_close (ctx->fdt.fds[idx].fd);
	    _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &err);
	  }
      }
      break;

    case GPGME_EVENT_DONE:
      {
	gpgme_error_t *errp = (gpgme_error_t *) type_data;
	assert (errp);
	ctx_done (ctx, *errp);
      }
      break;

    case GPGME_EVENT_NEXT_KEY:
      assert (!"Unexpected event GPGME_EVENT_NEXT_KEY");
      break;

    case GPGME_EVENT_NEXT_TRUSTITEM:
      assert (!"Unexpected event GPGME_EVENT_NEXT_TRUSTITEM");
      break;

    default:
      assert (!"Unexpected event");
      break;
    }
}
Exemplo n.º 12
0
static gpgme_error_t
g13_cancel (void *engine)
{
  engine_g13_t g13 = engine;

  if (!g13)
    return gpg_error (GPG_ERR_INV_VALUE);

  if (g13->status_cb.fd != -1)
    _gpgme_io_close (g13->status_cb.fd);

  if (g13->assuan_ctx)
    {
      assuan_release (g13->assuan_ctx);
      g13->assuan_ctx = NULL;
    }

  return 0;
}
Exemplo n.º 13
0
static gpgme_error_t
gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
{
  gpg_error_t err = 0;
  char line[COMMANDLINELEN];
  char *which;
  iocb_data_t *iocb_data;
  int dir;

  switch (fd_type)
    {
    case INPUT_FD:
      which = "INPUT";
      iocb_data = &gpgsm->input_cb;
      break;

    case OUTPUT_FD:
      which = "OUTPUT";
      iocb_data = &gpgsm->output_cb;
      break;

    case MESSAGE_FD:
      which = "MESSAGE";
      iocb_data = &gpgsm->message_cb;
      break;

    default:
      return gpg_error (GPG_ERR_INV_VALUE);
    }

  dir = iocb_data->dir;

#if USE_DESCRIPTOR_PASSING
  /* We try to short-cut the communication by giving GPGSM direct
     access to the file descriptor, rather than using a pipe.  */
  iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
  if (iocb_data->server_fd < 0)
    {
      int fds[2];

      if (_gpgme_io_pipe (fds, dir) < 0)
	return gpg_error_from_syserror ();

      iocb_data->fd = dir ? fds[0] : fds[1];
      iocb_data->server_fd = dir ? fds[1] : fds[0];

      if (_gpgme_io_set_close_notify (iocb_data->fd,
				      close_notify_handler, gpgsm))
	{
	  err = gpg_error (GPG_ERR_GENERAL);
	  goto leave_set_fd;
	}
    }

  err = assuan_sendfd (gpgsm->assuan_ctx, iocb_data->server_fd);
  if (err)
    goto leave_set_fd;

  _gpgme_io_close (iocb_data->server_fd);
  iocb_data->server_fd = -1;

  if (opt)
    snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
  else
    snprintf (line, COMMANDLINELEN, "%s FD", which);
#else
  if (opt)
    snprintf (line, COMMANDLINELEN, "%s FD=%s %s",
              which, iocb_data->server_fd_str, opt);
  else
    snprintf (line, COMMANDLINELEN, "%s FD=%s",
              which, iocb_data->server_fd_str);
#endif

  err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);

#if USE_DESCRIPTOR_PASSING
 leave_set_fd:
  if (err)
    {
      _gpgme_io_close (iocb_data->fd);
      iocb_data->fd = -1;
      if (iocb_data->server_fd != -1)
        {
          _gpgme_io_close (iocb_data->server_fd);
          iocb_data->server_fd = -1;
        }
    }
#endif

  return err;
}
Exemplo n.º 14
0
/* If COND is a null pointer, wait until the blocking operation in CTX
   finished and return its error value.  Otherwise, wait until COND is
   satisfied or the operation finished.  */
gpgme_error_t
_gpgme_wait_on_condition (gpgme_ctx_t ctx, volatile int *cond)
{
  gpgme_error_t err = 0;
  int hang = 1;

  do
    {
      int nr = _gpgme_io_select (ctx->fdt.fds, ctx->fdt.size, 0);
      unsigned int i;

      if (nr < 0)
	{
	  /* An error occured.  Close all fds in this context, and
	     signal it.  */
	  unsigned int idx;

	  err = gpg_error_from_errno (errno);
	  for (idx = 0; idx < ctx->fdt.size; idx++)
	    if (ctx->fdt.fds[idx].fd != -1)
	      _gpgme_io_close (ctx->fdt.fds[idx].fd);
	  _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &err);

	  return err;
	}
      
      for (i = 0; i < ctx->fdt.size && nr; i++)
	{
	  if (ctx->fdt.fds[i].fd != -1 && ctx->fdt.fds[i].signaled)
	    {
	      ctx->fdt.fds[i].signaled = 0;
	      assert (nr);
	      nr--;

	      err = _gpgme_run_io_cb (&ctx->fdt.fds[i], 0);
	      if (err)
		{
		  /* An error occured.  Close all fds in this context,
		     and signal it.  */
		  unsigned int idx;
		  
		  for (idx = 0; idx < ctx->fdt.size; idx++)
		    if (ctx->fdt.fds[idx].fd != -1)
		      _gpgme_io_close (ctx->fdt.fds[idx].fd);
		  _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &err);
		  return err;
		}
	    }
	}

      for (i = 0; i < ctx->fdt.size; i++)
	if (ctx->fdt.fds[i].fd != -1)
	  break;
      if (i == ctx->fdt.size)
	{
	  _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &err);
	  hang = 0;
	}
      if (cond && *cond)
	hang = 0;
    }
  while (hang);

  return 0;
}
Exemplo n.º 15
0
static gpgme_error_t
gpgsm_new (void **engine, const char *file_name, const char *home_dir)
{
  gpgme_error_t err = 0;
  engine_gpgsm_t gpgsm;
  const char *argv[5];
  int argc;
#if !USE_DESCRIPTOR_PASSING
  int fds[2];
  int child_fds[4];
#endif
  char *dft_display = NULL;
  char dft_ttyname[64];
  char *dft_ttytype = NULL;
  char *optstr;

  gpgsm = calloc (1, sizeof *gpgsm);
  if (!gpgsm)
    return gpg_error_from_syserror ();

  gpgsm->status_cb.fd = -1;
  gpgsm->status_cb.dir = 1;
  gpgsm->status_cb.tag = 0;
  gpgsm->status_cb.data = gpgsm;

  gpgsm->input_cb.fd = -1;
  gpgsm->input_cb.dir = 0;
  gpgsm->input_cb.tag = 0;
  gpgsm->input_cb.server_fd = -1;
  *gpgsm->input_cb.server_fd_str = 0;
  gpgsm->output_cb.fd = -1;
  gpgsm->output_cb.dir = 1;
  gpgsm->output_cb.tag = 0;
  gpgsm->output_cb.server_fd = -1;
  *gpgsm->output_cb.server_fd_str = 0;
  gpgsm->message_cb.fd = -1;
  gpgsm->message_cb.dir = 0;
  gpgsm->message_cb.tag = 0;
  gpgsm->message_cb.server_fd = -1;
  *gpgsm->message_cb.server_fd_str = 0;

  gpgsm->status.fnc = 0;
  gpgsm->colon.fnc = 0;
  gpgsm->colon.attic.line = 0;
  gpgsm->colon.attic.linesize = 0;
  gpgsm->colon.attic.linelen = 0;
  gpgsm->colon.any = 0;

  gpgsm->inline_data = NULL;

  gpgsm->io_cbs.add = NULL;
  gpgsm->io_cbs.add_priv = NULL;
  gpgsm->io_cbs.remove = NULL;
  gpgsm->io_cbs.event = NULL;
  gpgsm->io_cbs.event_priv = NULL;

#if !USE_DESCRIPTOR_PASSING
  if (_gpgme_io_pipe (fds, 0) < 0)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  gpgsm->input_cb.fd = fds[1];
  gpgsm->input_cb.server_fd = fds[0];

  if (_gpgme_io_pipe (fds, 1) < 0)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  gpgsm->output_cb.fd = fds[0];
  gpgsm->output_cb.server_fd = fds[1];

  if (_gpgme_io_pipe (fds, 0) < 0)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  gpgsm->message_cb.fd = fds[1];
  gpgsm->message_cb.server_fd = fds[0];

  child_fds[0] = gpgsm->input_cb.server_fd;
  child_fds[1] = gpgsm->output_cb.server_fd;
  child_fds[2] = gpgsm->message_cb.server_fd;
  child_fds[3] = -1;
#endif

  argc = 0;
  argv[argc++] = "gpgsm";
  if (home_dir)
    {
      argv[argc++] = "--homedir";
      argv[argc++] = home_dir;
    }
  argv[argc++] = "--server";
  argv[argc++] = NULL;

  err = assuan_new_ext (&gpgsm->assuan_ctx, GPG_ERR_SOURCE_GPGME,
			&_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
			NULL);
  if (err)
    goto leave;
  assuan_ctx_set_system_hooks (gpgsm->assuan_ctx, &_gpgme_assuan_system_hooks);

#if USE_DESCRIPTOR_PASSING
  err = assuan_pipe_connect
    (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
     argv, NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
#else
  {
    assuan_fd_t achild_fds[4];
    int i;

    /* For now... */
    for (i = 0; i < 4; i++)
      achild_fds[i] = (assuan_fd_t) child_fds[i];

    err = assuan_pipe_connect
      (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
       argv, achild_fds, NULL, NULL, 0);

    /* For now... */
    for (i = 0; i < 4; i++)
      child_fds[i] = (int) achild_fds[i];
  }

  /* On Windows, handles are inserted in the spawned process with
     DuplicateHandle, and child_fds contains the server-local names
     for the inserted handles when assuan_pipe_connect returns.  */
  if (!err)
    {
      /* Note: We don't use _gpgme_io_fd2str here.  On W32 the
	 returned handles are real W32 system handles, not whatever
	 GPGME uses internally (which may be a system handle, a C
	 library handle or a GLib/Qt channel.  Confusing, yes, but
	 remember these are server-local names, so they are not part
	 of GPGME at all.  */
      snprintf (gpgsm->input_cb.server_fd_str,
		sizeof gpgsm->input_cb.server_fd_str, "%d", child_fds[0]);
      snprintf (gpgsm->output_cb.server_fd_str,
		sizeof gpgsm->output_cb.server_fd_str, "%d", child_fds[1]);
      snprintf (gpgsm->message_cb.server_fd_str,
		sizeof gpgsm->message_cb.server_fd_str, "%d", child_fds[2]);
    }
#endif
  if (err)
    goto leave;

  err = _gpgme_getenv ("DISPLAY", &dft_display);
  if (err)
    goto leave;
  if (dft_display)
    {
      if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
        {
	  free (dft_display);
	  err = gpg_error_from_syserror ();
	  goto leave;
	}
      free (dft_display);

      err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
			     NULL, NULL, NULL);
      free (optstr);
      if (err)
	goto leave;
    }

  if (isatty (1))
    {
      int rc;

      rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
      if (rc)
	{
	  err = gpg_error_from_errno (rc);
	  goto leave;
	}
      else
	{
	  if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
	    {
	      err = gpg_error_from_syserror ();
	      goto leave;
	    }
	  err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
				 NULL, NULL, NULL);
	  free (optstr);
	  if (err)
	    goto leave;

	  err = _gpgme_getenv ("TERM", &dft_ttytype);
	  if (err)
	    goto leave;
	  if (dft_ttytype)
	    {
	      if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
		{
		  free (dft_ttytype);
		  err = gpg_error_from_syserror ();
		  goto leave;
		}
	      free (dft_ttytype);

	      err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
				     NULL, NULL, NULL, NULL);
	      free (optstr);
	      if (err)
		goto leave;
	    }
	}
    }

  /* Ask gpgsm to enable the audit log support.  */
  if (!err)
    {
      err = assuan_transact (gpgsm->assuan_ctx, "OPTION enable-audit-log=1",
                             NULL, NULL, NULL, NULL, NULL, NULL);
      if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
        err = 0; /* This is an optional feature of gpgsm.  */
    }


#ifdef HAVE_W32_SYSTEM
  /* Under Windows we need to use AllowSetForegroundWindow.  Tell
     gpgsm to tell us when it needs it.  */
  if (!err)
    {
      err = assuan_transact (gpgsm->assuan_ctx, "OPTION allow-pinentry-notify",
                             NULL, NULL, NULL, NULL, NULL, NULL);
      if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
        err = 0; /* This is a new feature of gpgsm.  */
    }
#endif /*HAVE_W32_SYSTEM*/

#if !USE_DESCRIPTOR_PASSING
  if (!err
      && (_gpgme_io_set_close_notify (gpgsm->input_cb.fd,
				      close_notify_handler, gpgsm)
	  || _gpgme_io_set_close_notify (gpgsm->output_cb.fd,
					 close_notify_handler, gpgsm)
	  || _gpgme_io_set_close_notify (gpgsm->message_cb.fd,
					 close_notify_handler, gpgsm)))
    {
      err = gpg_error (GPG_ERR_GENERAL);
      goto leave;
    }
#endif

 leave:
  /* Close the server ends of the pipes (because of this, we must use
     the stored server_fd_str in the function start).  Our ends are
     closed in gpgsm_release().  */
#if !USE_DESCRIPTOR_PASSING
  if (gpgsm->input_cb.server_fd != -1)
    _gpgme_io_close (gpgsm->input_cb.server_fd);
  if (gpgsm->output_cb.server_fd != -1)
    _gpgme_io_close (gpgsm->output_cb.server_fd);
  if (gpgsm->message_cb.server_fd != -1)
    _gpgme_io_close (gpgsm->message_cb.server_fd);
#endif

  if (err)
    gpgsm_release (gpgsm);
  else
    *engine = gpgsm;

  return err;
}
Exemplo n.º 16
0
static gpgme_error_t
gpgsm_new(void **engine, const char *file_name, const char *home_dir)
{
    gpgme_error_t err = 0;
    engine_gpgsm_t gpgsm;
    const char *argv[5];
    int argc;
#if !USE_DESCRIPTOR_PASSING
    int fds[2];
    int child_fds[4];
#endif
    char *dft_display = NULL;
    char dft_ttyname[64];
    char *dft_ttytype = NULL;
    char *optstr;

    gpgsm = calloc(1, sizeof * gpgsm);
    if(!gpgsm)
        return gpg_error_from_errno(errno);

    gpgsm->status_cb.fd = -1;
    gpgsm->status_cb.dir = 1;
    gpgsm->status_cb.tag = 0;
    gpgsm->status_cb.data = gpgsm;

    gpgsm->input_cb.fd = -1;
    gpgsm->input_cb.dir = 0;
    gpgsm->input_cb.tag = 0;
    gpgsm->input_cb.server_fd = -1;
    gpgsm->output_cb.fd = -1;
    gpgsm->output_cb.dir = 1;
    gpgsm->output_cb.tag = 0;
    gpgsm->output_cb.server_fd = -1;
    gpgsm->message_cb.fd = -1;
    gpgsm->message_cb.dir = 0;
    gpgsm->message_cb.tag = 0;
    gpgsm->message_cb.server_fd = -1;

    gpgsm->status.fnc = 0;
    gpgsm->colon.fnc = 0;
    gpgsm->colon.attic.line = 0;
    gpgsm->colon.attic.linesize = 0;
    gpgsm->colon.attic.linelen = 0;
    gpgsm->colon.any = 0;

    gpgsm->io_cbs.add = NULL;
    gpgsm->io_cbs.add_priv = NULL;
    gpgsm->io_cbs.remove = NULL;
    gpgsm->io_cbs.event = NULL;
    gpgsm->io_cbs.event_priv = NULL;

#if !USE_DESCRIPTOR_PASSING
    if(_gpgme_io_pipe(fds, 0) < 0)
    {
        err = gpg_error_from_errno(errno);
        goto leave;
    }
    gpgsm->input_cb.fd = fds[1];
    gpgsm->input_cb.server_fd = fds[0];

    if(_gpgme_io_pipe(fds, 1) < 0)
    {
        err = gpg_error_from_errno(errno);
        goto leave;
    }
    gpgsm->output_cb.fd = fds[0];
    gpgsm->output_cb.server_fd = fds[1];

    if(_gpgme_io_pipe(fds, 0) < 0)
    {
        err = gpg_error_from_errno(errno);
        goto leave;
    }
    gpgsm->message_cb.fd = fds[1];
    gpgsm->message_cb.server_fd = fds[0];

    child_fds[0] = gpgsm->input_cb.server_fd;
    child_fds[1] = gpgsm->output_cb.server_fd;
    child_fds[2] = gpgsm->message_cb.server_fd;
    child_fds[3] = -1;
#endif

    argc = 0;
    argv[argc++] = "gpgsm";
    if(home_dir)
    {
        argv[argc++] = "--homedir";
        argv[argc++] = home_dir;
    }
    argv[argc++] = "--server";
    argv[argc++] = NULL;

#if USE_DESCRIPTOR_PASSING
    err = assuan_pipe_connect_ext
          (&gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path(),
           argv, NULL, NULL, NULL, 1);
#else
    err = assuan_pipe_connect
          (&gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path(),
           argv, child_fds);
#endif
    if(err)
        goto leave;

    err = _gpgme_getenv("DISPLAY", &dft_display);
    if(err)
        goto leave;
    if(dft_display)
    {
        if(asprintf(&optstr, "OPTION display=%s", dft_display) < 0)
        {
            free(dft_display);
            err = gpg_error_from_errno(errno);
            goto leave;
        }
        free(dft_display);

        err = assuan_transact(gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
                              NULL, NULL, NULL);
        free(optstr);
        if(err)
        {
            err = map_assuan_error(err);
            goto leave;
        }
    }

    if(isatty(1))
    {
        if(ttyname_r(1, dft_ttyname, sizeof(dft_ttyname)))
        {
            err = gpg_error_from_errno(errno);
            goto leave;
        }
        else
        {
            if(asprintf(&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
            {
                err = gpg_error_from_errno(errno);
                goto leave;
            }
            err = assuan_transact(gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
                                  NULL, NULL, NULL);
            free(optstr);
            if(err)
            {
                err = map_assuan_error(err);
                goto leave;
            }

            err = _gpgme_getenv("TERM", &dft_ttytype);
            if(err)
                goto leave;
            if(dft_ttytype)
            {
                if(asprintf(&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
                {
                    free(dft_ttytype);
                    err = gpg_error_from_errno(errno);
                    goto leave;
                }
                free(dft_ttytype);

                err = assuan_transact(gpgsm->assuan_ctx, optstr, NULL, NULL,
                                      NULL, NULL, NULL, NULL);
                free(optstr);
                if(err)
                {
                    err = map_assuan_error(err);
                    goto leave;
                }
            }
        }
    }

#if !USE_DESCRIPTOR_PASSING
    if(!err
            && (_gpgme_io_set_close_notify(gpgsm->input_cb.fd,
                                           close_notify_handler, gpgsm)
                || _gpgme_io_set_close_notify(gpgsm->output_cb.fd,
                        close_notify_handler, gpgsm)
                || _gpgme_io_set_close_notify(gpgsm->message_cb.fd,
                        close_notify_handler, gpgsm)))
    {
        err = gpg_error(GPG_ERR_GENERAL);
        goto leave;
    }
#endif

leave:
    /* Close the server ends of the pipes.  Our ends are closed in
       gpgsm_release().  */
#if !USE_DESCRIPTOR_PASSING
    if(gpgsm->input_cb.server_fd != -1)
        _gpgme_io_close(gpgsm->input_cb.server_fd);
    if(gpgsm->output_cb.server_fd != -1)
        _gpgme_io_close(gpgsm->output_cb.server_fd);
    if(gpgsm->message_cb.server_fd != -1)
        _gpgme_io_close(gpgsm->message_cb.server_fd);
#endif

    if(err)
        gpgsm_release(gpgsm);
    else
        *engine = gpgsm;

    return err;
}
Exemplo n.º 17
0
int
_gpgme_io_spawn (const char *path, char * const argv[], unsigned int flags,
		 struct spawn_fd_item_s *fd_list,
		 void (*atfork) (void *opaque, int reserved),
		 void *atforkvalue, pid_t *r_pid)
{
  SECURITY_ATTRIBUTES sec_attr;
  PROCESS_INFORMATION pi =
    {
      NULL,      /* returns process handle */
      0,         /* returns primary thread handle */
      0,         /* returns pid */
      0          /* returns tid */
    };
  STARTUPINFO si;
  int cr_flags = (CREATE_DEFAULT_ERROR_MODE
                  | GetPriorityClass (GetCurrentProcess ()));
  int i;
  char **args;
  char *arg_string;
  /* FIXME.  */
  int debug_me = 0;
  int tmp_fd;
  char *tmp_name;

  TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
	      "path=%s", path);
  i = 0;
  while (argv[i])
    {
      TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
      i++;
    }
  
  /* We do not inherit any handles by default, and just insert those
     handles we want the child to have afterwards.  But some handle
     values occur on the command line, and we need to move
     stdin/out/err to the right location.  So we use a wrapper program
     which gets the information from a temporary file.  */
  if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
    {
      TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
      return TRACE_SYSRES (-1);
    }
  TRACE_LOG1 ("tmp_name = %s", tmp_name);

  args = calloc (2 + i + 1, sizeof (*args));
  args[0] = (char *) _gpgme_get_w32spawn_path ();
  args[1] = tmp_name;
  args[2] = path;
  memcpy (&args[3], &argv[1], i * sizeof (*args));

  memset (&sec_attr, 0, sizeof sec_attr);
  sec_attr.nLength = sizeof sec_attr;
  sec_attr.bInheritHandle = FALSE;
  
  arg_string = build_commandline (args);
  free (args);
  if (!arg_string)
    {
      close (tmp_fd);
      DeleteFile (tmp_name);
      return TRACE_SYSRES (-1);
    }

  memset (&si, 0, sizeof si);
  si.cb = sizeof (si);
  si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
  si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
  si.hStdInput = INVALID_HANDLE_VALUE;
  si.hStdOutput = INVALID_HANDLE_VALUE;
  si.hStdError = INVALID_HANDLE_VALUE;

  cr_flags |= CREATE_SUSPENDED;
  cr_flags |= DETACHED_PROCESS;
  if (!CreateProcessA (_gpgme_get_w32spawn_path (),
		       arg_string,
		       &sec_attr,     /* process security attributes */
		       &sec_attr,     /* thread security attributes */
		       FALSE,         /* inherit handles */
		       cr_flags,      /* creation flags */
		       NULL,          /* environment */
		       NULL,          /* use current drive/directory */
		       &si,           /* startup information */
		       &pi))          /* returns process information */
    {
      TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
      free (arg_string);
      close (tmp_fd);
      DeleteFile (tmp_name);

      /* FIXME: Should translate the error code.  */
      errno = EIO;
      return TRACE_SYSRES (-1);
    }

  free (arg_string);
  
  if (flags & IOSPAWN_FLAG_ALLOW_SET_FG)
    _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId);

  /* Insert the inherited handles.  */
  for (i = 0; fd_list[i].fd != -1; i++)
    {
      HANDLE hd;

      /* Make it inheritable for the wrapper process.  */
      if (!DuplicateHandle (GetCurrentProcess(),
			    _get_osfhandle (giochannel_table[fd_list[i].fd].fd),
			    pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
	{
	  TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
	  TerminateProcess (pi.hProcess, 0);
	  /* Just in case TerminateProcess didn't work, let the
	     process fail on its own.  */
	  ResumeThread (pi.hThread);
	  CloseHandle (pi.hThread);
	  CloseHandle (pi.hProcess);

	  close (tmp_fd);
	  DeleteFile (tmp_name);

	  /* FIXME: Should translate the error code.  */
	  errno = EIO;
	  return TRACE_SYSRES (-1);
        }
      /* Return the child name of this handle.  */
      fd_list[i].peer_name = (int) hd;
    }

  /* Write the handle translation information to the temporary
     file.  */
  {
    /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
       notation: "0xFEDCBA9876543210" with an extra white space after
       every quadruplet.  10*(19*4 + 1) - 1 = 769.  This plans ahead
       for a time when a HANDLE is 64 bit.  */
#define BUFFER_MAX 800
    char line[BUFFER_MAX + 1];
    int res;
    int written;
    size_t len;

    if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
      strcpy (line, "~1 \n");
    else
      strcpy (line, "\n");
    for (i = 0; fd_list[i].fd != -1; i++)
      {
	/* Strip the newline.  */
	len = strlen (line) - 1;
	
	/* Format is: Local name, stdin/stdout/stderr, peer name, argv idx.  */
	snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d  \n",
		  fd_list[i].fd, fd_list[i].dup_to,
		  fd_list[i].peer_name, fd_list[i].arg_loc);
	/* Rather safe than sorry.  */
	line[BUFFER_MAX - 1] = '\n';
	line[BUFFER_MAX] = '\0';
      }
    len = strlen (line);
    written = 0;
    do
      {
	res = write (tmp_fd, &line[written], len - written);
	if (res > 0)
	  written += res;
      }
    while (res > 0 || (res < 0 && errno == EAGAIN));
  }
  close (tmp_fd);
  /* The temporary file is deleted by the gpgme-w32spawn process
     (hopefully).  */
    
  TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
	      "dwProcessID=%d, dwThreadId=%d",
	      pi.hProcess, pi.hThread, 
	      (int) pi.dwProcessId, (int) pi.dwThreadId);
  
  if (r_pid)
    *r_pid = (pid_t)pi.dwProcessId;

  if (ResumeThread (pi.hThread) < 0)
    TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
  
  if (!CloseHandle (pi.hThread))
    TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
		(int) GetLastError ());

  TRACE_LOG1 ("process=%p", pi.hProcess);

  /* We don't need to wait for the process.  */
  if (!CloseHandle (pi.hProcess))
    TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
		(int) GetLastError ());

  if (! (flags & IOSPAWN_FLAG_NOCLOSE))
    {
      for (i = 0; fd_list[i].fd != -1; i++)
	_gpgme_io_close (fd_list[i].fd);
    }

  for (i = 0; fd_list[i].fd != -1; i++)
    if (fd_list[i].dup_to == -1)
      TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
		  fd_list[i].peer_name);
    else
      TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
		  fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
		  ((fd_list[i].dup_to == 1) ? "out" : "err"));

  return TRACE_SYSRES (0);
}
Exemplo n.º 18
0
int
_gpgme_io_pipe (int filedes[2], int inherit_idx)
{
  int fds[2];

  TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
	      "inherit_idx=%i (GPGME uses it for %s)",
	      inherit_idx, inherit_idx ? "reading" : "writing");

#define PIPEBUF_SIZE  4096
  if (_pipe (fds, PIPEBUF_SIZE, O_NOINHERIT | O_BINARY) == -1)
    return TRACE_SYSRES (-1);

  /* Make one end inheritable. */
  if (inherit_idx == 0)
    {
      int new_read;

      new_read = _dup (fds[0]);
      _close (fds[0]);
      fds[0] = new_read;

      if (new_read < 0)
	{
	  _close (fds[1]);
	  return TRACE_SYSRES (-1);
	}
    }
  else if (inherit_idx == 1)
    {
      int new_write;

      new_write = _dup (fds[1]);
      _close (fds[1]);
      fds[1] = new_write;

      if (new_write < 0)
	{
	  _close (fds[0]);
	  return TRACE_SYSRES (-1);
	}
    }

  /* For _gpgme_io_close.  */
  filedes[inherit_idx] = new_dummy_channel_from_fd (fds[inherit_idx]);
  if (filedes[inherit_idx] < 0)
    {
      int saved_errno = errno;
      
      _close (fds[0]);
      _close (fds[1]);
      errno = saved_errno;
      return TRACE_SYSRES (-1);
    }

  /* Now we have a pipe with the correct end inheritable.  The other end
     should have a giochannel.  */
  filedes[1 - inherit_idx] = new_channel_from_fd (fds[1 - inherit_idx]);
  if (filedes[1 - inherit_idx] < 0)
    {
      int saved_errno = errno;
      
      _gpgme_io_close (fds[inherit_idx]);
      _close (fds[1 - inherit_idx]);
      errno = saved_errno;
      return TRACE_SYSRES (-1);
    }
  
  return TRACE_SUC5 ("read=0x%x/%p, write=0x%x/%p, channel=%p",
		     filedes[0],
		     (HANDLE) _get_osfhandle (giochannel_table[filedes[0]].fd),
		     filedes[1],
		     (HANDLE) _get_osfhandle (giochannel_table[filedes[1]].fd),
		     giochannel_table[1 - inherit_idx].chan);
}
Exemplo n.º 19
0
static gpgme_error_t
status_handler(void *opaque, int fd)
{
    gpg_error_t assuan_err;
    gpgme_error_t err = 0;
    engine_gpgsm_t gpgsm = opaque;
    char *line;
    size_t linelen;

    do
    {
        assuan_err = assuan_read_line(gpgsm->assuan_ctx, &line, &linelen);
        if(assuan_err)
        {
            /* Try our best to terminate the connection friendly.  */
            /*	  assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
            err = map_assuan_error(assuan_err);
            DEBUG3("fd %d: error from assuan (%d) getting status line : %s \n",
                   fd, assuan_err, gpg_strerror(err));
        }
        else if(linelen >= 3
                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
                && (line[3] == '\0' || line[3] == ' '))
        {
            if(line[3] == ' ')
                err = map_assuan_error(atoi(&line[4]));
            else
                err = gpg_error(GPG_ERR_GENERAL);
            DEBUG2("fd %d: ERR line - mapped to: %s\n",
                   fd, err ? gpg_strerror(err) : "ok");
        }
        else if(linelen >= 2
                && line[0] == 'O' && line[1] == 'K'
                && (line[2] == '\0' || line[2] == ' '))
        {
            if(gpgsm->status.fnc)
                err = gpgsm->status.fnc(gpgsm->status.fnc_value,
                                        GPGME_STATUS_EOF, "");

            if(!err && gpgsm->colon.fnc && gpgsm->colon.any)
            {
                /* We must tell a colon function about the EOF. We do
                   this only when we have seen any data lines.  Note
                   that this inlined use of colon data lines will
                   eventually be changed into using a regular data
                   channel. */
                gpgsm->colon.any = 0;
                err = gpgsm->colon.fnc(gpgsm->colon.fnc_value, NULL);
            }
            _gpgme_io_close(gpgsm->status_cb.fd);
            DEBUG2("fd %d: OK line - final status: %s\n",
                   fd, err ? gpg_strerror(err) : "ok");
            return err;
        }
        else if(linelen > 2
                && line[0] == 'D' && line[1] == ' '
                && gpgsm->colon.fnc)
        {
            /* We are using the colon handler even for plain inline data
                   - strange name for that function but for historic reasons
                   we keep it.  */
            /* FIXME We can't use this for binary data because we
               assume this is a string.  For the current usage of colon
               output it is correct.  */
            char *src = line + 2;
            char *end = line + linelen;
            char *dst;
            char **aline = &gpgsm->colon.attic.line;
            int *alinelen = &gpgsm->colon.attic.linelen;

            if(gpgsm->colon.attic.linesize
                    < *alinelen + linelen + 1)
            {
                char *newline = realloc(*aline, *alinelen + linelen + 1);
                if(!newline)
                    err = gpg_error_from_errno(errno);
                else
                {
                    *aline = newline;
                    gpgsm->colon.attic.linesize += linelen + 1;
                }
            }
            if(!err)
            {
                dst = *aline + *alinelen;

                while(!err && src < end)
                {
                    if(*src == '%' && src + 2 < end)
                    {
                        /* Handle escaped characters.  */
                        ++src;
                        *dst = _gpgme_hextobyte(src);
                        (*alinelen)++;
                        src += 2;
                    }
                    else
                    {
                        *dst = *src++;
                        (*alinelen)++;
                    }

                    if(*dst == '\n')
                    {
                        /* Terminate the pending line, pass it to the colon
                        handler and reset it.  */

                        gpgsm->colon.any = 1;
                        if(*alinelen > 1 && *(dst - 1) == '\r')
                            dst--;
                        *dst = '\0';

                        /* FIXME How should we handle the return code?  */
                        err = gpgsm->colon.fnc(gpgsm->colon.fnc_value, *aline);
                        if(!err)
                        {
                            dst = *aline;
                            *alinelen = 0;
                        }
                    }
                    else
                        dst++;
                }
            }
            DEBUG2("fd %d: D line; final status: %s\n",
                   fd, err ? gpg_strerror(err) : "ok");
        }
        else if(linelen > 2
                && line[0] == 'S' && line[1] == ' ')
        {
            char *rest;
            gpgme_status_code_t r;

            rest = strchr(line + 2, ' ');
            if(!rest)
                rest = line + linelen; /* set to an empty string */
            else
                *(rest++) = 0;

            r = parse_status(line + 2);

            if(r >= 0)
            {
                if(gpgsm->status.fnc)
                    err = gpgsm->status.fnc(gpgsm->status.fnc_value, r, rest);
            }
            else
                fprintf(stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
            DEBUG3("fd %d: S line (%s) - final status: %s\n",
                   fd, line + 2, err ? gpg_strerror(err) : "ok");
        }
    }
    while(!err && assuan_pending_line(gpgsm->assuan_ctx));

    return err;
}
Exemplo n.º 20
0
/* Read from gpgconf and pass line after line to the hook function.
   We put a limit of 64 k on the maximum size for a line.  This should
   allow for quite a long "group" line, which is usually the longest
   line (mine is currently ~3k).  */
static gpgme_error_t
gpgconf_read (void *engine, char *arg1, char *arg2,
	      gpgme_error_t (*cb) (void *hook, char *line),
	      void *hook)
{
  struct engine_gpgconf *gpgconf = engine;
  gpgme_error_t err = 0;
  char *linebuf;
  size_t linebufsize;
  int linelen;
  char *argv[4] = { NULL /* file_name */, NULL, NULL, NULL };
  int rp[2];
  struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
				   {-1, -1} };
  int status;
  int nread;
  char *mark = NULL;

  argv[1] = arg1;
  argv[2] = arg2;


  /* FIXME: Deal with engine->home_dir.  */

  /* _gpgme_engine_new guarantees that this is not NULL.  */
  argv[0] = gpgconf->file_name;

  if (_gpgme_io_pipe (rp, 1) < 0)
    return gpg_error_from_syserror ();

  cfd[0].fd = rp[1];

  status = _gpgme_io_spawn (gpgconf->file_name, argv, 0, cfd, NULL, NULL, NULL);
  if (status < 0)
    {
      _gpgme_io_close (rp[0]);
      _gpgme_io_close (rp[1]);
      return gpg_error_from_syserror ();
    }

  linebufsize = 1024; /* Usually enough for conf lines.  */
  linebuf = malloc (linebufsize);
  if (!linebuf)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  linelen = 0;

  while ((nread = _gpgme_io_read (rp[0], linebuf + linelen,
                                  linebufsize - linelen - 1)))
    {
      char *line;
      const char *lastmark = NULL;
      size_t nused;

      if (nread < 0)
        {
          err = gpg_error_from_syserror ();
          goto leave;
        }

      linelen += nread;
      linebuf[linelen] = '\0';

      for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
        {
          lastmark = mark;
          if (mark > line && mark[-1] == '\r')
            mark[-1] = '\0';
          else
            mark[0] = '\0';

          /* Got a full line.  Due to the CR removal code (which
             occurs only on Windows) we might be one-off and thus
             would see empty lines.  Don't pass them to the
             callback. */
          err = *line? (*cb) (hook, line) : 0;
          if (err)
            goto leave;
        }

      nused = lastmark? (lastmark + 1 - linebuf) : 0;
      memmove (linebuf, linebuf + nused, linelen - nused);
      linelen -= nused;

      if (!(linelen < linebufsize - 1))
        {
          char *newlinebuf;

          if (linelen <  8 * 1024 - 1)
            linebufsize = 8 * 1024;
          else if (linelen < 64 * 1024 - 1)
            linebufsize = 64 * 1024;
          else
            {
              /* We reached our limit - give up.  */
              err = gpg_error (GPG_ERR_LINE_TOO_LONG);
              goto leave;
            }

          newlinebuf = realloc (linebuf, linebufsize);
          if (!newlinebuf)
            {
              err = gpg_error_from_syserror ();
              goto leave;
            }
          linebuf = newlinebuf;
        }
    }

 leave:
  free (linebuf);
  _gpgme_io_close (rp[0]);
  return err;
}
Exemplo n.º 21
0
/* Returns 0 on success, -1 on error.  */
int
_gpgme_io_spawn(const char *path, char **argv,
                struct spawn_fd_item_s *fd_child_list,
                struct spawn_fd_item_s *fd_parent_list)
{
    pid_t pid;
    int i;
    int status, signo;

    pid = fork();
    if(pid == -1)
        return -1;

    if(!pid)
    {
        /* Intermediate child to prevent zombie processes.  */
        if((pid = fork()) == 0)
        {
            /* Child.  */
            int duped_stdin = 0;
            int duped_stderr = 0;

            /* First close all fds which will not be duped.  */
            for(i = 0; fd_child_list[i].fd != -1; i++)
                if(fd_child_list[i].dup_to == -1)
                    close(fd_child_list[i].fd);

            /* And now dup and close the rest.  */
            for(i = 0; fd_child_list[i].fd != -1; i++)
            {
                if(fd_child_list[i].dup_to != -1)
                {
                    if(dup2(fd_child_list[i].fd,
                            fd_child_list[i].dup_to) == -1)
                    {
                        DEBUG1("dup2 failed in child: %s\n", strerror(errno));
                        _exit(8);
                    }
                    if(fd_child_list[i].dup_to == 0)
                        duped_stdin = 1;
                    if(fd_child_list[i].dup_to == 2)
                        duped_stderr = 1;
                    close(fd_child_list[i].fd);
                }
            }

            if(!duped_stdin || !duped_stderr)
            {
                int fd = open("/dev/null", O_RDWR);
                if(fd == -1)
                {
                    DEBUG1("can't open `/dev/null': %s\n", strerror(errno));
                    _exit(8);
                }
                /* Make sure that the process has a connected stdin.  */
                if(!duped_stdin)
                {
                    if(dup2(fd, 0) == -1)
                    {
                        DEBUG1("dup2(/dev/null, 0) failed: %s\n",
                               strerror(errno));
                        _exit(8);
                    }
                }
                if(!duped_stderr)
                    if(dup2(fd, 2) == -1)
                    {
                        DEBUG1("dup2(dev/null, 2) failed: %s\n",
                               strerror(errno));
                        _exit(8);
                    }
                close(fd);
            }

            execv(path, argv);
            /* Hmm: in that case we could write a special status code to the
               status-pipe.  */
            DEBUG1("exec of `%s' failed\n", path);
            _exit(8);
        } /* End child.  */
        if(pid == -1)
            _exit(1);
        else
            _exit(0);
    }

    _gpgme_io_waitpid(pid, 1, &status, &signo);
    if(status)
        return -1;

    /* .dup_to is not used in the parent list.  */
    for(i = 0; fd_parent_list[i].fd != -1; i++)
        _gpgme_io_close(fd_parent_list[i].fd);

    return 0;
}
Exemplo n.º 22
0
/* Perform asynchronous operations in the global event loop (ie, any
   asynchronous operation except key listing and trustitem listing
   operations).  If CTX is not a null pointer, the function will
   return if the asynchronous operation in the context CTX finished.
   Otherwise the function will return if any asynchronous operation
   finished.  If HANG is zero, the function will not block for a long
   time.  Otherwise the function does not return until an operation
   matching CTX finished.

   If a matching context finished, it is returned, and *STATUS is set
   to the error value of the operation in that context.  Otherwise, if
   the timeout expires, NULL is returned and *STATUS is 0.  If an
   error occurs, NULL is returned and *STATUS is set to the error
   value.  */
gpgme_ctx_t
gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang)
{
  do
    {
      unsigned int i = 0;
      struct ctx_list_item *li;
      struct fd_table fdt;
      int nr;

      /* Collect the active file descriptors.  */
      LOCK (ctx_list_lock);
      for (li = ctx_active_list; li; li = li->next)
	i += li->ctx->fdt.size;
      fdt.fds = malloc (i * sizeof (struct io_select_fd_s));
      if (!fdt.fds)
	{
	  int saved_errno = errno;
	  UNLOCK (ctx_list_lock);
	  if (status)
	    *status = gpg_error_from_errno (saved_errno);
	  return NULL;
	}
      fdt.size = i;
      i = 0;
      for (li = ctx_active_list; li; li = li->next)
	{
	  memcpy (&fdt.fds[i], li->ctx->fdt.fds,
		  li->ctx->fdt.size * sizeof (struct io_select_fd_s));
	  i += li->ctx->fdt.size;
	}
      UNLOCK (ctx_list_lock);

      nr = _gpgme_io_select (fdt.fds, fdt.size, 0);
      if (nr < 0)
	{
	  int saved_errno = errno;
	  free (fdt.fds);
	  if (status)
	    *status = gpg_error_from_errno (saved_errno);
	  return NULL;
	}

      for (i = 0; i < fdt.size && nr; i++)
	{
	  if (fdt.fds[i].fd != -1 && fdt.fds[i].signaled)
	    {
	      gpgme_ctx_t ictx;
	      gpgme_error_t err;
	      struct wait_item_s *item;
	      
	      assert (nr);
	      nr--;
	      
	      item = (struct wait_item_s *) fdt.fds[i].opaque;
	      assert (item);
	      ictx = item->ctx;
	      assert (ictx);

	      err = _gpgme_run_io_cb (&fdt.fds[i], 0);
	      if (err)
		{
		  /* An error occured.  Close all fds in this context,
		     and signal it.  */
		  unsigned int idx;
	    
		  for (idx = 0; idx < ictx->fdt.size; idx++)
		    if (ictx->fdt.fds[idx].fd != -1)
		      _gpgme_io_close (ictx->fdt.fds[idx].fd);
		  _gpgme_engine_io_event (ictx->engine, GPGME_EVENT_DONE,
					  &err);

		  /* Break out of the loop, and retry the select()
		     from scratch, because now all fds should be
		     gone.  */
		  break;
		}
	    }
	}
      free (fdt.fds);

      /* Now some contexts might have finished successfully.  */
      LOCK (ctx_list_lock);
    retry:
      for (li = ctx_active_list; li; li = li->next)
	{
	  gpgme_ctx_t actx = li->ctx;

	  for (i = 0; i < actx->fdt.size; i++)
	    if (actx->fdt.fds[i].fd != -1)
	      break;
	  if (i == actx->fdt.size)
	    {
	      gpgme_error_t err = 0;

	      /* FIXME: This does not perform too well.  We have to
		 release the lock because the I/O event handler
		 acquires it to remove the context from the active
		 list.  Two alternative strategies are worth
		 considering: Either implement the DONE event handler
		 here in a lock-free manner, or save a list of all
		 contexts to be released and call the DONE events
		 afterwards.  */
	      UNLOCK (ctx_list_lock);
	      _gpgme_engine_io_event (actx->engine, GPGME_EVENT_DONE, &err);
	      LOCK (ctx_list_lock);
	      goto retry;
	    }
	}
      UNLOCK (ctx_list_lock);

      {
	gpgme_ctx_t dctx = ctx_wait (ctx, status);

	if (dctx)
	  {
	    ctx = dctx;
	    hang = 0;
	  }
	else if (!hang)
	  {
	    ctx = NULL;
	    if (status)
	      *status = 0;
	  }
      }
    }
  while (hang);

  return ctx;
}
Exemplo n.º 23
0
int
_gpgme_io_spawn ( const char *path, char **argv,
                  struct spawn_fd_item_s *fd_child_list,
                  struct spawn_fd_item_s *fd_parent_list )
{
    SECURITY_ATTRIBUTES sec_attr;
    PROCESS_INFORMATION pi = {
        NULL,      /* returns process handle */
        0,         /* returns primary thread handle */
        0,         /* returns pid */
        0         /* returns tid */
    };
    STARTUPINFO si;
    char *envblock = NULL;
    int cr_flags = CREATE_DEFAULT_ERROR_MODE
                 | GetPriorityClass (GetCurrentProcess ());
    int i;
    char *arg_string;
    int duped_stdin = 0;
    int duped_stderr = 0;
    HANDLE hnul = INVALID_HANDLE_VALUE;
    /* FIXME.  */
    int debug_me = 0;

    memset (&sec_attr, 0, sizeof sec_attr );
    sec_attr.nLength = sizeof sec_attr;
    sec_attr.bInheritHandle = FALSE;

    arg_string = build_commandline ( argv );
    if (!arg_string )
        return -1; 

    memset (&si, 0, sizeof si);
    si.cb = sizeof (si);
    si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
    si.wShowWindow = debug_me? SW_SHOW : SW_HIDE;
    si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
    si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
    si.hStdError = GetStdHandle (STD_ERROR_HANDLE);

    for (i=0; fd_child_list[i].fd != -1; i++ ) {
        if (fd_child_list[i].dup_to == 0 ) {
            si.hStdInput = fd_to_handle (fd_child_list[i].fd);
            DEBUG1 ("using %d for stdin", fd_child_list[i].fd );
            duped_stdin=1;
        }
        else if (fd_child_list[i].dup_to == 1 ) {
            si.hStdOutput = fd_to_handle (fd_child_list[i].fd);
            DEBUG1 ("using %d for stdout", fd_child_list[i].fd );
        }
        else if (fd_child_list[i].dup_to == 2 ) {
            si.hStdError = fd_to_handle (fd_child_list[i].fd);
            DEBUG1 ("using %d for stderr", fd_child_list[i].fd );
            duped_stderr = 1;
        }
    }

    if( !duped_stdin || !duped_stderr ) {
        SECURITY_ATTRIBUTES sa;

        memset (&sa, 0, sizeof sa );
        sa.nLength = sizeof sa;
        sa.bInheritHandle = TRUE;
        hnul = CreateFile ( "nul",
                            GENERIC_READ|GENERIC_WRITE,
                            FILE_SHARE_READ|FILE_SHARE_WRITE,
                            &sa,
                            OPEN_EXISTING,
                            FILE_ATTRIBUTE_NORMAL,
                            NULL );
        if ( hnul == INVALID_HANDLE_VALUE ) {
            DEBUG1 ("can't open `nul': ec=%d\n", (int)GetLastError ());
            free (arg_string);
            return -1;
        }
        /* Make sure that the process has a connected stdin */
        if ( !duped_stdin ) {
            si.hStdInput = hnul;
            DEBUG1 ("using %d for dummy stdin", (int)hnul );
        }
        /* We normally don't want all the normal output */
        if ( !duped_stderr ) {
            si.hStdError = hnul;
            DEBUG1 ("using %d for dummy stderr", (int)hnul );
        }
    }

    DEBUG2 ("CreateProcess, path=`%s' args=`%s'", path, arg_string);
    cr_flags |= CREATE_SUSPENDED; 
    if ( !CreateProcessA (path,
                          arg_string,
                          &sec_attr,     /* process security attributes */
                          &sec_attr,     /* thread security attributes */
                          TRUE,          /* inherit handles */
                          cr_flags,      /* creation flags */
                          envblock,      /* environment */
                          NULL,          /* use current drive/directory */
                          &si,           /* startup information */
                          &pi            /* returns process information */
        ) ) {
        DEBUG1 ("CreateProcess failed: ec=%d\n", (int) GetLastError ());
        free (arg_string);
        return -1;
    }

    /* Close the /dev/nul handle if used. */
    if (hnul != INVALID_HANDLE_VALUE ) {
        if ( !CloseHandle ( hnul ) )
            DEBUG1 ("CloseHandle(hnul) failed: ec=%d\n", (int)GetLastError());
    }

    /* Close the other ends of the pipes. */
    for (i = 0; fd_parent_list[i].fd != -1; i++)
      _gpgme_io_close (fd_parent_list[i].fd);

    DEBUG4 ("CreateProcess ready\n"
            "-   hProcess=%p  hThread=%p\n"
            "-   dwProcessID=%d dwThreadId=%d\n",
            pi.hProcess, pi.hThread, 
            (int) pi.dwProcessId, (int) pi.dwThreadId);

    if ( ResumeThread ( pi.hThread ) < 0 ) {
        DEBUG1 ("ResumeThread failed: ec=%d\n", (int)GetLastError ());
    }

    if ( !CloseHandle (pi.hThread) ) { 
        DEBUG1 ("CloseHandle of thread failed: ec=%d\n",
                 (int)GetLastError ());
    }

    return handle_to_pid (pi.hProcess);
}
Exemplo n.º 24
0
static gpgme_error_t
gpgconf_write (void *engine, char *arg1, char *arg2, gpgme_data_t conf)
{
  struct engine_gpgconf *gpgconf = engine;
  gpgme_error_t err = 0;
#define BUFLEN 1024
  char buf[BUFLEN];
  int buflen = 0;
  char *argv[] = { NULL /* file_name */, arg1, arg2, 0 };
  int rp[2];
  struct spawn_fd_item_s cfd[] = { {-1, 0 /* STDIN_FILENO */}, {-1, -1} };
  int status;
  int nwrite;

  /* FIXME: Deal with engine->home_dir.  */

  /* _gpgme_engine_new guarantees that this is not NULL.  */
  argv[0] = gpgconf->file_name;

  if (_gpgme_io_pipe (rp, 0) < 0)
    return gpg_error_from_syserror ();

  cfd[0].fd = rp[0];

  status = _gpgme_io_spawn (gpgconf->file_name, argv, 0, cfd, NULL, NULL, NULL);
  if (status < 0)
    {
      _gpgme_io_close (rp[0]);
      _gpgme_io_close (rp[1]);
      return gpg_error_from_syserror ();
    }

  for (;;)
    {
      if (buflen == 0)
	{
	  do
	    {
	      buflen = gpgme_data_read (conf, buf, BUFLEN);
	    }
	  while (buflen < 0 && errno == EAGAIN);

	  if (buflen < 0)
	    {
	      err = gpg_error_from_syserror ();
	      _gpgme_io_close (rp[1]);
	      return err;
	    }
	  else if (buflen == 0)
	    {
	      /* All is written.  */
	      _gpgme_io_close (rp[1]);
	      return 0;
	    }
	}

      do
	{
	  nwrite = _gpgme_io_write (rp[1], buf, buflen);
	}
      while (nwrite < 0 && errno == EAGAIN);

      if (nwrite > 0)
	{
	  buflen -= nwrite;
	  if (buflen > 0)
	    memmove (&buf[0], &buf[nwrite], buflen);
	}
      else if (nwrite < 0)
	{
	  _gpgme_io_close (rp[1]);
	  return gpg_error_from_syserror ();
	}
    }

  return 0;
}
Exemplo n.º 25
0
static gpgme_error_t
status_handler (void *opaque, int fd)
{
  struct io_cb_data *data = (struct io_cb_data *) opaque;
  engine_gpgsm_t gpgsm = (engine_gpgsm_t) data->handler_value;
  gpgme_error_t err = 0;
  char *line;
  size_t linelen;

  do
    {
      err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
      if (err)
	{
	  /* Try our best to terminate the connection friendly.  */
	  /*	  assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
          TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
		  "fd 0x%x: error from assuan (%d) getting status line : %s",
                  fd, err, gpg_strerror (err));
	}
      else if (linelen >= 3
	       && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
	       && (line[3] == '\0' || line[3] == ' '))
	{
	  if (line[3] == ' ')
	    err = atoi (&line[4]);
	  if (! err)
	    err = gpg_error (GPG_ERR_GENERAL);
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
		  "fd 0x%x: ERR line - mapped to: %s",
                  fd, err ? gpg_strerror (err) : "ok");
	  /* Try our best to terminate the connection friendly.  */
	  /*	  assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
	}
      else if (linelen >= 2
	       && line[0] == 'O' && line[1] == 'K'
	       && (line[2] == '\0' || line[2] == ' '))
	{
	  if (gpgsm->status.fnc)
	    err = gpgsm->status.fnc (gpgsm->status.fnc_value,
				     GPGME_STATUS_EOF, "");

	  if (!err && gpgsm->colon.fnc && gpgsm->colon.any)
            {
              /* We must tell a colon function about the EOF. We do
                 this only when we have seen any data lines.  Note
                 that this inlined use of colon data lines will
                 eventually be changed into using a regular data
                 channel. */
              gpgsm->colon.any = 0;
              err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
            }
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
		  "fd 0x%x: OK line - final status: %s",
                  fd, err ? gpg_strerror (err) : "ok");
	  _gpgme_io_close (gpgsm->status_cb.fd);
	  return err;
	}
      else if (linelen > 2
	       && line[0] == 'D' && line[1] == ' '
	       && gpgsm->colon.fnc)
        {
	  /* We are using the colon handler even for plain inline data
             - strange name for that function but for historic reasons
             we keep it.  */
          /* FIXME We can't use this for binary data because we
             assume this is a string.  For the current usage of colon
             output it is correct.  */
          char *src = line + 2;
	  char *end = line + linelen;
	  char *dst;
          char **aline = &gpgsm->colon.attic.line;
	  int *alinelen = &gpgsm->colon.attic.linelen;

	  if (gpgsm->colon.attic.linesize < *alinelen + linelen + 1)
	    {
	      char *newline = realloc (*aline, *alinelen + linelen + 1);
	      if (!newline)
		err = gpg_error_from_syserror ();
	      else
		{
		  *aline = newline;
		  gpgsm->colon.attic.linesize += linelen + 1;
		}
	    }
	  if (!err)
	    {
	      dst = *aline + *alinelen;

	      while (!err && src < end)
		{
		  if (*src == '%' && src + 2 < end)
		    {
		      /* Handle escaped characters.  */
		      ++src;
		      *dst = _gpgme_hextobyte (src);
		      (*alinelen)++;
		      src += 2;
		    }
		  else
		    {
		      *dst = *src++;
		      (*alinelen)++;
		    }

		  if (*dst == '\n')
		    {
		      /* Terminate the pending line, pass it to the colon
			 handler and reset it.  */

		      gpgsm->colon.any = 1;
		      if (*alinelen > 1 && *(dst - 1) == '\r')
			dst--;
		      *dst = '\0';

		      /* FIXME How should we handle the return code?  */
		      err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
		      if (!err)
			{
			  dst = *aline;
			  *alinelen = 0;
			}
		    }
		  else
		    dst++;
		}
	    }
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
		  "fd 0x%x: D line; final status: %s",
                  fd, err? gpg_strerror (err):"ok");
        }
      else if (linelen > 2
	       && line[0] == 'D' && line[1] == ' '
	       && gpgsm->inline_data)
        {
          char *src = line + 2;
	  char *end = line + linelen;
	  char *dst = src;
          ssize_t nwritten;

          linelen = 0;
          while (src < end)
            {
              if (*src == '%' && src + 2 < end)
                {
                  /* Handle escaped characters.  */
                  ++src;
                  *dst++ = _gpgme_hextobyte (src);
                  src += 2;
                }
              else
                *dst++ = *src++;

              linelen++;
            }

          src = line + 2;
          while (linelen > 0)
            {
              nwritten = gpgme_data_write (gpgsm->inline_data, src, linelen);
              if (!nwritten || (nwritten < 0 && errno != EINTR)
                  || nwritten > linelen)
                {
                  err = gpg_error_from_syserror ();
                  break;
                }
              src += nwritten;
              linelen -= nwritten;
            }

          TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
		  "fd 0x%x: D inlinedata; final status: %s",
                  fd, err? gpg_strerror (err):"ok");
        }
      else if (linelen > 2
	       && line[0] == 'S' && line[1] == ' ')
	{
	  char *rest;
	  gpgme_status_code_t r;

	  rest = strchr (line + 2, ' ');
	  if (!rest)
	    rest = line + linelen; /* set to an empty string */
	  else
	    *(rest++) = 0;

	  r = _gpgme_parse_status (line + 2);

	  if (r >= 0)
	    {
	      if (gpgsm->status.fnc)
		err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
	    }
	  else
	    fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
          TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
		  "fd 0x%x: S line (%s) - final status: %s",
                  fd, line+2, err? gpg_strerror (err):"ok");
	}
      else if (linelen >= 7
               && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
               && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
               && line[6] == 'E'
               && (line[7] == '\0' || line[7] == ' '))
        {
          char *keyword = line+7;

          while (*keyword == ' ')
            keyword++;;
          default_inq_cb (gpgsm, keyword);
          assuan_write_line (gpgsm->assuan_ctx, "END");
        }

    }
  while (!err && assuan_pending_line (gpgsm->assuan_ctx));

  return err;
}
Exemplo n.º 26
0
/* Close the given file descriptor, created with _assuan_pipe or one
   of the socket functions.  */
static int
my_close (assuan_context_t ctx, assuan_fd_t fd)
{
  return _gpgme_io_close ((int) fd);
}
Exemplo n.º 27
0
static gpgme_error_t
gpgconf_read (void *engine, char *arg1, char *arg2,
	      gpgme_error_t (*cb) (void *hook, char *line),
	      void *hook)
{
  struct engine_gpgconf *gpgconf = engine;
  gpgme_error_t err = 0;
#define LINELENGTH 1024
  char linebuf[LINELENGTH] = "";
  int linelen = 0;
  char *argv[4] = { NULL /* file_name */, NULL, NULL, NULL };
  int rp[2];
  struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
				   {-1, -1} };
  int status;
  int nread;
  char *mark = NULL;

  argv[1] = arg1;
  argv[2] = arg2;


  /* FIXME: Deal with engine->home_dir.  */

  /* _gpgme_engine_new guarantees that this is not NULL.  */
  argv[0] = gpgconf->file_name;
  
  if (_gpgme_io_pipe (rp, 1) < 0)
    return gpg_error_from_syserror ();

  cfd[0].fd = rp[1];

  status = _gpgme_io_spawn (gpgconf->file_name, argv, 0, cfd, NULL, NULL, NULL);
  if (status < 0)
    {
      _gpgme_io_close (rp[0]);
      _gpgme_io_close (rp[1]);
      return gpg_error_from_syserror ();
    }

  do
    {
      nread = _gpgme_io_read (rp[0], 
                              linebuf + linelen, LINELENGTH - linelen - 1);
      if (nread > 0)
	{
          char *line;
          const char *lastmark = NULL;
          size_t nused;

	  linelen += nread;
	  linebuf[linelen] = '\0';

	  for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
	    {
              lastmark = mark;
	      if (mark > line && mark[-1] == '\r')
		mark[-1] = '\0';
              else
                mark[0] = '\0';

	      /* Got a full line.  Due to the CR removal code (which
                 occurs only on Windows) we might be one-off and thus
                 would see empty lines.  Don't pass them to the
                 callback. */
	      err = *line? (*cb) (hook, line) : 0;
	      if (err)
		goto leave;
	    }

          nused = lastmark? (lastmark + 1 - linebuf) : 0;
          memmove (linebuf, linebuf + nused, linelen - nused);
          linelen -= nused;
	}
    }
  while (nread > 0 && linelen < LINELENGTH - 1);
  
  if (!err && nread < 0)
    err = gpg_error_from_syserror ();
  if (!err && nread > 0)
    err = gpg_error (GPG_ERR_LINE_TOO_LONG);

 leave:
  _gpgme_io_close (rp[0]);

  return err;
}
Exemplo n.º 28
0
static gpgme_error_t
status_handler (void *opaque, int fd)
{
  struct io_cb_data *data = (struct io_cb_data *) opaque;
  engine_g13_t g13 = (engine_g13_t) data->handler_value;
  gpgme_error_t err = 0;
  char *line;
  size_t linelen;

  do
    {
      err = assuan_read_line (g13->assuan_ctx, &line, &linelen);
      if (err)
	{
	  /* Try our best to terminate the connection friendly.  */
	  /*	  assuan_write_line (g13->assuan_ctx, "BYE"); */
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
		  "fd 0x%x: error reading assuan line: %s",
                  fd, gpg_strerror (err));
	}
      else if (linelen >= 3
	       && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
	       && (line[3] == '\0' || line[3] == ' '))
	{
	  if (line[3] == ' ')
	    err = atoi (&line[4]);
	  if (! err)
	    err = gpg_error (GPG_ERR_GENERAL);
          TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
		  "fd 0x%x: ERR line: %s",
                  fd, err ? gpg_strerror (err) : "ok");

	  /* Command execution errors are not fatal, as we use
	     a session based protocol.  */
	  data->op_err = err;

	  /* The caller will do the rest (namely, call cancel_op,
	     which closes status_fd).  */
	  return 0;
	}
      else if (linelen >= 2
	       && line[0] == 'O' && line[1] == 'K'
	       && (line[2] == '\0' || line[2] == ' '))
	{
          TRACE1 (DEBUG_CTX, "gpgme:status_handler", g13,
		  "fd 0x%x: OK line", fd);

	  _gpgme_io_close (g13->status_cb.fd);
	  return 0;
	}
      else if (linelen > 2
	       && line[0] == 'D' && line[1] == ' ')
        {
	  /* We are using the colon handler even for plain inline data
             - strange name for that function but for historic reasons
             we keep it.  */
          /* FIXME We can't use this for binary data because we
             assume this is a string.  For the current usage of colon
             output it is correct.  */
          char *src = line + 2;
	  char *end = line + linelen;
	  char *dst = src;

          linelen = 0;
          while (src < end)
            {
              if (*src == '%' && src + 2 < end)
                {
                  /* Handle escaped characters.  */
                  ++src;
                  *dst++ = _gpgme_hextobyte (src);
                  src += 2;
                }
              else
                *dst++ = *src++;

              linelen++;
            }

          src = line + 2;
          if (linelen && g13->user.data_cb)
            err = g13->user.data_cb (g13->user.data_cb_value,
                                       src, linelen);
          else
            err = 0;

          TRACE2 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
		  "fd 0x%x: D inlinedata; status from cb: %s",
                  fd, (g13->user.data_cb ?
                       (err? gpg_strerror (err):"ok"):"no callback"));

        }
      else if (linelen > 2
	       && line[0] == 'S' && line[1] == ' ')
	{
	  char *src;
	  char *args;

	  src = line + 2;
          while (*src == ' ')
            src++;

	  args = strchr (line + 2, ' ');
	  if (!args)
	    args = line + linelen; /* set to an empty string */
	  else
	    *(args++) = 0;

          while (*args == ' ')
            args++;

          if (g13->user.status_cb)
            err = g13->user.status_cb (g13->user.status_cb_value,
				       src, args);
          else
            err = 0;

          TRACE3 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
		  "fd 0x%x: S line (%s) - status from cb: %s",
                  fd, line+2, (g13->user.status_cb ?
                               (err? gpg_strerror (err):"ok"):"no callback"));
	}
      else if (linelen >= 7
               && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
               && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
               && line[6] == 'E'
               && (line[7] == '\0' || line[7] == ' '))
        {
          char *src;
	  char *args;

          for (src=line+7; *src == ' '; src++)
            ;

	  args = strchr (src, ' ');
	  if (!args)
	    args = line + linelen; /* Let it point to an empty string.  */
	  else
	    *(args++) = 0;

          while (*args == ' ')
            args++;

          err = default_inq_cb (g13, src, args);
          if (!err)
            {
              /* Flush and send END.  */
              err = assuan_send_data (g13->assuan_ctx, NULL, 0);
            }
          else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
            {
              /* Flush and send CANcel.  */
              err = assuan_send_data (g13->assuan_ctx, NULL, 1);
            }
          assuan_write_line (g13->assuan_ctx, "END");
        }
    }
  while (!err && assuan_pending_line (g13->assuan_ctx));

  return err;
}