diff --git a/trunk/AUTHORS b/trunk/AUTHORS index e69de29b..20ea6e00 100644 --- a/trunk/AUTHORS +++ b/trunk/AUTHORS @@ -0,0 +1,9 @@ +Program: gpgme +Maintainer: Werner Koch + + +FSF + * Code taken from GnuPG: gpgme/w32-util.c + +Werner Koch + * Design and most stuff. diff --git a/trunk/TODO b/trunk/TODO index 59a6d8c8..a829ea0e 100644 --- a/trunk/TODO +++ b/trunk/TODO @@ -1,10 +1,10 @@ * Implement posix-sema.c * Add gpgme_mime_xxx to make handling of MIME/PGP easier * Allow to use GTK's main loop instead of the select stuff in wait.c -* Remove all that funny exit code handling - we donn't need it. +* Remove all that funny exit code handling - we don't need it. diff --git a/trunk/gpgme/ChangeLog b/trunk/gpgme/ChangeLog index 03c72d3f..0f704a5a 100644 --- a/trunk/gpgme/ChangeLog +++ b/trunk/gpgme/ChangeLog @@ -1,34 +1,38 @@ 2001-01-30 Werner Koch + * posix-util.c, w32-util.c: New. + (_gpgme_get_gpg_path): New, suggested by Jan-Oliver. + * rungpg.c (_gpgme_gpg_spawn): Use new function to get GPG's path. + * signers.c (gpgme_signers_add): Ooops, one should test code and not just write it; the newarr was not assigned. Thanks to José for pointing this out. Hmmm, still not tested, why shoudl a coder test his fix :-) * w32-io.c: Does now use reader threads, so that we can use WaitForMultipleObjects. * sema.h, posix-sema.c, w32-sema.c: Support for Critcial sections. Does currently only work for W32. * debug.c, util.h : New. Changed all fprintfs to use this new set of debugging functions. 2001-01-23 Werner Koch * data.c (_gpgme_data_release_and_return_string): Fixed string termination. 2001-01-22 Werner Koch * delete.c: New. * signers.c: New. * key.c (gpgme_key_ref, gpgme_key_unref): New. * sign.c (gpgme_op_sign_start): Allow the use of other keys. * version.c (gpgme_get_engine_info,gpgme_check_engine): New. * rungpg.c (_gpgme_gpg_set_simple_line_handler): New. 2001-01-05 Werner Koch * data.c (gpgme_data_rewind): Allow to rewind data_type_none. diff --git a/trunk/gpgme/Makefile.am b/trunk/gpgme/Makefile.am index 20944ac8..59faab48 100644 --- a/trunk/gpgme/Makefile.am +++ b/trunk/gpgme/Makefile.am @@ -1,48 +1,49 @@ # Process this file with automake to produce Makefile.in 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 include_HEADERS = gpgme.h lib_LTLIBRARIES = libgpgme.la libgpgme_la_LDFLAGS = -version-info \ @LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@ libgpgme_la_INCLUDES = -I$(top_srcdir)/lib libgpgme_la_SOURCES = \ - gpgme.h types.h util.h util.c debug.c \ + gpgme.h types.h \ + util.h util.c posix-util.c w32-util.c \ context.h ops.h \ data.c recipient.c signers.c \ wait.c wait.h \ encrypt.c \ decrypt.c \ verify.c \ sign.c \ key.c key.h \ keylist.c \ trustlist.c \ import.c \ export.c \ genkey.c \ delete.c \ rungpg.c rungpg.h status-table.h \ sema.h posix-sema.c w32-sema.c \ syshdr.h io.h posix-io.c w32-io.c \ - gpgme.c version.c errors.c + gpgme.c debug.c version.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/debug.c b/trunk/gpgme/debug.c index f41929b3..7cb5121d 100644 --- a/trunk/gpgme/debug.c +++ b/trunk/gpgme/debug.c @@ -1,156 +1,156 @@ /* debug.c * Copyright (C) 2001 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 "util.h" #include "sema.h" DEFINE_STATIC_LOCK (debug_lock); struct debug_control_s { FILE *fp; char fname[100]; }; static int debug_level = 0; static void debug_init (void) { static volatile int initialized = 0; if (initialized) return; LOCK (debug_lock); if (!initialized) { const char *e = getenv ("GPGME_DEBUG"); debug_level = e? atoi (e): 0; initialized = 1; if (debug_level > 0) fprintf (stderr,"gpgme_debug: level=%d\n", debug_level); } UNLOCK (debug_lock); } void _gpgme_debug (int level, const char *format, ...) { va_list arg_ptr ; debug_init (); if ( debug_level < level ) return; va_start ( arg_ptr, format ) ; LOCK (debug_lock); vfprintf (stderr, format, arg_ptr) ; va_end ( arg_ptr ) ; if( format && *format && format[strlen(format)-1] != '\n' ) putc ('\n', stderr); UNLOCK (debug_lock); fflush (stderr); } void _gpgme_debug_begin ( void **helper, int level, const char *text) { struct debug_control_s *ctl; debug_init (); *helper = NULL; if ( debug_level < level ) return; ctl = xtrycalloc (1, sizeof *ctl ); if (!ctl) { _gpgme_debug (255, __FILE__ ":" STR2(__LINE__)": out of core"); return; } - /* Oh what a pitty sthat we don't have a asprintf or snprintf under + /* Oh what a pitty that we don't have a asprintf or snprintf under * Windoze. We definitely should write our own clib for W32! */ sprintf ( ctl->fname, "/tmp/gpgme_debug.%d.%p", getpid (), ctl ); ctl->fp = fopen (ctl->fname, "w+"); if (!ctl->fp) { _gpgme_debug (255,__FILE__ ":" STR2(__LINE__)": failed to create `%s'", ctl->fname ); xfree (ctl); return; } *helper = ctl; _gpgme_debug_add (helper, "%s", text ); } int _gpgme_debug_enabled (void **helper) { return helper && *helper; } void _gpgme_debug_add (void **helper, const char *format, ...) { struct debug_control_s *ctl = *helper; va_list arg_ptr ; if ( !*helper ) return; va_start ( arg_ptr, format ) ; vfprintf (ctl->fp, format, arg_ptr) ; va_end ( arg_ptr ) ; } void _gpgme_debug_end (void **helper, const char *text) { struct debug_control_s *ctl = *helper; int c, last_c=EOF; if ( !*helper ) return; _gpgme_debug_add (helper, "%s", text ); rewind (ctl->fp); LOCK (debug_lock); while ( (c=getc (ctl->fp)) != EOF ) { putc (c, stderr); last_c = c; } if (last_c != '\n') putc ('\n', stderr); UNLOCK (debug_lock); remove (ctl->fname); xfree (ctl); *helper = NULL; } diff --git a/trunk/gpgme/posix-util.c b/trunk/gpgme/posix-util.c new file mode 100644 index 00000000..3c0c692a --- /dev/null +++ b/trunk/gpgme/posix-util.c @@ -0,0 +1,45 @@ +/* posix-util.c - Utility functions for Posix + * Copyright (C) 2001 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 +#ifndef HAVE_DOSISH_SYSTEM + +#include +#include +#include +#include + +#include "util.h" + + +const char * +_gpgme_get_gpg_path (void) +{ + return GPG_PATH; +} + + +#endif /*!HAVE_DOSISH_SYSTEM*/ + + + + + diff --git a/trunk/gpgme/rungpg.c b/trunk/gpgme/rungpg.c index 101d9a9f..8c9d2b77 100644 --- a/trunk/gpgme/rungpg.c +++ b/trunk/gpgme/rungpg.c @@ -1,1276 +1,1277 @@ /* 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 "unistd.h" #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 "io.h" #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 */ }; 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; /* This is a kludge - see the comment at gpg_colon_line_handler */ struct { int fd[2]; size_t bufsize; char *buffer; size_t readpos; int eof; GpgColonLineHandler fnc; /* this indicate use of this structrue */ void *fnc_value; int simple; } colon; char **argv; struct fd_data_map_s *fd_data_map; int pid; /* we can't use pid_t because we don't use it in Windoze */ int running; int exit_status; int exit_signal; /* stuff needed for pipemode */ struct { int used; int active; GpgmeData sig; GpgmeData text; int stream_started; } pm; /* stuff needed for interactive (command) mode */ struct { int used; int fd; GpgmeData cb_data; /* hack to get init the above fd later */ GpgStatusCode code; /* last code */ char *keyword; /* what has been requested (malloced) */ GpgCommandHandler fnc; void *fnc_value; } cmd; }; 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_inbound_handler ( void *opaque, int pid, int fd ); static int gpg_outbound_handler ( void *opaque, int pid, int fd ); static int gpg_status_handler ( void *opaque, int pid, int fd ); static GpgmeError read_status ( GpgObject gpg ); static int gpg_colon_line_handler ( void *opaque, int pid, int fd ); static GpgmeError read_colon_line ( GpgObject gpg ); static int pipemode_cb ( void *opaque, char *buffer, size_t length, size_t *nread ); static int command_cb ( void *opaque, char *buffer, size_t length, size_t *nread ); GpgmeError _gpgme_gpg_new ( GpgObject *r_gpg ) { GpgObject gpg; 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; gpg->colon.fd[0] = -1; gpg->colon.fd[1] = -1; gpg->cmd.fd = -1; /* 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 (_gpgme_io_pipe (gpg->status.fd, 1) == -1) { rc = mk_error (Pipe_Error); goto leave; } gpg->status.eof = 0; _gpgme_gpg_add_arg ( gpg, "--status-fd" ); { char buf[25]; sprintf ( buf, "%d", gpg->status.fd[1]); _gpgme_gpg_add_arg ( gpg, buf ); } _gpgme_gpg_add_arg ( gpg, "--no-tty" ); leave: if (rc) { _gpgme_gpg_release (gpg); *r_gpg = NULL; } else *r_gpg = gpg; return rc; } void _gpgme_gpg_release ( GpgObject gpg ) { if ( !gpg ) return; xfree (gpg->status.buffer); xfree (gpg->colon.buffer); if ( gpg->argv ) free_argv (gpg->argv); xfree (gpg->cmd.keyword); #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 ) _gpgme_io_close (gpg->status.fd[1]); if (gpg->colon.fd[0] != -1 ) _gpgme_io_close (gpg->colon.fd[0]); if (gpg->colon.fd[1] != -1 ) _gpgme_io_close (gpg->colon.fd[1]); #endif free_fd_data_map (gpg->fd_data_map); kill_gpg (gpg); /* fixme: should be done asyncronously */ xfree (gpg); } static void kill_gpg ( GpgObject gpg ) { #if 0 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; } #endif } void _gpgme_gpg_enable_pipemode ( GpgObject gpg ) { gpg->pm.used = 1; assert ( !gpg->pm.sig ); assert ( !gpg->pm.text ); } GpgmeError _gpgme_gpg_add_arg ( GpgObject gpg, const char *arg ) { struct arg_and_data_s *a; assert (gpg); assert (arg); if (gpg->pm.active) return 0; 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); if (gpg->pm.active) return 0; a = xtrymalloc ( sizeof *a - 1 ); if ( !a ) { gpg->arg_error = 1; return mk_error(Out_Of_Core); } 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; } GpgmeError _gpgme_gpg_add_pm_data ( GpgObject gpg, GpgmeData data, int what ) { GpgmeError rc=0; assert ( gpg->pm.used ); if ( !what ) { /* the signature */ assert ( !gpg->pm.sig ); gpg->pm.sig = data; } else if (what == 1) { /* the signed data */ assert ( !gpg->pm.text ); gpg->pm.text = data; } else { assert (0); } if ( gpg->pm.sig && gpg->pm.text ) { if ( !gpg->pm.active ) { /* create the callback handler and connect it to stdin */ GpgmeData tmp; rc = gpgme_data_new_with_read_cb ( &tmp, pipemode_cb, gpg ); if (!rc ) rc = _gpgme_gpg_add_data (gpg, tmp, 0); } if ( !rc ) { /* here we can reset the handler stuff */ gpg->pm.stream_started = 0; } } return rc; } /* * 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); if (gpg->pm.active) return; gpg->status.fnc = fnc; gpg->status.fnc_value = fnc_value; } /* Kludge to process --with-colon output */ GpgmeError _gpgme_gpg_set_colon_line_handler ( GpgObject gpg, GpgColonLineHandler fnc, void *fnc_value ) { assert (gpg); if (gpg->pm.active) return 0; gpg->colon.bufsize = 1024; gpg->colon.readpos = 0; gpg->colon.buffer = xtrymalloc (gpg->colon.bufsize); if (!gpg->colon.buffer) { return mk_error (Out_Of_Core); } if (_gpgme_io_pipe (gpg->colon.fd, 1) == -1) { xfree (gpg->colon.buffer); gpg->colon.buffer = NULL; return mk_error (Pipe_Error); } gpg->colon.eof = 0; gpg->colon.fnc = fnc; gpg->colon.fnc_value = fnc_value; gpg->colon.simple = 0; return 0; } GpgmeError _gpgme_gpg_set_simple_line_handler ( GpgObject gpg, GpgColonLineHandler fnc, void *fnc_value ) { GpgmeError err; err = _gpgme_gpg_set_colon_line_handler (gpg, fnc, fnc_value); if (!err) gpg->colon.simple = 1; return err; } /* * The Fnc will be called to get a value for one of the commands with * a key KEY. If the Code pssed to FNC is 0, the function may release * resources associated with the returned value from another call. To * match such a second call to a first call, the returned value from * the first call is passed as keyword. */ GpgmeError _gpgme_gpg_set_command_handler ( GpgObject gpg, GpgCommandHandler fnc, void *fnc_value ) { GpgmeData tmp; GpgmeError err; assert (gpg); if (gpg->pm.active) return 0; err = gpgme_data_new_with_read_cb ( &tmp, command_cb, gpg ); if (err) return err; _gpgme_gpg_add_arg ( gpg, "--command-fd" ); _gpgme_gpg_add_data (gpg, tmp, -2); gpg->cmd.cb_data = tmp; gpg->cmd.fnc = fnc; gpg->cmd.fnc_value = fnc_value; gpg->cmd.used = 1; return 0; } static void free_argv ( char **argv ) { int i; for (i=0; argv[i]; i++ ) xfree (argv[i]); xfree (argv); } static void free_fd_data_map ( struct fd_data_map_s *fd_data_map ) { int i; if ( !fd_data_map ) return; for (i=0; fd_data_map[i].data; i++ ) { #if 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 release data because this is only a reference */ } xfree (fd_data_map); } 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; int need_special = 0; int use_agent = !!getenv ("GPG_AGENT_INFO"); if ( gpg->argv ) { free_argv ( gpg->argv ); gpg->argv = NULL; } 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" );*/ datac++; if ( a->dup_to == -1 && !a->print_fd ) need_special = 1; } else { /* fprintf (stderr, "build_argv: arg=`%s'\n", a->arg );*/ } } if ( need_special ) argc++; if (use_agent) argc++; if (!gpg->cmd.used) argc++; 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; 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++; } if ( use_agent ) { argv[argc] = xtrystrdup ( "--use-agent" ); if (!argv[argc]) { xfree (fd_data_map); free_argv (argv); return mk_error (Out_Of_Core); } argc++; } if ( !gpg->cmd.used ) { argv[argc] = xtrystrdup ( "--batch" ); 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_data_get_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_data_get_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: case GPGME_DATA_TYPE_CB: 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 (_gpgme_io_pipe (fds, fd_data_map[datac].inbound?1:0 ) == -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]; } } /* Hack to get hands on the fd later */ if ( gpg->cmd.used && gpg->cmd.cb_data == a->data ) { assert (gpg->cmd.fd == -1); gpg->cmd.fd = fd_data_map[datac].fd; } 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], a->print_fd? "%d" : "-&%d", fd_data_map[datac].peer_fd ); argc++; } 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, n; int pid; struct spawn_fd_item_s *fd_child_list, *fd_parent_list; 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); if (gpg->pm.active) return 0; rc = build_argv ( gpg ); if ( rc ) return rc; n = 4; /* status fd, 2*colon_fd and end of list */ for (i=0; gpg->fd_data_map[i].data; i++ ) n += 2; fd_child_list = xtrycalloc ( n+n, sizeof *fd_child_list ); if (!fd_child_list) return mk_error (Out_Of_Core); fd_parent_list = fd_child_list + n; /* build the fd list for the child */ n=0; fd_child_list[n].fd = gpg->status.fd[0]; fd_child_list[n].dup_to = -1; n++; if ( gpg->colon.fnc ) { fd_child_list[n].fd = gpg->colon.fd[0]; fd_child_list[n].dup_to = -1; n++; fd_child_list[n].fd = gpg->colon.fd[1]; fd_child_list[n].dup_to = 1; /* dup to stdout */ n++; } for (i=0; gpg->fd_data_map[i].data; i++ ) { fd_child_list[n].fd = gpg->fd_data_map[i].fd; fd_child_list[n].dup_to = -1; n++; if (gpg->fd_data_map[i].dup_to != -1) { fd_child_list[n].fd = gpg->fd_data_map[i].peer_fd; fd_child_list[n].dup_to = gpg->fd_data_map[i].dup_to; n++; } } fd_child_list[n].fd = -1; fd_child_list[n].dup_to = -1; /* build the fd list for the parent */ n=0; if ( gpg->status.fd[1] != -1 ) { fd_parent_list[n].fd = gpg->status.fd[1]; fd_parent_list[n].dup_to = -1; n++; gpg->status.fd[1] = -1; } if ( gpg->colon.fd[1] != -1 ) { fd_parent_list[n].fd = gpg->colon.fd[1]; fd_parent_list[n].dup_to = -1; n++; gpg->colon.fd[1] = -1; } for (i=0; gpg->fd_data_map[i].data; i++ ) { fd_parent_list[n].fd = gpg->fd_data_map[i].peer_fd; fd_parent_list[n].dup_to = -1; n++; gpg->fd_data_map[i].peer_fd = -1; } fd_parent_list[n].fd = -1; fd_parent_list[n].dup_to = -1; - pid = _gpgme_io_spawn (GPG_PATH, gpg->argv, fd_child_list, fd_parent_list); + pid = _gpgme_io_spawn (_gpgme_get_gpg_path (), + gpg->argv, fd_child_list, fd_parent_list); xfree (fd_child_list); if (pid == -1) { return mk_error (Exec_Error); } gpg->pid = pid; if (gpg->pm.used) gpg->pm.active = 1; /*_gpgme_register_term_handler ( closure, closure_value, pid );*/ 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); } if ( gpg->colon.fnc ) { assert ( gpg->colon.fd[0] != -1 ); if ( _gpgme_register_pipe_handler ( opaque, gpg_colon_line_handler, gpg, pid, gpg->colon.fd[0], 1 ) ) { /* FIXME: kill the child */ return mk_error (General_Error); } } for (i=0; gpg->fd_data_map[i].data; i++ ) { /* Due to problems with select and write we set outbound pipes * to non-blocking */ if (!gpg->fd_data_map[i].inbound) { _gpgme_io_set_nonblocking (gpg->fd_data_map[i].fd); } 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); } } if ( gpg->cmd.used ) _gpgme_freeze_fd ( gpg->cmd.fd ); /* fixme: check what data we can release here */ gpg->running = 1; return 0; } static int gpg_inbound_handler ( void *opaque, int pid, int fd ) { GpgmeData dh = opaque; GpgmeError err; int nread; char buf[200]; assert ( _gpgme_data_get_mode (dh) == GPGME_DATA_MODE_IN ); nread = _gpgme_io_read (fd, buf, 200 ); if ( nread < 0 ) { DEBUG3 ("read_mem_data: read failed on fd %d (n=%d): %s", fd, nread, strerror (errno) ); return 1; } 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_data_append ( dh, buf, nread ); if ( err ) { DEBUG1 ("_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; } static int write_mem_data ( GpgmeData dh, int fd ) { size_t nbytes; int nwritten; nbytes = dh->len - dh->readpos; if ( !nbytes ) { _gpgme_io_close (fd); return 1; } /* FIXME: Arggg, the pipe blocks on large write request, although * select told us that it is okay to write - need to figure out * why this happens? Stevens says nothing about this problem (or * is it my Linux kernel 2.4.0test1) * To avoid that we have set the pipe to nonblocking. */ nwritten = _gpgme_io_write ( fd, dh->data+dh->readpos, nbytes ); if (nwritten == -1 && errno == EAGAIN ) return 0; if ( nwritten < 1 ) { DEBUG3 ("write_mem_data(%d): write failed (n=%d): %s", fd, nwritten, strerror (errno) ); _gpgme_io_close (fd); return 1; } dh->readpos += nwritten; 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, buffer, nbytes ); if (nwritten == -1 && errno == EAGAIN ) return 0; if ( nwritten < 1 ) { DEBUG3 ("write_cb_data(%d): write failed (n=%d): %s", fd, nwritten, strerror (errno) ); _gpgme_io_close (fd); return 1; } if ( nwritten < nbytes ) { /* ugly, ugly: It does currently only for for MEM type data */ if ( _gpgme_data_unread (dh, buffer + nwritten, nbytes - nwritten ) ) DEBUG1 ("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 ) { GpgmeData dh = opaque; assert ( _gpgme_data_get_mode (dh) == GPGME_DATA_MODE_OUT ); switch ( gpgme_data_get_type (dh) ) { case GPGME_DATA_TYPE_MEM: 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; } static int gpg_status_handler ( void *opaque, int pid, int fd ) { GpgObject gpg = opaque; int rc = 0; assert ( fd == gpg->status.fd[0] ); rc = read_status ( gpg ); if ( rc ) { DEBUG1 ("gpg_handler: read_status problem %d\n - stop", rc); return 1; } return gpg->status.eof; } static int 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); } /* * 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); } nread = _gpgme_io_read ( gpg->status.fd[0], buffer+readpos, bufsize-readpos ); 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' ) { 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 ) { if ( gpg->cmd.used && ( r->code == STATUS_GET_BOOL || r->code == STATUS_GET_LINE || r->code == STATUS_GET_HIDDEN )) { gpg->cmd.code = r->code; xfree (gpg->cmd.keyword); gpg->cmd.keyword = xtrystrdup (rest); if ( !gpg->cmd.keyword ) return mk_error (Out_Of_Core); /* this should be the last thing we have received * and the next thing will be that the command * handler does its action */ if ( nread > 1 ) DEBUG0 ("ERROR, unexpected data in read_status"); _gpgme_thaw_fd (gpg->cmd.fd); } else if ( gpg->status.fnc ) { gpg->status.fnc ( gpg->status.fnc_value, r->code, rest); } } if ( r->code == STATUS_END_STREAM ) { /* _gpgme_freeze_fd ( ? );*/ } } /* 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; } /* * This colonline handler thing is not the clean way to do it. * It might be better to enhance the GpgmeData object to act as * a wrapper for a callback. Same goes for the status thing. * For now we use this thing here becuase it is easier to implement. */ static int gpg_colon_line_handler ( void *opaque, int pid, int fd ) { GpgObject gpg = opaque; GpgmeError rc = 0; assert ( fd == gpg->colon.fd[0] ); rc = read_colon_line ( gpg ); if ( rc ) { DEBUG1 ("gpg_colon_line_handler: " "read problem %d\n - stop", rc); return 1; } return gpg->status.eof; } static GpgmeError read_colon_line ( GpgObject gpg ) { char *p; int nread; size_t bufsize = gpg->colon.bufsize; char *buffer = gpg->colon.buffer; size_t readpos = gpg->colon.readpos; assert (buffer); if (bufsize - readpos < 256) { /* need more room for the read */ bufsize += 1024; buffer = xtryrealloc (buffer, bufsize); if ( !buffer ) return mk_error (Out_Of_Core); } nread = _gpgme_io_read ( gpg->colon.fd[0], buffer+readpos, bufsize-readpos ); if (nread == -1) return mk_error(Read_Error); if (!nread) { gpg->colon.eof = 1; assert (gpg->colon.fnc); gpg->colon.fnc ( gpg->colon.fnc_value, NULL ); return 0; } while (nread > 0) { for (p = buffer + readpos; nread; nread--, p++) { if ( *p == '\n' ) { /* (we require that the last line is terminated by a * LF) and we skip empty lines. Note: we use UTF8 * encoding and escaping of special characters * We require at least one colon to cope with * some other printed information. */ *p = 0; if ( gpg->colon.simple || (*buffer && strchr (buffer, ':')) ) { assert (gpg->colon.fnc); gpg->colon.fnc ( gpg->colon.fnc_value, buffer ); } /* To reuse the buffer for the next line we have to * shift the remaining data to the buffer start and * restart the loop Hmmm: We can optimize this * function by looking forward in the buffer to see * whether a second complete line is available and in * this case avoid the memmove for this line. */ nread--; p++; if (nread) memmove (buffer, p, nread); readpos = 0; break; /* the for loop */ } else readpos++; } } /* Update the gpg object. */ gpg->colon.bufsize = bufsize; gpg->colon.buffer = buffer; gpg->colon.readpos = readpos; return 0; } static GpgmeError pipemode_copy (char *buffer, size_t length, size_t *nread, GpgmeData data ) { GpgmeError err; int nbytes; char tmp[1000], *s, *d; /* we can optimize this whole thing but for now we just * return after each escape character */ if (length > 990) length = 990; err = gpgme_data_read ( data, tmp, length, &nbytes ); if (err) return err; for (s=tmp, d=buffer; nbytes; s++, nbytes--) { *d++ = *s; if (*s == '@' ) { *d++ = '@'; break; } } *nread = d - buffer; return 0; } static int pipemode_cb ( void *opaque, char *buffer, size_t length, size_t *nread ) { GpgObject gpg = opaque; GpgmeError err; if ( !buffer || !length || !nread ) return 0; /* those values are reserved for extensions */ *nread =0; if ( !gpg->pm.stream_started ) { assert (length > 4 ); strcpy (buffer, "@<@B" ); *nread = 4; gpg->pm.stream_started = 1; } else if ( gpg->pm.sig ) { err = pipemode_copy ( buffer, length, nread, gpg->pm.sig ); if ( err == GPGME_EOF ) { gpg->pm.sig = NULL; assert (length > 4 ); strcpy (buffer, "@t" ); *nread = 2; } else if (err) { DEBUG1 ("pipemode_cb: copy sig failed: %s\n", gpgme_strerror (err) ); return -1; } } else if ( gpg->pm.text ) { err = pipemode_copy ( buffer, length, nread, gpg->pm.text ); if ( err == GPGME_EOF ) { gpg->pm.text = NULL; assert (length > 4 ); strcpy (buffer, "@.@>" ); *nread = 4; } else if (err) { DEBUG1 ("pipemode_cb: copy data failed: %s\n", gpgme_strerror (err) ); return -1; } } else { return 0; /* eof */ } return 0; } /* * Here we handle --command-fd. This works closely together with * the status handler. */ static int command_cb ( void *opaque, char *buffer, size_t length, size_t *nread ) { GpgObject gpg = opaque; const char *value; int value_len; DEBUG0 ("command_cb: enter\n"); assert (gpg->cmd.used); if ( !buffer || !length || !nread ) return 0; /* those values are reserved for extensions */ *nread =0; if ( !gpg->cmd.code ) { DEBUG0 ("command_cb: no code\n"); return -1; } if ( !gpg->cmd.fnc ) { DEBUG0 ("command_cb: no user cb\n"); return -1; } value = gpg->cmd.fnc ( gpg->cmd.fnc_value, gpg->cmd.code, gpg->cmd.keyword ); if ( !value ) { DEBUG0 ("command_cb: no data from user cb\n"); gpg->cmd.fnc ( gpg->cmd.fnc_value, 0, value); return -1; } value_len = strlen (value); if ( value_len+1 > length ) { DEBUG0 ("command_cb: too much data from user cb\n"); gpg->cmd.fnc ( gpg->cmd.fnc_value, 0, value); return -1; } memcpy ( buffer, value, value_len ); if ( !value_len || (value_len && value[value_len-1] != '\n') ) buffer[value_len++] = '\n'; *nread = value_len; gpg->cmd.fnc ( gpg->cmd.fnc_value, 0, value); gpg->cmd.code = 0; /* and sleep again until read_status will wake us up again */ _gpgme_freeze_fd ( gpg->cmd.fd ); return 0; } diff --git a/trunk/gpgme/util.h b/trunk/gpgme/util.h index 65bfa2b4..0d901e65 100644 --- a/trunk/gpgme/util.h +++ b/trunk/gpgme/util.h @@ -1,126 +1,131 @@ /* 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) #ifndef STR #define STR(v) #v #endif #define STR2(v) STR(v) void _gpgme_debug (int level, const char *format, ...); void _gpgme_debug_begin ( void **helper, int level, const char *text); int _gpgme_debug_enabled ( void **helper ); void _gpgme_debug_add (void **helper, const char *format, ...); void _gpgme_debug_end (void **helper, const char *text); #define DEBUG0(x) _gpgme_debug (1, __FILE__ ":" \ STR2 (__LINE__) ": " x ) #define DEBUG1(x,a) _gpgme_debug (1, __FILE__ ":" \ STR2 (__LINE__)": " x, (a) ) #define DEBUG2(x,a,b) _gpgme_debug (1, __FILE__ ":" \ STR2 (__LINE__) ": " x, (a), (b) ) #define DEBUG3(x,a,b,c) _gpgme_debug (1, __FILE__ ":" \ STR2 (__LINE__) ": " x, (a), (b), (c) ) #define DEBUG4(x,a,b,c,d) _gpgme_debug (1, __FILE__ ":" \ STR2 (__LINE__) ": " x, (a), (b), (c), (d) ) #define DEBUG5(x,a,b,c,d,e) _gpgme_debug (1, __FILE__ ":" \ STR2 (__LINE__) ": " x, (a), (b), (c), (d), (e) ) #define DEBUG6(x,a,b,c,d,e,f) _gpgme_debug (1, __FILE__ ":" \ STR2 (__LINE__) ": " x, (a), (b), (c), (d), (e), (f) ) #define DEBUG7(x,a,b,c,d,e,f,g) _gpgme_debug (1, __FILE__ ":" \ STR2 (__LINE__) ": " x, (a), (b), (c), (d), (e), (f), (g) ) #define DEBUG8(x,a,b,c,d,e,f,g,h) _gpgme_debug (1, __FILE__ ":" \ STR2 (__LINE__) ": " x, (a), (b), (c), (d), (e), (f), (g), (h) ) #define DEBUG9(x,a,b,c,d,e,f,g,h,i) _gpgme_debug (1, __FILE__ ":" \ STR2 (__LINE__) ": " x, (a), (b), (c), (d), (e), (f), (g), (h), (i) ) #define DEBUG10(x,a,b,c,d,e,f,g,h,i,j) _gpgme_debug (1, __FILE__ ":" \ STR2 (__LINE__) ": " x, (a), (b), (c), (d), (e), (f), (g), (h), (i), (j) ) #define DEBUG_BEGIN(y,x) _gpgme_debug_begin (&(y), 1, __FILE__ ":" \ STR2 (__LINE__) ": " x ) #define DEBUG_ENABLED(y) _gpgme_debug_enabled(&(y)) #define DEBUG_ADD0(y,x) _gpgme_debug_add (&(y), (x), \ ) #define DEBUG_ADD1(y,x,a) _gpgme_debug_add (&(y), (x), \ (a) ) #define DEBUG_ADD2(y,x,a,b) _gpgme_debug_add (&(y), (x), \ (a), (b) ) #define DEBUG_ADD3(y,x,a,b,c) _gpgme_debug_add (&(y), (x), \ (a), (b), (c) ) #define DEBUG_ADD4(y,x,a,b,c,d) _gpgme_debug_add (&(y), (x), \ (a), (b), (c), (d) ) #define DEBUG_ADD5(y,x,a,b,c,d,e) _gpgme_debug_add (&(y), (x), \ (a), (b), (c), (d), (e) ) #define DEBUG_END(y,x) _gpgme_debug_end (&(y), (x) ) #ifndef HAVE_STPCPY char *stpcpy (char *a, const char *b); #endif #define return_if_fail(expr) do { \ if (!(expr)) { \ fprintf (stderr, "%s:%d: assertion `%s' failed", \ __FILE__, __LINE__, #expr ); \ return; \ } } while (0) #define return_null_if_fail(expr) do { \ if (!(expr)) { \ fprintf (stderr, "%s:%d: assertion `%s' failed", \ __FILE__, __LINE__, #expr ); \ return NULL; \ } } while (0) #define return_val_if_fail(expr,val) do { \ if (!(expr)) { \ fprintf (stderr, "%s:%d: assertion `%s' failed", \ __FILE__, __LINE__, #expr ); \ return (val); \ } } while (0) +/*-- {posix,w32}-util.c --*/ +const char *_gpgme_get_gpg_path (void); + + + #endif /* UTIL_H */ diff --git a/trunk/gpgme/w32-util.c b/trunk/gpgme/w32-util.c new file mode 100644 index 00000000..f1001a68 --- /dev/null +++ b/trunk/gpgme/w32-util.c @@ -0,0 +1,114 @@ +/* w32-util.c - Utility functions for the W32 API + * Copyright (C) 2001 Werner Koch (dd9jn) + * Copyright (C) 1999 Free Software Foundation, Inc + * + * 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 +#ifdef HAVE_DOSISH_SYSTEM + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "syshdr.h" + +#include "util.h" + +/**************** + * Return a string from the Win32 Registry or NULL in case of + * error. Caller must release the return value. A NULL for root + * is an alias fro HKEY_CURRENT_USER + */ +static char * +read_w32_registry_string ( const char *root, + const char *dir, const char *name ) +{ + HKEY root_key, key_handle; + DWORD n1, nbytes; + char *result = NULL; + + if( !root ) + root_key = HKEY_CURRENT_USER; + else if( !strcmp( root, "HKEY_CLASSES_ROOT" ) ) + root_key = HKEY_CLASSES_ROOT; + else if( !strcmp( root, "HKEY_CURRENT_USER" ) ) + root_key = HKEY_CURRENT_USER; + else if( !strcmp( root, "HKEY_LOCAL_MACHINE" ) ) + root_key = HKEY_LOCAL_MACHINE; + else if( !strcmp( root, "HKEY_USERS" ) ) + root_key = HKEY_USERS; + else if( !strcmp( root, "HKEY_PERFORMANCE_DATA" ) ) + root_key = HKEY_PERFORMANCE_DATA; + else if( !strcmp( root, "HKEY_CURRENT_CONFIG" ) ) + root_key = HKEY_CURRENT_CONFIG; + else + return NULL; + + if( RegOpenKeyEx( root_key, dir, 0, KEY_READ, &key_handle ) ) + return NULL; /* no need for a RegClose, so return direct */ + + nbytes = 1; + if( RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes ) ) + goto leave; + result = xtrymalloc( (n1=nbytes+1) ); + if( !result ) + goto leave; + if( RegQueryValueEx( key_handle, name, 0, NULL, result, &n1 ) ) { + xfree(result); result = NULL; + goto leave; + } + result[nbytes] = 0; /* make sure it is really a string */ + + leave: + RegCloseKey( key_handle ); + return result; +} + + +const char * +_gpgme_get_gpg_path (void) +{ + static char *gpg_program = NULL; + + if (!gpg_program) { + gpg_program = read_w32_registry_string ( NULL, + "Software\\GNU\\GnuPG", "gpgProgram" ); + if (!gpg_program) + gpg_program = GPG_PATH; + } + + return gpg_program; +} + + + + +#endif /*HAVE_DOSISH_SYSTEM*/ + + + + +