diff options
Diffstat (limited to 'gpgme/rungpg.c')
-rw-r--r-- | gpgme/rungpg.c | 981 |
1 files changed, 510 insertions, 471 deletions
diff --git a/gpgme/rungpg.c b/gpgme/rungpg.c index b6bc45d2..19ca74d3 100644 --- a/gpgme/rungpg.c +++ b/gpgme/rungpg.c @@ -1,25 +1,26 @@ -/* rungpg.c - * Copyright (C) 2000 Werner Koch (dd9jn) - * Copyright (C) 2001, 2002 g10 Code GmbH - * - * 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 - */ - +/* rungpg.c - Gpg Engine. + Copyright (C) 2000 Werner Koch (dd9jn) + Copyright (C) 2001, 2002 g10 Code GmbH + + 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 GPGME; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#if HAVE_CONFIG_H #include <config.h> +#endif #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -36,12 +37,12 @@ #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" #include "sema.h" #include "status-table.h" +#include "engine-backend.h" /* This type is used to build a list of gpg arguments and data @@ -56,6 +57,7 @@ struct arg_and_data_s char arg[1]; /* Used if data above is not used. */ }; + struct fd_data_map_s { GpgmeData data; @@ -85,7 +87,7 @@ struct gpg_object_s void *tag; } status; - /* This is a kludge - see the comment at gpg_colon_line_handler. */ + /* This is a kludge - see the comment at colon_line_handler. */ struct { int fd[2]; @@ -121,17 +123,16 @@ struct gpg_object_s struct GpgmeIOCbs io_cbs; }; -static void free_argv (char **argv); -static void free_fd_data_map (struct fd_data_map_s *fd_data_map); -static void gpg_status_handler (void *opaque, int fd); -static GpgmeError read_status (GpgObject gpg); +static void +gpg_io_event (void *engine, GpgmeEventIO type, void *type_data) +{ + GpgObject gpg = engine; -static void gpg_colon_line_handler (void *opaque, int fd); -static GpgmeError read_colon_line (GpgObject gpg); + if (gpg->io_cbs.event) + (*gpg->io_cbs.event) (gpg->io_cbs.event_priv, type, type_data); +} -static int command_cb (void *opaque, char *buffer, size_t length, - size_t *nread); static void close_notify_handler (int fd, void *opaque) @@ -204,7 +205,7 @@ close_notify_handler (int fd, void *opaque) } } if (!not_done) - _gpgme_gpg_io_event (gpg, GPGME_EVENT_DONE, NULL); + gpg_io_event (gpg, GPGME_EVENT_DONE, NULL); } static GpgmeError @@ -263,8 +264,8 @@ add_data (GpgObject gpg, GpgmeData data, int dup_to, int inbound) } -const char * -_gpgme_gpg_get_version (void) +static const char * +gpg_get_version (void) { static const char *gpg_version; DEFINE_STATIC_LOCK (gpg_version_lock); @@ -276,16 +277,86 @@ _gpgme_gpg_get_version (void) return gpg_version; } -GpgmeError -_gpgme_gpg_check_version (void) + +static GpgmeError +gpg_check_version (void) { - return _gpgme_compare_versions (_gpgme_gpg_get_version (), - NEED_GPG_VERSION) + return _gpgme_compare_versions (gpg_get_version (), NEED_GPG_VERSION) ? 0 : mk_error (Invalid_Engine); } -GpgmeError -_gpgme_gpg_new (GpgObject *r_gpg) + +static void +free_argv (char **argv) +{ + int i; + + for (i = 0; argv[i]; i++) + free (argv[i]); + free (argv); +} + + +static void +free_fd_data_map (struct fd_data_map_s *fd_data_map) +{ + int i; + + if (!fd_data_map) + return; + + for (i = 0; fd_data_map[i].data; i++) + { + if (fd_data_map[i].fd != -1) + _gpgme_io_close (fd_data_map[i].fd); + if (fd_data_map[i].peer_fd != -1) + _gpgme_io_close (fd_data_map[i].peer_fd); + /* Don't release data because this is only a reference. */ + } + free (fd_data_map); +} + + +static void +gpg_release (void *engine) +{ + GpgObject gpg = engine; + + if (!gpg) + return; + + while (gpg->arglist) + { + struct arg_and_data_s *next = gpg->arglist->next; + + free (gpg->arglist); + gpg->arglist = next; + } + + free (gpg->status.buffer); + free (gpg->colon.buffer); + if (gpg->argv) + free_argv (gpg->argv); + gpgme_data_release (gpg->cmd.cb_data); + free (gpg->cmd.keyword); + + 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->cmd.fd != -1) + _gpgme_io_close (gpg->cmd.fd); + free (gpg); +} + + +static GpgmeError +gpg_new (void **engine) { GpgObject gpg; int rc = 0; @@ -344,55 +415,18 @@ _gpgme_gpg_new (GpgObject *r_gpg) leave: if (rc) - { - _gpgme_gpg_release (gpg); - *r_gpg = NULL; - } + gpg_release (gpg); else - *r_gpg = gpg; + *engine = gpg; return rc; } -void -_gpgme_gpg_release (GpgObject gpg) +static GpgmeError +gpg_set_verbosity (void *engine, int verbosity) { - if (!gpg) - return; - - while (gpg->arglist) - { - struct arg_and_data_s *next = gpg->arglist->next; - - free (gpg->arglist); - gpg->arglist = next; - } - - free (gpg->status.buffer); - free (gpg->colon.buffer); - if (gpg->argv) - free_argv (gpg->argv); - gpgme_data_release (gpg->cmd.cb_data); - free (gpg->cmd.keyword); - - 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->cmd.fd != -1) - _gpgme_io_close (gpg->cmd.fd); - free (gpg); -} - + GpgObject gpg = engine; -GpgmeError -_gpgme_gpg_set_verbosity (GpgObject gpg, int verbosity) -{ GpgmeError err = 0; while (!err && verbosity-- > 0) err = add_arg (gpg, "--verbose"); @@ -401,22 +435,21 @@ _gpgme_gpg_set_verbosity (GpgObject gpg, int verbosity) /* Note, that the status_handler is allowed to modifiy the args value. */ -void -_gpgme_gpg_set_status_handler (GpgObject gpg, - GpgmeStatusHandler fnc, void *fnc_value) +static void +gpg_set_status_handler (void *engine, GpgmeStatusHandler fnc, void *fnc_value) { - assert (gpg); + GpgObject gpg = engine; gpg->status.fnc = fnc; gpg->status.fnc_value = fnc_value; } /* Kludge to process --with-colon output. */ -GpgmeError -_gpgme_gpg_set_colon_line_handler (GpgObject gpg, - GpgmeColonLineHandler fnc, void *fnc_value) +static GpgmeError +gpg_set_colon_line_handler (void *engine, GpgmeColonLineHandler fnc, + void *fnc_value) { - assert (gpg); + GpgObject gpg = engine; gpg->colon.bufsize = 1024; gpg->colon.readpos = 0; @@ -441,21 +474,80 @@ _gpgme_gpg_set_colon_line_handler (GpgObject gpg, } +/* 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; /* These 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. */ + /* XXX We must check if there are any more fds active after removing + this one. */ + (*gpg->io_cbs.remove) (gpg->fd_data_map[gpg->cmd.idx].tag); + gpg->cmd.fd = gpg->fd_data_map[gpg->cmd.idx].fd; + gpg->fd_data_map[gpg->cmd.idx].fd = -1; + + return 0; +} + + /* 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, - GpgmeCommandHandler fnc, void *fnc_value, - GpgmeData linked_data) +static GpgmeError +gpg_set_command_handler (void *engine, GpgmeCommandHandler fnc, + void *fnc_value, GpgmeData linked_data) { + GpgObject gpg = engine; GpgmeData tmp; GpgmeError err; - assert (gpg); - err = gpgme_data_new_with_read_cb (&tmp, command_cb, gpg); if (err) return err; @@ -471,37 +563,6 @@ _gpgme_gpg_set_command_handler (GpgObject gpg, } -static void -free_argv (char **argv) -{ - int i; - - for (i = 0; argv[i]; i++) - free (argv[i]); - free (argv); -} - - -static void -free_fd_data_map (struct fd_data_map_s *fd_data_map) -{ - int i; - - if (!fd_data_map) - return; - - for (i = 0; fd_data_map[i].data; i++) - { - if (fd_data_map[i].fd != -1) - _gpgme_io_close (fd_data_map[i].fd); - if (fd_data_map[i].peer_fd != -1) - _gpgme_io_close (fd_data_map[i].peer_fd); - /* Don't release data because this is only a reference. */ - } - free (fd_data_map); -} - - static GpgmeError build_argv (GpgObject gpg) { @@ -712,9 +773,10 @@ build_argv (GpgObject gpg) return 0; } + static GpgmeError -_gpgme_gpg_add_io_cb (GpgObject gpg, int fd, int dir, - GpgmeIOCb handler, void *data, void **tag) +add_io_cb (GpgObject gpg, int fd, int dir, GpgmeIOCb handler, void *data, + void **tag) { GpgmeError err; @@ -727,179 +789,24 @@ _gpgme_gpg_add_io_cb (GpgObject gpg, int fd, int dir, return err; } -GpgmeError -_gpgme_gpg_spawn (GpgObject gpg, void *opaque) -{ - GpgmeError rc; - int i, n; - int status; - struct spawn_fd_item_s *fd_child_list, *fd_parent_list; - - if (!gpg) - return mk_error (Invalid_Value); - - if (! _gpgme_get_gpg_path ()) - return mk_error (Invalid_Engine); - - /* Kludge, so that we don't need to check the return code of all the - add_arg (). We bail out here instead. */ - if (gpg->arg_error) - return mk_error (Out_Of_Core); - - 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 = calloc (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++; - } - 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++; - } - } - fd_child_list[n].fd = -1; - fd_child_list[n].dup_to = -1; - - /* 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; - - status = _gpgme_io_spawn (_gpgme_get_gpg_path (), - gpg->argv, fd_child_list, fd_parent_list); - free (fd_child_list); - if (status == -1) - return mk_error (Exec_Error); - - /*_gpgme_register_term_handler ( closure, closure_value, pid );*/ - - rc = _gpgme_gpg_add_io_cb (gpg, gpg->status.fd[0], 1, - gpg_status_handler, gpg, &gpg->status.tag); - if (rc) - /* FIXME: kill the child */ - return rc; - - if (gpg->colon.fnc) - { - assert (gpg->colon.fd[0] != -1); - rc = _gpgme_gpg_add_io_cb (gpg, gpg->colon.fd[0], 1, - gpg_colon_line_handler, gpg, - &gpg->colon.tag); - if (rc) - /* FIXME: kill the child */ - return rc; - } - - for (i = 0; gpg->fd_data_map[i].data; i++) - { - if (gpg->cmd.used && i == gpg->cmd.idx) - { - /* Park the cmd fd. */ - gpg->cmd.fd = gpg->fd_data_map[i].fd; - gpg->fd_data_map[i].fd = -1; - } - else - { - rc = _gpgme_gpg_add_io_cb (gpg, gpg->fd_data_map[i].fd, - gpg->fd_data_map[i].inbound, - gpg->fd_data_map[i].inbound - ? _gpgme_data_inbound_handler - : _gpgme_data_outbound_handler, - gpg->fd_data_map[i].data, - &gpg->fd_data_map[i].tag); - - if (rc) - /* FIXME: kill the child */ - return rc; - } - } - - /* fixme: check what data we can release here */ - return 0; -} - - -static void -gpg_status_handler (void *opaque, int fd) -{ - GpgObject gpg = opaque; - int err; - - 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); - _gpgme_io_close (fd); - return; - } - if (gpg->status.eof) - _gpgme_io_close (fd); -} - static int status_cmp (const void *ap, const void *bp) { - const struct status_table_s *a = ap; - const struct status_table_s *b = bp; + const struct status_table_s *a = ap; + const struct status_table_s *b = bp; - return strcmp (a->name, b->name); + 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). */ +/* 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) { @@ -997,11 +904,10 @@ read_status (GpgObject gpg) while (fds.signaled); } - _gpgme_gpg_add_io_cb - (gpg, gpg->cmd.fd, - 0, _gpgme_data_outbound_handler, - gpg->fd_data_map[gpg->cmd.idx].data, - &gpg->fd_data_map[gpg->cmd.idx].tag); + add_io_cb (gpg, gpg->cmd.fd, 0, + _gpgme_data_outbound_handler, + gpg->fd_data_map[gpg->cmd.idx].data, + &gpg->fd_data_map[gpg->cmd.idx].tag); gpg->fd_data_map[gpg->cmd.idx].fd = gpg->cmd.fd; gpg->cmd.fd = -1; } @@ -1051,12 +957,109 @@ read_status (GpgObject gpg) } +static void +status_handler (void *opaque, int fd) +{ + GpgObject gpg = opaque; + int err; + + 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); + _gpgme_io_close (fd); + return; + } + if (gpg->status.eof) + _gpgme_io_close (fd); +} + + +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 = realloc (buffer, bufsize); + if (!buffer) + return mk_error (Out_Of_Core); + } + + nread = _gpgme_io_read (gpg->colon.fd[0], buffer+readpos, bufsize-readpos); + 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; + if (*buffer && strchr (buffer, ':')) + { + 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; +} + + /* 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 because it is easier to implement. */ static void -gpg_colon_line_handler (void *opaque, int fd) +colon_line_handler (void *opaque, int fd) { GpgObject gpg = opaque; GpgmeError rc = 0; @@ -1074,143 +1077,141 @@ gpg_colon_line_handler (void *opaque, int fd) _gpgme_io_close (fd); } + static GpgmeError -read_colon_line ( GpgObject gpg ) +gpg_start (void *engine, void *opaque) { - 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 = realloc (buffer, bufsize); - if ( !buffer ) - return mk_error (Out_Of_Core); - } - + GpgObject gpg = engine; + GpgmeError rc; + int i, n; + int status; + struct spawn_fd_item_s *fd_child_list, *fd_parent_list; - nread = _gpgme_io_read ( gpg->colon.fd[0], - buffer+readpos, bufsize-readpos ); - if (nread == -1) - return mk_error(Read_Error); + if (!gpg) + return mk_error (Invalid_Value); - if (!nread) { - gpg->colon.eof = 1; - assert (gpg->colon.fnc); - gpg->colon.fnc ( gpg->colon.fnc_value, NULL ); - return 0; - } + if (! _gpgme_get_gpg_path ()) + return mk_error (Invalid_Engine); - 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; - if (*buffer && strchr (buffer, ':')) - { - 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; -} + /* Kludge, so that we don't need to check the return code of all the + add_arg (). We bail out here instead. */ + if (gpg->arg_error) + return mk_error (Out_Of_Core); -/* - * Here we handle --command-fd. This works closely together with - * the status handler. - */ + rc = build_argv (gpg); + if (rc) + return rc; -static int -command_cb (void *opaque, char *buffer, size_t length, size_t *nread) -{ - GpgObject gpg = opaque; - const char *value; - int value_len; + n = 3; /* status_fd, colon_fd and end of list */ + for (i = 0; gpg->fd_data_map[i].data; i++) + n++; + fd_child_list = calloc (n + n, sizeof *fd_child_list); + if (!fd_child_list) + return mk_error (Out_Of_Core); + fd_parent_list = fd_child_list + n; - DEBUG0 ("command_cb: enter\n"); - assert (gpg->cmd.used); - if (!buffer || !length || !nread) - return 0; /* These values are reserved for extensions. */ - *nread = 0; - if (!gpg->cmd.code) + /* build the fd list for the child */ + n = 0; + if (gpg->colon.fnc) { - DEBUG0 ("command_cb: no code\n"); - return -1; + fd_child_list[n].fd = gpg->colon.fd[1]; + fd_child_list[n].dup_to = 1; /* dup to stdout */ + n++; } - - if (!gpg->cmd.fnc) + for (i = 0; gpg->fd_data_map[i].data; i++) { - DEBUG0 ("command_cb: no user cb\n"); - return -1; + 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++; + } } + fd_child_list[n].fd = -1; + fd_child_list[n].dup_to = -1; - value = gpg->cmd.fnc (gpg->cmd.fnc_value, - gpg->cmd.code, gpg->cmd.keyword); - if (!value) + /* Build the fd list for the parent. */ + n = 0; + if (gpg->status.fd[1] != -1) { - DEBUG0 ("command_cb: no data from user cb\n"); - gpg->cmd.fnc (gpg->cmd.fnc_value, 0, value); - return -1; + fd_parent_list[n].fd = gpg->status.fd[1]; + fd_parent_list[n].dup_to = -1; + n++; + gpg->status.fd[1] = -1; } - - value_len = strlen (value); - if (value_len + 1 > length) + if (gpg->colon.fd[1] != -1) { - DEBUG0 ("command_cb: too much data from user cb\n"); - gpg->cmd.fnc (gpg->cmd.fnc_value, 0, value); - return -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; - 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. */ - /* XXX We must check if there are any more fds active after removing - this one. */ - (*gpg->io_cbs.remove) (gpg->fd_data_map[gpg->cmd.idx].tag); - gpg->cmd.fd = gpg->fd_data_map[gpg->cmd.idx].fd; - gpg->fd_data_map[gpg->cmd.idx].fd = -1; + status = _gpgme_io_spawn (_gpgme_get_gpg_path (), + gpg->argv, fd_child_list, fd_parent_list); + free (fd_child_list); + if (status == -1) + return mk_error (Exec_Error); + + /*_gpgme_register_term_handler ( closure, closure_value, pid );*/ + rc = add_io_cb (gpg, gpg->status.fd[0], 1, status_handler, gpg, + &gpg->status.tag); + if (rc) + /* FIXME: kill the child */ + return rc; + + if (gpg->colon.fnc) + { + assert (gpg->colon.fd[0] != -1); + rc = add_io_cb (gpg, gpg->colon.fd[0], 1, colon_line_handler, gpg, + &gpg->colon.tag); + if (rc) + /* FIXME: kill the child */ + return rc; + } + + for (i = 0; gpg->fd_data_map[i].data; i++) + { + if (gpg->cmd.used && i == gpg->cmd.idx) + { + /* Park the cmd fd. */ + gpg->cmd.fd = gpg->fd_data_map[i].fd; + gpg->fd_data_map[i].fd = -1; + } + else + { + rc = add_io_cb (gpg, gpg->fd_data_map[i].fd, + gpg->fd_data_map[i].inbound, + gpg->fd_data_map[i].inbound + ? _gpgme_data_inbound_handler + : _gpgme_data_outbound_handler, + gpg->fd_data_map[i].data, &gpg->fd_data_map[i].tag); + + if (rc) + /* FIXME: kill the child */ + return rc; + } + } + + /* fixme: check what data we can release here */ return 0; } -GpgmeError -_gpgme_gpg_op_decrypt (GpgObject gpg, GpgmeData ciph, GpgmeData plain) + +static GpgmeError +gpg_decrypt (void *engine, GpgmeData ciph, GpgmeData plain) { + GpgObject gpg = engine; GpgmeError err; err = add_arg (gpg, "--decrypt"); @@ -1228,9 +1229,10 @@ _gpgme_gpg_op_decrypt (GpgObject gpg, GpgmeData ciph, GpgmeData plain) return err; } -GpgmeError -_gpgme_gpg_op_delete (GpgObject gpg, GpgmeKey key, int allow_secret) +static GpgmeError +gpg_delete (void *engine, GpgmeKey key, int allow_secret) { + GpgObject gpg = engine; GpgmeError err; err = add_arg (gpg, allow_secret ? "--delete-secret-and-public-key" @@ -1251,8 +1253,7 @@ _gpgme_gpg_op_delete (GpgObject gpg, GpgmeKey key, int allow_secret) static GpgmeError -_gpgme_append_gpg_args_from_signers (GpgObject gpg, - GpgmeCtx ctx /* FIXME */) +append_args_from_signers (GpgObject gpg, GpgmeCtx ctx /* FIXME */) { GpgmeError err = 0; int i; @@ -1276,15 +1277,15 @@ _gpgme_append_gpg_args_from_signers (GpgObject gpg, } -GpgmeError -_gpgme_gpg_op_edit (GpgObject gpg, GpgmeKey key, GpgmeData out, - GpgmeCtx ctx /* FIXME */) +static GpgmeError +gpg_edit (void *engine, GpgmeKey key, GpgmeData out, GpgmeCtx ctx /* FIXME */) { + GpgObject gpg = engine; GpgmeError err; err = add_arg (gpg, "--with-colons"); if (!err) - err = _gpgme_append_gpg_args_from_signers (gpg, ctx); + err = append_args_from_signers (gpg, ctx); if (!err) err = add_arg (gpg, "--edit-key"); if (!err) @@ -1305,8 +1306,7 @@ _gpgme_gpg_op_edit (GpgObject gpg, GpgmeKey key, GpgmeData out, static GpgmeError -_gpgme_append_gpg_args_from_recipients (GpgObject gpg, - const GpgmeRecipients rset) +append_args_from_recipients (GpgObject gpg, const GpgmeRecipients rset) { GpgmeError err = 0; struct user_id_s *r; @@ -1324,10 +1324,11 @@ _gpgme_append_gpg_args_from_recipients (GpgObject gpg, } -GpgmeError -_gpgme_gpg_op_encrypt (GpgObject gpg, GpgmeRecipients recp, - GpgmeData plain, GpgmeData ciph, int use_armor) +static GpgmeError +gpg_encrypt (void *engine, GpgmeRecipients recp, GpgmeData plain, + GpgmeData ciph, int use_armor) { + GpgObject gpg = engine; GpgmeError err; int symmetric = !recp; @@ -1344,7 +1345,7 @@ _gpgme_gpg_op_encrypt (GpgObject gpg, GpgmeRecipients recp, err = add_arg (gpg, "--always-trust"); if (!err) - err = _gpgme_append_gpg_args_from_recipients (gpg, recp); + err = append_args_from_recipients (gpg, recp); } /* Tell the gpg object about the data. */ @@ -1362,11 +1363,11 @@ _gpgme_gpg_op_encrypt (GpgObject gpg, GpgmeRecipients recp, return err; } -GpgmeError -_gpgme_gpg_op_encrypt_sign (GpgObject gpg, GpgmeRecipients recp, - GpgmeData plain, GpgmeData ciph, int use_armor, - GpgmeCtx ctx /* FIXME */) +static GpgmeError +gpg_encrypt_sign (void *engine, GpgmeRecipients recp, GpgmeData plain, + GpgmeData ciph, int use_armor, GpgmeCtx ctx /* FIXME */) { + GpgObject gpg = engine; GpgmeError err; err = add_arg (gpg, "--encrypt"); @@ -1381,10 +1382,10 @@ _gpgme_gpg_op_encrypt_sign (GpgObject gpg, GpgmeRecipients recp, err = add_arg (gpg, "--always-trust"); if (!err) - err = _gpgme_append_gpg_args_from_recipients (gpg, recp); + err = append_args_from_recipients (gpg, recp); if (!err) - err = _gpgme_append_gpg_args_from_signers (gpg, ctx); + err = append_args_from_signers (gpg, ctx); /* Tell the gpg object about the data. */ if (!err) @@ -1401,10 +1402,11 @@ _gpgme_gpg_op_encrypt_sign (GpgObject gpg, GpgmeRecipients recp, return err; } -GpgmeError -_gpgme_gpg_op_export (GpgObject gpg, GpgmeRecipients recp, - GpgmeData keydata, int use_armor) +static GpgmeError +gpg_export (void *engine, GpgmeRecipients recp, GpgmeData keydata, + int use_armor) { + GpgObject gpg = engine; GpgmeError err; err = add_arg (gpg, "--export"); @@ -1430,19 +1432,21 @@ _gpgme_gpg_op_export (GpgObject gpg, GpgmeRecipients recp, return err; } -GpgmeError -_gpgme_gpg_op_genkey (GpgObject gpg, GpgmeData help_data, int use_armor, - GpgmeData pubkey, GpgmeData seckey) + +static GpgmeError +gpg_genkey (void *engine, GpgmeData help_data, int use_armor, + GpgmeData pubkey, GpgmeData seckey) { + GpgObject gpg = engine; GpgmeError err; if (!gpg) return mk_error (Invalid_Value); - /* We need a special mechanism to get the fd of a pipe here, so - * that we can use this for the %pubring and %secring parameters. - * We don't have this yet, so we implement only the adding to the - * standard keyrings */ + /* We need a special mechanism to get the fd of a pipe here, so that + we can use this for the %pubring and %secring parameters. We + don't have this yet, so we implement only the adding to the + standard keyrings. */ if (pubkey || seckey) return err = mk_error (Not_Implemented); @@ -1455,9 +1459,11 @@ _gpgme_gpg_op_genkey (GpgObject gpg, GpgmeData help_data, int use_armor, return err; } -GpgmeError -_gpgme_gpg_op_import (GpgObject gpg, GpgmeData keydata) + +static GpgmeError +gpg_import (void *engine, GpgmeData keydata) { + GpgObject gpg = engine; GpgmeError err; err = add_arg (gpg, "--import"); @@ -1468,10 +1474,11 @@ _gpgme_gpg_op_import (GpgObject gpg, GpgmeData keydata) } -GpgmeError -_gpgme_gpg_op_keylist (GpgObject gpg, const char *pattern, int secret_only, - int keylist_mode) +static GpgmeError +gpg_keylist (void *engine, const char *pattern, int secret_only, + int keylist_mode) { + GpgObject gpg = engine; GpgmeError err; err = add_arg (gpg, "--with-colons"); @@ -1495,10 +1502,11 @@ _gpgme_gpg_op_keylist (GpgObject gpg, const char *pattern, int secret_only, } -GpgmeError -_gpgme_gpg_op_keylist_ext (GpgObject gpg, const char *pattern[], - int secret_only, int reserved, int keylist_mode) +static GpgmeError +gpg_keylist_ext (void *engine, const char *pattern[], int secret_only, + int reserved, int keylist_mode) { + GpgObject gpg = engine; GpgmeError err; if (reserved) @@ -1525,11 +1533,12 @@ _gpgme_gpg_op_keylist_ext (GpgObject gpg, const char *pattern[], } -GpgmeError -_gpgme_gpg_op_sign (GpgObject gpg, GpgmeData in, GpgmeData out, - GpgmeSigMode mode, int use_armor, - int use_textmode, GpgmeCtx ctx /* FIXME */) +static GpgmeError +gpg_sign (void *engine, GpgmeData in, GpgmeData out, GpgmeSigMode mode, + int use_armor, int use_textmode, int include_certs, + GpgmeCtx ctx /* FIXME */) { + GpgObject gpg = engine; GpgmeError err; if (mode == GPGME_SIG_MODE_CLEAR) @@ -1546,7 +1555,7 @@ _gpgme_gpg_op_sign (GpgObject gpg, GpgmeData in, GpgmeData out, } if (!err) - err = _gpgme_append_gpg_args_from_signers (gpg, ctx); + err = append_args_from_signers (gpg, ctx); /* Tell the gpg object about the data. */ if (!err) @@ -1557,9 +1566,10 @@ _gpgme_gpg_op_sign (GpgObject gpg, GpgmeData in, GpgmeData out, return err; } -GpgmeError -_gpgme_gpg_op_trustlist (GpgObject gpg, const char *pattern) +static GpgmeError +gpg_trustlist (void *engine, const char *pattern) { + GpgObject gpg = engine; GpgmeError err; err = add_arg (gpg, "--with-colons"); @@ -1575,10 +1585,12 @@ _gpgme_gpg_op_trustlist (GpgObject gpg, const char *pattern) return err; } -GpgmeError -_gpgme_gpg_op_verify (GpgObject gpg, GpgmeData sig, GpgmeData signed_text, - GpgmeData plaintext) + +static GpgmeError +gpg_verify (void *engine, GpgmeData sig, GpgmeData signed_text, + GpgmeData plaintext) { + GpgObject gpg = engine; GpgmeError err = 0; if (plaintext) @@ -1614,16 +1626,43 @@ _gpgme_gpg_op_verify (GpgObject gpg, GpgmeData sig, GpgmeData signed_text, } -void -_gpgme_gpg_set_io_cbs (GpgObject gpg, struct GpgmeIOCbs *io_cbs) +static void +gpg_set_io_cbs (void *engine, struct GpgmeIOCbs *io_cbs) { + GpgObject gpg = engine; + gpg->io_cbs = *io_cbs; } - -void -_gpgme_gpg_io_event (GpgObject gpg, GpgmeEventIO type, void *type_data) -{ - if (gpg->io_cbs.event) - (*gpg->io_cbs.event) (gpg->io_cbs.event_priv, type, type_data); -} + +struct engine_ops _gpgme_engine_ops_gpg = + { + /* Static functions. */ + _gpgme_get_gpg_path, + gpg_get_version, + gpg_check_version, + gpg_new, + + /* Member functions. */ + gpg_release, + gpg_set_status_handler, + gpg_set_command_handler, + gpg_set_colon_line_handler, + gpg_set_verbosity, + gpg_decrypt, + gpg_delete, + gpg_edit, + gpg_encrypt, + gpg_encrypt_sign, + gpg_export, + gpg_genkey, + gpg_import, + gpg_keylist, + gpg_keylist_ext, + gpg_sign, + gpg_trustlist, + gpg_verify, + gpg_start, + gpg_set_io_cbs, + gpg_io_event + }; |