gpgme/gpgme/rungpg.c

1485 lines
40 KiB
C
Raw Normal View History

2000-11-07 13:32:38 +00:00
/* rungpg.c
* Copyright (C) 2000 Werner Koch (dd9jn)
* Copyright (C) 2001, 2002 g10 Code GmbH
2000-11-07 13:32:38 +00:00
*
* This file is part of GPGME.
*
* GPGME is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GPGME is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
2001-03-15 13:09:41 +00:00
#include <time.h>
2000-11-07 13:32:38 +00:00
#include <sys/time.h>
#include <sys/types.h>
#include <signal.h>
2000-11-09 16:35:35 +00:00
#include <fcntl.h>
#include "unistd.h"
2000-11-07 13:32:38 +00:00
#include "gpgme.h"
#include "util.h"
#include "ops.h"
#include "wait.h"
#include "rungpg.h"
#include "context.h" /*temp hack until we have GpmeData methods to do I/O */
#include "io.h"
2001-02-13 15:00:31 +00:00
#include "sema.h"
2000-11-07 13:32:38 +00:00
#include "status-table.h"
2000-12-06 12:17:10 +00:00
2000-11-07 13:32:38 +00:00
/* This type is used to build a list of gpg arguments and
* data sources/sinks */
struct arg_and_data_s {
struct arg_and_data_s *next;
GpgmeData data; /* If this is not NULL .. */
int dup_to;
2000-12-06 12:17:10 +00:00
int print_fd; /* print the fd number and not the special form of it */
2000-11-07 13:32:38 +00:00
char arg[1]; /* .. this is used */
};
struct fd_data_map_s {
GpgmeData data;
int inbound; /* true if this is used for reading from gpg */
int dup_to;
int fd; /* the fd to use */
int peer_fd; /* the outher side of the pipe */
};
struct gpg_object_s {
struct arg_and_data_s *arglist;
struct arg_and_data_s **argtail;
int arg_error;
struct {
int fd[2];
size_t bufsize;
char *buffer;
size_t readpos;
int eof;
GpgStatusHandler fnc;
void *fnc_value;
} status;
2000-11-10 17:50:24 +00:00
/* This is a kludge - see the comment at gpg_colon_line_handler */
struct {
int fd[2];
size_t bufsize;
char *buffer;
size_t readpos;
int eof;
GpgColonLineHandler fnc; /* this indicate use of this structrue */
void *fnc_value;
2001-01-22 20:22:41 +00:00
int simple;
2000-11-10 17:50:24 +00:00
} colon;
2000-11-07 13:32:38 +00:00
char **argv;
struct fd_data_map_s *fd_data_map;
int pid; /* we can't use pid_t because we don't use it in Windoze */
2000-11-07 13:32:38 +00:00
int running;
/* stuff needed for pipemode */
struct {
int used;
int active;
GpgmeData sig;
GpgmeData text;
int stream_started;
} pm;
/* stuff needed for interactive (command) mode */
struct {
int used;
int fd;
GpgmeData cb_data; /* hack to get init the above fd later */
GpgStatusCode code; /* last code */
char *keyword; /* what has been requested (malloced) */
GpgCommandHandler fnc;
void *fnc_value;
} cmd;
2000-11-07 13:32:38 +00:00
};
static void free_argv ( char **argv );
2000-11-09 16:35:35 +00:00
static void free_fd_data_map ( struct fd_data_map_s *fd_data_map );
2000-11-10 17:50:24 +00:00
static int gpg_status_handler ( void *opaque, int pid, int fd );
2000-11-07 13:32:38 +00:00
static GpgmeError read_status ( GpgObject gpg );
static int gpg_colon_line_handler ( void *opaque, int pid, int fd );
2000-11-10 17:50:24 +00:00
static GpgmeError read_colon_line ( GpgObject gpg );
static int pipemode_cb ( void *opaque,
char *buffer, size_t length, size_t *nread );
static int command_cb ( void *opaque,
char *buffer, size_t length, size_t *nread );
2000-11-07 13:32:38 +00:00
static void
close_notify_handler ( int fd, void *opaque )
{
GpgObject gpg = opaque;
assert (fd != -1);
if (gpg->status.fd[0] == fd )
gpg->status.fd[0] = -1;
else if (gpg->status.fd[1] == fd )
gpg->status.fd[1] = -1;
else if (gpg->colon.fd[0] == fd )
gpg->colon.fd[0] = -1;
else if (gpg->colon.fd[1] == fd )
gpg->colon.fd[1] = -1;
else if (gpg->fd_data_map) {
int i;
for (i=0; gpg->fd_data_map[i].data; i++ ) {
if ( gpg->fd_data_map[i].fd == fd ) {
gpg->fd_data_map[i].fd = -1;
break;
}
if ( gpg->fd_data_map[i].peer_fd == fd ) {
gpg->fd_data_map[i].peer_fd = -1;
break;
}
}
}
}
const char *
_gpgme_gpg_get_version (void)
{
static const char *gpg_version;
/* FIXME: Locking. */
if (!gpg_version)
gpg_version = _gpgme_get_program_version (_gpgme_get_gpg_path ());
return gpg_version;
}
GpgmeError
_gpgme_gpg_check_version (void)
{
return _gpgme_compare_versions (_gpgme_gpg_get_version (),
NEED_GPG_VERSION)
? 0 : mk_error (Invalid_Engine);
}
2000-11-07 13:32:38 +00:00
GpgmeError
2000-11-13 13:25:22 +00:00
_gpgme_gpg_new ( GpgObject *r_gpg )
2000-11-07 13:32:38 +00:00
{
GpgObject gpg;
int rc = 0;
gpg = xtrycalloc ( 1, sizeof *gpg );
if ( !gpg ) {
rc = mk_error (Out_Of_Core);
goto leave;
}
gpg->argtail = &gpg->arglist;
gpg->status.fd[0] = -1;
gpg->status.fd[1] = -1;
2000-11-10 17:50:24 +00:00
gpg->colon.fd[0] = -1;
gpg->colon.fd[1] = -1;
gpg->cmd.fd = -1;
2000-11-07 13:32:38 +00:00
2001-02-20 13:31:56 +00:00
gpg->pid = -1;
2000-11-07 13:32:38 +00:00
/* allocate the read buffer for the status pipe */
gpg->status.bufsize = 1024;
gpg->status.readpos = 0;
gpg->status.buffer = xtrymalloc (gpg->status.bufsize);
if (!gpg->status.buffer) {
rc = mk_error (Out_Of_Core);
goto leave;
}
/* In any case we need a status pipe - create it right here and
* don't handle it with our generic GpgmeData mechanism */
2000-12-06 12:17:10 +00:00
if (_gpgme_io_pipe (gpg->status.fd, 1) == -1) {
2000-11-07 13:32:38 +00:00
rc = mk_error (Pipe_Error);
goto leave;
}
if ( _gpgme_io_set_close_notify (gpg->status.fd[0],
close_notify_handler, gpg)
|| _gpgme_io_set_close_notify (gpg->status.fd[1],
close_notify_handler, gpg) ) {
rc = mk_error (General_Error);
goto leave;
}
2000-11-07 13:32:38 +00:00
gpg->status.eof = 0;
_gpgme_gpg_add_arg ( gpg, "--status-fd" );
2000-11-09 16:35:35 +00:00
{
char buf[25];
sprintf ( buf, "%d", gpg->status.fd[1]);
_gpgme_gpg_add_arg ( gpg, buf );
}
_gpgme_gpg_add_arg ( gpg, "--no-tty" );
2000-11-07 13:32:38 +00:00
2000-12-06 12:17:10 +00:00
2000-11-07 13:32:38 +00:00
leave:
if (rc) {
2000-11-13 13:25:22 +00:00
_gpgme_gpg_release (gpg);
2000-11-07 13:32:38 +00:00
*r_gpg = NULL;
}
else
*r_gpg = gpg;
return rc;
}
2000-11-07 13:32:38 +00:00
void
_gpgme_gpg_release (GpgObject gpg)
2000-11-07 13:32:38 +00:00
{
if (!gpg)
return;
while (gpg->arglist)
{
struct arg_and_data_s *next = gpg->arglist->next;
xfree (gpg->arglist);
gpg->arglist = next;
2001-02-13 15:00:31 +00:00
}
xfree (gpg->status.buffer);
xfree (gpg->colon.buffer);
if (gpg->argv)
free_argv (gpg->argv);
xfree (gpg->cmd.cb_data);
xfree (gpg->cmd.keyword);
if (gpg->pid != -1)
_gpgme_remove_proc_from_wait_queue (gpg->pid);
if (gpg->status.fd[0] != -1)
_gpgme_io_close (gpg->status.fd[0]);
if (gpg->status.fd[1] != -1)
_gpgme_io_close (gpg->status.fd[1]);
if (gpg->colon.fd[0] != -1)
_gpgme_io_close (gpg->colon.fd[0]);
if (gpg->colon.fd[1] != -1)
_gpgme_io_close (gpg->colon.fd[1]);
free_fd_data_map (gpg->fd_data_map);
if (gpg->running)
_gpgme_engine_add_child_to_reap_list (gpg, sizeof *gpg, gpg->pid);
else
xfree (gpg);
2000-11-07 13:32:38 +00:00
}
void
_gpgme_gpg_enable_pipemode ( GpgObject gpg )
{
gpg->pm.used = 1;
assert ( !gpg->pm.sig );
assert ( !gpg->pm.text );
}
2000-11-20 20:17:36 +00:00
2000-11-07 13:32:38 +00:00
GpgmeError
_gpgme_gpg_add_arg ( GpgObject gpg, const char *arg )
{
struct arg_and_data_s *a;
assert (gpg);
assert (arg);
if (gpg->pm.active)
return 0;
2000-11-07 13:32:38 +00:00
a = xtrymalloc ( sizeof *a + strlen (arg) );
if ( !a ) {
gpg->arg_error = 1;
return mk_error(Out_Of_Core);
}
a->next = NULL;
a->data = NULL;
a->dup_to = -1;
strcpy ( a->arg, arg );
*gpg->argtail = a;
gpg->argtail = &a->next;
return 0;
}
GpgmeError
_gpgme_gpg_add_data ( GpgObject gpg, GpgmeData data, int dup_to )
{
struct arg_and_data_s *a;
assert (gpg);
assert (data);
if (gpg->pm.active)
return 0;
2000-11-07 13:32:38 +00:00
a = xtrymalloc ( sizeof *a - 1 );
if ( !a ) {
gpg->arg_error = 1;
return mk_error(Out_Of_Core);
}
a->next = NULL;
a->data = data;
2000-12-06 12:17:10 +00:00
if ( dup_to == -2 ) {
a->print_fd = 1;
a->dup_to = -1;
}
else {
a->print_fd = 0;
a->dup_to = dup_to;
}
2000-11-07 13:32:38 +00:00
*gpg->argtail = a;
gpg->argtail = &a->next;
return 0;
}
GpgmeError
_gpgme_gpg_add_pm_data ( GpgObject gpg, GpgmeData data, int what )
{
GpgmeError rc=0;
assert ( gpg->pm.used );
if ( !what ) {
/* the signature */
assert ( !gpg->pm.sig );
gpg->pm.sig = data;
}
else if (what == 1) {
/* the signed data */
assert ( !gpg->pm.text );
gpg->pm.text = data;
}
else {
assert (0);
}
if ( gpg->pm.sig && gpg->pm.text ) {
if ( !gpg->pm.active ) {
/* create the callback handler and connect it to stdin */
GpgmeData tmp;
rc = gpgme_data_new_with_read_cb ( &tmp, pipemode_cb, gpg );
if (!rc )
rc = _gpgme_gpg_add_data (gpg, tmp, 0);
}
if ( !rc ) {
/* here we can reset the handler stuff */
gpg->pm.stream_started = 0;
}
}
return rc;
}
2000-11-07 13:32:38 +00:00
/*
* Note, that the status_handler is allowed to modifiy the args value
*/
void
_gpgme_gpg_set_status_handler ( GpgObject gpg,
GpgStatusHandler fnc, void *fnc_value )
{
assert (gpg);
if (gpg->pm.active)
return;
2000-11-07 13:32:38 +00:00
gpg->status.fnc = fnc;
gpg->status.fnc_value = fnc_value;
}
2000-11-10 17:50:24 +00:00
/* Kludge to process --with-colon output */
GpgmeError
_gpgme_gpg_set_colon_line_handler ( GpgObject gpg,
GpgColonLineHandler fnc, void *fnc_value )
{
assert (gpg);
if (gpg->pm.active)
return 0;
2000-11-10 17:50:24 +00:00
gpg->colon.bufsize = 1024;
gpg->colon.readpos = 0;
gpg->colon.buffer = xtrymalloc (gpg->colon.bufsize);
if (!gpg->colon.buffer) {
return mk_error (Out_Of_Core);
}
2000-12-06 12:17:10 +00:00
if (_gpgme_io_pipe (gpg->colon.fd, 1) == -1) {
2000-11-10 17:50:24 +00:00
xfree (gpg->colon.buffer); gpg->colon.buffer = NULL;
return mk_error (Pipe_Error);
}
if ( _gpgme_io_set_close_notify (gpg->colon.fd[0],
close_notify_handler, gpg)
|| _gpgme_io_set_close_notify (gpg->colon.fd[1],
close_notify_handler, gpg) ) {
return mk_error (General_Error);
}
2000-11-10 17:50:24 +00:00
gpg->colon.eof = 0;
gpg->colon.fnc = fnc;
gpg->colon.fnc_value = fnc_value;
2001-01-22 20:22:41 +00:00
gpg->colon.simple = 0;
2000-11-10 17:50:24 +00:00
return 0;
}
2001-01-22 20:22:41 +00:00
GpgmeError
_gpgme_gpg_set_simple_line_handler ( GpgObject gpg,
GpgColonLineHandler fnc,
void *fnc_value )
{
GpgmeError err;
err = _gpgme_gpg_set_colon_line_handler (gpg, fnc, fnc_value);
if (!err)
gpg->colon.simple = 1;
return err;
}
/*
* The Fnc will be called to get a value for one of the commands with
* a key KEY. If the Code pssed to FNC is 0, the function may release
* resources associated with the returned value from another call. To
* match such a second call to a first call, the returned value from
* the first call is passed as keyword.
*/
GpgmeError
_gpgme_gpg_set_command_handler ( GpgObject gpg,
GpgCommandHandler fnc, void *fnc_value )
{
GpgmeData tmp;
GpgmeError err;
assert (gpg);
if (gpg->pm.active)
return 0;
err = gpgme_data_new_with_read_cb ( &tmp, command_cb, gpg );
if (err)
return err;
_gpgme_gpg_add_arg ( gpg, "--command-fd" );
_gpgme_gpg_add_data (gpg, tmp, -2);
gpg->cmd.cb_data = tmp;
gpg->cmd.fnc = fnc;
gpg->cmd.fnc_value = fnc_value;
gpg->cmd.used = 1;
return 0;
}
2000-11-07 13:32:38 +00:00
static void
free_argv ( char **argv )
{
int i;
for (i=0; argv[i]; i++ )
xfree (argv[i]);
xfree (argv);
}
2000-11-09 16:35:35 +00:00
static void
free_fd_data_map ( struct fd_data_map_s *fd_data_map )
{
int i;
2000-12-06 12:17:10 +00:00
if ( !fd_data_map )
return;
2000-11-09 16:35:35 +00:00
for (i=0; fd_data_map[i].data; i++ ) {
if ( fd_data_map[i].fd != -1 )
_gpgme_io_close (fd_data_map[i].fd);
2000-11-09 16:35:35 +00:00
if ( fd_data_map[i].peer_fd != -1 )
_gpgme_io_close (fd_data_map[i].peer_fd);
2000-12-18 20:54:43 +00:00
/* don't release data because this is only a reference */
2000-11-09 16:35:35 +00:00
}
xfree (fd_data_map);
}
2000-11-07 13:32:38 +00:00
static GpgmeError
build_argv ( GpgObject gpg )
{
struct arg_and_data_s *a;
struct fd_data_map_s *fd_data_map;
size_t datac=0, argc=0;
char **argv;
2000-11-09 16:35:35 +00:00
int need_special = 0;
2000-11-16 17:26:53 +00:00
int use_agent = !!getenv ("GPG_AGENT_INFO");
2000-11-07 13:32:38 +00:00
if ( gpg->argv ) {
free_argv ( gpg->argv );
gpg->argv = NULL;
}
2000-11-09 16:35:35 +00:00
if (gpg->fd_data_map) {
free_fd_data_map (gpg->fd_data_map);
gpg->fd_data_map = NULL;
}
2000-11-07 13:32:38 +00:00
2000-11-09 16:35:35 +00:00
argc++; /* for argv[0] */
2000-11-07 13:32:38 +00:00
for ( a=gpg->arglist; a; a = a->next ) {
argc++;
if (a->data) {
2000-11-09 16:35:35 +00:00
/*fprintf (stderr, "build_argv: data\n" );*/
2000-11-07 13:32:38 +00:00
datac++;
2000-12-06 12:17:10 +00:00
if ( a->dup_to == -1 && !a->print_fd )
2000-11-09 16:35:35 +00:00
need_special = 1;
}
else {
/* fprintf (stderr, "build_argv: arg=`%s'\n", a->arg );*/
2000-11-07 13:32:38 +00:00
}
}
2000-11-09 16:35:35 +00:00
if ( need_special )
argc++;
2000-11-16 17:26:53 +00:00
if (use_agent)
argc++;
if (!gpg->cmd.used)
argc++;
argc += 2; /* --comment */
2000-11-07 13:32:38 +00:00
argv = xtrycalloc ( argc+1, sizeof *argv );
if (!argv)
return mk_error (Out_Of_Core);
fd_data_map = xtrycalloc ( datac+1, sizeof *fd_data_map );
if (!fd_data_map) {
free_argv (argv);
return mk_error (Out_Of_Core);
}
argc = datac = 0;
2000-11-09 16:35:35 +00:00
argv[argc] = xtrystrdup ( "gpg" ); /* argv[0] */
if (!argv[argc]) {
xfree (fd_data_map);
free_argv (argv);
return mk_error (Out_Of_Core);
}
argc++;
if ( need_special ) {
argv[argc] = xtrystrdup ( "--enable-special-filenames" );
if (!argv[argc]) {
xfree (fd_data_map);
free_argv (argv);
return mk_error (Out_Of_Core);
}
argc++;
}
2000-11-16 17:26:53 +00:00
if ( use_agent ) {
argv[argc] = xtrystrdup ( "--use-agent" );
if (!argv[argc]) {
xfree (fd_data_map);
free_argv (argv);
return mk_error (Out_Of_Core);
}
argc++;
}
if ( !gpg->cmd.used ) {
argv[argc] = xtrystrdup ( "--batch" );
if (!argv[argc]) {
xfree (fd_data_map);
free_argv (argv);
return mk_error (Out_Of_Core);
}
argc++;
}
argv[argc] = xtrystrdup ( "--comment" );
if (!argv[argc]) {
xfree (fd_data_map);
free_argv (argv);
return mk_error (Out_Of_Core);
}
argc++;
argv[argc] = xtrystrdup ( "" );
if (!argv[argc]) {
xfree (fd_data_map);
free_argv (argv);
return mk_error (Out_Of_Core);
}
argc++;
2000-11-07 13:32:38 +00:00
for ( a=gpg->arglist; a; a = a->next ) {
if ( a->data ) {
2000-11-13 13:25:22 +00:00
switch ( _gpgme_data_get_mode (a->data) ) {
2000-11-07 13:32:38 +00:00
case GPGME_DATA_MODE_NONE:
case GPGME_DATA_MODE_INOUT:
xfree (fd_data_map);
free_argv (argv);
return mk_error (Invalid_Mode);
case GPGME_DATA_MODE_IN:
/* create a pipe to read from gpg */
fd_data_map[datac].inbound = 1;
break;
case GPGME_DATA_MODE_OUT:
/* create a pipe to pass it down to gpg */
fd_data_map[datac].inbound = 0;
break;
}
2000-11-13 13:25:22 +00:00
switch ( gpgme_data_get_type (a->data) ) {
2000-11-07 13:32:38 +00:00
case GPGME_DATA_TYPE_NONE:
if ( fd_data_map[datac].inbound )
break; /* allowed */
xfree (fd_data_map);
free_argv (argv);
return mk_error (Invalid_Type);
case GPGME_DATA_TYPE_MEM:
2000-12-06 12:17:10 +00:00
case GPGME_DATA_TYPE_CB:
2000-11-07 13:32:38 +00:00
break;
case GPGME_DATA_TYPE_FD:
case GPGME_DATA_TYPE_FILE:
xfree (fd_data_map);
free_argv (argv);
return mk_error (Not_Implemented);
}
/* create a pipe */
{
int fds[2];
2000-12-06 12:17:10 +00:00
if (_gpgme_io_pipe (fds, fd_data_map[datac].inbound?1:0 )
== -1) {
2000-11-07 13:32:38 +00:00
xfree (fd_data_map);
free_argv (argv);
return mk_error (Pipe_Error);
}
if ( _gpgme_io_set_close_notify (fds[0],
close_notify_handler, gpg)
|| _gpgme_io_set_close_notify (fds[1],
close_notify_handler,
gpg)) {
return mk_error (General_Error);
}
2000-11-07 13:32:38 +00:00
/* if the data_type is FD, we have to do a dup2 here */
if (fd_data_map[datac].inbound) {
fd_data_map[datac].fd = fds[0];
fd_data_map[datac].peer_fd = fds[1];
}
else {
fd_data_map[datac].fd = fds[1];
fd_data_map[datac].peer_fd = fds[0];
}
}
/* Hack to get hands on the fd later */
if ( gpg->cmd.used && gpg->cmd.cb_data == a->data ) {
assert (gpg->cmd.fd == -1);
gpg->cmd.fd = fd_data_map[datac].fd;
}
2000-11-07 13:32:38 +00:00
fd_data_map[datac].data = a->data;
fd_data_map[datac].dup_to = a->dup_to;
2000-11-09 16:35:35 +00:00
if ( a->dup_to == -1 ) {
argv[argc] = xtrymalloc ( 25 );
if (!argv[argc]) {
xfree (fd_data_map);
free_argv (argv);
return mk_error (Out_Of_Core);
}
2000-12-06 12:17:10 +00:00
sprintf ( argv[argc],
a->print_fd? "%d" : "-&%d",
fd_data_map[datac].peer_fd );
2000-11-09 16:35:35 +00:00
argc++;
}
2000-11-07 13:32:38 +00:00
datac++;
}
else {
argv[argc] = xtrystrdup ( a->arg );
if (!argv[argc]) {
xfree (fd_data_map);
free_argv (argv);
return mk_error (Out_Of_Core);
}
2000-11-09 16:35:35 +00:00
argc++;
2000-11-07 13:32:38 +00:00
}
}
gpg->argv = argv;
gpg->fd_data_map = fd_data_map;
return 0;
}
GpgmeError
_gpgme_gpg_spawn( GpgObject gpg, void *opaque )
{
int rc;
int i, n;
int pid;
struct spawn_fd_item_s *fd_child_list, *fd_parent_list;
2000-11-07 13:32:38 +00:00
if (!gpg)
return mk_error (Invalid_Value);
if (! _gpgme_get_gpg_path ())
return mk_error (Invalid_Engine);
2000-11-07 13:32:38 +00:00
/* Kludge, so that we don't need to check the return code of
* all the gpgme_gpg_add_arg(). we bail out here instead */
if ( gpg->arg_error )
return mk_error (Out_Of_Core);
if (gpg->pm.active)
return 0;
2000-11-07 13:32:38 +00:00
rc = build_argv ( gpg );
if ( rc )
return rc;
n = 3; /* status_fd, colon_fd and end of list */
for (i=0; gpg->fd_data_map[i].data; i++ )
n++;
fd_child_list = xtrycalloc ( n+n, sizeof *fd_child_list );
if (!fd_child_list)
return mk_error (Out_Of_Core);
fd_parent_list = fd_child_list + n;
/* build the fd list for the child */
n=0;
if ( gpg->colon.fnc ) {
fd_child_list[n].fd = gpg->colon.fd[1];
fd_child_list[n].dup_to = 1; /* dup to stdout */
n++;
2000-11-07 13:32:38 +00:00
}
for (i=0; gpg->fd_data_map[i].data; i++ ) {
if (gpg->fd_data_map[i].dup_to != -1) {
fd_child_list[n].fd = gpg->fd_data_map[i].peer_fd;
fd_child_list[n].dup_to = gpg->fd_data_map[i].dup_to;
n++;
2000-11-07 13:32:38 +00:00
}
}
fd_child_list[n].fd = -1;
fd_child_list[n].dup_to = -1;
2000-11-07 13:32:38 +00:00
/* build the fd list for the parent */
n=0;
if ( gpg->status.fd[1] != -1 ) {
fd_parent_list[n].fd = gpg->status.fd[1];
fd_parent_list[n].dup_to = -1;
n++;
gpg->status.fd[1] = -1;
}
if ( gpg->colon.fd[1] != -1 ) {
fd_parent_list[n].fd = gpg->colon.fd[1];
fd_parent_list[n].dup_to = -1;
n++;
gpg->colon.fd[1] = -1;
}
for (i=0; gpg->fd_data_map[i].data; i++ ) {
fd_parent_list[n].fd = gpg->fd_data_map[i].peer_fd;
fd_parent_list[n].dup_to = -1;
n++;
gpg->fd_data_map[i].peer_fd = -1;
}
fd_parent_list[n].fd = -1;
fd_parent_list[n].dup_to = -1;
2000-11-09 16:35:35 +00:00
2001-01-30 16:00:50 +00:00
pid = _gpgme_io_spawn (_gpgme_get_gpg_path (),
gpg->argv, fd_child_list, fd_parent_list);
xfree (fd_child_list);
if (pid == -1) {
return mk_error (Exec_Error);
2000-11-07 13:32:38 +00:00
}
2000-11-07 13:32:38 +00:00
gpg->pid = pid;
if (gpg->pm.used)
gpg->pm.active = 1;
2000-11-07 13:32:38 +00:00
2000-11-09 16:35:35 +00:00
/*_gpgme_register_term_handler ( closure, closure_value, pid );*/
2000-11-07 13:32:38 +00:00
if ( _gpgme_register_pipe_handler ( opaque, gpg_status_handler,
gpg, pid, gpg->status.fd[0], 1 ) ) {
/* FIXME: kill the child */
return mk_error (General_Error);
}
2000-11-10 17:50:24 +00:00
if ( gpg->colon.fnc ) {
2000-11-10 17:50:24 +00:00
assert ( gpg->colon.fd[0] != -1 );
if ( _gpgme_register_pipe_handler ( opaque, gpg_colon_line_handler,
gpg, pid, gpg->colon.fd[0], 1 ) ) {
/* FIXME: kill the child */
return mk_error (General_Error);
}
}
2000-11-07 13:32:38 +00:00
for (i=0; gpg->fd_data_map[i].data; i++ ) {
2000-11-20 20:17:36 +00:00
/* Due to problems with select and write we set outbound pipes
* to non-blocking */
if (!gpg->fd_data_map[i].inbound) {
_gpgme_io_set_nonblocking (gpg->fd_data_map[i].fd);
2000-11-20 20:17:36 +00:00
}
2000-11-07 13:32:38 +00:00
if ( _gpgme_register_pipe_handler (
opaque,
gpg->fd_data_map[i].inbound?
_gpgme_data_inbound_handler:_gpgme_data_outbound_handler,
2000-11-07 13:32:38 +00:00
gpg->fd_data_map[i].data,
pid, gpg->fd_data_map[i].fd,
gpg->fd_data_map[i].inbound )
) {
/* FIXME: kill the child */
return mk_error (General_Error);
}
}
if ( gpg->cmd.used )
_gpgme_freeze_fd ( gpg->cmd.fd );
2000-11-07 13:32:38 +00:00
/* fixme: check what data we can release here */
2000-11-07 13:32:38 +00:00
gpg->running = 1;
return 0;
}
static int
2002-02-02 Marcus Brinkmann <marcus@g10code.de> This patch has gotten a bit large... mmh. The main thing that happens here is that error values are now not determined in the operation function after gpgme_wait completed, but in the status handler when EOF is received. It should always be the case that either an error is flagged or EOF is received, so that after a gpgme_wait you should never have the situation that no error is flagged and EOF is not received. One problem is that the engine status handlers don't have access to the context, a horrible kludge works around this for now. All errors that happen during a pending operation should be catched and reported in ctx->error, including out-of-core and cancellation. This rounds up neatly a couple of loose ends, and makes it possible to pass up any errors in the communication with the backend as well. As a bonus, there will be a function to access gpgme->wait, so that the operations can truly be implemented with their _start function. * engine-gpgsm.c (gpgsm_status_handler): Horrible kludge to report error back to the context. * rungpg.c (gpg_status_handler): Same horrible kludge applied here. * engine-gpgsm.c (gpgsm_assuan_simple_command): Add error checking. * wait.c (_gpgme_wait_on_condition): If canceled, set CTX->error to a value indication that. * verify.c (add_notation): Set error, not out_of_core. (finish_sig): Likewise. (gpgme_op_verify_start): Don't clear out_of_core. (_gpgme_verify_status_handler): At EOF, clean up the notation data. (gpgme_op_verify): And don't do it here. * trustlist.c (trustlist_status_handler): Check error, not out_of_core. (gpgme_op_trustlist_start): Don't clear out_of_core. (gpgme_op_trustlist_next): Check error, not out_of_core. (gpgme_op_trustlist_end): Likewise. * ops.h (test_and_allocate_result): New macro. (_gpgme_passphrase_result): Remove prototype. * delete.c (gpgme_op_delete): Return error from context. (delete_status_handler): Use macro test_and_allocate_result. Perform error checking at EOF. (gpgme_op_delete_start): Release result. * passphrase.c (_gpgme_passphrase_status_handler): Use macro test_and_allocate_result, and perform error checking here. (_gpgme_passphrase_result): Function removed. * sign.c (gpgme_op_sign_start): Do not set out_of_core to zero. (gpgme_op_sign): Just return the error value from the context. (sign_status_handler): Only progress if no error is set yet. If we process an EOF, set the resulting error value (if any). * decrypt.c (_gpgme_decrypt_result): Function removed. (create_result_struct): Function removed. (_gpgme_decrypt_status_handler): Use macro test_and_allocate_result, caclulate error on EOF, do not progress with errors. (_gpgme_decrypt_start): Do not set out_of_core to zero. (gpgme_op_decrypt): Just return the error value from the context. * encrypt.c (encrypt_status_handler): Perform the error checking here. (gpgme_op_encrypt_start): Do not clear out_of_core. * export.c (export_status_handler): Return if error is set in context. (gpgme_op_export_start): Release result. (gpgme_op_export): Return error from context. * decrypt-verify.c (gpgme_op_decrypt_verify): Return the error in the context. * genkey.c (genkey_status_handler): Use macro test_and_allocate_result. Perform error checking at EOF. (gpgme_op_genkey): Just return the error from context. * import.c (gpgme_op_import): Return the error from context. (import_status_handler): Use macro test_and_allocate_result. * keylist.c (gpgme_op_keylist_start): Do not clear out_of_core. (gpgme_op_keylist_next): Return error of context. (keylist_colon_handler): Set error instead out_of_code. (finish_key): Likewise. * context.h: Remove member out_of_core, add member error. * gpgme.c (_gpgme_release_result): Clear error flag. * engine.h (_gpgme_engine_get_error): New prototype. * engine.c (_gpgme_engine_get_error): New function. * engine-gpgsm.c (_gpgme_gpgsm_get_error): New function. * engine-gpgsm.c (map_assuan_error): New function. (gpgsm_assuan_simple_command): Change return type to GpgmeError, use the new function to map error values. (gpgsm_set_fd): Change return type tp GpgmeError. (_gpgme_gpgsm_op_decrypt): Change type of ERR to GpgmeError. (gpgsm_set_recipients): Likewise. Change type of return value equivalently. Adjust error values. (_gpgme_gpgsm_op_import): Likewise. (_gpgme_gpgsm_op_sign): Likewise. (struct gpgsm_object_s): New member error. (gpgsm_status_handler): Set error if error occurs. Determine error number from ERR line received. If assuan_read_line fails, terminate the connection.
2002-02-02 03:52:59 +00:00
gpg_status_handler (void *opaque, int pid, int fd)
2000-11-07 13:32:38 +00:00
{
2002-02-02 Marcus Brinkmann <marcus@g10code.de> This patch has gotten a bit large... mmh. The main thing that happens here is that error values are now not determined in the operation function after gpgme_wait completed, but in the status handler when EOF is received. It should always be the case that either an error is flagged or EOF is received, so that after a gpgme_wait you should never have the situation that no error is flagged and EOF is not received. One problem is that the engine status handlers don't have access to the context, a horrible kludge works around this for now. All errors that happen during a pending operation should be catched and reported in ctx->error, including out-of-core and cancellation. This rounds up neatly a couple of loose ends, and makes it possible to pass up any errors in the communication with the backend as well. As a bonus, there will be a function to access gpgme->wait, so that the operations can truly be implemented with their _start function. * engine-gpgsm.c (gpgsm_status_handler): Horrible kludge to report error back to the context. * rungpg.c (gpg_status_handler): Same horrible kludge applied here. * engine-gpgsm.c (gpgsm_assuan_simple_command): Add error checking. * wait.c (_gpgme_wait_on_condition): If canceled, set CTX->error to a value indication that. * verify.c (add_notation): Set error, not out_of_core. (finish_sig): Likewise. (gpgme_op_verify_start): Don't clear out_of_core. (_gpgme_verify_status_handler): At EOF, clean up the notation data. (gpgme_op_verify): And don't do it here. * trustlist.c (trustlist_status_handler): Check error, not out_of_core. (gpgme_op_trustlist_start): Don't clear out_of_core. (gpgme_op_trustlist_next): Check error, not out_of_core. (gpgme_op_trustlist_end): Likewise. * ops.h (test_and_allocate_result): New macro. (_gpgme_passphrase_result): Remove prototype. * delete.c (gpgme_op_delete): Return error from context. (delete_status_handler): Use macro test_and_allocate_result. Perform error checking at EOF. (gpgme_op_delete_start): Release result. * passphrase.c (_gpgme_passphrase_status_handler): Use macro test_and_allocate_result, and perform error checking here. (_gpgme_passphrase_result): Function removed. * sign.c (gpgme_op_sign_start): Do not set out_of_core to zero. (gpgme_op_sign): Just return the error value from the context. (sign_status_handler): Only progress if no error is set yet. If we process an EOF, set the resulting error value (if any). * decrypt.c (_gpgme_decrypt_result): Function removed. (create_result_struct): Function removed. (_gpgme_decrypt_status_handler): Use macro test_and_allocate_result, caclulate error on EOF, do not progress with errors. (_gpgme_decrypt_start): Do not set out_of_core to zero. (gpgme_op_decrypt): Just return the error value from the context. * encrypt.c (encrypt_status_handler): Perform the error checking here. (gpgme_op_encrypt_start): Do not clear out_of_core. * export.c (export_status_handler): Return if error is set in context. (gpgme_op_export_start): Release result. (gpgme_op_export): Return error from context. * decrypt-verify.c (gpgme_op_decrypt_verify): Return the error in the context. * genkey.c (genkey_status_handler): Use macro test_and_allocate_result. Perform error checking at EOF. (gpgme_op_genkey): Just return the error from context. * import.c (gpgme_op_import): Return the error from context. (import_status_handler): Use macro test_and_allocate_result. * keylist.c (gpgme_op_keylist_start): Do not clear out_of_core. (gpgme_op_keylist_next): Return error of context. (keylist_colon_handler): Set error instead out_of_code. (finish_key): Likewise. * context.h: Remove member out_of_core, add member error. * gpgme.c (_gpgme_release_result): Clear error flag. * engine.h (_gpgme_engine_get_error): New prototype. * engine.c (_gpgme_engine_get_error): New function. * engine-gpgsm.c (_gpgme_gpgsm_get_error): New function. * engine-gpgsm.c (map_assuan_error): New function. (gpgsm_assuan_simple_command): Change return type to GpgmeError, use the new function to map error values. (gpgsm_set_fd): Change return type tp GpgmeError. (_gpgme_gpgsm_op_decrypt): Change type of ERR to GpgmeError. (gpgsm_set_recipients): Likewise. Change type of return value equivalently. Adjust error values. (_gpgme_gpgsm_op_import): Likewise. (_gpgme_gpgsm_op_sign): Likewise. (struct gpgsm_object_s): New member error. (gpgsm_status_handler): Set error if error occurs. Determine error number from ERR line received. If assuan_read_line fails, terminate the connection.
2002-02-02 03:52:59 +00:00
GpgObject gpg = opaque;
int err;
2000-11-07 13:32:38 +00:00
2002-02-02 Marcus Brinkmann <marcus@g10code.de> This patch has gotten a bit large... mmh. The main thing that happens here is that error values are now not determined in the operation function after gpgme_wait completed, but in the status handler when EOF is received. It should always be the case that either an error is flagged or EOF is received, so that after a gpgme_wait you should never have the situation that no error is flagged and EOF is not received. One problem is that the engine status handlers don't have access to the context, a horrible kludge works around this for now. All errors that happen during a pending operation should be catched and reported in ctx->error, including out-of-core and cancellation. This rounds up neatly a couple of loose ends, and makes it possible to pass up any errors in the communication with the backend as well. As a bonus, there will be a function to access gpgme->wait, so that the operations can truly be implemented with their _start function. * engine-gpgsm.c (gpgsm_status_handler): Horrible kludge to report error back to the context. * rungpg.c (gpg_status_handler): Same horrible kludge applied here. * engine-gpgsm.c (gpgsm_assuan_simple_command): Add error checking. * wait.c (_gpgme_wait_on_condition): If canceled, set CTX->error to a value indication that. * verify.c (add_notation): Set error, not out_of_core. (finish_sig): Likewise. (gpgme_op_verify_start): Don't clear out_of_core. (_gpgme_verify_status_handler): At EOF, clean up the notation data. (gpgme_op_verify): And don't do it here. * trustlist.c (trustlist_status_handler): Check error, not out_of_core. (gpgme_op_trustlist_start): Don't clear out_of_core. (gpgme_op_trustlist_next): Check error, not out_of_core. (gpgme_op_trustlist_end): Likewise. * ops.h (test_and_allocate_result): New macro. (_gpgme_passphrase_result): Remove prototype. * delete.c (gpgme_op_delete): Return error from context. (delete_status_handler): Use macro test_and_allocate_result. Perform error checking at EOF. (gpgme_op_delete_start): Release result. * passphrase.c (_gpgme_passphrase_status_handler): Use macro test_and_allocate_result, and perform error checking here. (_gpgme_passphrase_result): Function removed. * sign.c (gpgme_op_sign_start): Do not set out_of_core to zero. (gpgme_op_sign): Just return the error value from the context. (sign_status_handler): Only progress if no error is set yet. If we process an EOF, set the resulting error value (if any). * decrypt.c (_gpgme_decrypt_result): Function removed. (create_result_struct): Function removed. (_gpgme_decrypt_status_handler): Use macro test_and_allocate_result, caclulate error on EOF, do not progress with errors. (_gpgme_decrypt_start): Do not set out_of_core to zero. (gpgme_op_decrypt): Just return the error value from the context. * encrypt.c (encrypt_status_handler): Perform the error checking here. (gpgme_op_encrypt_start): Do not clear out_of_core. * export.c (export_status_handler): Return if error is set in context. (gpgme_op_export_start): Release result. (gpgme_op_export): Return error from context. * decrypt-verify.c (gpgme_op_decrypt_verify): Return the error in the context. * genkey.c (genkey_status_handler): Use macro test_and_allocate_result. Perform error checking at EOF. (gpgme_op_genkey): Just return the error from context. * import.c (gpgme_op_import): Return the error from context. (import_status_handler): Use macro test_and_allocate_result. * keylist.c (gpgme_op_keylist_start): Do not clear out_of_core. (gpgme_op_keylist_next): Return error of context. (keylist_colon_handler): Set error instead out_of_code. (finish_key): Likewise. * context.h: Remove member out_of_core, add member error. * gpgme.c (_gpgme_release_result): Clear error flag. * engine.h (_gpgme_engine_get_error): New prototype. * engine.c (_gpgme_engine_get_error): New function. * engine-gpgsm.c (_gpgme_gpgsm_get_error): New function. * engine-gpgsm.c (map_assuan_error): New function. (gpgsm_assuan_simple_command): Change return type to GpgmeError, use the new function to map error values. (gpgsm_set_fd): Change return type tp GpgmeError. (_gpgme_gpgsm_op_decrypt): Change type of ERR to GpgmeError. (gpgsm_set_recipients): Likewise. Change type of return value equivalently. Adjust error values. (_gpgme_gpgsm_op_import): Likewise. (_gpgme_gpgsm_op_sign): Likewise. (struct gpgsm_object_s): New member error. (gpgsm_status_handler): Set error if error occurs. Determine error number from ERR line received. If assuan_read_line fails, terminate the connection.
2002-02-02 03:52:59 +00:00
assert (fd == gpg->status.fd[0]);
err = read_status (gpg);
if (err)
{
/* XXX Horrible kludge. We really must not make use of
fnc_value. */
GpgmeCtx ctx = (GpgmeCtx) gpg->status.fnc_value;
ctx->error = err;
DEBUG1 ("gpg_handler: read_status problem %d\n - stop", err);
return 1;
}
return gpg->status.eof;
2000-11-07 13:32:38 +00:00
}
static int
2000-11-09 16:35:35 +00:00
status_cmp (const void *ap, const void *bp)
2000-11-07 13:32:38 +00:00
{
2000-11-09 16:35:35 +00:00
const struct status_table_s *a = ap;
const struct status_table_s *b = bp;
2000-11-07 13:32:38 +00:00
return strcmp (a->name, b->name);
}
/*
* Handle the status output of GnuPG. This function does read entire
* lines and passes them as C strings to the callback function (we can
* use C Strings because the status output is always UTF-8 encoded).
* Of course we have to buffer the lines to cope with long lines
* e.g. with a large user ID. Note: We can optimize this to only cope
* with status line code we know about and skip all other stuff
* without buffering (i.e. without extending the buffer). */
static GpgmeError
read_status ( GpgObject gpg )
{
char *p;
int nread;
size_t bufsize = gpg->status.bufsize;
char *buffer = gpg->status.buffer;
size_t readpos = gpg->status.readpos;
assert (buffer);
if (bufsize - readpos < 256) {
/* need more room for the read */
bufsize += 1024;
buffer = xtryrealloc (buffer, bufsize);
if ( !buffer )
return mk_error (Out_Of_Core);
}
nread = _gpgme_io_read ( gpg->status.fd[0],
buffer+readpos, bufsize-readpos );
2000-11-07 13:32:38 +00:00
if (nread == -1)
return mk_error(Read_Error);
if (!nread) {
gpg->status.eof = 1;
if (gpg->status.fnc)
gpg->status.fnc ( gpg->status.fnc_value, STATUS_EOF, "" );
return 0;
}
while (nread > 0) {
for (p = buffer + readpos; nread; nread--, p++) {
if ( *p == '\n' ) {
/* (we require that the last line is terminated by a LF) */
*p = 0;
if (!strncmp (buffer, "[GNUPG:] ", 9 )
&& buffer[9] >= 'A' && buffer[9] <= 'Z' ) {
2000-11-07 13:32:38 +00:00
struct status_table_s t, *r;
char *rest;
rest = strchr (buffer+9, ' ');
if ( !rest )
rest = p; /* set to an empty string */
else
*rest++ = 0;
t.name = buffer+9;
/* (the status table as one extra element) */
r = bsearch ( &t, status_table, DIM(status_table)-1,
sizeof t, status_cmp );
if ( r ) {
if ( gpg->cmd.used
&& ( r->code == STATUS_GET_BOOL
|| r->code == STATUS_GET_LINE
|| r->code == STATUS_GET_HIDDEN )) {
gpg->cmd.code = r->code;
xfree (gpg->cmd.keyword);
gpg->cmd.keyword = xtrystrdup (rest);
if ( !gpg->cmd.keyword )
return mk_error (Out_Of_Core);
/* this should be the last thing we have received
* and the next thing will be that the command
* handler does its action */
if ( nread > 1 )
DEBUG0 ("ERROR, unexpected data in read_status");
_gpgme_thaw_fd (gpg->cmd.fd);
}
else if ( gpg->status.fnc ) {
gpg->status.fnc ( gpg->status.fnc_value,
r->code, rest);
}
2001-02-01 09:25:56 +00:00
if ( r->code == STATUS_END_STREAM ) {
if ( gpg->cmd.used )
_gpgme_freeze_fd ( gpg->cmd.fd );
}
2000-11-07 13:32:38 +00:00
}
}
/* To reuse the buffer for the next line we have to
* shift the remaining data to the buffer start and
* restart the loop Hmmm: We can optimize this
* function by looking forward in the buffer to see
* whether a second complete line is available and in
* this case avoid the memmove for this line. */
nread--; p++;
if (nread)
memmove (buffer, p, nread);
readpos = 0;
break; /* the for loop */
}
else
readpos++;
}
}
/* Update the gpg object. */
gpg->status.bufsize = bufsize;
gpg->status.buffer = buffer;
gpg->status.readpos = readpos;
return 0;
}
2000-11-10 17:50:24 +00:00
/*
* This colonline handler thing is not the clean way to do it.
* It might be better to enhance the GpgmeData object to act as
* a wrapper for a callback. Same goes for the status thing.
* For now we use this thing here becuase it is easier to implement.
*/
static int
gpg_colon_line_handler ( void *opaque, int pid, int fd )
2000-11-10 17:50:24 +00:00
{
GpgObject gpg = opaque;
GpgmeError rc = 0;
assert ( fd == gpg->colon.fd[0] );
rc = read_colon_line ( gpg );
if ( rc ) {
DEBUG1 ("gpg_colon_line_handler: "
2000-11-10 17:50:24 +00:00
"read problem %d\n - stop", rc);
return 1;
}
return gpg->colon.eof;
2000-11-10 17:50:24 +00:00
}
static GpgmeError
read_colon_line ( GpgObject gpg )
{
char *p;
int nread;
size_t bufsize = gpg->colon.bufsize;
char *buffer = gpg->colon.buffer;
size_t readpos = gpg->colon.readpos;
assert (buffer);
if (bufsize - readpos < 256) {
/* need more room for the read */
bufsize += 1024;
buffer = xtryrealloc (buffer, bufsize);
if ( !buffer )
return mk_error (Out_Of_Core);
}
nread = _gpgme_io_read ( gpg->colon.fd[0],
buffer+readpos, bufsize-readpos );
2000-11-10 17:50:24 +00:00
if (nread == -1)
return mk_error(Read_Error);
if (!nread) {
gpg->colon.eof = 1;
assert (gpg->colon.fnc);
gpg->colon.fnc ( gpg->colon.fnc_value, NULL );
return 0;
}
while (nread > 0) {
for (p = buffer + readpos; nread; nread--, p++) {
if ( *p == '\n' ) {
/* (we require that the last line is terminated by a
* LF) and we skip empty lines. Note: we use UTF8
* encoding and escaping of special characters
* We require at least one colon to cope with
* some other printed information.
*/
*p = 0;
2001-01-22 20:22:41 +00:00
if ( gpg->colon.simple
|| (*buffer && strchr (buffer, ':')) ) {
2000-11-10 17:50:24 +00:00
assert (gpg->colon.fnc);
gpg->colon.fnc ( gpg->colon.fnc_value, buffer );
}
/* To reuse the buffer for the next line we have to
* shift the remaining data to the buffer start and
* restart the loop Hmmm: We can optimize this
* function by looking forward in the buffer to see
* whether a second complete line is available and in
* this case avoid the memmove for this line. */
nread--; p++;
if (nread)
memmove (buffer, p, nread);
readpos = 0;
break; /* the for loop */
}
else
readpos++;
}
}
/* Update the gpg object. */
gpg->colon.bufsize = bufsize;
gpg->colon.buffer = buffer;
gpg->colon.readpos = readpos;
return 0;
}
static GpgmeError
pipemode_copy (char *buffer, size_t length, size_t *nread, GpgmeData data )
{
GpgmeError err;
size_t nbytes;
char tmp[1000], *s, *d;
/* we can optimize this whole thing but for now we just
* return after each escape character */
if (length > 990)
length = 990;
err = gpgme_data_read ( data, tmp, length, &nbytes );
if (err)
return err;
for (s=tmp, d=buffer; nbytes; s++, nbytes--) {
*d++ = *s;
if (*s == '@' ) {
*d++ = '@';
break;
}
}
*nread = d - buffer;
return 0;
}
static int
pipemode_cb ( void *opaque, char *buffer, size_t length, size_t *nread )
{
GpgObject gpg = opaque;
GpgmeError err;
if ( !buffer || !length || !nread )
return 0; /* those values are reserved for extensions */
*nread =0;
if ( !gpg->pm.stream_started ) {
assert (length > 4 );
strcpy (buffer, "@<@B" );
*nread = 4;
gpg->pm.stream_started = 1;
}
else if ( gpg->pm.sig ) {
err = pipemode_copy ( buffer, length, nread, gpg->pm.sig );
if ( err == GPGME_EOF ) {
gpg->pm.sig = NULL;
assert (length > 4 );
strcpy (buffer, "@t" );
*nread = 2;
}
else if (err) {
DEBUG1 ("pipemode_cb: copy sig failed: %s\n",
gpgme_strerror (err) );
return -1;
}
}
else if ( gpg->pm.text ) {
err = pipemode_copy ( buffer, length, nread, gpg->pm.text );
if ( err == GPGME_EOF ) {
gpg->pm.text = NULL;
assert (length > 4 );
strcpy (buffer, "@.@>" );
*nread = 4;
}
else if (err) {
DEBUG1 ("pipemode_cb: copy data failed: %s\n",
gpgme_strerror (err) );
return -1;
}
}
else {
return 0; /* eof */
}
return 0;
}
/*
* Here we handle --command-fd. This works closely together with
* the status handler.
*/
static int
command_cb ( void *opaque, char *buffer, size_t length, size_t *nread )
{
GpgObject gpg = opaque;
const char *value;
int value_len;
DEBUG0 ("command_cb: enter\n");
assert (gpg->cmd.used);
if ( !buffer || !length || !nread )
return 0; /* those values are reserved for extensions */
*nread =0;
if ( !gpg->cmd.code ) {
DEBUG0 ("command_cb: no code\n");
return -1;
}
if ( !gpg->cmd.fnc ) {
DEBUG0 ("command_cb: no user cb\n");
return -1;
}
value = gpg->cmd.fnc ( gpg->cmd.fnc_value,
gpg->cmd.code, gpg->cmd.keyword );
if ( !value ) {
DEBUG0 ("command_cb: no data from user cb\n");
gpg->cmd.fnc ( gpg->cmd.fnc_value, 0, value);
return -1;
}
value_len = strlen (value);
if ( value_len+1 > length ) {
DEBUG0 ("command_cb: too much data from user cb\n");
gpg->cmd.fnc ( gpg->cmd.fnc_value, 0, value);
return -1;
}
memcpy ( buffer, value, value_len );
if ( !value_len || (value_len && value[value_len-1] != '\n') )
buffer[value_len++] = '\n';
*nread = value_len;
gpg->cmd.fnc ( gpg->cmd.fnc_value, 0, value);
gpg->cmd.code = 0;
/* and sleep again until read_status will wake us up again */
_gpgme_freeze_fd ( gpg->cmd.fd );
return 0;
}
2001-11-21 Marcus Brinkmann <marcus@g10code.de> * context.h: Do not include rungpg.h, but engine.h. (struct gpgme_context_s): Replace member gpg with engine. * gpgme.c (gpgme_release): Release engine, not gpg. * recipient.c (_gpgme_append_gpg_args_from_recifgpients): Function moved ... * rungpg.c (_gpgme_append_gpg_args_from_recipients): ... here. Make static, change order of arguments, and return an error value. * ops.h (_gpgme_append_gpg_args_from_recipients): Removed prototype. * rungpg.h (_gpgme_gpg_op_verify): Add prototype. (_gpgme_gpg_op_encrypt): Likewise. (_gpgme_gpg_op_decrypt): Likewise. (_gpgme_gpg_op_delete): Likewise. (_gpgme_gpg_op_export): Likewise. (_gpgme_gpg_op_genkey): Likewise. (_gpgme_gpg_op_import): Likewise. (_gpgme_gpg_op_keylist): Likewise. (_gpgme_gpg_op_sign): Likewise. (_gpgme_gpg_op_trustlist): Likewise. * rungpg.c (_gpgme_gpg_op_verify): New function. (_gpgme_gpg_op_encrypt): Likewise. (_gpgme_gpg_op_decrypt): Likewise. (_gpgme_gpg_op_delete): Likewise. (_gpgme_gpg_op_export): Likewise. (_gpgme_gpg_op_genkey): Likewise. (_gpgme_gpg_op_import): Likewise. (_gpgme_gpg_op_keylist): Likewise. (_gpgme_gpg_op_sign): Likewise. (_gpgme_gpg_op_trustlist): Likewise. * engine.h (_gpgme_engine_set_status_handler): Add prototype. (_gpgme_engine_set_command_handler): Likewise. (_gpgme_engine_set_colon_line_handler): Likewise. (_gpgme_engine_op_decrypt): Likewise. (_gpgme_engine_op_delete): Likewise. (_gpgme_engine_op_encrypt): Likewise. (_gpgme_engine_op_export): Likewise. (_gpgme_engine_op_genkey): Likewise. (_gpgme_engine_op_import): Likewise. (_gpgme_engine_op_keylist): Likewise. (_gpgme_engine_op_sign): Likewise. (_gpgme_engine_op_trustlist): Likewise. (_gpgme_engine_op_verify): Likewise. (_gpgme_engine_start): Likewise. * engine.c (_gpgme_engine_set_status_handler): New function. (_gpgme_engine_set_command_handler): Likewise. (_gpgme_engine_set_colon_line_handler): Likewise. (_gpgme_engine_op_decrypt): Likewise. (_gpgme_engine_op_delete): Likewise. (_gpgme_engine_op_encrypt): Likewise. (_gpgme_engine_op_export): Likewise. (_gpgme_engine_op_genkey): Likewise. (_gpgme_engine_op_import): Likewise. (_gpgme_engine_op_keylist): Likewise. (_gpgme_engine_op_sign): Likewise. (_gpgme_engine_op_trustlist): Likewise. (_gpgme_engine_op_verify): Likewise. (_gpgme_engine_start): Likewise. * verify.c (gpgme_op_verify_start): Reimplement in terms of above functions. * encrypt.c (gpgme_op_encrypt_start): Likewise. * decrypt.c (_gpgme_decrypt_start): Likewise. * passphrase.c (_gpgme_passphrase_start): Likewise. * keylist.c (gpgme_op_keylist_start): Likewise.
2001-11-21 03:40:17 +00:00
GpgmeError
_gpgme_gpg_op_decrypt (GpgObject gpg, GpgmeData ciph, GpgmeData plain)
{
GpgmeError err;
err = _gpgme_gpg_add_arg (gpg, "--decrypt");
/* Tell the gpg object about the data. */
if (!err)
err = _gpgme_gpg_add_arg (gpg, "--output");
if (!err)
err = _gpgme_gpg_add_arg (gpg, "-");
if (!err)
err = _gpgme_gpg_add_data (gpg, plain, 1);
if (!err)
err = _gpgme_gpg_add_data (gpg, ciph, 0);
return err;
}
GpgmeError
_gpgme_gpg_op_delete (GpgObject gpg, GpgmeKey key, int allow_secret)
{
GpgmeError err;
err = _gpgme_gpg_add_arg (gpg, allow_secret
? "--delete-secret-and-public-key"
: "--delete-key");
if (!err)
err = _gpgme_gpg_add_arg (gpg, "--");
if (!err)
{
const char *s = gpgme_key_get_string_attr (key, GPGME_ATTR_FPR, NULL, 0);
if (!s)
err = mk_error (Invalid_Key);
else
err = _gpgme_gpg_add_arg (gpg, s);
}
return err;
}
static GpgmeError
_gpgme_append_gpg_args_from_recipients (GpgObject gpg,
const GpgmeRecipients rset)
{
GpgmeError err = 0;
struct user_id_s *r;
assert (rset);
for (r = rset->list; r; r = r->next)
{
err = _gpgme_gpg_add_arg (gpg, "-r");
if (!err)
_gpgme_gpg_add_arg (gpg, r->name);
if (err)
break;
}
return err;
}
GpgmeError
_gpgme_gpg_op_encrypt (GpgObject gpg, GpgmeRecipients recp,
GpgmeData plain, GpgmeData ciph, int use_armor)
{
GpgmeError err;
err = _gpgme_gpg_add_arg (gpg, "--encrypt");
if (!err && use_armor)
err = _gpgme_gpg_add_arg (gpg, "--armor");
/* If we know that all recipients are valid (full or ultimate trust)
* we can suppress further checks */
if (!err && _gpgme_recipients_all_valid (recp))
err = _gpgme_gpg_add_arg (gpg, "--always-trust");
if (!err)
err = _gpgme_append_gpg_args_from_recipients (gpg, recp);
/* Tell the gpg object about the data. */
if (!err)
err = _gpgme_gpg_add_arg (gpg, "--output");
if (!err)
err = _gpgme_gpg_add_arg (gpg, "-");
if (!err)
err = _gpgme_gpg_add_data (gpg, ciph, 1);
if (!err)
err = _gpgme_gpg_add_arg (gpg, "--");
if (!err)
err = _gpgme_gpg_add_data (gpg, plain, 0);
return err;
}
GpgmeError
_gpgme_gpg_op_export (GpgObject gpg, GpgmeRecipients recp,
GpgmeData keydata, int use_armor)
{
GpgmeError err;
err = _gpgme_gpg_add_arg (gpg, "--export");
if (!err && use_armor)
err = _gpgme_gpg_add_arg (gpg, "--armor");
if (!err)
err = _gpgme_gpg_add_data (gpg, keydata, 1);
if (!err)
err = _gpgme_gpg_add_arg (gpg, "--");
if (!err)
{
void *ec;
const char *s;
err = gpgme_recipients_enum_open (recp, &ec);
while (!err && (s = gpgme_recipients_enum_read (recp, &ec)))
err = _gpgme_gpg_add_arg (gpg, s);
if (!err)
err = gpgme_recipients_enum_close (recp, &ec);
}
return err;
}
GpgmeError
_gpgme_gpg_op_genkey (GpgObject gpg, GpgmeData help_data, int use_armor)
{
GpgmeError err;
2001-11-21 Marcus Brinkmann <marcus@g10code.de> * context.h: Do not include rungpg.h, but engine.h. (struct gpgme_context_s): Replace member gpg with engine. * gpgme.c (gpgme_release): Release engine, not gpg. * recipient.c (_gpgme_append_gpg_args_from_recifgpients): Function moved ... * rungpg.c (_gpgme_append_gpg_args_from_recipients): ... here. Make static, change order of arguments, and return an error value. * ops.h (_gpgme_append_gpg_args_from_recipients): Removed prototype. * rungpg.h (_gpgme_gpg_op_verify): Add prototype. (_gpgme_gpg_op_encrypt): Likewise. (_gpgme_gpg_op_decrypt): Likewise. (_gpgme_gpg_op_delete): Likewise. (_gpgme_gpg_op_export): Likewise. (_gpgme_gpg_op_genkey): Likewise. (_gpgme_gpg_op_import): Likewise. (_gpgme_gpg_op_keylist): Likewise. (_gpgme_gpg_op_sign): Likewise. (_gpgme_gpg_op_trustlist): Likewise. * rungpg.c (_gpgme_gpg_op_verify): New function. (_gpgme_gpg_op_encrypt): Likewise. (_gpgme_gpg_op_decrypt): Likewise. (_gpgme_gpg_op_delete): Likewise. (_gpgme_gpg_op_export): Likewise. (_gpgme_gpg_op_genkey): Likewise. (_gpgme_gpg_op_import): Likewise. (_gpgme_gpg_op_keylist): Likewise. (_gpgme_gpg_op_sign): Likewise. (_gpgme_gpg_op_trustlist): Likewise. * engine.h (_gpgme_engine_set_status_handler): Add prototype. (_gpgme_engine_set_command_handler): Likewise. (_gpgme_engine_set_colon_line_handler): Likewise. (_gpgme_engine_op_decrypt): Likewise. (_gpgme_engine_op_delete): Likewise. (_gpgme_engine_op_encrypt): Likewise. (_gpgme_engine_op_export): Likewise. (_gpgme_engine_op_genkey): Likewise. (_gpgme_engine_op_import): Likewise. (_gpgme_engine_op_keylist): Likewise. (_gpgme_engine_op_sign): Likewise. (_gpgme_engine_op_trustlist): Likewise. (_gpgme_engine_op_verify): Likewise. (_gpgme_engine_start): Likewise. * engine.c (_gpgme_engine_set_status_handler): New function. (_gpgme_engine_set_command_handler): Likewise. (_gpgme_engine_set_colon_line_handler): Likewise. (_gpgme_engine_op_decrypt): Likewise. (_gpgme_engine_op_delete): Likewise. (_gpgme_engine_op_encrypt): Likewise. (_gpgme_engine_op_export): Likewise. (_gpgme_engine_op_genkey): Likewise. (_gpgme_engine_op_import): Likewise. (_gpgme_engine_op_keylist): Likewise. (_gpgme_engine_op_sign): Likewise. (_gpgme_engine_op_trustlist): Likewise. (_gpgme_engine_op_verify): Likewise. (_gpgme_engine_start): Likewise. * verify.c (gpgme_op_verify_start): Reimplement in terms of above functions. * encrypt.c (gpgme_op_encrypt_start): Likewise. * decrypt.c (_gpgme_decrypt_start): Likewise. * passphrase.c (_gpgme_passphrase_start): Likewise. * keylist.c (gpgme_op_keylist_start): Likewise.
2001-11-21 03:40:17 +00:00
err = _gpgme_gpg_add_arg (gpg, "--gen-key");
if (!err && use_armor)
err = _gpgme_gpg_add_arg (gpg, "--armor");
if (!err)
err = _gpgme_gpg_add_data (gpg, help_data, 0);
2001-11-21 Marcus Brinkmann <marcus@g10code.de> * context.h: Do not include rungpg.h, but engine.h. (struct gpgme_context_s): Replace member gpg with engine. * gpgme.c (gpgme_release): Release engine, not gpg. * recipient.c (_gpgme_append_gpg_args_from_recifgpients): Function moved ... * rungpg.c (_gpgme_append_gpg_args_from_recipients): ... here. Make static, change order of arguments, and return an error value. * ops.h (_gpgme_append_gpg_args_from_recipients): Removed prototype. * rungpg.h (_gpgme_gpg_op_verify): Add prototype. (_gpgme_gpg_op_encrypt): Likewise. (_gpgme_gpg_op_decrypt): Likewise. (_gpgme_gpg_op_delete): Likewise. (_gpgme_gpg_op_export): Likewise. (_gpgme_gpg_op_genkey): Likewise. (_gpgme_gpg_op_import): Likewise. (_gpgme_gpg_op_keylist): Likewise. (_gpgme_gpg_op_sign): Likewise. (_gpgme_gpg_op_trustlist): Likewise. * rungpg.c (_gpgme_gpg_op_verify): New function. (_gpgme_gpg_op_encrypt): Likewise. (_gpgme_gpg_op_decrypt): Likewise. (_gpgme_gpg_op_delete): Likewise. (_gpgme_gpg_op_export): Likewise. (_gpgme_gpg_op_genkey): Likewise. (_gpgme_gpg_op_import): Likewise. (_gpgme_gpg_op_keylist): Likewise. (_gpgme_gpg_op_sign): Likewise. (_gpgme_gpg_op_trustlist): Likewise. * engine.h (_gpgme_engine_set_status_handler): Add prototype. (_gpgme_engine_set_command_handler): Likewise. (_gpgme_engine_set_colon_line_handler): Likewise. (_gpgme_engine_op_decrypt): Likewise. (_gpgme_engine_op_delete): Likewise. (_gpgme_engine_op_encrypt): Likewise. (_gpgme_engine_op_export): Likewise. (_gpgme_engine_op_genkey): Likewise. (_gpgme_engine_op_import): Likewise. (_gpgme_engine_op_keylist): Likewise. (_gpgme_engine_op_sign): Likewise. (_gpgme_engine_op_trustlist): Likewise. (_gpgme_engine_op_verify): Likewise. (_gpgme_engine_start): Likewise. * engine.c (_gpgme_engine_set_status_handler): New function. (_gpgme_engine_set_command_handler): Likewise. (_gpgme_engine_set_colon_line_handler): Likewise. (_gpgme_engine_op_decrypt): Likewise. (_gpgme_engine_op_delete): Likewise. (_gpgme_engine_op_encrypt): Likewise. (_gpgme_engine_op_export): Likewise. (_gpgme_engine_op_genkey): Likewise. (_gpgme_engine_op_import): Likewise. (_gpgme_engine_op_keylist): Likewise. (_gpgme_engine_op_sign): Likewise. (_gpgme_engine_op_trustlist): Likewise. (_gpgme_engine_op_verify): Likewise. (_gpgme_engine_start): Likewise. * verify.c (gpgme_op_verify_start): Reimplement in terms of above functions. * encrypt.c (gpgme_op_encrypt_start): Likewise. * decrypt.c (_gpgme_decrypt_start): Likewise. * passphrase.c (_gpgme_passphrase_start): Likewise. * keylist.c (gpgme_op_keylist_start): Likewise.
2001-11-21 03:40:17 +00:00
return err;
}
GpgmeError
_gpgme_gpg_op_import (GpgObject gpg, GpgmeData keydata)
{
GpgmeError err;
err = _gpgme_gpg_add_arg (gpg, "--import");
if (!err)
err = _gpgme_gpg_add_data (gpg, keydata, 0);
return err;
}
GpgmeError
_gpgme_gpg_op_keylist (GpgObject gpg, const char *pattern, int secret_only,
int keylist_mode)
{
GpgmeError err;
err = _gpgme_gpg_add_arg (gpg, "--with-colons");
if (!err)
err = _gpgme_gpg_add_arg (gpg, "--fixed-list-mode");
if (!err)
err = _gpgme_gpg_add_arg (gpg, "--with-fingerprint");
if (!err)
err = _gpgme_gpg_add_arg (gpg, secret_only ? "--list-secret-keys"
: "--list-keys");
/* Tell the gpg object about the data */
if (!err)
err = _gpgme_gpg_add_arg (gpg, "--");
if (!err && pattern && *pattern)
err = _gpgme_gpg_add_arg (gpg, pattern);
return err;
}
GpgmeError
_gpgme_gpg_op_sign (GpgObject gpg, GpgmeData in, GpgmeData out,
GpgmeSigMode mode, int use_armor,
int use_textmode, GpgmeCtx ctx /* FIXME */)
{
GpgmeError err;
GpgmeKey key;
int i;
if (mode == GPGME_SIG_MODE_CLEAR)
err = _gpgme_gpg_add_arg (gpg, "--clearsign");
else
{
err = _gpgme_gpg_add_arg (gpg, "--sign");
if (!err && mode == GPGME_SIG_MODE_DETACH)
err = _gpgme_gpg_add_arg (gpg, "--detach");
if (!err && use_armor)
err = _gpgme_gpg_add_arg (gpg, "--armor");
if (!err && use_textmode)
_gpgme_gpg_add_arg (gpg, "--textmode");
}
for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
{
const char *s = gpgme_key_get_string_attr (key, GPGME_ATTR_KEYID,
NULL, 0);
if (s)
{
if (!err)
err = _gpgme_gpg_add_arg (gpg, "-u");
if (!err)
err = _gpgme_gpg_add_arg (gpg, s);
}
gpgme_key_unref (key);
if (err) break;
}
/* Tell the gpg object about the data. */
if (!err)
err = _gpgme_gpg_add_data (gpg, in, 0);
if (!err)
err = _gpgme_gpg_add_data (gpg, out, 1);
return err;
}
GpgmeError
_gpgme_gpg_op_trustlist (GpgObject gpg, const char *pattern)
{
GpgmeError err;
err = _gpgme_gpg_add_arg (gpg, "--with-colons");
if (!err)
err = _gpgme_gpg_add_arg (gpg, "--list-trust-path");
/* Tell the gpg object about the data */
if (!err)
err = _gpgme_gpg_add_arg (gpg, "--");
if (!err)
err = _gpgme_gpg_add_arg (gpg, pattern);
return err;
}
2001-11-21 Marcus Brinkmann <marcus@g10code.de> * context.h: Do not include rungpg.h, but engine.h. (struct gpgme_context_s): Replace member gpg with engine. * gpgme.c (gpgme_release): Release engine, not gpg. * recipient.c (_gpgme_append_gpg_args_from_recifgpients): Function moved ... * rungpg.c (_gpgme_append_gpg_args_from_recipients): ... here. Make static, change order of arguments, and return an error value. * ops.h (_gpgme_append_gpg_args_from_recipients): Removed prototype. * rungpg.h (_gpgme_gpg_op_verify): Add prototype. (_gpgme_gpg_op_encrypt): Likewise. (_gpgme_gpg_op_decrypt): Likewise. (_gpgme_gpg_op_delete): Likewise. (_gpgme_gpg_op_export): Likewise. (_gpgme_gpg_op_genkey): Likewise. (_gpgme_gpg_op_import): Likewise. (_gpgme_gpg_op_keylist): Likewise. (_gpgme_gpg_op_sign): Likewise. (_gpgme_gpg_op_trustlist): Likewise. * rungpg.c (_gpgme_gpg_op_verify): New function. (_gpgme_gpg_op_encrypt): Likewise. (_gpgme_gpg_op_decrypt): Likewise. (_gpgme_gpg_op_delete): Likewise. (_gpgme_gpg_op_export): Likewise. (_gpgme_gpg_op_genkey): Likewise. (_gpgme_gpg_op_import): Likewise. (_gpgme_gpg_op_keylist): Likewise. (_gpgme_gpg_op_sign): Likewise. (_gpgme_gpg_op_trustlist): Likewise. * engine.h (_gpgme_engine_set_status_handler): Add prototype. (_gpgme_engine_set_command_handler): Likewise. (_gpgme_engine_set_colon_line_handler): Likewise. (_gpgme_engine_op_decrypt): Likewise. (_gpgme_engine_op_delete): Likewise. (_gpgme_engine_op_encrypt): Likewise. (_gpgme_engine_op_export): Likewise. (_gpgme_engine_op_genkey): Likewise. (_gpgme_engine_op_import): Likewise. (_gpgme_engine_op_keylist): Likewise. (_gpgme_engine_op_sign): Likewise. (_gpgme_engine_op_trustlist): Likewise. (_gpgme_engine_op_verify): Likewise. (_gpgme_engine_start): Likewise. * engine.c (_gpgme_engine_set_status_handler): New function. (_gpgme_engine_set_command_handler): Likewise. (_gpgme_engine_set_colon_line_handler): Likewise. (_gpgme_engine_op_decrypt): Likewise. (_gpgme_engine_op_delete): Likewise. (_gpgme_engine_op_encrypt): Likewise. (_gpgme_engine_op_export): Likewise. (_gpgme_engine_op_genkey): Likewise. (_gpgme_engine_op_import): Likewise. (_gpgme_engine_op_keylist): Likewise. (_gpgme_engine_op_sign): Likewise. (_gpgme_engine_op_trustlist): Likewise. (_gpgme_engine_op_verify): Likewise. (_gpgme_engine_start): Likewise. * verify.c (gpgme_op_verify_start): Reimplement in terms of above functions. * encrypt.c (gpgme_op_encrypt_start): Likewise. * decrypt.c (_gpgme_decrypt_start): Likewise. * passphrase.c (_gpgme_passphrase_start): Likewise. * keylist.c (gpgme_op_keylist_start): Likewise.
2001-11-21 03:40:17 +00:00
GpgmeError
_gpgme_gpg_op_verify (GpgObject gpg, GpgmeData sig, GpgmeData text)
{
GpgmeError err = 0;
if (gpg->pm.used)
{
err = _gpgme_gpg_add_arg (gpg, gpg->pm.used ? "--pipemode" : "--verify");
if (!err)
err = _gpgme_gpg_add_arg (gpg, "--");
if (!err)
err = _gpgme_gpg_add_pm_data (gpg, sig, 0);
if (!err)
err = _gpgme_gpg_add_pm_data (gpg, text, 1);
}
else
{
err = _gpgme_gpg_add_arg (gpg, "--verify");
if (!err)
err = _gpgme_gpg_add_arg (gpg, "--");
if (!err)
err = _gpgme_gpg_add_data (gpg, sig, -1);
if (text)
{
if (!err)
err = _gpgme_gpg_add_arg (gpg, "-");
if (!err)
err = _gpgme_gpg_add_data (gpg, text, 0);
}
}
return err;
}