From cd8ac507e14853e3c6258665688c4aafd7441fcb Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 7 Nov 2000 13:32:38 +0000 Subject: [PATCH] Just a backup for now --- trunk/Makefile.am | 2 +- trunk/configure.in | 8 + trunk/gpgme/Makefile.am | 19 +- trunk/gpgme/context.h | 79 +++++ trunk/gpgme/data.c | 139 +++++++++ trunk/gpgme/encrypt.c | 142 +++++++++ trunk/gpgme/gpgme.c | 70 ++++- trunk/gpgme/gpgme.h | 76 ++++- trunk/gpgme/mkerrors | 83 ++++++ trunk/gpgme/mkstatus | 55 ++++ trunk/gpgme/ops.h | 45 +++ trunk/gpgme/recipient.c | 99 +++++++ trunk/gpgme/rungpg.c | 622 ++++++++++++++++++++++++++++++++++++++++ trunk/gpgme/rungpg.h | 103 +++++++ trunk/gpgme/types.h | 52 ++++ trunk/gpgme/util.c | 63 ++++ trunk/gpgme/util.h | 49 ++++ trunk/gpgme/wait.c | 282 ++++++++++++++++++ trunk/gpgme/wait.h | 42 +++ trunk/tests/Makefile.am | 12 + trunk/tests/t-encrypt.c | 69 +++++ 21 files changed, 2099 insertions(+), 12 deletions(-) create mode 100644 trunk/gpgme/context.h create mode 100644 trunk/gpgme/data.c create mode 100644 trunk/gpgme/encrypt.c create mode 100755 trunk/gpgme/mkerrors create mode 100755 trunk/gpgme/mkstatus create mode 100644 trunk/gpgme/ops.h create mode 100644 trunk/gpgme/recipient.c create mode 100644 trunk/gpgme/rungpg.c create mode 100644 trunk/gpgme/rungpg.h create mode 100644 trunk/gpgme/types.h create mode 100644 trunk/gpgme/util.c create mode 100644 trunk/gpgme/util.h create mode 100644 trunk/gpgme/wait.c create mode 100644 trunk/gpgme/wait.h create mode 100644 trunk/tests/Makefile.am create mode 100644 trunk/tests/t-encrypt.c diff --git a/trunk/Makefile.am b/trunk/Makefile.am index 96b22bd..1b9784b 100644 --- a/trunk/Makefile.am +++ b/trunk/Makefile.am @@ -1,6 +1,6 @@ EXTRA_DIST = README-alpha -SUBDIRS = gpgme +SUBDIRS = gpgme tests diff --git a/trunk/configure.in b/trunk/configure.in index 3e27c71..9adf05b 100644 --- a/trunk/configure.in +++ b/trunk/configure.in @@ -77,5 +77,13 @@ AC_OUTPUT([ Makefile gpgme/Makefile gpgme/gpgme-config +tests/Makefile ]) + + + + + + + diff --git a/trunk/gpgme/Makefile.am b/trunk/gpgme/Makefile.am index ba044b6..87de1de 100644 --- a/trunk/gpgme/Makefile.am +++ b/trunk/gpgme/Makefile.am @@ -1,6 +1,7 @@ # Process this file with automake to produce Makefile.in -EXTRA_DIST = gpgme-config.in gpgme.m4 +EXTRA_DIST = gpgme-config.in gpgme.m4 mkerrors mkstatus +BUILT_SOURCES = errors.c status-table.h bin_SCRIPTS = gpgme-config m4datadir = $(datadir)/aclocal m4data_DATA = gpgme.m4 @@ -12,11 +13,21 @@ libgpgme_la_LDFLAGS = -version-info \ libgpgme_la_INCLUDES = -I$(top_srcdir)/lib libgpgme_la_SOURCES = \ - gpgme.h \ - gpgme-types.h gpgme-memory.h \ - gpgme.c + gpgme.h types.h util.h util.c \ + context.h ops.h \ + data.c recipient.c \ + wait.c wait.h \ + encrypt.c \ + rungpg.c rungpg.h status-table.h \ + gpgme.c errors.c +errors.c : gpgme.h + $(srcdir)/mkerrors < $(srcdir)/gpgme.h > errors.c + +status-table.h : rungpg.h + $(srcdir)/mkstatus < $(srcdir)/rungpg.h > status-table.h + diff --git a/trunk/gpgme/context.h b/trunk/gpgme/context.h new file mode 100644 index 0000000..504ec89 --- /dev/null +++ b/trunk/gpgme/context.h @@ -0,0 +1,79 @@ +/* context.h + * 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 CONTEXT_H +#define CONTEXT_H + +#include "gpgme.h" +#include "types.h" +#include "rungpg.h" /* for GpgObject */ + +/* 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 */ + + GpgObject gpg; /* the running gpg process */ + + int verbosity; /* level of verbosity to use */ + int use_armor; /* use armoring */ +}; + + +struct gpgme_data_s { + size_t len; + const char *data; + GpgmeDataType type; + GpgmeDataMode mode; + size_t readpos; + char *private_buffer; +}; + +struct recipient_s { + struct recipient_s *next; + char name[1]; +}; + +struct gpgme_recipient_set_s { + struct recipient_s *list; + int checked; /* wether the recipients are all valid */ +}; + + +#define fail_on_pending_request(c) \ + do { \ + if (!(c)) return GPGME_Invalid_Value; \ + if ((c)->pending) return GPGME_Busy; \ + } while (0) + +#define wait_on_request_or_fail(c) \ + do { \ + if (!(c)) return GPGME_Invalid_Value;\ + if (!(c)->pending) return GPGME_No_Request; \ + gpgme_wait ((c), 1); \ + } while (0) + + + +#endif /* CONTEXT_H */ + + + diff --git a/trunk/gpgme/data.c b/trunk/gpgme/data.c new file mode 100644 index 0000000..253e101 --- /dev/null +++ b/trunk/gpgme/data.c @@ -0,0 +1,139 @@ +/* data.c + * 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 "util.h" +#include "context.h" + + +/** + * gpgme_new_data: + * @r_dh: Returns a new data object. + * @buffer: If not NULL, used to initialize the data object. + * @size: Size of the buffer + * @copy: Flag wether a copy of the buffer should be used. + * + * Create a new data object and optionally initialize with data + * from the memory. A @copy with value %TRUE creates a copy of the + * memory, a value of %FALSE uses the original memory of @buffer and the + * caller has to make sure that this buffer is valid until gpgme_release_data() + * is called. + * + * Return value: + **/ +GpgmeError +gpgme_new_data ( GpgmeData *r_dh, const char *buffer, size_t size, int copy ) +{ + GpgmeData dh; + + *r_dh = NULL; + dh = xtrycalloc ( 1, sizeof *dh ); + if (!dh) + return mk_error (Out_Of_Core); + if ( buffer ) { + dh->len = size; + if (copy) { + dh->private_buffer = xtrymalloc ( size ); + if ( !dh->private_buffer ) { + xfree (dh); + return mk_error (Out_Of_Core); + } + memcpy (dh->private_buffer, buffer, size ); + dh->data = dh->private_buffer; + } + else { + dh->data = buffer; + } + dh->type = GPGME_DATA_TYPE_MEM; + } + dh->mode = GPGME_DATA_MODE_INOUT; + *r_dh = dh; + return 0; +} + +/** + * gpgme_release_data: + * @dh: Data object + * + * Release the data object @dh. @dh may be NULL in which case nothing + * happens. + **/ +void +gpgme_release_data ( GpgmeData dh ) +{ + if (dh) { + xfree (dh->private_buffer); + xfree (dh); + } +} + + +GpgmeDataType +gpgme_query_data_type ( GpgmeData dh ) +{ + if ( !dh || !dh->data ) + return GPGME_DATA_TYPE_NONE; + + return dh->type; +} + +void +_gpgme_set_data_mode ( GpgmeData dh, GpgmeDataMode mode ) +{ + assert (dh); + dh->mode = mode; +} + + +GpgmeDataMode +_gpgme_query_data_mode ( GpgmeData dh ) +{ + assert (dh); + return dh->mode; +} + + + +GpgmeError +_gpgme_append_data ( GpgmeData dh, const char *buf, size_t length ) +{ + assert (dh); + + if ( dh->type == GPGME_DATA_TYPE_NONE ) { + /* convert it to a mem data type */ + } + 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); + + + return 0; +} + + + diff --git a/trunk/gpgme/encrypt.c b/trunk/gpgme/encrypt.c new file mode 100644 index 0000000..eaf5742 --- /dev/null +++ b/trunk/gpgme/encrypt.c @@ -0,0 +1,142 @@ +/* encrypt.c - encrypt functions + * 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" + +static void +encrypt_status_handler ( GpgmeCtx ctx, GpgStatusCode code, char *args ) +{ + fprintf (stderr, "encrypt_status: code=%d args=`%s'\n", + code, args ); + +} + + + +GpgmeError +gpgme_req_encrypt ( GpgmeCtx c, GpgmeRecipientSet recp, + GpgmeData plain, GpgmeData ciph ) +{ + int rc = 0; + int i; + + fail_on_pending_request( c ); + c->pending = 1; + + /* do some checks */ + assert ( !c->gpg ); + if ( !gpgme_count_recipients ( recp ) ) { + /* Fixme: In this case we should do symmentric encryption */ + rc = mk_error (No_Recipients); + goto leave; + } + + /* create a process object */ + rc = _gpgme_gpg_new_object ( &c->gpg ); + if (rc) + goto leave; + + _gpgme_gpg_set_status_handler ( c->gpg, encrypt_status_handler, c ); + + /* build the commandline */ + _gpgme_gpg_add_arg ( c->gpg, "--encrypt" ); + if ( c->use_armor ) + _gpgme_gpg_add_arg ( c->gpg, "--armor" ); + for ( i=0; i < c->verbosity; i++ ) + _gpgme_gpg_add_arg ( c->gpg, "--verbose" ); + + _gpgme_append_gpg_args_from_recipients ( recp, c->gpg ); + + /* Check the supplied data */ + if ( gpgme_query_data_type (plain) == GPGME_DATA_TYPE_NONE ) { + rc = mk_error (No_Data); + goto leave; + } + _gpgme_set_data_mode (plain, GPGME_DATA_MODE_OUT ); + if ( !ciph || gpgme_query_data_type (ciph) != GPGME_DATA_TYPE_NONE ) { + rc = mk_error (Invalid_Value); + goto leave; + } + _gpgme_set_data_mode (ciph, GPGME_DATA_MODE_IN ); + /* Tell the gpg object about the data */ + _gpgme_gpg_add_arg ( c->gpg, "--output" ); + _gpgme_gpg_add_arg ( c->gpg, "-" ); + _gpgme_gpg_add_data ( c->gpg, ciph, 1 ); + + _gpgme_gpg_add_data ( c->gpg, plain, 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_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 + * @recp: A set of recipients + * @in: plaintext input + * @out: ciphertext output + * + * This function encrypts @in to @out for all recipients from + * @recp. Other parameters are take from the context @c. + * The function does wait for the result. + * + * Return value: 0 on success or an errorcode. + **/ +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 ); + return rc; +} + + + + diff --git a/trunk/gpgme/gpgme.c b/trunk/gpgme/gpgme.c index bb9165f..fd55c4b 100644 --- a/trunk/gpgme/gpgme.c +++ b/trunk/gpgme/gpgme.c @@ -22,17 +22,75 @@ #include #include -#include "gpgme-memory.h" -#include "gpgme-types.h" +#include "util.h" +#include "context.h" -GPGME -gpgme_new_context () +/** + * gpgme_new_context: + * @r_ctx: Returns the new context + * + * Create a new context to be used with most of the other GPGME + * functions. Use gpgme_release_contect() to release all resources + * + * Return value: An error code + **/ +GpgmeError +gpgme_new_context (GpgmeCtx *r_ctx) { - GPGME *c; + GpgmeCtx c; - c = gpgme_xmalloc + c = xtrycalloc ( 1, sizeof *c ); + if (!c) + return mk_error (Out_Of_Core); + c->verbosity = 1; + c->use_armor = 1; + *r_ctx = c; + return 0; +} +/** + * gpgme_release_contect: + * @c: Context to be released. + * + * Release all resources associated with the given context. + **/ +void +gpgme_release_context ( GpgmeCtx c ) +{ + xfree ( c ); } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/trunk/gpgme/gpgme.h b/trunk/gpgme/gpgme.h index eaabd4e..f5ede58 100644 --- a/trunk/gpgme/gpgme.h +++ b/trunk/gpgme/gpgme.h @@ -28,9 +28,81 @@ extern "C" { #endif struct gpgme_context_s; -typedef struct gpgme_context_s *GPGME; +typedef struct gpgme_context_s *GpgmeCtx; +struct gpgme_data_s; +typedef struct gpgme_data_s *GpgmeData; +struct gpgme_recipient_set_s; +typedef struct gpgme_recipient_set_s *GpgmeRecipientSet; + + +typedef enum { + GPGME_No_Error = 0, + GPGME_General_Error = 1, + GPGME_Out_Of_Core = 2, + GPGME_Invalid_Value = 3, + GPGME_Busy = 4, + GPGME_No_Request = 5, + GPGME_Exec_Error = 6, + GPGME_Too_Many_Procs = 7, + GPGME_Pipe_Error = 8, + GPGME_No_Recipients = 9, + GPGME_No_Data = 10, + GPGME_Conflict = 11, + GPGME_Not_Implemented = 12, + GPGME_Read_Error = 13, + GPGME_Write_Error = 14, + GPGME_Invalid_Type = 15, + GPGME_Invalid_Mode = 16, +} GpgmeError; + +typedef enum { + GPGME_DATA_TYPE_NONE = 0, + GPGME_DATA_TYPE_MEM = 1, + GPGME_DATA_TYPE_FD = 2, + GPGME_DATA_TYPE_FILE = 3 +} GpgmeDataType; + + +/* Context management */ +GpgmeError gpgme_new_context (GpgmeCtx *r_ctx); +void gpgme_release_context ( GpgmeCtx c ); +GpgmeCtx gpgme_wait ( GpgmeCtx c, int hang ); + +/* Functions to handle recipients */ +GpgmeError gpgme_new_recipient_set (GpgmeRecipientSet *r_rset); +void gpgme_release_recipient_set ( GpgmeRecipientSet rset); +GpgmeError gpgme_add_recipient (GpgmeRecipientSet rset, const char *name); +unsigned int gpgme_count_recipients ( const GpgmeRecipientSet rset ); + +/* Functions to handle data sources */ +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 ); + + +/* Basic GnuPG functions */ +GpgmeError gpgme_req_encrypt ( GpgmeCtx c, GpgmeRecipientSet recp, + GpgmeData in, GpgmeData out ); +GpgmeError gpgme_wait_encrypt ( GpgmeCtx c, GpgmeData *r_out ); + + +/* Key management functions */ + + + + + + +/* Convenience functions for syncronous usage */ +GpgmeError gpgme_encrypt ( GpgmeCtx c, GpgmeRecipientSet recp, + GpgmeData in, GpgmeData out ); + + +/* miscellaneous functions */ +const char *gpgme_strerror (GpgmeError err); #ifdef __cplusplus @@ -42,3 +114,5 @@ typedef struct gpgme_context_s *GPGME; + + diff --git a/trunk/gpgme/mkerrors b/trunk/gpgme/mkerrors new file mode 100755 index 0000000..4ad5812 --- /dev/null +++ b/trunk/gpgme/mkerrors @@ -0,0 +1,83 @@ +#!/bin/sh +# mkerrors - Extract error strings from gpgme.h +# and create C source for gpgme_strerror +# 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 + +cat < +#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) { +EOF + +awk ' +/GPGME_No_Error/ { okay=1 } +!okay {next} +/}/ { exit 0 } +/GPGME_[A-Za-z_]*/ { print_code($1) } + + +function print_code( s ) +{ +printf " case %s: s=\"", s ; +gsub(/_/, " ", s ); +printf "%s\"; break;\n", substr(s,7); +} +' + +cat < +#include +#include +#include + +#include "util.h" +#include "context.h" +#include "rungpg.h" + +GpgmeError +gpgme_new_recipient_set (GpgmeRecipientSet *r_rset) +{ + GpgmeRecipientSet rset; + + rset = xtrycalloc ( 1, sizeof *rset ); + if (!rset) + return mk_error (Out_Of_Core); + *r_rset = rset; + return 0; +} + +void +gpgme_release_recipient_set ( GpgmeRecipientSet rset ) +{ + /* fixme: release the linked list */ + xfree ( rset ); +} + + +GpgmeError +gpgme_add_recipient (GpgmeRecipientSet rset, const char *name ) +{ + struct recipient_s *r; + + if (!name || !rset ) + return mk_error (Invalid_Value); + r = xtrymalloc ( sizeof *r + strlen (name) ); + if (!r) + return mk_error (Out_Of_Core); + strcpy (r->name, name ); + r->next = rset->list; + rset->list = r; + return 0; +} + +unsigned int +gpgme_count_recipients ( const GpgmeRecipientSet rset ) +{ + struct recipient_s *r; + unsigned int count = 0; + + if ( rset ) { + for (r=rset->list ; r; r = r->next ) + count++; + } + return count; +} + + +void +_gpgme_append_gpg_args_from_recipients ( + const GpgmeRecipientSet rset, + GpgObject gpg ) +{ + struct recipient_s *r; + + assert (rset); + for (r=rset->list ; r; r = r->next ) { + _gpgme_gpg_add_arg ( gpg, "-r" ); + _gpgme_gpg_add_arg ( gpg, r->name ); + } +} + + + + + + + diff --git a/trunk/gpgme/rungpg.c b/trunk/gpgme/rungpg.c new file mode 100644 index 0000000..6353ad2 --- /dev/null +++ b/trunk/gpgme/rungpg.c @@ -0,0 +1,622 @@ +/* rungpg.c + * 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 +#include +#include + +#include "gpgme.h" +#include "util.h" +#include "ops.h" +#include "wait.h" +#include "rungpg.h" +#include "context.h" /*temp hack until we have GpmeData methods to do I/O */ + +#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; + char arg[1]; /* .. this is used */ +}; + +struct fd_data_map_s { + GpgmeData data; + int inbound; /* true if this is used for reading from gpg */ + int dup_to; + int fd; /* the fd to use */ + int peer_fd; /* the outher side of the pipe */ +}; + + +struct gpg_object_s { + struct arg_and_data_s *arglist; + struct arg_and_data_s **argtail; + int arg_error; + + struct { + int fd[2]; + size_t bufsize; + char *buffer; + size_t readpos; + int eof; + GpgStatusHandler fnc; + void *fnc_value; + } status; + + char **argv; + struct fd_data_map_s *fd_data_map; + + pid_t pid; + + int running; + int exit_status; + int exit_signal; +}; + +static void kill_gpg ( GpgObject gpg ); +static void free_argv ( char **argv ); +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 ); + +static GpgmeError read_status ( GpgObject gpg ); + + + +GpgmeError +_gpgme_gpg_new_object ( GpgObject *r_gpg ) +{ + GpgObject gpg; + char buf[20]; + int rc = 0; + + gpg = xtrycalloc ( 1, sizeof *gpg ); + if ( !gpg ) { + rc = mk_error (Out_Of_Core); + goto leave; + } + gpg->argtail = &gpg->arglist; + + gpg->status.fd[0] = -1; + gpg->status.fd[1] = -1; + + /* 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; + gpg->status.buffer = xtrymalloc (gpg->status.bufsize); + if (!gpg->status.buffer) { + rc = mk_error (Out_Of_Core); + goto leave; + } + /* In any case we need a status pipe - create it right here and + * don't handle it with our generic GpgmeData mechanism */ + if (pipe (gpg->status.fd) == -1) { + rc = mk_error (Pipe_Error); + goto leave; + } + gpg->status.eof = 0; + _gpgme_gpg_add_arg ( gpg, "--status-fd" ); + sprintf ( buf, "%d", gpg->status.fd[1]); + _gpgme_gpg_add_arg ( gpg, buf ); + + leave: + if (rc) { + _gpgme_gpg_release_object (gpg); + *r_gpg = NULL; + } + else + *r_gpg = gpg; + return rc; +} + +void +_gpgme_gpg_release_object ( GpgObject gpg ) +{ + if ( !gpg ) + return; + xfree (gpg->status.buffer); + if ( gpg->argv ) + free_argv (gpg->argv); + if (gpg->status.fd[0] != -1 ) + 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); + kill_gpg (gpg); /* fixme: should be done asyncronously */ + xfree (gpg); +} + +static void +kill_gpg ( GpgObject gpg ) +{ + if ( gpg->running ) { + /* still running? Must send a killer */ + kill ( gpg->pid, SIGTERM); + sleep (2); + if ( !waitpid (gpg->pid, NULL, WNOHANG) ) { + /* pay the murderer better and then forget about it */ + kill (gpg->pid, SIGKILL); + } + gpg->running = 0; + } +} + + +GpgmeError +_gpgme_gpg_add_arg ( GpgObject gpg, const char *arg ) +{ + struct arg_and_data_s *a; + + assert (gpg); + assert (arg); + a = xtrymalloc ( sizeof *a + strlen (arg) ); + if ( !a ) { + gpg->arg_error = 1; + return mk_error(Out_Of_Core); + } + a->next = NULL; + a->data = NULL; + a->dup_to = -1; + strcpy ( a->arg, arg ); + *gpg->argtail = a; + gpg->argtail = &a->next; + return 0; +} + +GpgmeError +_gpgme_gpg_add_data ( GpgObject gpg, GpgmeData data, int dup_to ) +{ + struct arg_and_data_s *a; + + assert (gpg); + assert (data); + a = xtrymalloc ( sizeof *a - 1 ); + if ( !a ) { + gpg->arg_error = 1; + return mk_error(Out_Of_Core); + } + a->next = NULL; + a->data = data; + a->dup_to = dup_to; + *gpg->argtail = a; + gpg->argtail = &a->next; + return 0; +} + +/* + * Note, that the status_handler is allowed to modifiy the args value + */ +void +_gpgme_gpg_set_status_handler ( GpgObject gpg, + GpgStatusHandler fnc, void *fnc_value ) +{ + assert (gpg); + gpg->status.fnc = fnc; + gpg->status.fnc_value = fnc_value; +} + +static void +free_argv ( char **argv ) +{ + int i; + + for (i=0; argv[i]; i++ ) + xfree (argv[i]); + xfree (argv); +} + + +static GpgmeError +build_argv ( GpgObject gpg ) +{ + struct arg_and_data_s *a; + struct fd_data_map_s *fd_data_map; + size_t datac=0, argc=0; + char **argv; + + if ( gpg->argv ) { + free_argv ( gpg->argv ); + gpg->argv = NULL; + } + /* fixme: release fd_data_map */ + + for ( a=gpg->arglist; a; a = a->next ) { + argc++; + if (a->data) { + fprintf (stderr, "build_argv: data\n" ); + datac++; + } + else + fprintf (stderr, "build_argv: arg=`%s'\n", a->arg ); + } + + argv = xtrycalloc ( argc+1, sizeof *argv ); + if (!argv) + return mk_error (Out_Of_Core); + fd_data_map = xtrycalloc ( datac+1, sizeof *fd_data_map ); + if (!fd_data_map) { + free_argv (argv); + return mk_error (Out_Of_Core); + } + + argc = datac = 0; + for ( a=gpg->arglist; a; a = a->next ) { + if ( a->data ) { + switch ( _gpgme_query_data_mode (a->data) ) { + case GPGME_DATA_MODE_NONE: + case GPGME_DATA_MODE_INOUT: + xfree (fd_data_map); + free_argv (argv); + return mk_error (Invalid_Mode); + case GPGME_DATA_MODE_IN: + /* create a pipe to read from gpg */ + fd_data_map[datac].inbound = 1; + break; + case GPGME_DATA_MODE_OUT: + /* create a pipe to pass it down to gpg */ + fd_data_map[datac].inbound = 0; + break; + } + + switch ( gpgme_query_data_type (a->data) ) { + case GPGME_DATA_TYPE_NONE: + if ( fd_data_map[datac].inbound ) + break; /* allowed */ + xfree (fd_data_map); + free_argv (argv); + return mk_error (Invalid_Type); + case GPGME_DATA_TYPE_MEM: + break; + case GPGME_DATA_TYPE_FD: + case GPGME_DATA_TYPE_FILE: + xfree (fd_data_map); + free_argv (argv); + return mk_error (Not_Implemented); + } + + /* create a pipe */ + { + int fds[2]; + + if (pipe (fds) == -1) { + xfree (fd_data_map); + free_argv (argv); + return mk_error (Pipe_Error); + } + /* if the data_type is FD, we have to do a dup2 here */ + if (fd_data_map[datac].inbound) { + fd_data_map[datac].fd = fds[0]; + fd_data_map[datac].peer_fd = fds[1]; + } + else { + fd_data_map[datac].fd = fds[1]; + fd_data_map[datac].peer_fd = fds[0]; + } + } + fd_data_map[datac].data = a->data; + fd_data_map[datac].dup_to = a->dup_to; + datac++; + } + else { + argv[argc] = xtrystrdup ( a->arg ); + if (!argv[argc]) { + xfree (fd_data_map); + free_argv (argv); + return mk_error (Out_Of_Core); + } + } + argc++; + } + + gpg->argv = argv; + gpg->fd_data_map = fd_data_map; + return 0; +} + +GpgmeError +_gpgme_gpg_spawn( GpgObject gpg, void *opaque ) +{ + int rc; + int i; + pid_t pid; + + if ( !gpg ) + return mk_error (Invalid_Value); + + /* Kludge, so that we don't need to check the return code of + * all the gpgme_gpg_add_arg(). we bail out here instead */ + if ( gpg->arg_error ) + return mk_error (Out_Of_Core); + + rc = build_argv ( gpg ); + if ( rc ) + return rc; + + fflush (stderr); + pid = fork (); + if (pid == -1) { + return mk_error (Exec_Error); + } + + if ( !pid ) { /* child */ + for (i=0; gpg->fd_data_map[i].data; i++ ) { + close (gpg->fd_data_map[i].fd); + gpg->fd_data_map[i].fd = -1; + if ( gpg->fd_data_map[i].dup_to != -1 ) { + if ( dup2 (gpg->fd_data_map[i].peer_fd, + gpg->fd_data_map[i].dup_to ) == -1 ) { + fprintf (stderr, "dup2 failed in child: %s\n", + strerror (errno)); + _exit (8); + } + 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); + _exit (8); + } + gpg->pid = pid; + + if ( gpg->status.fd[1] != -1 ) + close (gpg->status.fd[1]); + if ( _gpgme_register_pipe_handler ( opaque, gpg_status_handler, + gpg, pid, gpg->status.fd[0], 1 ) ) { + /* FIXME: kill the child */ + return mk_error (General_Error); + + } + for (i=0; gpg->fd_data_map[i].data; i++ ) { + close (gpg->fd_data_map[i].peer_fd); + gpg->fd_data_map[i].peer_fd = -1; + if ( _gpgme_register_pipe_handler ( + opaque, + gpg->fd_data_map[i].inbound? + gpg_inbound_handler:gpg_outbound_handler, + gpg->fd_data_map[i].data, + pid, gpg->fd_data_map[i].fd, + gpg->fd_data_map[i].inbound ) + ) { + /* FIXME: kill the child */ + return mk_error (General_Error); + } + } + + /* fixme: check what data we can release here */ + + gpg->running = 1; + return 0; + +} + + +static int +gpg_inbound_handler ( void *opaque, pid_t pid, int fd ) +{ + GpgmeData dh = opaque; + int nread; + char buf[200]; + + assert ( _gpgme_query_data_mode (dh) == GPGME_DATA_MODE_IN ); + + do { + nread = read (fd, buf, 200 ); + } while ( nread == -1 && errno == EINTR); + 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) ); + return 1; + } + else if (!nread) + return 1; /* eof */ + + + return 0; +} + + +static int +write_mem_data ( GpgmeData dh, int fd ) +{ + size_t nbytes; + int nwritten; + + nbytes = dh->len - dh->readpos; + if ( !nbytes ) { + close (fd); + return 1; + } + + do { + nwritten = write ( fd, dh->data+dh->readpos, nbytes ); + } while ( nwritten == -1 && errno == EINTR ); + if ( nwritten < 1 ) { + fprintf (stderr, "write_mem_data: write failed on fd %d (n=%d): %s\n", + fd, nwritten, strerror (errno) ); + close (fd); + return 1; + } + + dh->readpos += nwritten; + return 0; +} + + +static int +gpg_outbound_handler ( void *opaque, pid_t pid, int fd ) +{ + GpgmeData dh = opaque; + + assert ( _gpgme_query_data_mode (dh) == GPGME_DATA_MODE_OUT ); + switch ( gpgme_query_data_type (dh) ) { + case GPGME_DATA_TYPE_MEM: + if ( write_mem_data ( dh, fd ) ) + return 1; /* ready */ + break; + default: + assert (0); + } + + + return 0; +} + + + +static int +gpg_status_handler ( void *opaque, pid_t pid, int fd ) +{ + GpgObject gpg = opaque; + int rc = 0; + + assert ( fd == gpg->status.fd[0] ); + rc = read_status ( gpg ); + if ( rc ) { + fprintf (stderr, "gpg_handler: read_status problem %d\n - stop", rc); + return 1; + } + + return gpg->status.eof; +} + + +static int +status_cmp (const struct status_table_s *a, const struct status_table_s *b) +{ + return strcmp (a->name, b->name); +} + + + +/* + * Handle the status output of GnuPG. This function does read entire + * lines and passes them as C strings to the callback function (we can + * use C Strings because the status output is always UTF-8 encoded). + * Of course we have to buffer the lines to cope with long lines + * e.g. with a large user ID. Note: We can optimize this to only cope + * with status line code we know about and skip all other stuff + * without buffering (i.e. without extending the buffer). */ +static GpgmeError +read_status ( GpgObject gpg ) +{ + char *p; + int nread; + size_t bufsize = gpg->status.bufsize; + char *buffer = gpg->status.buffer; + size_t readpos = gpg->status.readpos; + + assert (buffer); + if (bufsize - readpos < 256) { + /* need more room for the read */ + bufsize += 1024; + buffer = xtryrealloc (buffer, bufsize); + if ( !buffer ) + return mk_error (Out_Of_Core); + } + + + do { + nread = read ( gpg->status.fd[0], buffer+readpos, bufsize-readpos ); + } while (nread == -1 && errno == EINTR); + + if (nread == -1) + return mk_error(Read_Error); + + if (!nread) { + gpg->status.eof = 1; + if (gpg->status.fnc) + gpg->status.fnc ( gpg->status.fnc_value, STATUS_EOF, "" ); + return 0; + } + + while (nread > 0) { + for (p = buffer + readpos; nread; nread--, p++) { + if ( *p == '\n' ) { + /* (we require that the last line is terminated by a LF) */ + *p = 0; + fprintf (stderr, "read_status: `%s'\n", buffer); + if (!strncmp (buffer, "[GNUPG:] ", 9 ) + && buffer[9] >= 'A' && buffer[9] <= 'Z' + && gpg->status.fnc ) { + struct status_table_s t, *r; + char *rest; + + rest = strchr (buffer+9, ' '); + if ( !rest ) + rest = p; /* set to an empty string */ + else + *rest++ = 0; + + t.name = buffer+9; + /* (the status table as one extra element) */ + r = bsearch ( &t, status_table, DIM(status_table)-1, + sizeof t, status_cmp ); + if ( r ) { + gpg->status.fnc ( gpg->status.fnc_value, + r->code, rest); + } + } + /* To reuse the buffer for the next line we have to + * shift the remaining data to the buffer start and + * restart the loop Hmmm: We can optimize this + * function by looking forward in the buffer to see + * whether a second complete line is available and in + * this case avoid the memmove for this line. */ + nread--; p++; + if (nread) + memmove (buffer, p, nread); + readpos = 0; + break; /* the for loop */ + } + else + readpos++; + } + } + + /* Update the gpg object. */ + gpg->status.bufsize = bufsize; + gpg->status.buffer = buffer; + gpg->status.readpos = readpos; + return 0; +} + + diff --git a/trunk/gpgme/rungpg.h b/trunk/gpgme/rungpg.h new file mode 100644 index 0000000..5a27ce9 --- /dev/null +++ b/trunk/gpgme/rungpg.h @@ -0,0 +1,103 @@ +/* rungpg.h - gpg calling functions + * 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 RUNGPG_H +#define RUNGPG_H + +#include "types.h" + + +typedef enum { + STATUS_EOF , + /* mkstatus starts here */ + STATUS_ENTER , + STATUS_LEAVE , + STATUS_ABORT , + STATUS_GOODSIG , + STATUS_BADSIG , + STATUS_ERRSIG , + STATUS_BADARMOR , + STATUS_RSA_OR_IDEA , + STATUS_SIGEXPIRED , + STATUS_KEYREVOKED , + STATUS_TRUST_UNDEFINED , + STATUS_TRUST_NEVER , + STATUS_TRUST_MARGINAL , + STATUS_TRUST_FULLY , + STATUS_TRUST_ULTIMATE , + STATUS_SHM_INFO , + STATUS_SHM_GET , + STATUS_SHM_GET_BOOL , + STATUS_SHM_GET_HIDDEN , + STATUS_NEED_PASSPHRASE , + STATUS_VALIDSIG , + STATUS_SIG_ID , + STATUS_ENC_TO , + STATUS_NODATA , + STATUS_BAD_PASSPHRASE , + STATUS_NO_PUBKEY , + STATUS_NO_SECKEY , + STATUS_NEED_PASSPHRASE_SYM, + STATUS_DECRYPTION_FAILED , + STATUS_DECRYPTION_OKAY , + STATUS_MISSING_PASSPHRASE , + STATUS_GOOD_PASSPHRASE , + STATUS_GOODMDC , + STATUS_BADMDC , + STATUS_ERRMDC , + STATUS_IMPORTED , + STATUS_IMPORT_RES , + STATUS_FILE_START , + STATUS_FILE_DONE , + STATUS_FILE_ERROR , + STATUS_BEGIN_DECRYPTION , + STATUS_END_DECRYPTION , + STATUS_BEGIN_ENCRYPTION , + STATUS_END_ENCRYPTION , + STATUS_DELETE_PROBLEM , + STATUS_GET_BOOL , + STATUS_GET_LINE , + STATUS_GET_HIDDEN , + STATUS_GOT_IT , + STATUS_PROGRESS , + STATUS_SIG_CREATED , + STATUS_SESSION_KEY +} GpgStatusCode; + +typedef void (*GpgStatusHandler)( GpgmeCtx, GpgStatusCode code, char *args ); + +GpgmeError _gpgme_gpg_new_object ( GpgObject *r_gpg ); +void _gpgme_gpg_release_object ( GpgObject gpg ); +GpgmeError _gpgme_gpg_add_arg ( GpgObject gpg, const char *arg ); +GpgmeError _gpgme_gpg_add_data ( GpgObject gpg, GpgmeData data, int dup_to ); +void _gpgme_gpg_set_status_handler ( GpgObject gpg, + GpgStatusHandler fnc, + void *fnc_value ); +GpgmeError _gpgme_gpg_spawn ( GpgObject gpg, void *opaque ); + + + +#endif /* RUNGPG_H */ + + + + + + diff --git a/trunk/gpgme/types.h b/trunk/gpgme/types.h new file mode 100644 index 0000000..c8b2302 --- /dev/null +++ b/trunk/gpgme/types.h @@ -0,0 +1,52 @@ +/* types.h - Some type definitions + * 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 TYPES_H +#define TYPES_H + +#include "gpgme.h" /* external objects and prototypes */ + +typedef unsigned char byte; + + +typedef enum { + GPGME_DATA_MODE_NONE = 0, + GPGME_DATA_MODE_IN = 1, + GPGME_DATA_MODE_OUT = 2, + GPGME_DATA_MODE_INOUT = 3 +} GpgmeDataMode; + + +/* + * Declaration of internal objects + */ + +/*-- rungpg.c --*/ +struct gpg_object_s; +typedef struct gpg_object_s *GpgObject; + + + +#endif /* TYPES_H */ + + + + + diff --git a/trunk/gpgme/util.c b/trunk/gpgme/util.c new file mode 100644 index 0000000..2711f6b --- /dev/null +++ b/trunk/gpgme/util.c @@ -0,0 +1,63 @@ +/* util.c + * 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" + +void * +_gpgme_malloc (size_t n ) +{ + return malloc (n); +} + +void * +_gpgme_calloc (size_t n, size_t m ) +{ + return calloc (n, m); +} + +void * +_gpgme_realloc (void *p, size_t n) +{ + return realloc (p, n ); +} + + +char * +_gpgme_strdup (const char *p) +{ + return strdup (p); +} + + +void +_gpgme_free ( void *a ) +{ + free (a); +} + + + + diff --git a/trunk/gpgme/util.h b/trunk/gpgme/util.h new file mode 100644 index 0000000..c755773 --- /dev/null +++ b/trunk/gpgme/util.h @@ -0,0 +1,49 @@ +/* util.h + * 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 UTIL_H +#define UTIL_H + +#include "types.h" + +void *_gpgme_malloc (size_t n ); +void *_gpgme_calloc (size_t n, size_t m ); +void *_gpgme_realloc (void *p, size_t n); +char *_gpgme_strdup (const char *p); +void _gpgme_free ( void *a ); + +#define xtrymalloc(a) _gpgme_malloc((a)) +#define xtrycalloc(a,b) _gpgme_calloc((a),(b)) +#define xtryrealloc(a,b) _gpgme_realloc((a),(b)) +#define xtrystrdup(a) _gpgme_strdup((a)) +#define xfree(a) _gpgme_free((a)) + + +#define mk_error(a) ( GPGME_##a ) + +#define DIM(v) (sizeof(v)/sizeof((v)[0])) +#define DIMof(type,member) DIM(((type *)0)->member) + + +#endif /* UTIL_H */ + + + + diff --git a/trunk/gpgme/wait.c b/trunk/gpgme/wait.c new file mode 100644 index 0000000..f8cadc0 --- /dev/null +++ b/trunk/gpgme/wait.c @@ -0,0 +1,282 @@ +/* wait.c + * 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 +#include + +#include "util.h" +#include "context.h" +#include "wait.h" + +/* Fixme: implement the following stuff to make the code MT safe. + * To avoid the need to link against a specific threads lib, such + * an implementation should require the caller to register a function + * which does this task. + * enter_crit() and leave_crit() are used to embrace an area of code + * which should be executed only by one thread at a time. + * lock_xxxx() and unlock_xxxx() protect access to an data object. + * */ +#define enter_crit() do { } while (0) +#define leave_crit() do { } while (0) +#define lock_queue() do { } while (0) +#define unlock_queue() do { } while (0) + +struct wait_queue_item_s { + struct wait_queue_item_s *next; + volatile int used; + volatile int active; + int (*handler)(void*,pid_t,int); + void *handler_value; + pid_t pid; + int fd; + int inbound; /* this is an inbound data handler fd */ + + int exited; + int exit_status; + int exit_signal; + + GpgmeCtx ctx; +}; + + +static struct wait_queue_item_s wait_queue[SIZEOF_WAIT_QUEUE]; + +static int the_big_select ( void ); + + +static void +init_wait_queue (void) +{ + int i; + static int initialized = 0; + + if ( initialized ) /* FIXME: This leads to a race */ + return; + + lock_queue (); + for (i=1; i < SIZEOF_WAIT_QUEUE; i++ ) + wait_queue[i-1].next = &wait_queue[i]; + initialized = 1; + unlock_queue(); +} + +static struct wait_queue_item_s * +queue_item_from_context ( GpgmeCtx ctx ) +{ + struct wait_queue_item_s *q; + + for (q=wait_queue; q; q = q->next) { + if ( q->used && q->ctx == ctx ) + return q; + } + return NULL; +} + + +/** + * gpgme_wait: + * @c: + * @hang: + * + * Wait for a finished request, if @c is given the function does only + * wait on a finsihed request for that context, otherwise it will return + * on any request. When @hang is true the function will wait, otherwise + * it will return immediately when there is no pending finished request. + * + * Return value: Context of the finished request or NULL if @hang is false + * and no (or the given) request has finished. + **/ +GpgmeCtx +gpgme_wait ( GpgmeCtx c, int hang ) +{ + struct wait_queue_item_s *q; + + init_wait_queue (); + do { + if ( !the_big_select() ) { + int status; + + /* We did no read/write - see whether this process is still + * alive */ + assert (c); /* !c is not yet implemented */ + q = queue_item_from_context ( c ); + assert (q); + + if ( waitpid ( q->pid, &status, WNOHANG ) == q->pid ) { + q->exited = 1; + if ( WIFSIGNALED (status) ) { + q->exit_status = 4; /* Need some value here */ + q->exit_signal = WTERMSIG (status); + } + else if ( WIFEXITED (status) ) { + q->exit_status = WEXITSTATUS (status); + } + else { + q->exited++; + q->exit_status = 4; + } + /* okay, the process has terminated - we are ready */ + hang = 0; + } + } + } while (hang); + return c; +} + + + +/* + * We use this function to do the select stuff for all running + * gpgs. A future version might provide a facility to delegate + * those selects to the GDK select stuff. + * This function must be called only by one thread!! + * FIXME: The data structures and algorithms are stupid. + * Returns: 0 = nothing to run + * 1 = did run something + */ + +static int +the_big_select ( void ) +{ + static fd_set readfds; + static fd_set writefds; + struct wait_queue_item_s *q; + int max_fd, n; + struct timeval timeout = { 1, 0 }; /* Use a one second timeout */ + + FD_ZERO ( &readfds ); + FD_ZERO ( &writefds ); + max_fd = 0; + lock_queue (); + for ( q = wait_queue; q; q = q->next ) { + if ( q->used && q->active ) { + if (q->inbound) { + assert ( !FD_ISSET ( q->fd, &readfds ) ); + FD_SET ( q->fd, &readfds ); + } + else { + assert ( !FD_ISSET ( q->fd, &writefds ) ); + FD_SET ( q->fd, &writefds ); + } + if ( q->fd > max_fd ) + max_fd = q->fd; + } + } + unlock_queue (); + + + n = select ( max_fd+1, &readfds, &writefds, NULL, &timeout ); + if ( n <= 0 ) { + if ( n && errno != EINTR ) { + fprintf (stderr, "the_big_select: select failed: %s\n", + strerror (errno) ); + } + return 0; + } + + /* something has to be done. Go over the queue and call + * the handlers */ + restart: + while ( n ) { + lock_queue (); + for ( q = wait_queue; q; q = q->next ) { + if ( q->used && q->active + && FD_ISSET (q->fd, q->inbound? &readfds : &writefds ) ) { + FD_CLR (q->fd, q->inbound? &readfds : &writefds ); + assert (n); + n--; + unlock_queue (); + if ( q->handler (q->handler_value, q->pid, q->fd ) ) + q->active = 0; + goto restart; + } + } + unlock_queue (); + } + return 1; +} + + + +/* + * called by rungpg.c to register something for select() + */ +GpgmeError +_gpgme_register_pipe_handler( void *opaque, + int (*handler)(void*,pid_t,int), + void *handler_value, + pid_t pid, int fd, int inbound ) +{ + GpgmeCtx ctx = opaque; + struct wait_queue_item_s *q; + + init_wait_queue(); + assert (opaque); + assert (handler); + + lock_queue (); + for ( q = wait_queue; q; q = q->next ) { + if ( !q->used ) { + q->used = 1; + q->active = 0; + break; + } + } + unlock_queue (); + if ( !q ) + return mk_error (Too_Many_Procs); + + q->fd = fd; + q->inbound = inbound; + q->handler = handler; + q->handler_value = handler_value; + q->pid = pid; + q->ctx = ctx; + + /* and enable this entry for the next select */ + q->exited = 0; + q->active = 1; + return 0; +} + + + + + + + + + + + + + + + + + diff --git a/trunk/gpgme/wait.h b/trunk/gpgme/wait.h new file mode 100644 index 0000000..f637414 --- /dev/null +++ b/trunk/gpgme/wait.h @@ -0,0 +1,42 @@ +/* wait.h - definitions for wait.c + * 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 WAIT_H +#define WAIT_H + +#include "gpgme.h" + +#define SIZEOF_WAIT_QUEUE 10 + + +GpgmeError _gpgme_register_pipe_handler( + void *opaque, + int (*handler)(void*,pid_t,int), + void *handler_value, + pid_t pid, int fd, int inbound ); + + + +#endif /* WAIT_H */ + + + + + diff --git a/trunk/tests/Makefile.am b/trunk/tests/Makefile.am new file mode 100644 index 0000000..eab4df3 --- /dev/null +++ b/trunk/tests/Makefile.am @@ -0,0 +1,12 @@ +## Process this file with automake to create Makefile.in + +TESTS = t-encrypt + +INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/intl + +INCLUDES = +LDADD = ../gpgme/libgpgme.la + +noinst_PROGRAMS = $(TESTS) + + diff --git a/trunk/tests/t-encrypt.c b/trunk/tests/t-encrypt.c new file mode 100644 index 0000000..8f6e5c5 --- /dev/null +++ b/trunk/tests/t-encrypt.c @@ -0,0 +1,69 @@ +/* t-encrypt.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" + +#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 in, out; + GpgmeRecipientSet rset; + + err = gpgme_new_context (&ctx); + fail_if_err (err); + + err = gpgme_new_data ( &in, "Hallo Leute", 11, 0 ); + fail_if_err (err); + + err = gpgme_new_data ( &out, NULL, 0,0 ); + fail_if_err (err); + + err = gpgme_new_recipient_set (&rset); + fail_if_err (err); + err = gpgme_add_recipient (rset, "Bob"); + fail_if_err (err); + + + err = gpgme_encrypt (ctx, rset, in, out ); + fail_if_err (err); + + + gpgme_release_recipient_set (rset); + gpgme_release_data (in); + gpgme_release_data (out); + gpgme_release_context (ctx); + return 0; +} + + -- 2.26.2