2000-11-07 13:32:38 +00:00
|
|
|
/* rungpg.c
|
|
|
|
* Copyright (C) 2000 Werner Koch (dd9jn)
|
2002-02-06 01:41:15 +00:00
|
|
|
* 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>
|
2000-12-12 13:31:25 +00:00
|
|
|
#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 */
|
2000-11-22 17:10:48 +00:00
|
|
|
#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;
|
|
|
|
|
2000-11-23 15:28:15 +00:00
|
|
|
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;
|
2000-12-12 13:31:25 +00:00
|
|
|
|
|
|
|
/* 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
|
|
|
|
2000-11-23 15:28:15 +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 );
|
|
|
|
|
2000-11-23 15:28:15 +00:00
|
|
|
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 );
|
|
|
|
|
2000-12-12 13:31:25 +00:00
|
|
|
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
|
|
|
|
2001-02-19 17:22: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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-11-20 06:01:24 +00:00
|
|
|
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 ());
|
2001-02-19 17:22:38 +00:00
|
|
|
|
2001-11-20 06:01:24 +00:00
|
|
|
return gpg_version;
|
|
|
|
}
|
2001-02-19 17:22:38 +00:00
|
|
|
|
2001-11-20 06:01:24 +00:00
|
|
|
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;
|
2000-12-12 13:31:25 +00:00
|
|
|
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;
|
|
|
|
}
|
2001-02-19 17:22:38 +00:00
|
|
|
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-12-12 13:31:25 +00:00
|
|
|
|
2000-11-07 13:32:38 +00:00
|
|
|
void
|
2001-11-11 18:54:57 +00:00
|
|
|
_gpgme_gpg_release (GpgObject gpg)
|
2000-11-07 13:32:38 +00:00
|
|
|
{
|
2001-11-11 18:54:57 +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
|
|
|
}
|
2001-11-11 18:54:57 +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)
|
2001-12-18 Marcus Brinkmann <marcus@g10code.de>
* rungpg.c (struct reap_s, reap_list, reap_list_lock): Moved to ...
* engine.c (struct reap_s, reap_list, reap_list_lock): ... here.
Include `time.h', `sys/types.h', `assert.h', and `sema.h'.
* rungpg.c (_gpgme_engine_add_child_to_reap_list): New function.
(do_reaping, _gpgme_gpg_housecleaning): Moved to ...
* engine.c (do_reaping, _gpgme_engine_housecleaning): ... here.
* rungpg.c (_gpgme_gpg_release): Replace code that is now in its
own function by call to _gpgme_engine_add_child_to_reap_list().
* wait.c: Include `engine.h'.
(run_idle): Call _gpgme_engine_housecleaning(), not
_gpgme_gpg_housecleaning().
2001-12-18 21:02:35 +00:00
|
|
|
_gpgme_engine_add_child_to_reap_list (gpg, sizeof *gpg, gpg->pid);
|
2001-11-11 18:54:57 +00:00
|
|
|
else
|
|
|
|
xfree (gpg);
|
2000-11-07 13:32:38 +00:00
|
|
|
}
|
|
|
|
|
2000-12-12 13:31:25 +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);
|
2000-12-12 13:31:25 +00:00
|
|
|
|
|
|
|
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);
|
2000-12-12 13:31:25 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2000-12-12 13:31:25 +00:00
|
|
|
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);
|
2000-12-12 13:31:25 +00:00
|
|
|
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);
|
2000-12-12 13:31:25 +00:00
|
|
|
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);
|
|
|
|
}
|
2001-02-19 17:22:38 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-12-12 13:31:25 +00:00
|
|
|
/*
|
|
|
|
* 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 )
|
2000-11-23 15:28:15 +00:00
|
|
|
_gpgme_io_close (fd_data_map[i].fd);
|
2000-11-09 16:35:35 +00:00
|
|
|
if ( fd_data_map[i].peer_fd != -1 )
|
2000-11-23 15:28:15 +00:00
|
|
|
_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++;
|
2000-12-12 13:31:25 +00:00
|
|
|
if (!gpg->cmd.used)
|
|
|
|
argc++;
|
2001-07-31 15:21:58 +00:00
|
|
|
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++;
|
|
|
|
}
|
2000-12-12 13:31:25 +00:00
|
|
|
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++;
|
|
|
|
}
|
2001-07-31 15:21:58 +00:00
|
|
|
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);
|
|
|
|
}
|
2001-02-19 17:22:38 +00:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
}
|
2000-12-12 13:31:25 +00:00
|
|
|
|
|
|
|
/* 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;
|
2000-11-22 17:10:48 +00:00
|
|
|
int i, n;
|
2000-11-23 15:28:15 +00:00
|
|
|
int pid;
|
2000-11-22 17:10:48 +00:00
|
|
|
struct spawn_fd_item_s *fd_child_list, *fd_parent_list;
|
2000-11-07 13:32:38 +00:00
|
|
|
|
2001-11-18 03:31:31 +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);
|
|
|
|
|
2000-12-12 13:31:25 +00:00
|
|
|
if (gpg->pm.active)
|
|
|
|
return 0;
|
|
|
|
|
2000-11-07 13:32:38 +00:00
|
|
|
rc = build_argv ( gpg );
|
|
|
|
if ( rc )
|
|
|
|
return rc;
|
|
|
|
|
2001-12-13 22:36:03 +00:00
|
|
|
n = 3; /* status_fd, colon_fd and end of list */
|
2000-11-22 17:10:48 +00:00
|
|
|
for (i=0; gpg->fd_data_map[i].data; i++ )
|
2001-12-13 22:36:03 +00:00
|
|
|
n++;
|
2000-11-22 17:10:48 +00:00
|
|
|
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
|
|
|
}
|
2000-11-22 17:10:48 +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
|
|
|
}
|
2000-11-22 17:10:48 +00:00
|
|
|
}
|
|
|
|
fd_child_list[n].fd = -1;
|
|
|
|
fd_child_list[n].dup_to = -1;
|
2000-11-07 13:32:38 +00:00
|
|
|
|
2000-11-22 17:10:48 +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
|
|
|
|
2000-11-22 17:10:48 +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);
|
2000-11-22 17:10:48 +00:00
|
|
|
xfree (fd_child_list);
|
|
|
|
if (pid == -1) {
|
|
|
|
return mk_error (Exec_Error);
|
2000-11-07 13:32:38 +00:00
|
|
|
}
|
2000-11-22 17:10:48 +00:00
|
|
|
|
2000-11-07 13:32:38 +00:00
|
|
|
gpg->pid = pid;
|
2000-12-12 13:31:25 +00:00
|
|
|
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
|
|
|
|
2000-11-22 17:10:48 +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) {
|
2000-11-22 17:10:48 +00:00
|
|
|
_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?
|
2001-11-22 Marcus Brinkmann <marcus@g10code.de>
* rungpg.c (gpg_inbound_handler, write_mem_data, write_cb_data,
gpg_outbound_handler): Moved to ...
* data.c (_gpgme_data_inbound_handler, write_mem_data,
write_cb_data, _gpgme_data_outbound_handler): ... here. Make the
_gpgme_* ones non-static.
* data.c: Include io.h.
* ops.h (_gpgme_data_inbound_handler): New prototype.
(_gpgme_data_outbound_handler): Likewise.
(_gpgme_gpg_spawn): Use these new functions.
* engine-gpgsm.h (_gpgme_gpgsm_op_decrypt, _gpgme_gpgsm_op_delete,
_gpgme_gpgsm_op_encrypt, _gpgme_gpgsm_op_export,
_gpgme_gpgsm_op_genkey, _gpgme_gpgsm_op_import,
_gpgme_gpgsm_op_keylist, _gpgme_gpgsm_op_sign,
_gpgme_gpgsm_op_trustlist, _gpgme_gpgsm_op_verify,
_gpgme_gpgsm_start, _gpgme_gpgsm_set_status_handler): New prototype.
Include <rungpg.h> for status handler function.
* engine-gpgsm.c (struct gpgsm_object_s): New members input_fd,
input_data, output_fd, output_data, message_fd, message_data, command
and status.
(_gpgme_gpgsm_new): Open input, output and message pipes before
connecting to the client. Close server's ends afterwards.
(_gpgme_gpgsm_release): Close open file descriptors. Remove
server process from wait queue.
(_gpgme_gpgsm_op_verify, _gpgme_gpgsm_start,
_gpgme_gpgsm_set_status_handler, gpgms_status_handler): New function.
* engine.c (_gpgme_engine_start): Implement for GPGME_PROTOCOL_CMS.
(_gpgme_engine_set_status_handler): Likewise.
(_gpgme_engine_op_verify): Likewise.
2001-11-22 03:08:58 +00:00
|
|
|
_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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-12-12 13:31:25 +00:00
|
|
|
if ( gpg->cmd.used )
|
|
|
|
_gpgme_freeze_fd ( gpg->cmd.fd );
|
2000-11-07 13:32:38 +00:00
|
|
|
|
2000-12-12 13:31:25 +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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-11-22 17:10:48 +00:00
|
|
|
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 )
|
2000-12-12 13:31:25 +00:00
|
|
|
&& 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 ) {
|
2000-12-12 13:31:25 +00:00
|
|
|
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
|
2001-01-30 11:01:41 +00:00
|
|
|
* handler does its action */
|
2000-12-12 13:31:25 +00:00
|
|
|
if ( nread > 1 )
|
2001-01-30 11:01:41 +00:00
|
|
|
DEBUG0 ("ERROR, unexpected data in read_status");
|
2000-12-12 13:31:25 +00:00
|
|
|
_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
|
2000-11-23 15:28:15 +00:00
|
|
|
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 ) {
|
2001-01-30 11:01:41 +00:00
|
|
|
DEBUG1 ("gpg_colon_line_handler: "
|
2000-11-10 17:50:24 +00:00
|
|
|
"read problem %d\n - stop", rc);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2001-01-31 12:39:53 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-11-22 17:10:48 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2000-12-12 13:31:25 +00:00
|
|
|
static GpgmeError
|
|
|
|
pipemode_copy (char *buffer, size_t length, size_t *nread, GpgmeData data )
|
|
|
|
{
|
|
|
|
GpgmeError err;
|
2001-11-02 15:51:15 +00:00
|
|
|
size_t nbytes;
|
2000-12-12 13:31:25 +00:00
|
|
|
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) {
|
2001-01-30 11:01:41 +00:00
|
|
|
DEBUG1 ("pipemode_cb: copy sig failed: %s\n",
|
2000-12-12 13:31:25 +00:00
|
|
|
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) {
|
2001-01-30 11:01:41 +00:00
|
|
|
DEBUG1 ("pipemode_cb: copy data failed: %s\n",
|
2000-12-12 13:31:25 +00:00
|
|
|
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;
|
|
|
|
|
2001-01-30 11:01:41 +00:00
|
|
|
DEBUG0 ("command_cb: enter\n");
|
2000-12-12 13:31:25 +00:00
|
|
|
assert (gpg->cmd.used);
|
|
|
|
if ( !buffer || !length || !nread )
|
|
|
|
return 0; /* those values are reserved for extensions */
|
|
|
|
*nread =0;
|
|
|
|
if ( !gpg->cmd.code ) {
|
2001-01-30 11:01:41 +00:00
|
|
|
DEBUG0 ("command_cb: no code\n");
|
2000-12-12 13:31:25 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !gpg->cmd.fnc ) {
|
2001-01-30 11:01:41 +00:00
|
|
|
DEBUG0 ("command_cb: no user cb\n");
|
2000-12-12 13:31:25 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
value = gpg->cmd.fnc ( gpg->cmd.fnc_value,
|
|
|
|
gpg->cmd.code, gpg->cmd.keyword );
|
|
|
|
if ( !value ) {
|
2001-01-30 11:01:41 +00:00
|
|
|
DEBUG0 ("command_cb: no data from user cb\n");
|
2000-12-12 13:31:25 +00:00
|
|
|
gpg->cmd.fnc ( gpg->cmd.fnc_value, 0, value);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
value_len = strlen (value);
|
|
|
|
if ( value_len+1 > length ) {
|
2001-01-30 11:01:41 +00:00
|
|
|
DEBUG0 ("command_cb: too much data from user cb\n");
|
2000-12-12 13:31:25 +00:00
|
|
|
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 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;
|
2000-12-12 13:31:25 +00:00
|
|
|
|
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);
|
2000-12-12 13:31:25 +00:00
|
|
|
|
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;
|
|
|
|
}
|
2000-12-12 13:31:25 +00:00
|
|
|
|
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;
|
|
|
|
}
|