From 258892928e4b7fd8483af220c526d6bfc2782d26 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 9 Nov 2000 16:35:35 +0000 Subject: [PATCH] Add verify function --- trunk/gpgme/Makefile.am | 1 + trunk/gpgme/context.h | 22 ++++- trunk/gpgme/data.c | 84 ++++++++++++++++- trunk/gpgme/encrypt.c | 24 ++--- trunk/gpgme/gpgme.c | 22 ++++- trunk/gpgme/gpgme.h | 12 ++- trunk/gpgme/ops.h | 8 +- trunk/gpgme/rungpg.c | 146 ++++++++++++++++++++++++----- trunk/gpgme/types.h | 5 + trunk/gpgme/verify.c | 198 ++++++++++++++++++++++++++++++++++++++++ trunk/gpgme/wait.c | 65 ++++++++++++- trunk/tests/Makefile.am | 2 +- trunk/tests/t-encrypt.c | 28 +++++- trunk/tests/t-verify.c | 85 +++++++++++++++++ 14 files changed, 645 insertions(+), 57 deletions(-) create mode 100644 trunk/gpgme/verify.c create mode 100644 trunk/tests/t-verify.c diff --git a/trunk/gpgme/Makefile.am b/trunk/gpgme/Makefile.am index 87de1de..b47169c 100644 --- a/trunk/gpgme/Makefile.am +++ b/trunk/gpgme/Makefile.am @@ -18,6 +18,7 @@ libgpgme_la_SOURCES = \ data.c recipient.c \ wait.c wait.h \ encrypt.c \ + verify.c \ rungpg.c rungpg.h status-table.h \ gpgme.c errors.c diff --git a/trunk/gpgme/context.h b/trunk/gpgme/context.h index 504ec89..c45d1bf 100644 --- a/trunk/gpgme/context.h +++ b/trunk/gpgme/context.h @@ -25,16 +25,34 @@ #include "types.h" #include "rungpg.h" /* for GpgObject */ +typedef enum { + RESULT_TYPE_NONE = 0, + RESULT_TYPE_VERIFY, +} ResultType; + + /* Currently we need it at several places, so we put the definition * into this header file */ struct gpgme_context_s { int initialized; int pending; /* a gpg request is still pending */ - + + /* at some points we need to allocate memory but we are not + * able to handle a malloc problem at that point, so we set this + * flag to indicate this condition */ + int out_of_core; + GpgObject gpg; /* the running gpg process */ int verbosity; /* level of verbosity to use */ int use_armor; /* use armoring */ + + + + ResultType result_type; + union { + VerifyResult verify; + } result; }; @@ -44,6 +62,8 @@ struct gpgme_data_s { GpgmeDataType type; GpgmeDataMode mode; size_t readpos; + size_t writepos; + size_t private_len; char *private_buffer; }; diff --git a/trunk/gpgme/data.c b/trunk/gpgme/data.c index 253e101..8499069 100644 --- a/trunk/gpgme/data.c +++ b/trunk/gpgme/data.c @@ -27,6 +27,9 @@ #include "context.h" +#define ALLOC_CHUNK 1024 + + /** * gpgme_new_data: * @r_dh: Returns a new data object. @@ -59,8 +62,10 @@ gpgme_new_data ( GpgmeData *r_dh, const char *buffer, size_t size, int copy ) xfree (dh); return mk_error (Out_Of_Core); } + dh->private_len = size; memcpy (dh->private_buffer, buffer, size ); dh->data = dh->private_buffer; + dh->writepos = size; } else { dh->data = buffer; @@ -113,24 +118,95 @@ _gpgme_query_data_mode ( GpgmeData dh ) return dh->mode; } +GpgmeError +gpgme_rewind_data ( 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; + return 0; +} + +GpgmeError +gpgme_read_data ( GpgmeData dh, char *buffer, size_t length, size_t *nread ) +{ + size_t nbytes; + + if ( !dh ) + return mk_error (Invalid_Value); + 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; + return 0; +} GpgmeError -_gpgme_append_data ( GpgmeData dh, const char *buf, size_t length ) +_gpgme_append_data ( GpgmeData dh, const char *buffer, size_t length ) { assert (dh); if ( dh->type == GPGME_DATA_TYPE_NONE ) { /* convert it to a mem data type */ + assert (!dh->private_buffer); + dh->type = GPGME_DATA_TYPE_MEM; + dh->private_len = length < ALLOC_CHUNK? ALLOC_CHUNK : length; + dh->private_buffer = xtrymalloc ( dh->private_len ); + if (!dh->private_buffer) { + dh->private_len = 0; + return mk_error (Out_Of_Core); + } + dh->writepos = 0; + dh->data = dh->private_buffer; } - else if ( dh->type != GPGME_DATA_TYPE_MEM ) { + else if ( dh->type != GPGME_DATA_TYPE_MEM ) return mk_error (Invalid_Type); - } - + if ( dh->mode != GPGME_DATA_MODE_INOUT && dh->mode != GPGME_DATA_MODE_IN ) return mk_error (Invalid_Mode); + if ( !dh->private_buffer ) { + /* we have to copy it now */ + assert (dh->data); + dh->private_len = dh->len+length; + if (dh->private_len < ALLOC_CHUNK) + dh->private_len = ALLOC_CHUNK; + dh->private_buffer = xtrymalloc ( dh->private_len ); + if (!dh->private_buffer) { + dh->private_len = 0; + return mk_error (Out_Of_Core); + } + dh->writepos = 0; + dh->data = dh->private_buffer; + } + + /* allocate more memory if needed */ + if ( dh->writepos + length > dh->private_len ) { + char *p; + size_t newlen = dh->private_len + + (dh->len < ALLOC_CHUNK? ALLOC_CHUNK : length); + p = xtryrealloc ( dh->private_buffer, newlen ); + if ( !p ) + return mk_error (Out_Of_Core); + dh->private_buffer = p; + dh->private_len = newlen; + dh->data = dh->private_buffer; + assert ( !(dh->writepos + length > dh->private_len) ); + } + + memcpy ( dh->private_buffer + dh->writepos, buffer, length ); + dh->writepos += length; + dh->len += length; return 0; } diff --git a/trunk/gpgme/encrypt.c b/trunk/gpgme/encrypt.c index eaf5742..501e65f 100644 --- a/trunk/gpgme/encrypt.c +++ b/trunk/gpgme/encrypt.c @@ -39,7 +39,7 @@ encrypt_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args ) GpgmeError -gpgme_req_encrypt ( GpgmeCtx c, GpgmeRecipientSet recp, +gpgme_start_encrypt ( GpgmeCtx c, GpgmeRecipientSet recp, GpgmeData plain, GpgmeData ciph ) { int rc = 0; @@ -87,7 +87,7 @@ gpgme_req_encrypt ( GpgmeCtx c, GpgmeRecipientSet recp, _gpgme_gpg_add_arg ( c->gpg, "--output" ); _gpgme_gpg_add_arg ( c->gpg, "-" ); _gpgme_gpg_add_data ( c->gpg, ciph, 1 ); - + _gpgme_gpg_add_arg ( c->gpg, "--" ); _gpgme_gpg_add_data ( c->gpg, plain, 0 ); /* and kick off the process */ @@ -102,18 +102,6 @@ gpgme_req_encrypt ( GpgmeCtx c, GpgmeRecipientSet recp, } -GpgmeError -gpgme_wait_encrypt ( GpgmeCtx c, GpgmeData *r_out ) -{ - wait_on_request_or_fail (c); - - fprintf (stderr,"gpgme_wait_encrypt: main\n"); - - - return 0; -} - - /** * gpgme_encrypt: * @c: The context @@ -131,9 +119,11 @@ GpgmeError gpgme_encrypt ( GpgmeCtx c, GpgmeRecipientSet recp, GpgmeData in, GpgmeData out ) { - int rc = gpgme_req_encrypt ( c, recp, in, out ); - if ( !rc ) - rc = gpgme_wait_encrypt ( c, NULL ); + int rc = gpgme_start_encrypt ( c, recp, in, out ); + if ( !rc ) { + gpgme_wait (c, 1); + c->pending = 0; + } return rc; } diff --git a/trunk/gpgme/gpgme.c b/trunk/gpgme/gpgme.c index fd55c4b..10df508 100644 --- a/trunk/gpgme/gpgme.c +++ b/trunk/gpgme/gpgme.c @@ -24,7 +24,7 @@ #include "util.h" #include "context.h" - +#include "ops.h" /** * gpgme_new_context: @@ -58,14 +58,26 @@ gpgme_new_context (GpgmeCtx *r_ctx) void gpgme_release_context ( GpgmeCtx c ) { + + _gpgme_gpg_release_object ( c->gpg ); + _gpgme_release_result ( c ); xfree ( c ); } - - - - +void +_gpgme_release_result ( GpgmeCtx c ) +{ + switch (c->result_type) { + case RESULT_TYPE_NONE: + break; + case RESULT_TYPE_VERIFY: + _gpgme_release_verify_result ( c->result.verify ); + break; + } + c->result.verify = NULL; + c->result_type = RESULT_TYPE_NONE; +} diff --git a/trunk/gpgme/gpgme.h b/trunk/gpgme/gpgme.h index f5ede58..88a8434 100644 --- a/trunk/gpgme/gpgme.h +++ b/trunk/gpgme/gpgme.h @@ -38,6 +38,7 @@ typedef struct gpgme_recipient_set_s *GpgmeRecipientSet; typedef enum { + GPGME_EOF = -1, GPGME_No_Error = 0, GPGME_General_Error = 1, GPGME_Out_Of_Core = 2, @@ -81,12 +82,16 @@ GpgmeError gpgme_new_data ( GpgmeData *r_dh, const char *buffer, size_t size, int copy ); void gpgme_release_data ( GpgmeData dh ); GpgmeDataType gpgme_query_data_type ( GpgmeData dh ); +GpgmeError gpgme_rewind_data ( GpgmeData dh ); +GpgmeError gpgme_read_data ( GpgmeData dh, + char *buffer, size_t length, size_t *nread ); + /* Basic GnuPG functions */ -GpgmeError gpgme_req_encrypt ( GpgmeCtx c, GpgmeRecipientSet recp, - GpgmeData in, GpgmeData out ); -GpgmeError gpgme_wait_encrypt ( GpgmeCtx c, GpgmeData *r_out ); +GpgmeError gpgme_start_encrypt ( GpgmeCtx c, GpgmeRecipientSet recp, + GpgmeData in, GpgmeData out ); +GpgmeError gpgme_start_verify ( GpgmeCtx c, GpgmeData sig, GpgmeData text ); /* Key management functions */ @@ -99,6 +104,7 @@ GpgmeError gpgme_wait_encrypt ( GpgmeCtx c, GpgmeData *r_out ); /* Convenience functions for syncronous usage */ GpgmeError gpgme_encrypt ( GpgmeCtx c, GpgmeRecipientSet recp, GpgmeData in, GpgmeData out ); +GpgmeError gpgme_verify ( GpgmeCtx c, GpgmeData sig, GpgmeData text ); /* miscellaneous functions */ diff --git a/trunk/gpgme/ops.h b/trunk/gpgme/ops.h index ca361ca..59efeff 100644 --- a/trunk/gpgme/ops.h +++ b/trunk/gpgme/ops.h @@ -23,6 +23,9 @@ #include "types.h" +/*-- gpgme.c --*/ +void _gpgme_release_result ( GpgmeCtx c ); + /*-- recipient.c --*/ void _gpgme_append_gpg_args_from_recipients ( @@ -33,8 +36,11 @@ void _gpgme_append_gpg_args_from_recipients ( /*-- data.c --*/ GpgmeDataMode _gpgme_query_data_mode ( GpgmeData dh ); void _gpgme_set_data_mode ( GpgmeData dh, GpgmeDataMode mode ); +GpgmeError _gpgme_append_data ( GpgmeData dh, + const char *buffer, size_t length ); - +/*-- verify.c --*/ +void _gpgme_release_verify_result ( VerifyResult res ); #endif /* OPS_H */ diff --git a/trunk/gpgme/rungpg.c b/trunk/gpgme/rungpg.c index 6353ad2..ac6ba11 100644 --- a/trunk/gpgme/rungpg.c +++ b/trunk/gpgme/rungpg.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "gpgme.h" #include "util.h" @@ -84,6 +85,7 @@ struct gpg_object_s { static void kill_gpg ( GpgObject gpg ); static void free_argv ( char **argv ); +static void free_fd_data_map ( struct fd_data_map_s *fd_data_map ); static int gpg_status_handler ( void *opaque, pid_t pid, int fd ); static int gpg_inbound_handler ( void *opaque, pid_t pid, int fd ); static int gpg_outbound_handler ( void *opaque, pid_t pid, int fd ); @@ -96,7 +98,6 @@ GpgmeError _gpgme_gpg_new_object ( GpgObject *r_gpg ) { GpgObject gpg; - char buf[20]; int rc = 0; gpg = xtrycalloc ( 1, sizeof *gpg ); @@ -109,9 +110,6 @@ _gpgme_gpg_new_object ( GpgObject *r_gpg ) gpg->status.fd[0] = -1; gpg->status.fd[1] = -1; - /* The name of the beast will be gpg - so put it into argv[0] */ - _gpgme_gpg_add_arg ( gpg, "gpg" ); - /* allocate the read buffer for the status pipe */ gpg->status.bufsize = 1024; gpg->status.readpos = 0; @@ -128,8 +126,13 @@ _gpgme_gpg_new_object ( GpgObject *r_gpg ) } gpg->status.eof = 0; _gpgme_gpg_add_arg ( gpg, "--status-fd" ); - sprintf ( buf, "%d", gpg->status.fd[1]); - _gpgme_gpg_add_arg ( gpg, buf ); + { + char buf[25]; + sprintf ( buf, "%d", gpg->status.fd[1]); + _gpgme_gpg_add_arg ( gpg, buf ); + } + _gpgme_gpg_add_arg ( gpg, "--batch" ); + _gpgme_gpg_add_arg ( gpg, "--no-tty" ); leave: if (rc) { @@ -153,8 +156,7 @@ _gpgme_gpg_release_object ( GpgObject gpg ) close (gpg->status.fd[0]); if (gpg->status.fd[1] != -1 ) close (gpg->status.fd[1]); - /* fixme: walk over the data map and close all fds */ - xfree (gpg->fd_data_map); + free_fd_data_map (gpg->fd_data_map); kill_gpg (gpg); /* fixme: should be done asyncronously */ xfree (gpg); } @@ -162,6 +164,7 @@ _gpgme_gpg_release_object ( GpgObject gpg ) static void kill_gpg ( GpgObject gpg ) { + #if 0 if ( gpg->running ) { /* still running? Must send a killer */ kill ( gpg->pid, SIGTERM); @@ -172,6 +175,7 @@ kill_gpg ( GpgObject gpg ) } gpg->running = 0; } + #endif } @@ -238,6 +242,21 @@ free_argv ( char **argv ) xfree (argv); } +static void +free_fd_data_map ( struct fd_data_map_s *fd_data_map ) +{ + int i; + + for (i=0; fd_data_map[i].data; i++ ) { + if ( fd_data_map[i].fd != -1 ) + close (fd_data_map[i].fd); + if ( fd_data_map[i].peer_fd != -1 ) + close (fd_data_map[i].peer_fd); + /* don't realease data because this is only a reference */ + } + xfree (fd_data_map); +} + static GpgmeError build_argv ( GpgObject gpg ) @@ -246,22 +265,32 @@ build_argv ( GpgObject gpg ) struct fd_data_map_s *fd_data_map; size_t datac=0, argc=0; char **argv; + int need_special = 0; if ( gpg->argv ) { free_argv ( gpg->argv ); gpg->argv = NULL; } - /* fixme: release fd_data_map */ + if (gpg->fd_data_map) { + free_fd_data_map (gpg->fd_data_map); + gpg->fd_data_map = NULL; + } + argc++; /* for argv[0] */ for ( a=gpg->arglist; a; a = a->next ) { argc++; if (a->data) { - fprintf (stderr, "build_argv: data\n" ); + /*fprintf (stderr, "build_argv: data\n" );*/ datac++; + if ( a->dup_to == -1 ) + need_special = 1; + } + else { + /* fprintf (stderr, "build_argv: arg=`%s'\n", a->arg );*/ } - else - fprintf (stderr, "build_argv: arg=`%s'\n", a->arg ); } + if ( need_special ) + argc++; argv = xtrycalloc ( argc+1, sizeof *argv ); if (!argv) @@ -273,6 +302,22 @@ build_argv ( GpgObject gpg ) } argc = datac = 0; + argv[argc] = xtrystrdup ( "gpg" ); /* argv[0] */ + if (!argv[argc]) { + xfree (fd_data_map); + free_argv (argv); + return mk_error (Out_Of_Core); + } + argc++; + if ( need_special ) { + argv[argc] = xtrystrdup ( "--enable-special-filenames" ); + if (!argv[argc]) { + xfree (fd_data_map); + free_argv (argv); + return mk_error (Out_Of_Core); + } + argc++; + } for ( a=gpg->arglist; a; a = a->next ) { if ( a->data ) { switch ( _gpgme_query_data_mode (a->data) ) { @@ -328,6 +373,16 @@ build_argv ( GpgObject gpg ) } fd_data_map[datac].data = a->data; fd_data_map[datac].dup_to = a->dup_to; + if ( a->dup_to == -1 ) { + argv[argc] = xtrymalloc ( 25 ); + if (!argv[argc]) { + xfree (fd_data_map); + free_argv (argv); + return mk_error (Out_Of_Core); + } + sprintf ( argv[argc], "-&%d", fd_data_map[datac].peer_fd ); + argc++; + } datac++; } else { @@ -337,8 +392,8 @@ build_argv ( GpgObject gpg ) free_argv (argv); return mk_error (Out_Of_Core); } + argc++; } - argc++; } gpg->argv = argv; @@ -372,6 +427,11 @@ _gpgme_gpg_spawn( GpgObject gpg, void *opaque ) } if ( !pid ) { /* child */ + int duped_stdin = 0; + int duped_stderr = 0; + + close (gpg->status.fd[0]); + for (i=0; gpg->fd_data_map[i].data; i++ ) { close (gpg->fd_data_map[i].fd); gpg->fd_data_map[i].fd = -1; @@ -382,20 +442,49 @@ _gpgme_gpg_spawn( GpgObject gpg, void *opaque ) strerror (errno)); _exit (8); } + if ( gpg->fd_data_map[i].dup_to == 0 ) + duped_stdin=1; + if ( gpg->fd_data_map[i].dup_to == 2 ) + duped_stderr=1; close ( gpg->fd_data_map[i].peer_fd ); } } - - if ( gpg->status.fd[0] != -1 ) - close (gpg->status.fd[0]); - /* FIXME: dup /dev/null to stdin if nothing is connected to stdin */ - execv ("/usr/local/bin/gpg", gpg->argv ); - fprintf (stderr,"exec of gpg failed\n"); fflush (stderr); + if( !duped_stdin || !duped_stderr ) { + int fd = open ( "/dev/null", O_RDONLY ); + if ( fd == -1 ) { + fprintf (stderr,"can't open `/dev/null': %s\n", + strerror (errno) ); + _exit (8); + } + /* Make sure that gpg has a connected stdin */ + if ( !duped_stdin ) { + if ( dup2 ( fd, 0 ) == -1 ) { + fprintf (stderr,"dup2(/dev/null, 0) failed: %s\n", + strerror (errno) ); + _exit (8); + } + } + /* We normally don't want all the normal output */ + if ( !duped_stderr ) { + if ( dup2 ( fd, 2 ) == -1 ) { + fprintf (stderr,"dup2(dev/null, 2) failed: %s\n", + strerror (errno) ); + _exit (8); + } + } + close (fd); + } + + execv ("./gpg", gpg->argv ); + fprintf (stderr,"exec of gpg failed\n"); _exit (8); } + /* parent */ gpg->pid = pid; + /*_gpgme_register_term_handler ( closure, closure_value, pid );*/ + if ( gpg->status.fd[1] != -1 ) close (gpg->status.fd[1]); if ( _gpgme_register_pipe_handler ( opaque, gpg_status_handler, @@ -424,7 +513,6 @@ _gpgme_gpg_spawn( GpgObject gpg, void *opaque ) gpg->running = 1; return 0; - } @@ -432,6 +520,7 @@ static int gpg_inbound_handler ( void *opaque, pid_t pid, int fd ) { GpgmeData dh = opaque; + GpgmeError err; int nread; char buf[200]; @@ -448,7 +537,19 @@ gpg_inbound_handler ( void *opaque, pid_t pid, int fd ) } else if (!nread) return 1; /* eof */ + + /* We could improve this with a GpgmeData function which takes + * the read function or provides a memory area for writing to it. + */ + err = _gpgme_append_data ( dh, buf, nread ); + if ( err ) { + fprintf (stderr, "_gpgme_append_data failed: %s\n", + gpgme_strerror(err)); + /* Fixme: we should close the pipe or read it to /dev/null in + * this case. Returnin EOF is not sufficient */ + return 1; + } return 0; } @@ -520,8 +621,11 @@ gpg_status_handler ( void *opaque, pid_t pid, int fd ) static int -status_cmp (const struct status_table_s *a, const struct status_table_s *b) +status_cmp (const void *ap, const void *bp) { + const struct status_table_s *a = ap; + const struct status_table_s *b = bp; + return strcmp (a->name, b->name); } diff --git a/trunk/gpgme/types.h b/trunk/gpgme/types.h index c8b2302..82f6b79 100644 --- a/trunk/gpgme/types.h +++ b/trunk/gpgme/types.h @@ -43,6 +43,11 @@ struct gpg_object_s; typedef struct gpg_object_s *GpgObject; +/*-- verify.c --*/ +struct verify_result_s; +typedef struct verify_result_s *VerifyResult; + + #endif /* TYPES_H */ diff --git a/trunk/gpgme/verify.c b/trunk/gpgme/verify.c new file mode 100644 index 0000000..e984478 --- /dev/null +++ b/trunk/gpgme/verify.c @@ -0,0 +1,198 @@ +/* verify.c - signature verification + * 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 "util.h" +#include "context.h" +#include "ops.h" + +typedef enum { + VERIFY_STATUS_NONE = 0, + VERIFY_STATUS_NOSIG, + VERIFY_STATUS_NOKEY, + VERIFY_STATUS_ERROR, + VERIFY_STATUS_BAD, + VERIFY_STATUS_GOOD +} VerifyStatus; + +struct verify_result_s { + VerifyStatus status; + +}; + + +void +_gpgme_release_verify_result ( VerifyResult res ) +{ + xfree (res); +} + + +static void +verify_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args ) +{ + if ( ctx->out_of_core ) + return; + if ( ctx->result_type == RESULT_TYPE_NONE ) { + assert ( !ctx->result.verify ); + ctx->result.verify = xtrycalloc ( 1, sizeof *ctx->result.verify ); + if ( !ctx->result.verify ) { + ctx->out_of_core = 1; + return; + } + ctx->result_type = RESULT_TYPE_VERIFY; + } + assert ( ctx->result_type == RESULT_TYPE_VERIFY ); + + /* FIXME: For now we handle only one signature */ + /* FIXME: Collect useful information */ + switch (code) { + case STATUS_GOODSIG: + ctx->result.verify->status = VERIFY_STATUS_GOOD; + break; + case STATUS_BADSIG: + ctx->result.verify->status = VERIFY_STATUS_BAD; + break; + case STATUS_ERRSIG: + ctx->result.verify->status = VERIFY_STATUS_ERROR; + /* FIXME: distinguish between a regular error and a missing key. + * this is encoded in the args. */ + break; + default: + /* ignore all other codes */ + fprintf (stderr, "verify_status: code=%d not handled\n", code ); + break; + } +} + + + +GpgmeError +gpgme_start_verify ( GpgmeCtx c, GpgmeData sig, GpgmeData text ) +{ + int rc = 0; + int i; + + fail_on_pending_request( c ); + c->pending = 1; + + _gpgme_release_result (c); + c->out_of_core = 0; + + /* create a process object. + * To optimize this, we should reuse an existing one and + * run gpg in the new --pipemode (I started with this but it is + * not yet finished) */ + if ( c->gpg ) { + _gpgme_gpg_release_object ( c->gpg ); + c->gpg = NULL; + } + rc = _gpgme_gpg_new_object ( &c->gpg ); + if (rc) + goto leave; + + _gpgme_gpg_set_status_handler ( c->gpg, verify_status_handler, c ); + + /* build the commandline */ + _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_query_data_type (sig) == GPGME_DATA_TYPE_NONE ) { + rc = mk_error (No_Data); + goto leave; + } + if ( text && gpgme_query_data_type (text) == GPGME_DATA_TYPE_NONE ) { + rc = mk_error (No_Data); + goto leave; + } + _gpgme_set_data_mode (sig, GPGME_DATA_MODE_OUT ); + if (text) /* detached signature */ + _gpgme_set_data_mode (text, GPGME_DATA_MODE_OUT ); + /* Tell the gpg object about the data */ + _gpgme_gpg_add_arg ( c->gpg, "--" ); + _gpgme_gpg_add_data ( c->gpg, sig, -1 ); + if (text) + _gpgme_gpg_add_data ( c->gpg, text, 0 ); + + /* and kick off the process */ + rc = _gpgme_gpg_spawn ( c->gpg, c ); + + leave: + if (rc) { + c->pending = 0; + _gpgme_gpg_release_object ( c->gpg ); c->gpg = NULL; + } + return rc; +} + + + +GpgmeError +gpgme_verify ( GpgmeCtx c, GpgmeData sig, GpgmeData text ) +{ + int rc = gpgme_start_verify ( c, sig, text ); + if ( !rc ) { + gpgme_wait (c, 1); + if ( c->result_type != RESULT_TYPE_VERIFY ) + rc = mk_error (General_Error); + else if ( c->out_of_core ) + rc = mk_error (Out_Of_Core); + else { + assert ( c->result.verify ); + switch ( c->result.verify->status ) { + case VERIFY_STATUS_NONE: + fputs ("Verification Status: None\n", stdout); + break; + case VERIFY_STATUS_NOSIG: + fputs ("Verification Status: No Signature\n", stdout); + break; + case VERIFY_STATUS_GOOD: + fputs ("Verification Status: Good\n", stdout); + break; + case VERIFY_STATUS_BAD: + fputs ("Verification Status: Bad\n", stdout); + break; + case VERIFY_STATUS_NOKEY: + fputs ("Verification Status: No Key\n", stdout); + break; + case VERIFY_STATUS_ERROR: + fputs ("Verification Status: Error\n", stdout); + break; + } + } + c->pending = 0; + } + return rc; +} + + + + + + + diff --git a/trunk/gpgme/wait.c b/trunk/gpgme/wait.c index f8cadc0..a84c746 100644 --- a/trunk/gpgme/wait.c +++ b/trunk/gpgme/wait.c @@ -98,6 +98,53 @@ queue_item_from_context ( GpgmeCtx ctx ) } +static void +propagate_term_results ( const struct wait_queue_item_s *first_q ) +{ + struct wait_queue_item_s *q; + + for (q=wait_queue; q; q = q->next) { + if ( q->used && q != first_q && !q->exited + && q->pid == first_q->pid ) { + q->exited = first_q->exited; + q->exit_status = first_q->exit_status; + q->exit_signal = first_q->exit_signal; + } + } +} + +static int +count_active_fds ( pid_t pid ) +{ + struct wait_queue_item_s *q; + int count = 0; + + for (q=wait_queue; q; q = q->next) { + if ( q->used && q->active && q->pid == pid ) + count++; + } + return count; +} + + +/* remove the given process from the queue */ +static void +remove_process ( pid_t pid ) +{ + struct wait_queue_item_s *q; + + for (q=wait_queue; q; q = q->next) { + if ( q->used ) { + close (q->fd); + q->handler = NULL; + q->ctx = NULL; + q->used = 0; + } + } +} + + + /** * gpgme_wait: * @c: @@ -127,7 +174,9 @@ gpgme_wait ( GpgmeCtx c, int hang ) q = queue_item_from_context ( c ); assert (q); - if ( waitpid ( q->pid, &status, WNOHANG ) == q->pid ) { + if (q->exited) + ; + else if ( waitpid ( q->pid, &status, WNOHANG ) == q->pid ) { q->exited = 1; if ( WIFSIGNALED (status) ) { q->exit_status = 4; /* Need some value here */ @@ -140,8 +189,18 @@ gpgme_wait ( GpgmeCtx c, int hang ) q->exited++; q->exit_status = 4; } - /* okay, the process has terminated - we are ready */ - hang = 0; + propagate_term_results (q); + } + + if ( q->exited ) { + if ( !count_active_fds (q->pid) ) { + /* Hmmm, as long as we don't have a callback for + * the exit status, we have no use for these + * values and therefore we can remove this from + * the queue */ + remove_process (q->pid); + hang = 0; + } } } } while (hang); diff --git a/trunk/tests/Makefile.am b/trunk/tests/Makefile.am index eab4df3..aced400 100644 --- a/trunk/tests/Makefile.am +++ b/trunk/tests/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to create Makefile.in -TESTS = t-encrypt +TESTS = t-encrypt t-verify INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/intl diff --git a/trunk/tests/t-encrypt.c b/trunk/tests/t-encrypt.c index 8f6e5c5..d99beff 100644 --- a/trunk/tests/t-encrypt.c +++ b/trunk/tests/t-encrypt.c @@ -31,6 +31,23 @@ exit (1); } \ } while(0) +static void +print_data ( GpgmeData dh ) +{ + char buf[100]; + size_t nread; + GpgmeError err; + + err = gpgme_rewind_data ( dh ); + fail_if_err (err); + while ( !(err = gpgme_read_data ( dh, buf, 100, &nread )) ) { + fwrite ( buf, nread, 1, stdout ); + } + if (err != GPGME_EOF) + fail_if_err (err); +} + + int main (int argc, char **argv ) @@ -40,10 +57,11 @@ main (int argc, char **argv ) GpgmeData in, out; GpgmeRecipientSet rset; + do { err = gpgme_new_context (&ctx); fail_if_err (err); - err = gpgme_new_data ( &in, "Hallo Leute", 11, 0 ); + err = gpgme_new_data ( &in, "Hallo Leute\n", 12, 0 ); fail_if_err (err); err = gpgme_new_data ( &out, NULL, 0,0 ); @@ -53,16 +71,24 @@ main (int argc, char **argv ) fail_if_err (err); err = gpgme_add_recipient (rset, "Bob"); fail_if_err (err); + err = gpgme_add_recipient (rset, "Alpha"); + fail_if_err (err); err = gpgme_encrypt (ctx, rset, in, out ); fail_if_err (err); + fflush (NULL); + fputs ("Begin Result:\n", stdout ); + print_data (out); + fputs ("End Result.\n", stdout ); gpgme_release_recipient_set (rset); gpgme_release_data (in); gpgme_release_data (out); gpgme_release_context (ctx); + } while ( argc > 1 && !strcmp( argv[1], "--loop" ) ); + return 0; } diff --git a/trunk/tests/t-verify.c b/trunk/tests/t-verify.c new file mode 100644 index 0000000..3a948af --- /dev/null +++ b/trunk/tests/t-verify.c @@ -0,0 +1,85 @@ +/* t-verify.c - regression test + * 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/gpgme.h" + +static const char test_text1[] = "Just GNU it!\n"; +static const char test_text1f[]= "Just GNU it?\n"; +static const char test_sig1[] = +"-----BEGIN PGP SIGNATURE-----\n" +"\n" +"iEYEABECAAYFAjoKgjIACgkQLXJ8x2hpdzQMSwCeO/xUrhysZ7zJKPf/FyXA//u1\n" +"ZgIAn0204PBR7yxSdQx6CFxugstNqmRv\n" +"=yku6\n" +"-----END PGP SIGNATURE-----\n" +; + + +#define fail_if_err(a) do { if(a) { \ + fprintf (stderr, "%s:%d: GpgmeError %s\n", \ + __FILE__, __LINE__, gpgme_strerror(a)); \ + exit (1); } \ + } while(0) + + +int +main (int argc, char **argv ) +{ + GpgmeCtx ctx; + GpgmeError err; + GpgmeData sig, text; + + err = gpgme_new_context (&ctx); + fail_if_err (err); + + do { + err = gpgme_new_data ( &text, test_text1, strlen (test_text1), 0 ); + fail_if_err (err); + err = gpgme_new_data ( &sig, test_sig1, strlen (test_sig1), 0 ); + fail_if_err (err); + + puts ("checking a valid message:\n"); + err = gpgme_verify (ctx, sig, text ); + fail_if_err (err); + + puts ("checking a manipulated message:\n"); + gpgme_release_data (text); + err = gpgme_new_data ( &text, test_text1f, strlen (test_text1f), 0 ); + fail_if_err (err); + gpgme_rewind_data ( sig ); + err = gpgme_verify (ctx, sig, text ); + fail_if_err (err); + + gpgme_release_data (sig); + gpgme_release_data (text); + +} while ( argc > 1 && !strcmp( argv[1], "--loop" ) ); + gpgme_release_context (ctx); + + return 0; +} + + + -- 2.26.2