Okay, it runs at least on Windows 95

This commit is contained in:
Werner Koch 2000-12-06 12:17:10 +00:00
parent b7a7d9b9cc
commit 874f12ea70
27 changed files with 1363 additions and 147 deletions

View File

@ -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}

View File

@ -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"*/

View File

@ -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 <stdlib.h>
#include <sys/types.h>], [
#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
])

11
bonobo/Makefile.am Normal file
View File

@ -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

20
bonobo/gpgme.c Normal file
View File

@ -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
*/

20
bonobo/main.c Normal file
View File

@ -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
*/

42
bonobo/main.h Normal file
View File

@ -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 */

12
complus/Makefile.am Normal file
View File

@ -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

287
complus/main.c Normal file
View File

@ -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 <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <time.h>
#include <windows.h>
#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 <gpgme-bugs@gnupg.org>.\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, &reg );
CoResumeClassObjects ();
#endif
WaitForSingleObject ( running, INFINITE );
CloseHandle (running);
#if 0
CoRevokeClassObject ( reg );
factory->release ();
CoUnitialize ();
#endif
}

50
complus/main.h Normal file
View File

@ -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 */

View File

@ -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
"

View File

@ -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;

View File

@ -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 */
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,6 +377,7 @@ gpgme_data_read ( GpgmeData dh, char *buffer, size_t length, size_t *nread )
if ( !dh )
return mk_error (Invalid_Value);
if (dh->type == GPGME_DATA_TYPE_MEM ) {
nbytes = dh->len - dh->readpos;
if ( !nbytes ) {
*nread = 0;
@ -354,9 +388,55 @@ gpgme_data_read ( GpgmeData dh, char *buffer, size_t length, size_t *nread )
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.
*/

View File

@ -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 )

49
gpgme/errors.c Normal file
View File

@ -0,0 +1,49 @@
/* Generated automatically by mkerrors */
/* Do not edit! */
#include <stdio.h>
#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;
}

View File

@ -21,6 +21,7 @@
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "util.h"
#include "context.h"
@ -69,6 +70,7 @@ gpgme_release ( GpgmeCtx c )
_gpgme_key_release ( c->tmp_key );
gpgme_data_release ( c->notation );
/* fixme: release the key_queue */
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;
}

View File

@ -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

View File

@ -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,

View File

@ -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 );

View File

@ -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 );
}

View File

@ -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;
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++;
@ -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;
}

68
gpgme/status-table.h Normal file
View File

@ -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}
};

View File

@ -164,7 +164,6 @@ gpgme_op_verify_start ( GpgmeCtx c, GpgmeData sig, GpgmeData text )
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 ) {
rc = mk_error (No_Data);
@ -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 );

104
gpgme/version.c Normal file
View File

@ -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 <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#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;
}

View File

@ -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
@ -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;
if (!CreatePipe ( &r, &w, NULL, 0))
memset (&sec_attr, 0, sizeof sec_attr );
sec_attr.nLength = sizeof sec_attr;
sec_attr.bInheritHandle = FALSE;
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 ());
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 (!SetHandleInformation (si.hStdError,
HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
fprintf (stderr, "** SHI 2 failed: ec=%d\n", (int) GetLastError ());
}
if( !duped_stdin || !duped_stderr ) {
SECURITY_ATTRIBUTES sa;
fputs ("** CreateProcess ...\n", stderr);
fprintf (stderr, "** args=`%s'\n", arg_string);
fflush (stderr);
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,23 +382,27 @@ _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 {
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 ));
@ -303,11 +415,70 @@ _gpgme_io_select ( struct io_select_fd_s *fds, size_t nfds )
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*/

View File

@ -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;
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;

View File

@ -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" ) );