From 874f12ea700cc5f7dd256856a78701c8b19e415d Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 6 Dec 2000 12:17:10 +0000 Subject: [PATCH] Okay, it runs at least on Windows 95 --- Makefile.am | 16 +- acconfig.h | 9 ++ acinclude.m4 | 17 ++ bonobo/Makefile.am | 11 ++ bonobo/gpgme.c | 20 +++ bonobo/main.c | 20 +++ bonobo/main.h | 42 +++++ complus/Makefile.am | 12 ++ complus/main.c | 287 ++++++++++++++++++++++++++++++++++ complus/main.h | 50 ++++++ configure.in | 46 +++++- gpgme/context.h | 10 +- gpgme/data.c | 108 +++++++++++-- gpgme/decrypt.c | 40 +++-- gpgme/errors.c | 49 ++++++ gpgme/gpgme.c | 31 +++- gpgme/gpgme.h | 20 ++- gpgme/io.h | 3 +- gpgme/ops.h | 4 + gpgme/posix-io.c | 3 +- gpgme/rungpg.c | 85 ++++++++-- gpgme/status-table.h | 68 ++++++++ gpgme/verify.c | 5 +- gpgme/version.c | 104 +++++++++++++ gpgme/w32-io.c | 361 ++++++++++++++++++++++++++++++++++++------- gpgme/wait.c | 37 ++++- tests/t-decrypt.c | 52 +++++-- 27 files changed, 1363 insertions(+), 147 deletions(-) create mode 100644 bonobo/Makefile.am create mode 100644 bonobo/gpgme.c create mode 100644 bonobo/main.c create mode 100644 bonobo/main.h create mode 100644 complus/Makefile.am create mode 100644 complus/main.c create mode 100644 complus/main.h create mode 100644 gpgme/errors.c create mode 100644 gpgme/status-table.h create mode 100644 gpgme/version.c diff --git a/Makefile.am b/Makefile.am index 1b9784b4..13cb7973 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,18 @@ -EXTRA_DIST = README-alpha +EXTRA_DIST = README-alpha build-w32 -SUBDIRS = gpgme tests +if BUILD_BONOBO +bonobo = bonobo +else +bonobo = +endif +if BUILD_COMPLUS +complus = complus +else +complus = +endif + + +SUBDIRS = jnlib gpgme tests ${bonobo} ${complus} diff --git a/acconfig.h b/acconfig.h index 5b1dd4c6..e8cd7581 100644 --- a/acconfig.h +++ b/acconfig.h @@ -42,6 +42,15 @@ /* path to the gpg binary */ #undef GPG_PATH +/* stuff needed by lnlib/ */ +#undef HAVE_BYTE_TYPEDEF +#undef HAVE_USHORT_TYPEDEF +#undef HAVE_ULONG_TYPEDEF +#undef HAVE_U16_TYPEDEF +#undef HAVE_U32_TYPEDEF + + + @BOTTOM@ /* not yet needed #include "gpgme-defs.h"*/ diff --git a/acinclude.m4 b/acinclude.m4 index 4b870edb..0952b2fb 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -21,6 +21,23 @@ AC_DEFUN(GNUPG_FIX_HDR_VERSION, fi ]) +dnl GNUPG_CHECK_TYPEDEF(TYPE, HAVE_NAME) +dnl Check whether a typedef exists and create a #define $2 if it exists +dnl +AC_DEFUN(GNUPG_CHECK_TYPEDEF, + [ AC_MSG_CHECKING(for $1 typedef) + AC_CACHE_VAL(gnupg_cv_typedef_$1, + [AC_TRY_COMPILE([#include + #include ], [ + #undef $1 + int a = sizeof($1); + ], gnupg_cv_typedef_$1=yes, gnupg_cv_typedef_$1=no )]) + AC_MSG_RESULT($gnupg_cv_typedef_$1) + if test "$gnupg_cv_typedef_$1" = yes; then + AC_DEFINE($2) + fi + ]) + diff --git a/bonobo/Makefile.am b/bonobo/Makefile.am new file mode 100644 index 00000000..48bc03e0 --- /dev/null +++ b/bonobo/Makefile.am @@ -0,0 +1,11 @@ +## Process this file with automake to produce Makefile.in + +bin_PROGRAMS = gpgme + +INCLUDES = -I$(top_srcdir)/jnlib +LDADD = -L ../jnlib -ljnlib + +gpgme_SOURCES = main.c main.h + + + diff --git a/bonobo/gpgme.c b/bonobo/gpgme.c new file mode 100644 index 00000000..765de506 --- /dev/null +++ b/bonobo/gpgme.c @@ -0,0 +1,20 @@ +/* gpgme - Bonbobo component to access GnuPG + * Copyright (C) 2000 Werner Koch (dd9jn) + * + * 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 + */ + diff --git a/bonobo/main.c b/bonobo/main.c new file mode 100644 index 00000000..e42d20ad --- /dev/null +++ b/bonobo/main.c @@ -0,0 +1,20 @@ +/* main.c - Bonbobo component to access GnuPG + * Copyright (C) 2000 Werner Koch (dd9jn) + * + * 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 + */ + diff --git a/bonobo/main.h b/bonobo/main.h new file mode 100644 index 00000000..41b56df5 --- /dev/null +++ b/bonobo/main.h @@ -0,0 +1,42 @@ +/* main.h - GPGME Bonobo component + * Copyright (C) 2000 Werner Koch (dd9jn) + * + * 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 + */ + +#ifndef MAIN_H +#define MAIN_H + + +struct { + int verbose; + int quiet; + unsigned int debug; + char *homedir; +} opt; + + + + +#endif /* MAIN_H */ + + + + + + + diff --git a/complus/Makefile.am b/complus/Makefile.am new file mode 100644 index 00000000..8976cd80 --- /dev/null +++ b/complus/Makefile.am @@ -0,0 +1,12 @@ +## Process this file with automake to produce Makefile.in + +# No need to install this becuase we are cross-compiling anyway. +noinst_PROGRAMS = gpgme + +INCLUDES = -I$(top_srcdir)/jnlib +LDADD = -L ../jnlib -ljnlib + +gpgme_SOURCES = main.c main.h + + + diff --git a/complus/main.c b/complus/main.c new file mode 100644 index 00000000..221a66b0 --- /dev/null +++ b/complus/main.c @@ -0,0 +1,287 @@ +/* main.c - COM+ component to access GnuPG + * Copyright (C) 2000 Werner Koch (dd9jn) + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "argparse.h" + +#include "main.h" + + +static void register_server (void); +static void unregister_server (void); +static void enter_complus (void); + + +enum cmd_and_opt_values { aNull = 0, + oQuiet = 'q', + oVerbose = 'v', + + oNoVerbose = 500, + oOptions, + oDebug, + oDebugAll, + oNoGreeting, + oNoOptions, + oHomedir, + oGPGBinary, + oRegServer, + oUnregServer, + oEmbedding, +aTest }; + + +static ARGPARSE_OPTS opts[] = { + + { 301, NULL, 0, N_("@Options:\n ") }, + + { oVerbose, "verbose", 0, N_("verbose") }, + { oQuiet, "quiet", 0, N_("be somewhat more quiet") }, + { oOptions, "options" , 2, N_("read options from file")}, + { oDebug, "debug" ,4|16, N_("set debugging flags")}, + { oDebugAll, "debug-all" ,0, N_("enable full debugging")}, + { oGPGBinary, "gpg-program", 2 , "@" }, + { oRegServer, "RegServer" , 0, "@" }, + { oUnregServer, "UnregServer" , 0, "@" }, + { oEmbedding, "Embedding" , 0, "@" }, +{0} }; + +static const char * +my_strusage( int level ) +{ + const char *p; + switch( level ) { + case 11: p = "gpgme"; + break; + case 13: p = VERSION; break; + /*case 17: p = PRINTABLE_OS_NAME; break;*/ + case 19: p = + _("Please report bugs to .\n"); + break; + case 1: + case 40: p = + _("Usage: gpgme [options] (-h for help)"); + break; + case 41: p = + _("Syntax: gpgme [options]\n" + "GnuPG COM+ component\n"); + break; + + default: p = NULL; + } + return p; +} + + +int +main (int argc, char **argv ) +{ + ARGPARSE_ARGS pargs; + int orig_argc; + char **orig_argv; + FILE *configfp = NULL; + char *configname = NULL; + unsigned configlineno; + int parse_debug = 0; + int default_config =1; + int greeting = 0; + int nogreeting = 0; + int action = 0; + + set_strusage( my_strusage ); + /*log_set_name ("gpa"); not yet implemented in logging.c */ + + opt.homedir = getenv("GNUPGHOME"); + if( !opt.homedir || !*opt.homedir ) { + #ifdef HAVE_DRIVE_LETTERS + opt.homedir = "c:/gnupg"; + #else + opt.homedir = "~/.gnupg"; + #endif + } + + /* check whether we have a config file on the commandline */ + orig_argc = argc; + orig_argv = argv; + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */ + while( arg_parse( &pargs, opts) ) { + if( pargs.r_opt == oDebug || pargs.r_opt == oDebugAll ) + parse_debug++; + else if( pargs.r_opt == oOptions ) { + /* yes there is one, so we do not try the default one, but + * read the option file when it is encountered at the commandline + */ + default_config = 0; + } + else if( pargs.r_opt == oNoOptions ) + default_config = 0; /* --no-options */ + else if( pargs.r_opt == oHomedir ) + opt.homedir = pargs.r.ret_str; + } + + if( default_config ) + configname = make_filename(opt.homedir, "gpgme.conf", NULL ); + + + argc = orig_argc; + argv = orig_argv; + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1 | (1<<5); /* do not remove the args, allow one dash */ + next_pass: + if( configname ) { + configlineno = 0; + configfp = fopen( configname, "r" ); + if( !configfp ) { + if( default_config ) { + if( parse_debug ) + log_info(_("NOTE: no default option file `%s'\n"), + configname ); + } + else { + log_error(_("option file `%s': %s\n"), + configname, strerror(errno) ); + exit(2); + } + free(configname); configname = NULL; + } + if( parse_debug && configname ) + log_info(_("reading options from `%s'\n"), configname ); + default_config = 0; + } + + while( optfile_parse( configfp, configname, &configlineno, + &pargs, opts) ) { + switch( pargs.r_opt ) { + case oQuiet: opt.quiet = 1; break; + case oVerbose: opt.verbose++; break; + + case oDebug: opt.debug |= pargs.r.ret_ulong; break; + case oDebugAll: opt.debug = ~0; break; + + case oOptions: + /* config files may not be nested (silently ignore them) */ + if( !configfp ) { + free(configname); + configname = xstrdup(pargs.r.ret_str); + goto next_pass; + } + break; + case oNoGreeting: nogreeting = 1; break; + case oNoVerbose: opt.verbose = 0; break; + case oNoOptions: break; /* no-options */ + case oHomedir: opt.homedir = pargs.r.ret_str; break; + case oGPGBinary: break; + + case oRegServer: action = 1; break; + case oUnregServer: action = 2; break; + case oEmbedding: action = 3; break; + + default : pargs.err = configfp? 1:2; break; + } + } + if( configfp ) { + fclose( configfp ); + configfp = NULL; + free(configname); configname = NULL; + goto next_pass; + } + free( configname ); configname = NULL; + if( log_get_errorcount(0) ) + exit(2); + if( nogreeting ) + greeting = 0; + + if( greeting ) { + fprintf(stderr, "%s %s; %s\n", + strusage(11), strusage(13), strusage(14) ); + fprintf(stderr, "%s\n", strusage(15) ); + } + #ifdef IS_DEVELOPMENT_VERSION + log_info("NOTE: this is a development version!\n"); + #endif + + if ( action == 1 ) + register_server (); + else if (action == 2 ) + unregister_server (); + else if (action == 3 ) + enter_complus (); + else { + fprintf (stderr, "This is a COM+ component with no user interface.\n" + "gpgme --help will give you a list of options\n" ); + exit (1); + } + + return 0; +} + +static void +register_server () +{ +} + +static void +unregister_server () +{ +} + +static void +enter_complus () +{ + HANDLE running; + int reg; + void *factory; + + /* CoInitializeEx (NULL, COINIT_MULTITHREADED); */ + running = CreateEvent (NULL, FALSE, FALSE, NULL ); + + #if 0 + factory = create_class_factory (); + CoRegisterClassObject (CLSID_gpgme, factory, + CLSCTX_LOCAL_SERVER, + REGCLS_SUSPENDED|REGCLASS_MULTIPLEUSE, ® ); + CoResumeClassObjects (); + #endif + + WaitForSingleObject ( running, INFINITE ); + CloseHandle (running); + #if 0 + CoRevokeClassObject ( reg ); + factory->release (); + CoUnitialize (); + #endif +} + + + + + + diff --git a/complus/main.h b/complus/main.h new file mode 100644 index 00000000..9c48c7b5 --- /dev/null +++ b/complus/main.h @@ -0,0 +1,50 @@ +/* main.h - GPGME COM+ component + * Copyright (C) 2000 Werner Koch (dd9jn) + * + * 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 + */ + +#ifndef COMPLUS_MAIN_H +#define COMPLUS_MAIN_H + +#include "xmalloc.h" +#include "stringhelp.h" +#include "logging.h" + + +#define _(a) (a) +#define N_(a) (a) + + +struct { + int verbose; + int quiet; + unsigned int debug; + char *homedir; +} opt; + + + + +#endif /* COMPLUS_MAIN_H */ + + + + + + + diff --git a/configure.in b/configure.in index c1fde710..ec98903f 100644 --- a/configure.in +++ b/configure.in @@ -13,10 +13,10 @@ AM_MAINTAINER_MODE # AGE, set REVISION to 0. # 3. Interfaces removed (BAD, breaks upward compatibility): Increment # CURRENT, set AGE and REVISION to 0. -AM_INIT_AUTOMAKE(gpgme,0.1.0) +AM_INIT_AUTOMAKE(gpgme,0.1.1) LIBGPGME_LT_CURRENT=0 LIBGPGME_LT_AGE=0 -LIBGPGME_LT_REVISION=2 +LIBGPGME_LT_REVISION=3 ############################################## AC_SUBST(LIBGPGME_LT_CURRENT) @@ -38,6 +38,7 @@ if test "$GCC" = yes; then fi GPG= +component_system=None case "${target}" in *-*-mingw32* | i?86-emx-os2 | i?86-*-os2*emx | i?86-*-msdosdjgpp* ) # special stuff for Windoze NT @@ -46,6 +47,7 @@ case "${target}" in AC_DEFINE(HAVE_DRIVE_LETTERS) AC_DEFINE(HAVE_DOSISH_SYSTEM) GPG='c:\\gnupg\\gpg.exe' + component_system='COM+' ;; *) ;; @@ -63,9 +65,27 @@ dnl Checks for header files dnl + dnl dnl Checks for typedefs and structures dnl +GNUPG_CHECK_TYPEDEF(byte, HAVE_BYTE_TYPEDEF) +GNUPG_CHECK_TYPEDEF(ushort, HAVE_USHORT_TYPEDEF) +GNUPG_CHECK_TYPEDEF(ulong, HAVE_ULONG_TYPEDEF) +GNUPG_CHECK_TYPEDEF(u16, HAVE_U16_TYPEDEF) +GNUPG_CHECK_TYPEDEF(u32, HAVE_U32_TYPEDEF) +# We should not use them in this software; +# However jnlib/types.h needs them - so we take the easy way. +AC_CHECK_SIZEOF(unsigned short, 2) +AC_CHECK_SIZEOF(unsigned int, 4) +AC_CHECK_SIZEOF(unsigned long, 4) +if test "$ac_cv_sizeof_unsigned_short" = "0" \ + || test "$ac_cv_sizeof_unsigned_int" = "0" \ + || test "$ac_cv_sizeof_unsigned_long" = "0"; then + AC_MSG_WARN([Hmmm, something is wrong with the sizes - using defaults]); +fi + + dnl dnl Checks for compiler features @@ -74,14 +94,15 @@ dnl dnl dnl Checks for library functions dnl -AC_CHECK_FUNCS(stpcpy) +dnl These are needed by libjnlib +AC_CHECK_FUNCS(memicmp stpcpy strlwr strtoul memmove stricmp) dnl dnl Checks for system services dnl -if test -z "GPG"; then +if test -z "$GPG"; then AC_PATH_PROG(GPG, gpg) if test -z "$GPG"; then AC_MSG_ERROR([[ @@ -95,11 +116,19 @@ fi AC_DEFINE_UNQUOTED(GPG_PATH, "$GPG") +dnl +dnl FIXME: check whether Bonobo is installed +dnl + + dnl dnl Create config files dnl dnl +AM_CONDITIONAL(BUILD_COMPLUS, test "$component_system" = "COM+") +AM_CONDITIONAL(BUILD_BONOBO, test "$component_system" = "Bonobo") + dnl Make the version number in gpgme/gpgme.h the same as the one here. dnl (this is easier than to have a *.in file just for one substitution) GNUPG_FIX_HDR_VERSION(gpgme/gpgme.h, GPGME_VERSION) @@ -116,11 +145,20 @@ chmod +x gpgme/gpgme-config AC_OUTPUT([ Makefile +jnlib/Makefile gpgme/Makefile gpgme/gpgme-config tests/Makefile +bonobo/Makefile +complus/Makefile ]) +echo " + GPGME v${VERSION} has been configured as follows: + + GPG path: $GPG + Component: $component_system +" diff --git a/gpgme/context.h b/gpgme/context.h index e888aa8d..324245a4 100644 --- a/gpgme/context.h +++ b/gpgme/context.h @@ -56,9 +56,6 @@ struct gpgme_context_s { int use_armor; int use_textmode; - /* GpgmePassphraseCb passphrase_cb;*/ - /* void * passphrase_cb_value;*/ - ResultType result_type; union { VerifyResult verify; @@ -71,6 +68,8 @@ struct gpgme_context_s { GpgmeKey tmp_key; /* used by keylist.c */ volatile int key_cond; /* something new is available */ struct key_queue_item_s *key_queue; + + char *prompt_1; }; @@ -79,6 +78,11 @@ struct gpgme_data_s { const char *data; GpgmeDataType type; GpgmeDataMode mode; + + int (*read_cb)( void *, char *, size_t, size_t *); + void *read_cb_value; + int read_cb_eof; + size_t readpos; size_t writepos; size_t private_len; diff --git a/gpgme/data.c b/gpgme/data.c index dc6117fa..94438f62 100644 --- a/gpgme/data.c +++ b/gpgme/data.c @@ -113,6 +113,30 @@ gpgme_data_new_from_mem ( GpgmeData *r_dh, return 0; } + +GpgmeError +gpgme_data_new_with_read_cb ( GpgmeData *r_dh, + int (*read_cb)(void*,char *,size_t,size_t*), + void *read_cb_value ) +{ + GpgmeData dh; + GpgmeError err; + + if (!r_dh || !read_cb) + return mk_error (Invalid_Value); + *r_dh = NULL; + err = gpgme_data_new ( &dh ); + if (err) + return err; + dh->type = GPGME_DATA_TYPE_CB; + dh->mode = GPGME_DATA_MODE_OUT; + dh->read_cb = read_cb; + dh->read_cb_value = read_cb_value; + + *r_dh = dh; + return 0; +} + /** * gpgme_data_new_from_file: * @r_dh: returns the new data object @@ -279,7 +303,7 @@ gpgme_data_release_and_get_mem ( GpgmeData dh, size_t *r_len ) GpgmeDataType gpgme_data_get_type ( GpgmeData dh ) { - if ( !dh || !dh->data ) + if ( !dh || (!dh->data && !dh->read_cb)) return GPGME_DATA_TYPE_NONE; return dh->type; @@ -315,9 +339,18 @@ gpgme_data_rewind ( GpgmeData dh ) { if ( !dh ) return mk_error (Invalid_Value); - /* Fixme: We should check whether rewinding does make sense for the - * data type */ - dh->readpos = 0; + + if (dh->type == GPGME_DATA_TYPE_MEM ) { + dh->readpos = 0; + } + else if (dh->type == GPGME_DATA_TYPE_CB) { + dh->len = dh->readpos = 0; + dh->read_cb_eof = 0; + /* FIXME: do a special call to the read function to trigger a rewind + there */ + } + else + return mk_error (General_Error); return 0; } @@ -334,7 +367,7 @@ gpgme_data_rewind ( GpgmeData dh ) * If there are no more bytes available %GPGME_EOF is returned and @nread * is set to 0. * - * Return value: An errocodee or 0 on success, EOF is indcated by the + * Return value: An errorcode or 0 on success, EOF is indcated by the * error code GPGME_EOF. **/ GpgmeError @@ -344,19 +377,66 @@ gpgme_data_read ( GpgmeData dh, char *buffer, size_t length, size_t *nread ) if ( !dh ) return mk_error (Invalid_Value); - nbytes = dh->len - dh->readpos; - if ( !nbytes ) { - *nread = 0; - return mk_error(EOF); + if (dh->type == GPGME_DATA_TYPE_MEM ) { + nbytes = dh->len - dh->readpos; + if ( !nbytes ) { + *nread = 0; + return mk_error(EOF); + } + if (nbytes > length) + nbytes = length; + memcpy ( buffer, dh->data + dh->readpos, nbytes ); + *nread = nbytes; + dh->readpos += nbytes; } - if (nbytes > length) - nbytes = length; - memcpy ( buffer, dh->data + dh->readpos, nbytes ); - *nread = nbytes; - dh->readpos += nbytes; + else if (dh->type == GPGME_DATA_TYPE_CB) { + nbytes = dh->len - dh->readpos; + if ( nbytes ) { + /* we have unread data - return this */ + if (nbytes > length) + nbytes = length; + memcpy ( buffer, dh->data + dh->readpos, nbytes ); + *nread = nbytes; + dh->readpos += nbytes; + } + else { /* get the data from the callback */ + if (!dh->read_cb || dh->read_cb_eof) { + *nread = 0; + return mk_error (EOF); + } + if (dh->read_cb (dh->read_cb_value, buffer, length, nread )) { + *nread = 0; + dh->read_cb_eof = 1; + return mk_error (EOF); + } + } + } + else + return mk_error (General_Error); return 0; } +GpgmeError +_gpgme_data_unread (GpgmeData dh, const char *buffer, size_t length ) +{ + if ( !dh ) + return mk_error (Invalid_Value); + + if (dh->type == GPGME_DATA_TYPE_MEM ) { + /* check that we don't unread more than we have yet read */ + if ( dh->readpos < length ) + return mk_error (Invalid_Value); + /* No need to use the buffer for this data type */ + dh->readpos -= length; + } + else { + return mk_error (General_Error); + } + + return 0; +} + + /* * This function does make sense when we know that it contains no nil chars. */ diff --git a/gpgme/decrypt.c b/gpgme/decrypt.c index 017cbf92..0a2545f3 100644 --- a/gpgme/decrypt.c +++ b/gpgme/decrypt.c @@ -33,6 +33,7 @@ struct decrypt_result_s { int no_passphrase; int okay; int failed; + }; @@ -43,6 +44,17 @@ _gpgme_release_decrypt_result ( DecryptResult res ) } +static GpgmeError +create_result_struct ( GpgmeCtx ctx ) +{ + assert ( !ctx->result.decrypt ); + ctx->result.decrypt = xtrycalloc ( 1, sizeof *ctx->result.decrypt ); + if ( !ctx->result.decrypt ) { + return mk_error (Out_Of_Core); + } + ctx->result_type = RESULT_TYPE_DECRYPT; + return 0; +} static void decrypt_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args ) @@ -50,13 +62,10 @@ decrypt_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args ) if ( ctx->out_of_core ) return; if ( ctx->result_type == RESULT_TYPE_NONE ) { - assert ( !ctx->result.decrypt ); - ctx->result.decrypt = xtrycalloc ( 1, sizeof *ctx->result.decrypt ); - if ( !ctx->result.decrypt ) { + if ( create_result_struct ( ctx ) ) { ctx->out_of_core = 1; return; } - ctx->result_type = RESULT_TYPE_DECRYPT; } assert ( ctx->result_type == RESULT_TYPE_DECRYPT ); @@ -66,7 +75,9 @@ decrypt_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args ) case STATUS_NEED_PASSPHRASE: case STATUS_NEED_PASSPHRASE_SYM: - fprintf (stderr, "Ooops: Need a passphrase - use the agent\n"); + fprintf (stderr, "need a passphrase ...\n" ); + _gpgme_set_prompt (ctx, 1, "Hey! We need your passphrase!"); + /* next thing gpg has to do is to read it from the passphrase-fd */ break; case STATUS_MISSING_PASSPHRASE: @@ -91,9 +102,9 @@ decrypt_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args ) } - GpgmeError -gpgme_op_decrypt_start ( GpgmeCtx c, GpgmeData ciph, GpgmeData plain ) +gpgme_op_decrypt_start ( GpgmeCtx c, GpgmeData passphrase, + GpgmeData ciph, GpgmeData plain ) { int rc = 0; int i; @@ -118,14 +129,19 @@ gpgme_op_decrypt_start ( GpgmeCtx c, GpgmeData ciph, GpgmeData plain ) _gpgme_gpg_add_arg ( c->gpg, "--decrypt" ); for ( i=0; i < c->verbosity; i++ ) _gpgme_gpg_add_arg ( c->gpg, "--verbose" ); - + if (passphrase) { + _gpgme_gpg_add_arg (c->gpg, "--passphrase-fd" ); + _gpgme_gpg_add_data (c->gpg, passphrase, -2 ); + } + + /* Check the supplied data */ if ( !ciph || gpgme_data_get_type (ciph) == GPGME_DATA_TYPE_NONE ) { rc = mk_error (No_Data); goto leave; } - _gpgme_data_set_mode (ciph, GPGME_DATA_MODE_OUT ); + if ( gpgme_data_get_type (plain) != GPGME_DATA_TYPE_NONE ) { rc = mk_error (Invalid_Value); goto leave; @@ -153,6 +169,7 @@ gpgme_op_decrypt_start ( GpgmeCtx c, GpgmeData ciph, GpgmeData plain ) /** * gpgme_op_decrypt: * @c: The context + * @passphrase: A data object with the passphrase or NULL. * @in: ciphertext input * @out: plaintext output * @@ -163,9 +180,10 @@ gpgme_op_decrypt_start ( GpgmeCtx c, GpgmeData ciph, GpgmeData plain ) * Return value: 0 on success or an errorcode. **/ GpgmeError -gpgme_op_decrypt ( GpgmeCtx c, GpgmeData in, GpgmeData out ) +gpgme_op_decrypt ( GpgmeCtx c, GpgmeData passphrase, + GpgmeData in, GpgmeData out ) { - GpgmeError err = gpgme_op_decrypt_start ( c, in, out ); + GpgmeError err = gpgme_op_decrypt_start ( c, passphrase, in, out ); if ( !err ) { gpgme_wait (c, 1); if ( c->result_type != RESULT_TYPE_DECRYPT ) diff --git a/gpgme/errors.c b/gpgme/errors.c new file mode 100644 index 00000000..4445d179 --- /dev/null +++ b/gpgme/errors.c @@ -0,0 +1,49 @@ +/* Generated automatically by mkerrors */ +/* Do not edit! */ + +#include +#include "gpgme.h" + +/** + * gpgme_strerror: + * @err: Error code + * + * This function returns a textual representaion of the given + * errocode. If this is an unknown value, a string with the value + * is returned (which is hold in a static buffer). + * + * Return value: String with the error description. + **/ +const char * +gpgme_strerror (GpgmeError err) +{ + const char *s; + static char buf[25]; + + switch (err) { + case GPGME_No_Error: s="No Error"; break; + case GPGME_General_Error: s="General Error"; break; + case GPGME_Out_Of_Core: s="Out Of Core"; break; + case GPGME_Invalid_Value: s="Invalid Value"; break; + case GPGME_Busy: s="Busy"; break; + case GPGME_No_Request: s="No Request"; break; + case GPGME_Exec_Error: s="Exec Error"; break; + case GPGME_Too_Many_Procs: s="Too Many Procs"; break; + case GPGME_Pipe_Error: s="Pipe Error"; break; + case GPGME_No_Recipients: s="No Recipients"; break; + case GPGME_No_Data: s="No Data"; break; + case GPGME_Conflict: s="Conflict"; break; + case GPGME_Not_Implemented: s="Not Implemented"; break; + case GPGME_Read_Error: s="Read Error"; break; + case GPGME_Write_Error: s="Write Error"; break; + case GPGME_Invalid_Type: s="Invalid Type"; break; + case GPGME_Invalid_Mode: s="Invalid Mode"; break; + case GPGME_File_Error: s="File Error"; break; + case GPGME_Decryption_Failed: s="Decryption Failed"; break; + case GPGME_No_Passphrase: s="No Passphrase"; break; + default: sprintf (buf, "ec=%d", err ); s=buf; break; +} + +return s; +} + diff --git a/gpgme/gpgme.c b/gpgme/gpgme.c index 9a6d7b53..d6b189ce 100644 --- a/gpgme/gpgme.c +++ b/gpgme/gpgme.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "util.h" #include "context.h" @@ -69,7 +70,8 @@ gpgme_release ( GpgmeCtx c ) _gpgme_key_release ( c->tmp_key ); gpgme_data_release ( c->notation ); /* fixme: release the key_queue */ - xfree ( c ); + xfree (c->prompt_1); + xfree (c); } @@ -145,16 +147,31 @@ gpgme_set_textmode ( GpgmeCtx c, int yes ) c->use_textmode = yes; } -#if 0 +/* + * The only which currently allowed is 1 + */ void -gpgme_set_passphrase_cb ( GpgmeCtx c, GpgmePassphraseCb fnc, void *fncval ) +_gpgme_set_prompt ( GpgmeCtx c, int which, const char *text ) { - if ( c ) { - c->passphrase_cb = fnc; - c->passphrase_cb_value = fncval; + assert ( which == 1 ); + + xfree (c->prompt_1); c->prompt_1 = NULL; + if (text) { + c->prompt_1 = xtrystrdup (text); + if ( !c->prompt_1 ) + c->out_of_core = 1; } } -#endif + +const char * +gpgme_get_prompt ( GpgmeCtx c, int which ) +{ + if ( which != 1 ) + return NULL; + return c->prompt_1; +} + + diff --git a/gpgme/gpgme.h b/gpgme/gpgme.h index 692362b1..b9509cc4 100644 --- a/gpgme/gpgme.h +++ b/gpgme/gpgme.h @@ -34,7 +34,7 @@ extern "C" { * let autoconf (using the AM_PATH_GPGME macro) check that this * header matches the installed library. * Warning: Do not edit the next line. configure will do that for you! */ -#define GPGME_VERSION "0.1.0" +#define GPGME_VERSION "0.1.1" @@ -79,7 +79,8 @@ typedef enum { GPGME_DATA_TYPE_NONE = 0, GPGME_DATA_TYPE_MEM = 1, GPGME_DATA_TYPE_FD = 2, - GPGME_DATA_TYPE_FILE = 3 + GPGME_DATA_TYPE_FILE = 3, + GPGME_DATA_TYPE_CB = 4 } GpgmeDataType; typedef enum { @@ -91,8 +92,6 @@ typedef enum { GPGME_SIG_STAT_ERROR = 5 } GpgmeSigStat; -/*typedef GpgmeData (*GpgmePassphraseCb)( void *opaque, const char *desc );*/ - /* Context management */ GpgmeError gpgme_new (GpgmeCtx *r_ctx); @@ -102,8 +101,7 @@ GpgmeCtx gpgme_wait ( GpgmeCtx c, int hang ); char *gpgme_get_notation ( GpgmeCtx c ); void gpgme_set_armor ( GpgmeCtx c, int yes ); void gpgme_set_textmode ( GpgmeCtx c, int yes ); -/*void gpgme_set_passphrase_cb ( GpgmeCtx c, - GpgmePassphraseCb fnc, void *fncval );*/ + /* Functions to handle recipients */ @@ -118,6 +116,10 @@ GpgmeError gpgme_data_new ( GpgmeData *r_dh ); GpgmeError gpgme_data_new_from_mem ( GpgmeData *r_dh, const char *buffer, size_t size, int copy ); +GpgmeError gpgme_data_new_with_read_cb ( GpgmeData *r_dh, + int (*read_cb)(void*,char *,size_t,size_t*), + void *read_cb_value ); + GpgmeError gpgme_data_new_from_file ( GpgmeData *r_dh, const char *fname, int copy ); @@ -136,7 +138,7 @@ char *gpgme_key_get_as_xml ( GpgmeKey key ); GpgmeError gpgme_op_encrypt_start ( GpgmeCtx c, GpgmeRecipients recp, GpgmeData in, GpgmeData out ); -GpgmeError gpgme_op_decrypt_start ( GpgmeCtx c, +GpgmeError gpgme_op_decrypt_start ( GpgmeCtx c, GpgmeData passphrase, GpgmeData ciph, GpgmeData plain ); GpgmeError gpgme_op_sign_start ( GpgmeCtx c, GpgmeData in, GpgmeData out ); GpgmeError gpgme_op_verify_start ( GpgmeCtx c, @@ -152,7 +154,8 @@ GpgmeError gpgme_op_keylist_next ( GpgmeCtx c, GpgmeKey *r_key ); /* Convenience functions for normal usage */ GpgmeError gpgme_op_encrypt ( GpgmeCtx c, GpgmeRecipients recp, GpgmeData in, GpgmeData out ); -GpgmeError gpgme_op_decrypt ( GpgmeCtx c, GpgmeData in, GpgmeData out ); +GpgmeError gpgme_op_decrypt ( GpgmeCtx c, GpgmeData passphrase, + GpgmeData in, GpgmeData out ); GpgmeError gpgme_op_sign ( GpgmeCtx c, GpgmeData in, GpgmeData out ); GpgmeError gpgme_op_verify ( GpgmeCtx c, GpgmeData sig, GpgmeData text, GpgmeSigStat *r_status ); @@ -161,6 +164,7 @@ GpgmeError gpgme_op_verify ( GpgmeCtx c, GpgmeData sig, GpgmeData text, /* miscellaneous functions */ const char *gpgme_check_version ( const char *req_version ); const char *gpgme_strerror (GpgmeError err); +const char *gpgme_get_prompt ( GpgmeCtx c, int which ); #ifdef __cplusplus diff --git a/gpgme/io.h b/gpgme/io.h index 41ea160f..0ae58a2b 100644 --- a/gpgme/io.h +++ b/gpgme/io.h @@ -31,6 +31,7 @@ struct spawn_fd_item_s { struct io_select_fd_s { int fd; + int is_closed; int for_read; int for_write; int signaled; @@ -42,7 +43,7 @@ struct io_select_fd_s { int _gpgme_io_read ( int fd, void *buffer, size_t count ); int _gpgme_io_write ( int fd, const void *buffer, size_t count ); -int _gpgme_io_pipe ( int filedes[2] ); +int _gpgme_io_pipe ( int filedes[2], int inherit_idx ); int _gpgme_io_close ( int fd ); int _gpgme_io_set_nonblocking ( int fd ); int _gpgme_io_spawn ( const char *path, char **argv, diff --git a/gpgme/ops.h b/gpgme/ops.h index 9de6d245..ba382a9f 100644 --- a/gpgme/ops.h +++ b/gpgme/ops.h @@ -25,6 +25,7 @@ /*-- gpgme.c --*/ void _gpgme_release_result ( GpgmeCtx c ); +void _gpgme_set_prompt ( GpgmeCtx c, int which, const char *text ); /*-- wait.c --*/ GpgmeCtx _gpgme_wait_on_condition ( GpgmeCtx c, @@ -53,6 +54,9 @@ GpgmeError _gpgme_data_append_for_xml ( GpgmeData dh, GpgmeError _gpgme_data_append_percentstring_for_xml ( GpgmeData dh, const char *string ); +GpgmeError _gpgme_data_unread (GpgmeData dh, + const char *buffer, size_t length ); + /*-- key.c --*/ GpgmeError _gpgme_key_new( GpgmeKey *r_key ); diff --git a/gpgme/posix-io.c b/gpgme/posix-io.c index 098a83e5..dede3f90 100644 --- a/gpgme/posix-io.c +++ b/gpgme/posix-io.c @@ -68,8 +68,9 @@ _gpgme_io_write ( int fd, const void *buffer, size_t count ) } int -_gpgme_io_pipe ( int filedes[2] ) +_gpgme_io_pipe ( int filedes[2], int inherit_idx ) { + /* we don't need inherit_idx in this implementation */ return pipe ( filedes ); } diff --git a/gpgme/rungpg.c b/gpgme/rungpg.c index b7a3943c..1a8f6ae2 100644 --- a/gpgme/rungpg.c +++ b/gpgme/rungpg.c @@ -40,12 +40,14 @@ #include "status-table.h" + /* 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; + int print_fd; /* print the fd number and not the special form of it */ char arg[1]; /* .. this is used */ }; @@ -137,7 +139,7 @@ _gpgme_gpg_new ( GpgObject *r_gpg ) } /* In any case we need a status pipe - create it right here and * don't handle it with our generic GpgmeData mechanism */ - if (_gpgme_io_pipe (gpg->status.fd) == -1) { + if (_gpgme_io_pipe (gpg->status.fd, 1) == -1) { rc = mk_error (Pipe_Error); goto leave; } @@ -151,6 +153,7 @@ _gpgme_gpg_new ( GpgObject *r_gpg ) _gpgme_gpg_add_arg ( gpg, "--batch" ); _gpgme_gpg_add_arg ( gpg, "--no-tty" ); + leave: if (rc) { _gpgme_gpg_release (gpg); @@ -170,6 +173,11 @@ _gpgme_gpg_release ( GpgObject gpg ) xfree (gpg->colon.buffer); if ( gpg->argv ) free_argv (gpg->argv); + #if 0 + /* fixme: We need a way to communicate back closed fds, so that we + * don't do it a second time. One way to do it is by using a global + * table of open fds associated with gpg objects - but this requires + * additional locking. */ if (gpg->status.fd[0] != -1 ) _gpgme_io_close (gpg->status.fd[0]); if (gpg->status.fd[1] != -1 ) @@ -178,6 +186,7 @@ _gpgme_gpg_release ( GpgObject gpg ) _gpgme_io_close (gpg->colon.fd[0]); if (gpg->colon.fd[1] != -1 ) _gpgme_io_close (gpg->colon.fd[1]); + #endif free_fd_data_map (gpg->fd_data_map); kill_gpg (gpg); /* fixme: should be done asyncronously */ xfree (gpg); @@ -237,7 +246,14 @@ _gpgme_gpg_add_data ( GpgObject gpg, GpgmeData data, int dup_to ) } a->next = NULL; a->data = data; - a->dup_to = dup_to; + if ( dup_to == -2 ) { + a->print_fd = 1; + a->dup_to = -1; + } + else { + a->print_fd = 0; + a->dup_to = dup_to; + } *gpg->argtail = a; gpg->argtail = &a->next; return 0; @@ -268,7 +284,7 @@ _gpgme_gpg_set_colon_line_handler ( GpgObject gpg, if (!gpg->colon.buffer) { return mk_error (Out_Of_Core); } - if (_gpgme_io_pipe (gpg->colon.fd) == -1) { + if (_gpgme_io_pipe (gpg->colon.fd, 1) == -1) { xfree (gpg->colon.buffer); gpg->colon.buffer = NULL; return mk_error (Pipe_Error); } @@ -294,11 +310,16 @@ 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 0 /* fixme -> see gpg_release */ 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); +#endif /* don't realease data because this is only a reference */ } xfree (fd_data_map); @@ -330,7 +351,7 @@ build_argv ( GpgObject gpg ) if (a->data) { /*fprintf (stderr, "build_argv: data\n" );*/ datac++; - if ( a->dup_to == -1 ) + if ( a->dup_to == -1 && !a->print_fd ) need_special = 1; } else { @@ -403,6 +424,7 @@ build_argv ( GpgObject gpg ) free_argv (argv); return mk_error (Invalid_Type); case GPGME_DATA_TYPE_MEM: + case GPGME_DATA_TYPE_CB: break; case GPGME_DATA_TYPE_FD: case GPGME_DATA_TYPE_FILE: @@ -415,7 +437,8 @@ build_argv ( GpgObject gpg ) { int fds[2]; - if (_gpgme_io_pipe (fds) == -1) { + if (_gpgme_io_pipe (fds, fd_data_map[datac].inbound?1:0 ) + == -1) { xfree (fd_data_map); free_argv (argv); return mk_error (Pipe_Error); @@ -439,7 +462,9 @@ build_argv ( GpgObject gpg ) free_argv (argv); return mk_error (Out_Of_Core); } - sprintf ( argv[argc], "-&%d", fd_data_map[datac].peer_fd ); + sprintf ( argv[argc], + a->print_fd? "%d" : "-&%d", + fd_data_map[datac].peer_fd ); argc++; } datac++; @@ -490,7 +515,7 @@ _gpgme_gpg_spawn( GpgObject gpg, void *opaque ) /* build the fd list for the child */ n=0; - fd_child_list[n].fd = gpg->status.fd[0]; + fd_child_list[n].fd = gpg->status.fd[0]; fd_child_list[n].dup_to = -1; n++; if ( gpg->colon.fnc ) { @@ -538,7 +563,6 @@ _gpgme_gpg_spawn( GpgObject gpg, void *opaque ) fd_parent_list[n].dup_to = -1; - fflush (stderr); pid = _gpgme_io_spawn (GPG_PATH, gpg->argv, fd_child_list, fd_parent_list); xfree (fd_child_list); if (pid == -1) { @@ -604,7 +628,6 @@ gpg_inbound_handler ( void *opaque, int pid, int fd ) assert ( _gpgme_data_get_mode (dh) == GPGME_DATA_MODE_IN ); nread = _gpgme_io_read (fd, buf, 200 ); - fprintf(stderr, "inbound on fd %d: nread=%d\n", fd, nread ); if ( nread < 0 ) { fprintf (stderr, "read_mem_data: read failed on fd %d (n=%d): %s\n", fd, nread, strerror (errno) ); @@ -638,7 +661,6 @@ write_mem_data ( GpgmeData dh, int fd ) nbytes = dh->len - dh->readpos; if ( !nbytes ) { - fprintf (stderr, "write_mem_data(%d): closing\n", fd ); _gpgme_io_close (fd); return 1; } @@ -650,11 +672,7 @@ write_mem_data ( GpgmeData dh, int fd ) * To avoid that we have set the pipe to nonblocking. */ - - fprintf (stderr, "write_mem_data(%d): about to write %d bytes len=%d rpos=%d\n", - fd, (int)nbytes, (int)dh->len, dh->readpos ); nwritten = _gpgme_io_write ( fd, dh->data+dh->readpos, nbytes ); - fprintf (stderr, "write_mem_data(%d): wrote %d bytes\n", fd, nwritten ); if (nwritten == -1 && errno == EAGAIN ) return 0; if ( nwritten < 1 ) { @@ -668,6 +686,40 @@ write_mem_data ( GpgmeData dh, int fd ) return 0; } +static int +write_cb_data ( GpgmeData dh, int fd ) +{ + size_t nbytes; + int err, nwritten; + char buffer[512]; + + err = gpgme_data_read ( dh, buffer, DIM(buffer), &nbytes ); + if (err == GPGME_EOF) { + _gpgme_io_close (fd); + return 1; + } + + nwritten = _gpgme_io_write ( fd, dh->data+dh->readpos, nbytes ); + if (nwritten == -1 && errno == EAGAIN ) + return 0; + if ( nwritten < 1 ) { + fprintf (stderr, "write_cb_data(%d): write failed (n=%d): %s\n", + fd, nwritten, strerror (errno) ); + _gpgme_io_close (fd); + return 1; + } + + if ( nwritten < nbytes ) { + if ( _gpgme_data_unread (dh, buffer + nwritten, nbytes - nwritten ) ) + fprintf (stderr, "wite_cb_data: unread of %d bytes failed\n", + nbytes - nwritten ); + _gpgme_io_close (fd); + return 1; + } + + return 0; +} + static int gpg_outbound_handler ( void *opaque, int pid, int fd ) @@ -680,11 +732,14 @@ gpg_outbound_handler ( void *opaque, int pid, int fd ) if ( write_mem_data ( dh, fd ) ) return 1; /* ready */ break; + case GPGME_DATA_TYPE_CB: + if (write_cb_data (dh, fd)) + return 1; /* ready */ + break; default: assert (0); } - return 0; } diff --git a/gpgme/status-table.h b/gpgme/status-table.h new file mode 100644 index 00000000..ce427100 --- /dev/null +++ b/gpgme/status-table.h @@ -0,0 +1,68 @@ +/* Generated automatically by mkstatus */ +/* Do not edit! */ + +struct status_table_s { + const char *name; + GpgStatusCode code; +}; + +static struct status_table_s status_table[] = +{ + { "ABORT", STATUS_ABORT }, + { "BADARMOR", STATUS_BADARMOR }, + { "BADMDC", STATUS_BADMDC }, + { "BADSIG", STATUS_BADSIG }, + { "BAD_PASSPHRASE", STATUS_BAD_PASSPHRASE }, + { "BEGIN_DECRYPTION", STATUS_BEGIN_DECRYPTION }, + { "BEGIN_ENCRYPTION", STATUS_BEGIN_ENCRYPTION }, + { "DECRYPTION_FAILED", STATUS_DECRYPTION_FAILED }, + { "DECRYPTION_OKAY", STATUS_DECRYPTION_OKAY }, + { "DELETE_PROBLEM", STATUS_DELETE_PROBLEM }, + { "ENC_TO", STATUS_ENC_TO }, + { "END_DECRYPTION", STATUS_END_DECRYPTION }, + { "END_ENCRYPTION", STATUS_END_ENCRYPTION }, + { "ENTER", STATUS_ENTER }, + { "ERRMDC", STATUS_ERRMDC }, + { "ERRSIG", STATUS_ERRSIG }, + { "FILE_DONE", STATUS_FILE_DONE }, + { "FILE_ERROR", STATUS_FILE_ERROR }, + { "FILE_START", STATUS_FILE_START }, + { "GET_BOOL", STATUS_GET_BOOL }, + { "GET_HIDDEN", STATUS_GET_HIDDEN }, + { "GET_LINE", STATUS_GET_LINE }, + { "GOODMDC", STATUS_GOODMDC }, + { "GOODSIG", STATUS_GOODSIG }, + { "GOOD_PASSPHRASE", STATUS_GOOD_PASSPHRASE }, + { "GOT_IT", STATUS_GOT_IT }, + { "IMPORTED", STATUS_IMPORTED }, + { "IMPORT_RES", STATUS_IMPORT_RES }, + { "KEYREVOKED", STATUS_KEYREVOKED }, + { "LEAVE", STATUS_LEAVE }, + { "MISSING_PASSPHRASE", STATUS_MISSING_PASSPHRASE }, + { "NEED_PASSPHRASE", STATUS_NEED_PASSPHRASE }, + { "NEED_PASSPHRASE_SYM,", STATUS_NEED_PASSPHRASE_SYM, }, + { "NODATA", STATUS_NODATA }, + { "NOTATION_DATA", STATUS_NOTATION_DATA }, + { "NOTATION_NAME", STATUS_NOTATION_NAME }, + { "NO_PUBKEY", STATUS_NO_PUBKEY }, + { "NO_SECKEY", STATUS_NO_SECKEY }, + { "POLICY_URL", STATUS_POLICY_URL }, + { "PROGRESS", STATUS_PROGRESS }, + { "RSA_OR_IDEA", STATUS_RSA_OR_IDEA }, + { "SESSION_KEY", STATUS_SESSION_KEY }, + { "SHM_GET", STATUS_SHM_GET }, + { "SHM_GET_BOOL", STATUS_SHM_GET_BOOL }, + { "SHM_GET_HIDDEN", STATUS_SHM_GET_HIDDEN }, + { "SHM_INFO", STATUS_SHM_INFO }, + { "SIGEXPIRED", STATUS_SIGEXPIRED }, + { "SIG_CREATED", STATUS_SIG_CREATED }, + { "SIG_ID", STATUS_SIG_ID }, + { "TRUST_FULLY", STATUS_TRUST_FULLY }, + { "TRUST_MARGINAL", STATUS_TRUST_MARGINAL }, + { "TRUST_NEVER", STATUS_TRUST_NEVER }, + { "TRUST_ULTIMATE", STATUS_TRUST_ULTIMATE }, + { "TRUST_UNDEFINED", STATUS_TRUST_UNDEFINED }, + { "VALIDSIG", STATUS_VALIDSIG }, + {NULL, 0} +}; + diff --git a/gpgme/verify.c b/gpgme/verify.c index 3d97fd6a..4c3e61ba 100644 --- a/gpgme/verify.c +++ b/gpgme/verify.c @@ -163,7 +163,6 @@ gpgme_op_verify_start ( GpgmeCtx c, GpgmeData sig, GpgmeData text ) _gpgme_gpg_add_arg ( c->gpg, "--verify" ); for ( i=0; i < c->verbosity; i++ ) _gpgme_gpg_add_arg ( c->gpg, "--verbose" ); - /* Check the supplied data */ if ( gpgme_data_get_type (sig) == GPGME_DATA_TYPE_NONE ) { @@ -180,8 +179,10 @@ gpgme_op_verify_start ( GpgmeCtx c, GpgmeData sig, GpgmeData text ) /* Tell the gpg object about the data */ _gpgme_gpg_add_arg ( c->gpg, "--" ); _gpgme_gpg_add_data ( c->gpg, sig, -1 ); - if (text) + if (text) { + _gpgme_gpg_add_arg ( c->gpg, "-" ); _gpgme_gpg_add_data ( c->gpg, text, 0 ); + } /* and kick off the process */ rc = _gpgme_gpg_spawn ( c->gpg, c ); diff --git a/gpgme/version.c b/gpgme/version.c new file mode 100644 index 00000000..0319b06d --- /dev/null +++ b/gpgme/version.c @@ -0,0 +1,104 @@ +/* version.c - version check + * Copyright (C) 2000 Werner Koch (dd9jn) + * + * 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 +#include +#include +#include + +#include "gpgme.h" + +static const char* +parse_version_number ( const char *s, int *number ) +{ + int val = 0; + + if ( *s == '0' && isdigit(s[1]) ) + return NULL; /* leading zeros are not allowed */ + for ( ; isdigit(*s); s++ ) { + val *= 10; + val += *s - '0'; + } + *number = val; + return val < 0? NULL : s; +} + + +static const char * +parse_version_string( const char *s, int *major, int *minor, int *micro ) +{ + s = parse_version_number ( s, major ); + if ( !s || *s != '.' ) + return NULL; + s++; + s = parse_version_number ( s, minor ); + if ( !s || *s != '.' ) + return NULL; + s++; + s = parse_version_number ( s, micro ); + if ( !s ) + return NULL; + return s; /* patchlevel */ +} + +/** + * gpgme_check_version: + * @req_version: A string with a version + * + * Check that the the version of the library is at minimum the requested one + * and return the version string; return NULL if the condition is not + * met. If a NULL is passed to this function, no check is done and + * the version string is simply returned. + * + * Return value: The version string or NULL + **/ +const char * +gpgme_check_version ( const char *req_version ) +{ + const char *ver = VERSION; + int my_major, my_minor, my_micro; + int rq_major, rq_minor, rq_micro; + const char *my_plvl, *rq_plvl; + + if ( !req_version ) + return ver; + + my_plvl = parse_version_string ( ver, &my_major, &my_minor, &my_micro ); + if ( !my_plvl ) + return NULL; /* very strange: our own version is bogus */ + rq_plvl = parse_version_string( req_version, &rq_major, &rq_minor, + &rq_micro ); + if ( !rq_plvl ) + return NULL; /* req version string is invalid */ + + if ( my_major > rq_major + || (my_major == rq_major && my_minor > rq_minor) + || (my_major == rq_major && my_minor == rq_minor + && my_micro > rq_micro) + || (my_major == rq_major && my_minor == rq_minor + && my_micro == rq_micro + && strcmp( my_plvl, rq_plvl ) >= 0) ) { + return ver; + } + return NULL; +} + + + diff --git a/gpgme/w32-io.c b/gpgme/w32-io.c index 39e50826..21d4b1fd 100644 --- a/gpgme/w32-io.c +++ b/gpgme/w32-io.c @@ -36,7 +36,7 @@ #include "util.h" #include "io.h" -#define DEBUG_SELECT_ENABLED 1 +#define DEBUG_SELECT_ENABLED 0 #if DEBUG_SELECT_ENABLED # define DEBUG_SELECT(a) fprintf a @@ -47,7 +47,7 @@ /* - * We assume that a HANDLE can be represented by an int which should be true + * We assume that a HANDLE can be represented by an int which should be true * for all i386 systems (HANDLE is defined as void *) and these are the only * systems for which Windows is available. * Further we assume that -1 denotes an invalid handle. @@ -65,10 +65,12 @@ _gpgme_io_read ( int fd, void *buffer, size_t count ) int nread = 0; HANDLE h = fd_to_handle (fd); + DEBUG_SELECT ((stderr,"** fd %d: about to read %d bytes\n", fd, (int)count )); if ( !ReadFile ( h, buffer, count, &nread, NULL) ) { fprintf (stderr, "** ReadFile failed: ec=%d\n", (int)GetLastError ()); return -1; } + DEBUG_SELECT ((stderr,"** fd %d: got %d bytes\n", fd, nread )); return nread; } @@ -80,23 +82,62 @@ _gpgme_io_write ( int fd, const void *buffer, size_t count ) int nwritten; HANDLE h = fd_to_handle (fd); + DEBUG_SELECT ((stderr,"** fd %d: about to write %d bytes\n", fd, (int)count )); if ( !WriteFile ( h, buffer, count, &nwritten, NULL) ) { fprintf (stderr, "** WriteFile failed: ec=%d\n", (int)GetLastError ()); return -1; } + DEBUG_SELECT ((stderr,"** fd %d: wrote %d bytes\n", fd, nwritten )); return nwritten; } int -_gpgme_io_pipe ( int filedes[2] ) +_gpgme_io_pipe ( int filedes[2], int inherit_idx ) { HANDLE r, w; + SECURITY_ATTRIBUTES sec_attr; + + memset (&sec_attr, 0, sizeof sec_attr ); + sec_attr.nLength = sizeof sec_attr; + sec_attr.bInheritHandle = FALSE; - if (!CreatePipe ( &r, &w, NULL, 0)) + if (!CreatePipe ( &r, &w, &sec_attr, 0)) return -1; + /* make one end inheritable */ + if ( inherit_idx == 0 ) { + HANDLE h; + if (!DuplicateHandle( GetCurrentProcess(), r, + GetCurrentProcess(), &h, 0, + TRUE, DUPLICATE_SAME_ACCESS ) ) { + fprintf (stderr, "** DuplicateHandle failed: ec=%d\n", + (int)GetLastError()); + CloseHandle (r); + CloseHandle (w); + return -1; + } + CloseHandle (r); + r = h; + } + else if ( inherit_idx == 1 ) { + HANDLE h; + if (!DuplicateHandle( GetCurrentProcess(), w, + GetCurrentProcess(), &h, 0, + TRUE, DUPLICATE_SAME_ACCESS ) ) { + fprintf (stderr, "** DuplicateHandle failed: ec=%d\n", + (int)GetLastError()); + CloseHandle (r); + CloseHandle (w); + return -1; + } + CloseHandle (w); + w = h; + } + filedes[0] = handle_to_fd (r); filedes[1] = handle_to_fd (w); + DEBUG_SELECT ((stderr,"** create pipe %p %p %d %d inherit=%d\n", r, w, + filedes[0], filedes[1], inherit_idx )); return 0; } @@ -105,7 +146,15 @@ _gpgme_io_close ( int fd ) { if ( fd == -1 ) return -1; - return CloseHandle (fd_to_handle(fd)) ? 0 : -1; + + DEBUG_SELECT ((stderr,"** closing handle for fd %d\n", fd)); + if ( !CloseHandle (fd_to_handle (fd)) ) { + fprintf (stderr, "** CloseHandle for fd %d failed: ec=%d\n", + fd, (int)GetLastError ()); + return -1; + } + + return 0; } @@ -126,12 +175,13 @@ build_commandline ( char **argv ) * program parses the commandline and does some unquoting */ for (i=0; argv[i]; i++) n += strlen (argv[i]) + 1; - n += 5; /* "gpg " */ buf = p = xtrymalloc (n); if ( !buf ) return NULL; - p = stpcpy (p, "gpg"); - for (i = 0; argv[i]; i++) + *buf = 0; + if ( argv[0] ) + p = stpcpy (p, argv[0]); + for (i = 1; argv[i]; i++) p = stpcpy (stpcpy (p, " "), argv[i]); return buf; @@ -150,46 +200,84 @@ _gpgme_io_spawn ( const char *path, char **argv, 0, /* returns pid */ 0 /* returns tid */ }; - STARTUPINFO si = { - 0, NULL, NULL, NULL, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - NULL, NULL, NULL, NULL - }; + STARTUPINFO si; char *envblock = NULL; int cr_flags = CREATE_DEFAULT_ERROR_MODE | GetPriorityClass (GetCurrentProcess ()); - int i, rc; + int i; char *arg_string; - HANDLE save_stdout; - HANDLE outputfd[2], statusfd[2], inputfd[2]; + int duped_stdin = 0; + int duped_stderr = 0; + HANDLE hnul = INVALID_HANDLE_VALUE; - sec_attr.nLength = sizeof (sec_attr); + memset (&sec_attr, 0, sizeof sec_attr ); + sec_attr.nLength = sizeof sec_attr; sec_attr.bInheritHandle = FALSE; - sec_attr.lpSecurityDescriptor = NULL; - arg_string = build_commandline ( argv ); if (!arg_string ) return -1; + memset (&si, 0, sizeof si); si.cb = sizeof (si); si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = GetStdHandle (STD_INPUT_HANDLE); si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE); si.hStdError = GetStdHandle (STD_ERROR_HANDLE); - if (!SetHandleInformation (si.hStdOutput, - HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) { - fprintf (stderr, "** SHI 1 failed: ec=%d\n", (int) GetLastError ()); - } - if (!SetHandleInformation (si.hStdError, - HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) { - fprintf (stderr, "** SHI 2 failed: ec=%d\n", (int) GetLastError ()); - } - - fputs ("** CreateProcess ...\n", stderr); - fprintf (stderr, "** args=`%s'\n", arg_string); - fflush (stderr); + for (i=0; fd_child_list[i].fd != -1; i++ ) { + if (fd_child_list[i].dup_to == 0 ) { + si.hStdInput = fd_to_handle (fd_child_list[i].fd); + DEBUG_SELECT ((stderr,"** using %d for stdin\n", fd_child_list[i].fd )); + duped_stdin=1; + } + else if (fd_child_list[i].dup_to == 1 ) { + si.hStdOutput = fd_to_handle (fd_child_list[i].fd); + DEBUG_SELECT ((stderr,"** using %d for stdout\n", fd_child_list[i].fd )); + } + else if (fd_child_list[i].dup_to == 2 ) { + si.hStdError = fd_to_handle (fd_child_list[i].fd); + DEBUG_SELECT ((stderr,"** using %d for stderr\n", fd_child_list[i].fd )); + duped_stderr = 1; + } + } + + if( !duped_stdin || !duped_stderr ) { + SECURITY_ATTRIBUTES sa; + + memset (&sa, 0, sizeof sa ); + sa.nLength = sizeof sa; + sa.bInheritHandle = TRUE; + hnul = CreateFile ( "/dev/nul", + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + &sa, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + if ( hnul == INVALID_HANDLE_VALUE ) { + fprintf (stderr,"can't open `/dev/nul': ec=%d\n", + (int)GetLastError () ); + xfree (arg_string); + return -1; + } + /* Make sure that the process has a connected stdin */ + if ( !duped_stdin ) { + si.hStdInput = hnul; + DEBUG_SELECT ((stderr,"** using %d for stdin\n", (int)hnul )); + } + /* We normally don't want all the normal output */ + if ( !duped_stderr ) { + if (!getenv ("GPGME_DEBUG") ) { + si.hStdError = hnul; + DEBUG_SELECT ((stderr,"** using %d for stderr\n", (int)hnul )); + } + } + } + + DEBUG_SELECT ((stderr,"** CreateProcess ...\n")); + DEBUG_SELECT ((stderr,"** args=`%s'\n", arg_string)); + cr_flags |= CREATE_SUSPENDED; if ( !CreateProcessA (GPG_PATH, arg_string, &sec_attr, /* process security attributes */ @@ -203,27 +291,47 @@ _gpgme_io_spawn ( const char *path, char **argv, ) ) { fprintf (stderr, "** CreateProcess failed: ec=%d\n", (int) GetLastError ()); - fflush (stderr); xfree (arg_string); return -1; } - /* .dup_to is not used in the parent list */ - for (i=0; fd_parent_list[i].fd != -1; i++ ) { - CloseHandle ( fd_to_handle (fd_parent_list[i].fd) ); + /* close the /dev/nul handle if used */ + if (hnul != INVALID_HANDLE_VALUE ) { + if ( !CloseHandle ( hnul ) ) + fprintf (stderr, "** CloseHandle(hnul) failed: ec=%d\n", + (int)GetLastError()); } - fprintf (stderr, "** CreateProcess ready\n"); - fprintf (stderr, "** hProcess=%p hThread=%p\n", - pi.hProcess, pi.hThread); - fprintf (stderr, "** dwProcessID=%d dwThreadId=%d\n", - (int) pi.dwProcessId, (int) pi.dwThreadId); - fflush (stderr); + /* Close the other ends of the pipes */ + for (i=0; fd_parent_list[i].fd != -1; i++ ) { + DEBUG_SELECT ((stderr,"** Closing fd %d\n", fd_parent_list[i].fd )); + if ( !CloseHandle ( fd_to_handle (fd_parent_list[i].fd) ) ) + fprintf (stderr, "** CloseHandle failed: ec=%d\n", + (int)GetLastError()); + } + + DEBUG_SELECT ((stderr,"** CreateProcess ready\n" + "** hProcess=%p hThread=%p\n" + "** dwProcessID=%d dwThreadId=%d\n", + pi.hProcess, pi.hThread, + (int) pi.dwProcessId, (int) pi.dwThreadId)); + + if ( ResumeThread ( pi.hThread ) < 0 ) { + fprintf (stderr, "** ResumeThread failed: ec=%d\n", + (int)GetLastError ()); + } + + if ( !CloseHandle (pi.hThread) ) { + fprintf (stderr, "** CloseHandle of thread failed: ec=%d\n", + (int)GetLastError ()); + } return handle_to_pid (pi.hProcess); } + + int _gpgme_io_waitpid ( int pid, int hang, int *r_status, int *r_signal ) { @@ -246,15 +354,15 @@ _gpgme_io_waitpid ( int pid, int hang, int *r_status, int *r_signal ) *r_status = 4; } else { - fprintf (stderr, "** GECP pid=%d exit code=%d\n", - (int)pid, exc); + DEBUG_SELECT ((stderr,"** GECP pid=%d exit code=%d\n", + (int)pid, exc)); *r_status = exc; } ret = 1; break; case WAIT_TIMEOUT: - fprintf (stderr, "** WFSO pid=%d timed out\n", (int)pid); + DEBUG_SELECT ((stderr,"** WFSO pid=%d timed out\n", (int)pid)); break; default: @@ -274,24 +382,28 @@ _gpgme_io_waitpid ( int pid, int hang, int *r_status, int *r_signal ) int _gpgme_io_select ( struct io_select_fd_s *fds, size_t nfds ) { +#if 0 /* We can't use WFMO becaus a pipe handle is not a suitable object */ HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS]; int code, nwait; - int i, any, ret; + int i, any, any_write; + int count; + restart: DEBUG_SELECT ((stderr, "gpgme:select on [ ")); - any = 0; + any = any_write = 0; nwait = 0; for ( i=0; i < nfds; i++ ) { if ( fds[i].fd == -1 ) continue; if ( fds[i].for_read || fds[i].for_write ) { if ( nwait >= DIM (waitbuf) ) { - DEBUG_SELECT ((stderr, "oops ]\n" )); + DEBUG_SELECT ((stderr,stderr, "oops ]\n" )); fprintf (stderr, "** Too many objects for WFMO!\n" ); return -1; } else { - waitbuf[nwait++] = fd_to_handle (fds[i].fd); + if ( fds[i].for_read ) + waitbuf[nwait++] = fd_to_handle (fds[i].fd); DEBUG_SELECT ((stderr, "%c%d ", fds[i].for_read? 'r':'w',fds[i].fd )); any = 1; @@ -300,14 +412,73 @@ _gpgme_io_select ( struct io_select_fd_s *fds, size_t nfds ) fds[i].signaled = 0; } DEBUG_SELECT ((stderr, "]\n" )); - if (!any) + if (!any) return 0; - ret = 0; - code = WaitForMultipleObjects ( nwait, waitbuf, 0, 1000 ); + count = 0; + for ( i=0; i < nfds; i++ ) { + if ( fds[i].fd == -1 ) + continue; + if ( fds[i].for_write ) { + fds[i].signaled = 1; + any_write =1; + count++; + } + } + code = WaitForMultipleObjects ( nwait, waitbuf, 0, any_write? 0:1000); if (code == WAIT_FAILED ) { - fprintf (stderr, "** WFMO failed: %d\n", (int)GetLastError () ); - ret = -1; + int le = (int)GetLastError (); + if ( le == ERROR_INVALID_HANDLE || le == ERROR_INVALID_EVENT_COUNT ) { + any = 0; + for ( i=0; i < nfds; i++ ) { + if ( fds[i].fd == -1 ) + continue; + if ( fds[i].for_read /*|| fds[i].for_write*/ ) { + int navail; + if (PeekNamedPipe (fd_to_handle (fds[i].fd), + NULL, 0, NULL, + &navail, NULL) && navail ) { + fds[i].signaled = 1; + any = 1; + count++; + } + } + } + if (any) + return count; + /* find that handle and remove it from the list*/ + for (i=0; i < nwait; i++ ) { + code = WaitForSingleObject ( waitbuf[i], NULL ); + if (!code) { + int k, j = handle_to_fd (waitbuf[i]); + + fprintf (stderr, "** handle meanwhile signaled %d\n", j); + for (k=0 ; k < nfds; k++ ) { + if ( fds[k].fd == j ) { + fds[k].signaled = 1; + count++; + return count; + } + } + fprintf (stderr, "** oops, or not???\n"); + } + if ( GetLastError () == ERROR_INVALID_HANDLE) { + int k, j = handle_to_fd (waitbuf[i]); + + fprintf (stderr, "** WFMO invalid handle %d removed\n", j); + for (k=0 ; k < nfds; i++ ) { + if ( fds[k].fd == j ) { + fds[k].for_read = fds[k].for_write = 0; + goto restart; + } + } + fprintf (stderr, "** oops, or not???\n"); + } + } + } + + fprintf (stderr, "** WFMO failed: %d\n", le ); + count = -1; } else if ( code == WAIT_TIMEOUT ) { fprintf (stderr, "** WFMO timed out\n" ); @@ -326,22 +497,94 @@ _gpgme_io_select ( struct io_select_fd_s *fds, size_t nfds ) if (WaitForSingleObject ( waitbuf[i], NULL ) == WAIT_OBJECT_0) { fds[i].signaled = 1; any = 1; + count++; } } - if (any) - ret = 1; - else { + if (!any) { fprintf (stderr, "** Oops: No signaled objects found after WFMO\n"); - ret = -1; + count = -1; } } else { fprintf (stderr, "** WFMO returned %d\n", code ); - ret = -1; + count = -1; } - return ret; + return count; +#else /* This is the code we use */ + int i, any, count; + int once_more = 0; + + DEBUG_SELECT ((stderr, "gpgme:fakedselect on [ ")); + any = 0; + for ( i=0; i < nfds; i++ ) { + if ( fds[i].fd == -1 ) + continue; + if ( fds[i].for_read || fds[i].for_write ) { + DEBUG_SELECT ((stderr, "%c%d ", + fds[i].for_read? 'r':'w',fds[i].fd )); + any = 1; + } + fds[i].signaled = 0; + } + DEBUG_SELECT ((stderr, "]\n" )); + if (!any) + return 0; + + restart: + count = 0; + /* no way to see whether a handle is ready fro writing, signal all */ + for ( i=0; i < nfds; i++ ) { + if ( fds[i].fd == -1 ) + continue; + if ( fds[i].for_write ) { + fds[i].signaled = 1; + count++; + } + } + + /* now peek on all read handles */ + for ( i=0; i < nfds; i++ ) { + if ( fds[i].fd == -1 ) + continue; + if ( fds[i].for_read ) { + int navail; + + if ( !PeekNamedPipe (fd_to_handle (fds[i].fd), + NULL, 0, NULL, &navail, NULL) ) { + fprintf (stderr, "** select: PeekFile failed: ec=%d\n", + (int)GetLastError ()); + } + else if ( navail ) { + fprintf (stderr, "** fd %d has %d bytes to read\n", + fds[i].fd, navail ); + fds[i].signaled = 1; + count++; + } + } + } + if ( !once_more && !count ) { + once_more = 1; + Sleep (300); + goto restart; + } + + if ( count ) { + DEBUG_SELECT ((stderr, "gpgme: signaled [ ")); + for ( i=0; i < nfds; i++ ) { + if ( fds[i].fd == -1 ) + continue; + if ( (fds[i].for_read || fds[i].for_write) && fds[i].signaled ) { + DEBUG_SELECT ((stderr, "%c%d ", + fds[i].for_read? 'r':'w',fds[i].fd )); + } + } + DEBUG_SELECT ((stderr, "]\n" )); + } + + return count; +#endif } #endif /*HAVE_DOSISH_SYSTEM*/ diff --git a/gpgme/wait.c b/gpgme/wait.c index 080858e0..a337ad0a 100644 --- a/gpgme/wait.c +++ b/gpgme/wait.c @@ -111,6 +111,19 @@ count_active_fds ( int pid ) return count; } +static void +clear_active_fds ( int pid ) +{ + struct wait_item_s *q; + int i; + + for (i=0; i < fd_table_size; i++ ) { + if ( fd_table[i].fd != -1 && (q=fd_table[i].opaque) + && q->active && q->pid == pid ) + q->active = 0; + } +} + /* remove the given process from the queue */ static void @@ -123,7 +136,11 @@ remove_process ( int pid ) if (fd_table[i].fd != -1 && (q=fd_table[i].opaque) && q->pid == pid ) { xfree (q); fd_table[i].opaque = NULL; - _gpgme_io_close (fd_table[i].fd); + + if ( !fd_table[i].is_closed ) { + _gpgme_io_close (fd_table[i].fd); + fd_table[i].is_closed = 1; + } fd_table[i].fd = -1; } } @@ -168,8 +185,13 @@ _gpgme_wait_on_condition ( GpgmeCtx c, int hang, volatile int *cond ) q = queue_item_from_context ( c ); assert (q); - if (q->exited) - ; + if (q->exited) { + /* this is the second time we reached this and we got no + * more data from the pipe (which may happen to to buffering). + * Set all FDs inactive. + */ + clear_active_fds (q->pid); + } else if ( _gpgme_io_waitpid (q->pid, 0, &q->exit_status, &q->exit_signal)){ q->exited = 1; @@ -207,26 +229,30 @@ do_select ( void ) { struct wait_item_s *q; int i, n; + int any=0; n = _gpgme_io_select ( fd_table, fd_table_size ); if ( n <= 0 ) return 0; /* error or timeout */ - for (i=0; i < fd_table_size && n; i++ ) { + for (i=0; i < fd_table_size /*&& n*/; i++ ) { if ( fd_table[i].fd != -1 && fd_table[i].signaled ) { q = fd_table[i].opaque; assert (n); n--; + if ( q->active ) + any = 1; if ( q->active && q->handler (q->handler_value, q->pid, fd_table[i].fd ) ) { q->active = 0; fd_table[i].for_read = 0; fd_table[i].for_write = 0; + fd_table[i].is_closed = 1; } } } - return 1; + return any; } @@ -262,6 +288,7 @@ _gpgme_register_pipe_handler( void *opaque, for (i=0; i < fd_table_size; i++ ) { if ( fd_table[i].fd == -1 ) { fd_table[i].fd = fd; + fd_table[i].is_closed = 0; fd_table[i].for_read = inbound; fd_table[i].for_write = !inbound; fd_table[i].signaled = 0; diff --git a/tests/t-decrypt.c b/tests/t-decrypt.c index f98748da..dd646895 100644 --- a/tests/t-decrypt.c +++ b/tests/t-decrypt.c @@ -26,6 +26,12 @@ #include "../gpgme/gpgme.h" +struct passphrase_cb_info_s { + GpgmeCtx c; + int did_it; +}; + + #define fail_if_err(a) do { if(a) { int my_errno = errno; \ fprintf (stderr, "%s:%d: GpgmeError %s\n", \ __FILE__, __LINE__, gpgme_strerror(a)); \ @@ -50,17 +56,31 @@ print_data ( GpgmeData dh ) fail_if_err (err); } -#if 0 -static GpgmeData -passphrase_cb ( void *opaque, const char *description ) -{ - GpgmeData dh; - assert (NULL); - gpgme_data_new_from_mem ( &dh, "abc", 3, 0 ); - return dh; +static int +passphrase_cb ( void *opaque, char *buffer, size_t length, size_t *nread ) +{ + struct passphrase_cb_info_s *info = opaque; + const char *desc; + + assert (info); + assert (info->c); + if ( !buffer || !length || !nread ) + return 0; /* those values are reserved for extensions */ + if ( info->did_it ) + return -1; /* eof */ + + desc = gpgme_get_prompt (info->c, 1); + if (desc) + fprintf (stderr, "Request passphrase for '%s'\n", desc ); + if ( length < 3 ) + return -1; /* FIXME - sending an EOF here is wrong */ + memcpy (buffer, "abc", 3 ); + *nread = 3; + info->did_it = 1; + return 0; } -#endif + static char * mk_fname ( const char *fname ) @@ -84,17 +104,18 @@ main (int argc, char **argv ) { GpgmeCtx ctx; GpgmeError err; - GpgmeData in, out; + GpgmeData in, out, pwdata = NULL; + struct passphrase_cb_info_s info; const char *cipher_1_asc = mk_fname ("cipher-1.asc"); do { err = gpgme_new (&ctx); fail_if_err (err); -#if 0 - if ( !getenv("GPG_AGENT_INFO") { - gpgme_set_passphrase_cb ( ctx, passphrase_cb, NULL ); + if ( 0 && !getenv("GPG_AGENT_INFO") ) { + memset ( &info, 0, sizeof info ); + info.c = ctx; + gpgme_data_new_with_read_cb ( &pwdata, passphrase_cb, &info ); } -#endif err = gpgme_data_new_from_file ( &in, cipher_1_asc, 1 ); fail_if_err (err); @@ -102,7 +123,7 @@ main (int argc, char **argv ) err = gpgme_data_new ( &out ); fail_if_err (err); - err = gpgme_op_decrypt (ctx, in, out ); + err = gpgme_op_decrypt (ctx, pwdata, in, out ); fail_if_err (err); fflush (NULL); @@ -112,6 +133,7 @@ main (int argc, char **argv ) gpgme_data_release (in); gpgme_data_release (out); + gpgme_data_release (pwdata); gpgme_release (ctx); } while ( argc > 1 && !strcmp( argv[1], "--loop" ) );