Browse Source

2008-08-08 Marcus Brinkmann <marcus@g10code.de>

	* configure.ac: Update svn macros.
	(BUILD_REVISION, BUILD_FILEVERSION, BUILD_TIMESTAMP): New.
	(AC_CONFIG_OUTPUT): Add src/versioninfo.rc.
	(GPG_AGENT_DEFAULT, GPG_AGENT): New.
	(AC_REPLACE_FUNCS): Add vasprintf and stpcpy.
	(AC_CHECK_FUNCS): Add ttyname, localtime_r and timegm.
	* src/stpcpy.h, src/stpcpy.c, src/realloc.c: New file from gnulib.
	* src/vasprintf.c: New file from libiberty.
	* src/support.h [!HAVE_STPCPY]: Include "stpcpy.h".
	[!HAVE_TTYNAME]: Define simple replacement function.
	(get_gpgsm_path, get_gpg_agent_path)
	(default_homedir, make_filename): New prototypes.
	* src/Makefile.am: Add W32 support.
	(libscute_la_SOURCES): Add get-path.c.
	(EXTRA_DIST): Add versioninfo.rc.in.
	* src/versioninfo.rc.in: New file.
	* src/get-path.c: New file.
	* src/agent.c (PATHSEP_C): New macro.
	(build_w32_commandline_copy)
	(build_w32_commandline) [HAVE_W32_SYSTEM]: New functions.
	(spawn_process_deatched): New function.
	(agent_connect): Start gpg-agent if it is not running yet.
	(agent_configure): Also pass xauthority and pinentry-user-data.
	* src/cert-gpgsm.c (COMPAT_FALLBACK) [!HAVE_W32_SYSTEM]: New
	macro.
	(export_cert_compat) [!COMPAT_FALLBACK]: Remove.
	(export_cert) [!COMPAT_FALLBACK]: Don't call export_cert_compat.
	Don't create (unused) output pipe cruft.
	* src/cert-object.c (time_to_ck_date) [!HAVE_LOCALTIME_R]: Fall
	back to localtime.
	* tests/t-getslotinfo.c (main) [WIN32]: Call _sleep instead sleep.
Marcus Brinkmann 17 years ago
parent
commit
7a2a44ce97
16 changed files with 1381 additions and 56 deletions
  1. 33 1
      ChangeLog
  2. 3 0
      NEWS
  3. 1 0
      TODO
  4. 81 7
      configure.ac
  5. 43 5
      src/Makefile.am
  6. 325 27
      src/agent.c
  7. 18 15
      src/cert-gpgsm.c
  8. 10 0
      src/cert-object.c
  9. 456 0
      src/get-path.c
  10. 45 0
      src/realloc.c
  11. 47 0
      src/stpcpy.c
  12. 41 0
      src/stpcpy.h
  13. 29 0
      src/support.h
  14. 192 0
      src/vasprintf.c
  15. 52 0
      src/versioninfo.rc.in
  16. 5 1
      tests/t-getslotinfo.c

+ 33 - 1
ChangeLog

@@ -1,5 +1,37 @@
 2008-08-08  Marcus Brinkmann  <marcus@g10code.de>
 2008-08-08  Marcus Brinkmann  <marcus@g10code.de>
 
 
+	* configure.ac: Update svn macros.
+	(BUILD_REVISION, BUILD_FILEVERSION, BUILD_TIMESTAMP): New.
+	(AC_CONFIG_OUTPUT): Add src/versioninfo.rc.
+	(GPG_AGENT_DEFAULT, GPG_AGENT): New.
+	(AC_REPLACE_FUNCS): Add vasprintf and stpcpy.
+	(AC_CHECK_FUNCS): Add ttyname, localtime_r and timegm.
+	* src/stpcpy.h, src/stpcpy.c, src/realloc.c: New file from gnulib.
+	* src/vasprintf.c: New file from libiberty.
+	* src/support.h [!HAVE_STPCPY]: Include "stpcpy.h".
+	[!HAVE_TTYNAME]: Define simple replacement function.
+	(get_gpgsm_path, get_gpg_agent_path)
+	(default_homedir, make_filename): New prototypes.
+	* src/Makefile.am: Add W32 support.
+	(libscute_la_SOURCES): Add get-path.c.
+	(EXTRA_DIST): Add versioninfo.rc.in.
+	* src/versioninfo.rc.in: New file.
+	* src/get-path.c: New file.
+	* src/agent.c (PATHSEP_C): New macro.
+	(build_w32_commandline_copy)
+	(build_w32_commandline) [HAVE_W32_SYSTEM]: New functions.
+	(spawn_process_deatched): New function.
+	(agent_connect): Start gpg-agent if it is not running yet.
+	(agent_configure): Also pass xauthority and pinentry-user-data.
+	* src/cert-gpgsm.c (COMPAT_FALLBACK) [!HAVE_W32_SYSTEM]: New
+	macro.
+	(export_cert_compat) [!COMPAT_FALLBACK]: Remove.
+	(export_cert) [!COMPAT_FALLBACK]: Don't call export_cert_compat.
+	Don't create (unused) output pipe cruft.
+	* src/cert-object.c (time_to_ck_date) [!HAVE_LOCALTIME_R]: Fall
+	back to localtime.
+	* tests/t-getslotinfo.c (main) [WIN32]: Call _sleep instead sleep.
+
 	* src/cryptoki.h (CRYPTOKI_EXPORTS): Define symbol.
 	* src/cryptoki.h (CRYPTOKI_EXPORTS): Define symbol.
 	* src/error-mapping.h: Do not include <error.h>.
 	* src/error-mapping.h: Do not include <error.h>.
 	(scute_sys_to_ck): Change type of ERR to int.
 	(scute_sys_to_ck): Change type of ERR to int.
@@ -246,7 +278,7 @@
 	* Initial version.
 	* Initial version.
 
 
 
 
- Copyright 2006 g10 Code GmbH
+ Copyright 2006, 2007, 2008 g10 Code GmbH
 
 
  This file is free software; as a special exception the author gives
  This file is free software; as a special exception the author gives
  unlimited permission to copy and/or distribute it, with or without
  unlimited permission to copy and/or distribute it, with or without

+ 3 - 0
NEWS

@@ -1,6 +1,9 @@
 Noteworthy changes in version 1.2.0 (unreleased)
 Noteworthy changes in version 1.2.0 (unreleased)
 ------------------------------------------------
 ------------------------------------------------
 
 
+ * Ported to Windows 32.
+
+ * GPG Agent can now be launched on demand.
 
 
 Noteworthy changes in version 1.1.0 (2007-05-03)
 Noteworthy changes in version 1.1.0 (2007-05-03)
 ------------------------------------------------
 ------------------------------------------------

+ 1 - 0
TODO

@@ -9,6 +9,7 @@
 ** Mozilla does not unload the right security token!!!
 ** Mozilla does not unload the right security token!!!
 ** Duplicate certificates should be removed from the object list (this
 ** Duplicate certificates should be removed from the object list (this
    can occur when including all certificate chains).
    can occur when including all certificate chains).
+** Windows: Find thread-safe replacement for localtime_r and timegm.
 
 
 * Missing features:
 * Missing features:
 ** Implement random number generation function C_GenerateRandom.
 ** Implement random number generation function C_GenerateRandom.

+ 81 - 7
configure.ac

@@ -1,5 +1,5 @@
 # configure.ac: Configure script for Scute.
 # configure.ac: Configure script for Scute.
-# Copyright (C) 2006, 2007 g10 Code GmbH
+# Copyright (C) 2006, 2007, 2008 g10 Code GmbH
 # 
 # 
 # This file is part of Scute.
 # This file is part of Scute.
 #
 #
@@ -47,10 +47,13 @@ min_automake_version="1.9.3"
 # the configure script, so that a proper revision number for all files
 # the configure script, so that a proper revision number for all files
 # is available when running a "make distcheck".
 # is available when running a "make distcheck".
 m4_define(my_version, [1.2.0])
 m4_define(my_version, [1.2.0])
-m4_define(my_iscvs, yes)
-AC_INIT([scute], my_version[]m4_ifdef([my_iscvs], [-cvs[]m4_translit(
-               [$Revision: 1179 $],[Ra-z $:])]),
-               [marcus@g10code.com])
+m4_define(my_issvn, yes)
+m4_define([svn_revision], m4_esyscmd([echo -n $( (svn info 2>/dev/null \
+            || echo 'Revision: 0')|sed -n '/^Revision:/ {s/[^0-9]//gp;q;}')]))
+
+AC_INIT([scute],
+        [my_version[]m4_if(my_issvn,[yes],[-svn[]svn_revision])],
+        [marcus@g10code.com])
 
 
 # LT Version numbers, remember to change them just *before* a release.
 # LT Version numbers, remember to change them just *before* a release.
 #   (Code changed:			REVISION++)
 #   (Code changed:			REVISION++)
@@ -73,6 +76,7 @@ NEED_GPGSM_VERSION=1.9.6
 have_gpg_error=no
 have_gpg_error=no
 have_libassuan=no
 have_libassuan=no
 
 
+BUILD_REVISION=svn_revision
 PACKAGE=$PACKAGE_NAME
 PACKAGE=$PACKAGE_NAME
 VERSION=$PACKAGE_VERSION
 VERSION=$PACKAGE_VERSION
 
 
@@ -141,8 +145,42 @@ case "${host}" in
 esac
 esac
 AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes")
 AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes")
 
 
+GPGSM_DEFAULT=no
+GPG_AGENT_DEFAULT=no
+have_w32_system=no
+case "${host}" in
+    *-mingw32*)
+        # special stuff for Windoze NT
+	GPGSM_DEFAULT='c:\\gnupg\\gpgsm.exe'
+        GPG_AGENT_DEFAULT='c:\\gnupg\\gpg-agent.exe'
+	have_w32_system=yes
+        ;;
+    *)
+	;;
+esac
+
+if test "$have_w32_system" = yes; then
+   AC_DEFINE(HAVE_W32_SYSTEM,1, [Defined if we run on a W32 API based system])
+fi
+AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes)
+
+# Generate values for the DLL version info
+if test "$have_w32_system" = yes; then
+    BUILD_TIMESTAMP=`date --iso-8601=minutes`
+    changequote(,)dnl 
+    BUILD_FILEVERSION=`echo "$VERSION" | sed 's/\([0-9.]*\).*/\1./;s/\./,/g'`
+    changequote([,])dnl
+    BUILD_FILEVERSION="${BUILD_FILEVERSION}${BUILD_REVISION}"
+fi
+AC_SUBST(BUILD_REVISION)
+AC_SUBST(BUILD_TIMESTAMP)
+AC_SUBST(BUILD_FILEVERSION)
+
 # Checks for libraries.
 # Checks for libraries.
 
 
+AC_CHECK_FUNCS([ttyname localtime_r timegm])
+AC_REPLACE_FUNCS([vasprintf stpcpy])
+
 # The error code library.  Error codes are sent over the IPC layer and
 # The error code library.  Error codes are sent over the IPC layer and
 # have to be interpreted.
 # have to be interpreted.
 AM_PATH_GPG_ERROR("$NEED_GPG_ERROR_VERSION",
 AM_PATH_GPG_ERROR("$NEED_GPG_ERROR_VERSION",
@@ -153,7 +191,6 @@ AM_PATH_LIBASSUAN("$NEED_LIBASSUAN_VERSION",
                   have_libassuan=yes, have_libassuan=no)
                   have_libassuan=yes, have_libassuan=no)
 
 
 # GPGSM
 # GPGSM
-GPGSM_DEFAULT=no
 NO_OVERRIDE=no
 NO_OVERRIDE=no
 AC_ARG_WITH(gpgsm,
 AC_ARG_WITH(gpgsm,
 	    AC_HELP_STRING([--with-gpgsm=PATH], [use GpgSM binary at PATH]),
 	    AC_HELP_STRING([--with-gpgsm=PATH], [use GpgSM binary at PATH]),
@@ -246,6 +283,42 @@ if test "$ok" = "maybe"; then
 fi
 fi
 gpgsm_ok="$ok"
 gpgsm_ok="$ok"
 
 
+# GPG_AGENT
+NO_OVERRIDE=no
+AC_ARG_WITH(gpg-agent,
+	    AC_HELP_STRING([--with-gpg-agent=PATH], [use GPG Agent binary at PATH]),
+	    GPG_AGENT=$withval, NO_OVERRIDE=yes)
+if test "$NO_OVERRIDE" = "yes" || test "$GPG_AGENT" = "yes"; then
+  GPG_AGENT=
+  NO_OVERRIDE=yes
+  if test "$cross_compiling" != "yes"; then
+    AC_PATH_PROG(GPG_AGENT, gpg-agent)
+  fi
+  if test -z "$GPG_AGENT"; then
+    GPG_AGENT="$GPG_AGENT_DEFAULT"
+  fi
+fi
+if test "$GPG_AGENT" = no; then
+  if test "$NO_OVERRIDE" = "yes"; then
+    if test "$cross_compiling" != "yes"; then
+      AC_MSG_WARN([
+***
+*** Could not find GPG Agent, install GPG Agent or use --with-gpg-agent=PATH to enable it
+***])
+    else
+      AC_MSG_ERROR([
+***
+*** Can not determine path to GPG Agent when cross-compiling, use --with-gpg-agent=PATH
+***])
+    fi
+  fi
+else
+  AC_DEFINE_UNQUOTED(GPG_AGENT_PATH, "$GPG_AGENT", [Path to the GPG_AGENT binary.])
+  AC_DEFINE(ENABLE_GPG_AGENT,1, [Whether GPG Agent support is enabled])
+fi
+AM_CONDITIONAL(HAVE_GPG_AGENT, test "$GPG_AGENT" != "no")
+
+
 # Checks for header files.
 # Checks for header files.
 AC_HEADER_STDC
 AC_HEADER_STDC
 AC_CHECK_HEADERS([stdlib.h string.h])
 AC_CHECK_HEADERS([stdlib.h string.h])
@@ -303,5 +376,6 @@ AC_CONFIG_FILES([Makefile
                  src/Makefile
                  src/Makefile
                  tests/Makefile
                  tests/Makefile
 		 doc/manual/Makefile
 		 doc/manual/Makefile
-		 doc/Makefile])
+		 doc/Makefile
+                 src/versioninfo.rc])
 AC_OUTPUT
 AC_OUTPUT

+ 43 - 5
src/Makefile.am

@@ -29,7 +29,7 @@
 
 
 ## Process this file with automake to produce Makefile.in
 ## Process this file with automake to produce Makefile.in
 
 
-EXTRA_DIST = libscute.vers scute.def
+EXTRA_DIST = libscute.vers scute.def versioninfo.rc.in
 
 
 lib_LTLIBRARIES = libscute.la
 lib_LTLIBRARIES = libscute.la
 
 
@@ -39,15 +39,53 @@ else
 scute_version_script_cmd =
 scute_version_script_cmd =
 endif
 endif
 
 
-libscute_la_LDFLAGS = $(scute_version_script_cmd) -version-info \
-  @LIBSCUTE_LT_CURRENT@:@LIBSCUTE_LT_REVISION@:@LIBSCUTE_LT_AGE@
+if HAVE_W32_SYSTEM
+
+LTRCCOMPILE = $(LIBTOOL) --mode=compile $(RC) \
+     `echo $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) | \
+     sed -e 's/-I/--include-dir /g;s/-D/--define /g'`
+
+SUFFIXES: .rc .lo
+
+.rc.lo:
+	$(LTRCCOMPILE) -i $< -o $@
+
+scute_res = versioninfo.lo
+scute_res_ldflag = -Wl,.libs/versioninfo.o
+
+no_undefined = -no-undefined
+export_symbols = -export-symbols $(srcdir)/scute.def
+
+install-def-file:
+	$(INSTALL) $(srcdir)/scute.def $(DESTDIR)$(libdir)/scute.def
+
+uninstall-def-file:
+	-rm $(DESTDIR)$(libdir)/scute.def
+
+scute_deps = $(scute_res) scute.def
+
+else
+scute_res =
+scute_res_ldflag =
+no_undefined =
+export_symbols =
+install-def-file:
+uninstall-def-file:
+
+scute_deps =
+endif
+
+libscute_la_LDFLAGS = $(scute_res_ldflag) $(no_undefined) $(export_symbols) \
+	$(scute_version_script_cmd) -version-info \
+	@LIBSCUTE_LT_CURRENT@:@LIBSCUTE_LT_REVISION@:@LIBSCUTE_LT_AGE@
+libscute_la_DEPENDENCIES = @LTLIBOBJS@ $(srcdir)/libscute.vers $(scute_deps)
+libscute_la_LIBADD = @LTLIBOBJS@ @LIBASSUAN_LIBS@ @GPG_ERROR_LIBS@
 
 
 libscute_la_CPPFLAGS = -I$(srcdir)/../include \
 libscute_la_CPPFLAGS = -I$(srcdir)/../include \
 	@LIBASSUAN_CFLAGS@ @GPG_ERROR_CFLAGS@
 	@LIBASSUAN_CFLAGS@ @GPG_ERROR_CFLAGS@
-libscute_la_LIBADD = @LIBASSUAN_LIBS@ @GPG_ERROR_LIBS@
 libscute_la_SOURCES = cryptoki.h pkcs11.h debug.h settings.h support.h	\
 libscute_la_SOURCES = cryptoki.h pkcs11.h debug.h settings.h support.h	\
 	locking.h locking.c error-mapping.h error-mapping.c		\
 	locking.h locking.c error-mapping.h error-mapping.c		\
-	agent.h agent.c							\
+	get-path.c agent.h agent.c					\
 	slots.h slots.c table.h table.c					\
 	slots.h slots.c table.h table.c					\
 	cert.h cert-gpgsm.c cert-object.c gpgsm.h gpgsm.c		\
 	cert.h cert-gpgsm.c cert-object.c gpgsm.h gpgsm.c		\
 	p11-cancelfunction.c p11-closeallsessions.c p11-closesession.c	\
 	p11-cancelfunction.c p11-closeallsessions.c p11-closesession.c	\

+ 325 - 27
src/agent.c

@@ -1,5 +1,5 @@
 /* agent.c - Talking to gpg-agent.
 /* agent.c - Talking to gpg-agent.
-   Copyright (C) 2006, 2007 g10 Code GmbH
+   Copyright (C) 2006, 2007, 2008 g10 Code GmbH
 
 
    This file is part of Scute.
    This file is part of Scute.
  
  
@@ -45,53 +45,337 @@
 #include "support.h"
 #include "support.h"
 #include "agent.h"
 #include "agent.h"
 
 
+#ifdef HAVE_W32_SYSTEM
+#define PATHSEP_C ';'
+#else
+#define PATHSEP_C ':'
+#endif
+
 
 
 /* The global agent context.  */
 /* The global agent context.  */
 static assuan_context_t agent_ctx = NULL;
 static assuan_context_t agent_ctx = NULL;
 
 
+
+#ifdef HAVE_W32_SYSTEM
+/* Helper function to build_w32_commandline. */
+static char *
+build_w32_commandline_copy (char *buffer, const char *string)
+{
+  char *p = buffer;
+  const char *s;
+
+  if (!*string) /* Empty string. */
+    p = stpcpy (p, "\"\"");
+  else if (strpbrk (string, " \t\n\v\f\""))
+    {
+      /* Need top do some kind of quoting.  */
+      p = stpcpy (p, "\"");
+      for (s=string; *s; s++)
+        {
+          *p++ = *s;
+          if (*s == '\"')
+            *p++ = *s;
+        }
+      *p++ = '\"';
+      *p = 0;
+    }
+  else
+    p = stpcpy (p, string);
+
+  return p;
+}
+
+
+/* Build a command line for use with W32's CreateProcess.  On success
+   CMDLINE gets the address of a newly allocated string.  */
+static gpg_error_t
+build_w32_commandline (const char *pgmname, const char * const *argv, 
+                       char **cmdline)
+{
+  int i, n;
+  const char *s;
+  char *buf, *p;
+
+  *cmdline = NULL;
+  n = 0;
+  s = pgmname;
+  n += strlen (s) + 1 + 2;  /* (1 space, 2 quoting */
+  for (; *s; s++)
+    if (*s == '\"')
+      n++;  /* Need to double inner quotes.  */
+  for (i=0; (s=argv[i]); i++)
+    {
+      n += strlen (s) + 1 + 2;  /* (1 space, 2 quoting */
+      for (; *s; s++)
+        if (*s == '\"')
+          n++;  /* Need to double inner quotes.  */
+    }
+  n++;
+
+  buf = p = malloc (n);
+  if (!buf)
+    return gpg_error_from_syserror ();
+
+  p = build_w32_commandline_copy (p, pgmname);
+  for (i=0; argv[i]; i++) 
+    {
+      *p++ = ' ';
+      p = build_w32_commandline_copy (p, argv[i]);
+    }
+
+  *cmdline= buf;
+  return 0;
+}
+#endif /*HAVE_W32_SYSTEM*/
+
+
+/* Spawn a new process and immediatley detach from it.  The name of
+   the program to exec is PGMNAME and its arguments are in ARGV (the
+   programname is automatically passed as first argument).  An error
+   is returned if pgmname is not executable; to make this work it is
+   necessary to provide an absolute file name.  All standard file
+   descriptors are connected to /dev/null.  */
+static gpg_error_t
+spawn_process_detached (const char *pgmname, const char *argv[])
+{
+#ifdef HAVE_W32_SYSTEM
+  gpg_error_t err;
+  SECURITY_ATTRIBUTES sec_attr;
+  PROCESS_INFORMATION pi = 
+    {
+      NULL,      /* Returns process handle.  */
+      0,         /* Returns primary thread handle.  */
+      0,         /* Returns pid.  */
+      0          /* Returns tid.  */
+    };
+  STARTUPINFO si;
+  int cr_flags;
+  char *cmdline;
+
+  if (access (pgmname, X_OK))
+    return gpg_error_from_syserror ();
+
+  /* Prepare security attributes.  */
+  memset (&sec_attr, 0, sizeof sec_attr );
+  sec_attr.nLength = sizeof sec_attr;
+  sec_attr.bInheritHandle = FALSE;
+  
+  /* Build the command line.  */
+  err = build_w32_commandline (pgmname, argv, &cmdline);
+  if (err)
+    return err; 
+
+  /* Start the process.  */
+  memset (&si, 0, sizeof si);
+  si.cb = sizeof (si);
+  si.dwFlags = STARTF_USESHOWWINDOW;
+  si.wShowWindow = SW_MINIMIZE;
+
+  cr_flags = (CREATE_DEFAULT_ERROR_MODE
+              | GetPriorityClass (GetCurrentProcess ())
+              | CREATE_NEW_PROCESS_GROUP
+              | DETACHED_PROCESS); 
+  DEBUG ("CreateProcess(detached), path=`%s' cmdline=`%s'\n",
+	 pgmname, cmdline);
+  if (!CreateProcess (pgmname,       /* Program to start.  */
+                      cmdline,       /* Command line arguments.  */
+                      &sec_attr,     /* Process security attributes.  */
+                      &sec_attr,     /* Thread security attributes.  */
+                      FALSE,         /* Inherit handles.  */
+                      cr_flags,      /* Creation flags.  */
+                      NULL,          /* Environment.  */
+                      NULL,          /* Use current drive/directory.  */
+                      &si,           /* Startup information. */
+                      &pi            /* Returns process information.  */
+                      ))
+    {
+      DEBUG ("CreateProcess(detached) failed: %i\n", GetLastError ());
+      free (cmdline);
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+  free (cmdline);
+  cmdline = NULL;
+
+  DEBUG ("CreateProcess(detached) ready: hProcess=%p hThread=%p"
+	 " dwProcessID=%d dwThreadId=%d\n",
+	 pi.hProcess, pi.hThread,
+	 (int) pi.dwProcessId, (int) pi.dwThreadId);
+
+  CloseHandle (pi.hThread); 
+
+  return 0;
+
+#else
+  pid_t pid;
+  int i;
+
+  if (getuid() != geteuid())
+    return gpg_error (GPG_ERR_BUG);
+
+  if (access (pgmname, X_OK))
+    return gpg_error_from_syserror ();
+
+  pid = fork ();
+  if (pid == (pid_t)(-1))
+    {
+      DEBUG (_("error forking process: %s\n"), strerror (errno));
+      return gpg_error_from_syserror ();
+    }
+  if (!pid)
+    {
+      gcry_control (GCRYCTL_TERM_SECMEM);
+      if (setsid() == -1 || chdir ("/"))
+        _exit (1);
+      pid = fork (); /* Double fork to let init take over the new child. */
+      if (pid == (pid_t)(-1))
+        _exit (1);
+      if (pid)
+        _exit (0);  /* Let the parent exit immediately. */
+
+      do_exec (pgmname, argv, -1, -1, -1, NULL);
+
+      /*NOTREACHED*/
+    }
+  
+  if (waitpid (pid, NULL, 0) == -1)
+    DEBUG ("waitpid failed in spawn_process_detached: %s", strerror (errno));
+
+  return 0;
+#endif /* !HAVE_W32_SYSTEM*/
+}
+
 
 
 /* Establish a connection to a running GPG agent.  */
 /* Establish a connection to a running GPG agent.  */
 static gpg_error_t
 static gpg_error_t
 agent_connect (assuan_context_t *ctx_r)
 agent_connect (assuan_context_t *ctx_r)
 {
 {
+  /* If we ever failed to connect via a socket we will force the use
+     of the pipe based server for the lifetime of the process.  */
+  static int force_pipe_server = 0;
+
   gpg_error_t err = 0;
   gpg_error_t err = 0;
   char *infostr;
   char *infostr;
   char *ptr;
   char *ptr;
-  int pid;
-  int protocol_version;
 
 
-  infostr = getenv ("GPG_AGENT_INFO");
-  if (!infostr)
+ restart:
+
+  infostr = force_pipe_server ? NULL : getenv ("GPG_AGENT_INFO");
+  if (!infostr || !*infostr)
     {
     {
-      DEBUG ("missing GPG_AGENT_INFO environment variable");
-      return gpg_error (GPG_ERR_NO_AGENT);
-    }
+      char *sockname;
 
 
-  infostr = strdup (infostr);
-  if (!infostr)
-    return gpg_error_from_errno (errno);
+      /* First check whether we can connect at the standard
+         socket.  */
+      sockname = make_filename (default_homedir (), "S.gpg-agent", NULL);
+      if (! sockname)
+	return gpg_error_from_errno (errno);
+	
+      err = assuan_socket_connect (ctx_r, sockname, 0);
 
 
-  if (!(ptr = strchr (infostr, ':')) || ptr == infostr)
-    {
-      DEBUG ("malformed GPG_AGENT_INFO environment variable");
-      free (infostr);
-      return gpg_error (GPG_ERR_NO_AGENT);
+      if (err)
+        {
+	  const char *agent_program;
+
+          /* With no success start a new server.  */
+	  DEBUG ("no running GPG agent, starting one");
+          
+          agent_program = get_gpg_agent_path ();
+
+#ifdef HAVE_W32_SYSTEM
+          {
+            /* Under Windows we start the server in daemon mode.  This
+               is because the default is to use the standard socket
+               and thus there is no need for the GPG_AGENT_INFO
+               envvar.  This is possible as we don't have a real unix
+               domain socket but use a plain file and thus there is no
+               need to care about non-local file systems. */
+            const char *argv[3];
+
+            argv[0] = "--daemon";
+            argv[1] = "--use-standard-socket"; 
+            argv[2] = NULL;  
+
+            err = spawn_process_detached (agent_program, argv);
+            if (err)
+              DEBUG ("failed to start agent `%s': %s\n",
+		     agent_program, gpg_strerror (err));
+            else
+              {
+                /* Give the agent some time to prepare itself. */
+                _sleep (3);
+                /* Now try again to connect the agent.  */
+                err = assuan_socket_connect (ctx_r, sockname, 0);
+              }
+          }
+#else /*!HAVE_W32_SYSTEM*/
+          {
+            const char *pgmname;
+            const char *argv[3];
+            int no_close_list[3];
+            int i;
+
+            if ( !(pgmname = strrchr (agent_program, '/')))
+              pgmname = agent_program;
+            else
+              pgmname++;
+            
+            argv[0] = pgmname;
+            argv[1] = "--server";
+            argv[2] = NULL;
+            
+            i=0;
+            no_close_list[i++] = fileno (stderr);
+            no_close_list[i] = -1;
+            
+            /* Connect to the agent and perform initial handshaking. */
+            err = assuan_pipe_connect (ctx_r, agent_program, argv,
+				       no_close_list);
+          }
+#endif /*!HAVE_W32_SYSTEM*/
+        }
+      free (sockname);
     }
     }
-  *(ptr++) = 0;
-  pid = atoi (ptr);
-  while (*ptr && *ptr != ':')
-    ptr++;
-  protocol_version = *ptr ? atoi (ptr + 1) : 0;
-  if (protocol_version != 1)
+  else
     {
     {
-      DEBUG ("GPG agent protocol version '%d' not supported",
-	     protocol_version);
+      int pid;
+      int protocol_version;
+
+      infostr = strdup (infostr);
+      if (!infostr)
+	return gpg_error_from_errno (errno);
+
+      if (!(ptr = strchr (infostr, PATHSEP_C)) || ptr == infostr)
+	{
+	  DEBUG ("malformed GPG_AGENT_INFO environment variable");
+	  free (infostr);
+	  force_pipe_server = 1;
+	  goto restart;
+	}
+
+      *(ptr++) = 0;
+      pid = atoi (ptr);
+      while (*ptr && *ptr != PATHSEP_C)
+	ptr++;
+      protocol_version = *ptr ? atoi (ptr + 1) : 0;
+      if (protocol_version != 1)
+	{
+	  DEBUG ("GPG agent protocol version '%d' not supported",
+		 protocol_version);
+	  free (infostr);
+	  force_pipe_server = 1;
+	  goto restart;
+	}
+      
+      err = assuan_socket_connect (ctx_r, infostr, pid);
       free (infostr);
       free (infostr);
-      return gpg_error (GPG_ERR_NO_AGENT);
+      if (err)
+	{
+	  DEBUG ("cannot connect to GPG agent: %s", gpg_strerror (err));
+	  force_pipe_server = 1;
+	  goto restart;
+	}
     }
     }
 
 
-  err = assuan_socket_connect (ctx_r, infostr, pid);
-  free (infostr);
   if (err)
   if (err)
     {
     {
       DEBUG ("cannot connect to GPG agent: %s", gpg_strerror (err));
       DEBUG ("cannot connect to GPG agent: %s", gpg_strerror (err));
@@ -139,6 +423,8 @@ agent_configure (assuan_context_t ctx)
   char *old_lc = NULL;
   char *old_lc = NULL;
   char *dft_lc = NULL;
   char *dft_lc = NULL;
 #endif
 #endif
+  char *dft_xauthority = NULL;
+  char *dft_pinentry_user_data = NULL;
 
 
   err = agent_simple_cmd (ctx, "RESET");
   err = agent_simple_cmd (ctx, "RESET");
   if (err)
   if (err)
@@ -205,6 +491,18 @@ agent_configure (assuan_context_t ctx)
     }
     }
 #endif
 #endif
 
 
+  dft_xauthority = getenv ("XAUTHORITY");
+  if (dft_xauthority)
+    err = agent_simple_cmd (ctx, "OPTION xauthority=%s", dft_display);
+  if (err)
+    return err;
+
+  dft_pinentry_user_data = getenv ("PINENTRY_USER_DATA");
+  if (dft_pinentry_user_data)
+    err = agent_simple_cmd (ctx, "OPTION pinentry_user_data=%s", dft_display);
+  if (err)
+    return err;
+
   return err;
   return err;
 }
 }
 
 

+ 18 - 15
src/cert-gpgsm.c

@@ -43,6 +43,11 @@
 #include "cert.h"
 #include "cert.h"
 #include "support.h"
 #include "support.h"
 
 
+
+#ifndef HAVE_W32_SYSTEM
+#define COMPAT_FALLBACK
+#endif
+
 
 
 /* The maximum length of a key listing line.  We take the double of
 /* The maximum length of a key listing line.  We take the double of
    the allowed Assuan line length to avoid a memmove after a part of a
    the allowed Assuan line length to avoid a memmove after a part of a
@@ -128,7 +133,14 @@ parse_timestamp (const char *timestamp, char **endp)
 
 
       if (endp)
       if (endp)
         *endp = (char*)(timestamp + 15);
         *endp = (char*)(timestamp + 15);
+#ifdef HAVE_TIMEGM
       return timegm (&buf);
       return timegm (&buf);
+#else
+      /* FIXME: Need to set TZ to UTC, but that is not
+	 thread-safe.  */
+      return mktime (&buf);
+#endif
+
     }
     }
   else
   else
     return (time_t)strtoul (timestamp, endp, 10);
     return (time_t)strtoul (timestamp, endp, 10);
@@ -519,6 +531,7 @@ struct search_ctx_by_field
 };
 };
   
   
 
 
+#ifdef COMPAT_FALLBACK
 /* This is a compatibility function for GPGSM 2.0.0, which does not
 /* This is a compatibility function for GPGSM 2.0.0, which does not
    support the --data option with the EXPORT command.  */
    support the --data option with the EXPORT command.  */
 static gpg_error_t
 static gpg_error_t
@@ -581,6 +594,7 @@ export_cert_compat (char *fpr, struct cert *cert)
   close (output_fds[0]);
   close (output_fds[0]);
   return err;
   return err;
 }
 }
+#endif
 
 
 
 
 struct export_hook
 struct export_hook
@@ -630,23 +644,11 @@ export_cert (char *fpr, struct cert *cert)
   const char *argv[] = { "gpgsm", "--server", NULL };
   const char *argv[] = { "gpgsm", "--server", NULL };
 #define COMMANDLINELEN 80
 #define COMMANDLINELEN 80
   char cmd[COMMANDLINELEN];
   char cmd[COMMANDLINELEN];
-  int output_fds[2];
-  int child_fds[2];
   struct export_hook exp;
   struct export_hook exp;
 
 
-  if(pipe (output_fds) < 0)
-    return gpg_error_from_syserror ();
-
-  child_fds[0] = output_fds[1];
-  child_fds[1] = -1;
-
-  err = assuan_pipe_connect (&ctx, GPGSM_PATH, argv, child_fds);
-  close (output_fds[1]);
+  err = assuan_pipe_connect (&ctx, GPGSM_PATH, argv, NULL);
   if (err)
   if (err)
-    {
-      close (output_fds[0]);
-      return err;
-    }
+    return err;
 
 
   exp.buffer = NULL;
   exp.buffer = NULL;
   exp.buffer_len = 0;
   exp.buffer_len = 0;
@@ -656,13 +658,13 @@ export_cert (char *fpr, struct cert *cert)
   err = assuan_transact (ctx, cmd, export_cert_cb, &exp,
   err = assuan_transact (ctx, cmd, export_cert_cb, &exp,
 			 NULL, NULL, NULL, NULL);
 			 NULL, NULL, NULL, NULL);
   assuan_disconnect (ctx);
   assuan_disconnect (ctx);
-  close (output_fds[0]);
 
 
   if (!err)
   if (!err)
     {
     {
       cert->cert_der = exp.buffer;
       cert->cert_der = exp.buffer;
       cert->cert_der_len = exp.buffer_len;
       cert->cert_der_len = exp.buffer_len;
     }
     }
+#ifdef COMPAT_FALLBACK
   else if (gpg_err_code (err) == GPG_ERR_ASS_NO_OUTPUT)
   else if (gpg_err_code (err) == GPG_ERR_ASS_NO_OUTPUT)
     {
     {
       /* For compatibility with GPGSM 2.0.0, we fall back to a work
       /* For compatibility with GPGSM 2.0.0, we fall back to a work
@@ -674,6 +676,7 @@ export_cert (char *fpr, struct cert *cert)
 	}
 	}
       err = export_cert_compat (fpr, cert);
       err = export_cert_compat (fpr, cert);
     }
     }
+#endif
 
 
   if (!err)
   if (!err)
     err = scute_agent_is_trusted (fpr, &cert->is_trusted);
     err = scute_agent_is_trusted (fpr, &cert->is_trusted);

+ 10 - 0
src/cert-object.c

@@ -59,8 +59,18 @@ time_to_ck_date (time_t *atime, CK_DATE *ckdate)
   if (!*atime)
   if (!*atime)
     return false;
     return false;
 
 
+#ifdef HAVE_LOCALTIME_R
   if (!localtime_r (atime, &broken_time))
   if (!localtime_r (atime, &broken_time))
     return false;
     return false;
+#else
+  {
+    /* FIXME: This is not thread-safe, but it minimizes risk.  */
+    struct tm *b_time = localtime (atime);
+    if (!b_time)
+      return false;
+    memcpy (&broken_time, b_time, sizeof (*b_time));
+  }
+#endif
 
 
   /* We can only represent years until 9999.  */
   /* We can only represent years until 9999.  */
   if (!(broken_time.tm_year >= 0 && broken_time.tm_year <= 8099
   if (!(broken_time.tm_year >= 0 && broken_time.tm_year <= 8099

+ 456 - 0
src/get-path.c

@@ -0,0 +1,456 @@
+/* agent.c - Talking to gpg-agent.
+   Copyright (C) 2008 g10 Code GmbH
+
+   This file is part of Scute.
+ 
+   Scute 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.
+
+   Scute 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 Scute; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+   In addition, as a special exception, g10 Code GmbH gives permission
+   to link this library: with the Mozilla Foundation's code for
+   Mozilla (or with modified versions of it that use the same license
+   as the "Mozilla" code), and distribute the linked executables.  You
+   must obey the GNU General Public License in all respects for all of
+   the code used other than "Mozilla".  If you modify this file, you
+   may extend this exception to your version of the file, but you are
+   not obligated to do so.  If you do not wish to do so, delete this
+   exception statement from your version.  */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#ifdef HAVE_W32_SYSTEM
+#include <windows.h>
+#include <shlobj.h>
+#include <io.h>
+#endif
+
+#include "support.h"
+
+#ifdef HAVE_W32_SYSTEM
+#define GNUPG_DEFAULT_HOMEDIR "c:/gnupg"
+#elif defined(__VMS)
+#define GNUPG_DEFAULT_HOMEDIR "/SYS\$LOGIN/gnupg" 
+#else
+#define GNUPG_DEFAULT_HOMEDIR "~/.gnupg"
+#endif 
+
+#ifdef HAVE_DOSISH_SYSTEM
+#define DIRSEP_C '\\'
+#define DIRSEP_S "\\"
+#else
+#define DIRSEP_C '/'
+#define DIRSEP_S "/"
+#endif
+
+
+#ifdef HAVE_W32_SYSTEM
+#define RTLD_LAZY 0
+
+static __inline__ void *
+dlopen (const char * name, int flag)
+{
+  void * hd = LoadLibrary (name);
+  return hd;
+}
+
+static __inline__ void *
+dlsym (void * hd, const char * sym)
+{
+  if (hd && sym)
+    {
+      void * fnc = GetProcAddress (hd, sym);
+      if (!fnc)
+        return NULL;
+      return fnc;
+    }
+  return NULL;
+}
+
+static __inline__ int
+dlclose (void * hd)
+{
+  if (hd)
+    {
+      FreeLibrary (hd);
+      return 0;
+    }
+  return -1;
+}  
+
+
+/* Return a string from the W32 Registry or NULL in case of error.
+   Caller must release the return value.  A NULL for root is an alias
+   for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. */
+static char *
+read_w32_registry_string (const char *root, const char *dir, const char *name)
+{
+  HKEY root_key, key_handle;
+  DWORD n1, nbytes, type;
+  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 ) )
+    {
+      if (root)
+        return NULL; /* no need for a RegClose, so return direct */
+      /* It seems to be common practise to fall back to HKLM. */
+      if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
+        return NULL; /* still no need for a RegClose, so return direct */
+    }
+
+  nbytes = 1;
+  if ( RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes ) )
+    {
+      if (root)
+        goto leave;
+      /* Try to fallback to HKLM also vor a missing value.  */
+      RegCloseKey (key_handle);
+      if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
+        return NULL; /* Nope.  */
+      if (RegQueryValueEx ( key_handle, name, 0, NULL, NULL, &nbytes))
+        goto leave;
+    }
+  result = malloc ( (n1=nbytes+1) );
+  if ( !result )
+    goto leave;
+  if ( RegQueryValueEx ( key_handle, name, 0, &type, result, &n1 ) )
+    {
+      free(result); result = NULL;
+      goto leave;
+    }
+  result[nbytes] = 0; /* Make sure it is really a string.  */
+  if (type == REG_EXPAND_SZ && strchr (result, '%')) 
+    {
+      char *tmp;
+        
+      n1 += 1000;
+      tmp = malloc (n1+1);
+      if (!tmp)
+        goto leave;
+      nbytes = ExpandEnvironmentStrings (result, tmp, n1);
+      if (nbytes && nbytes > n1)
+        {
+          free (tmp);
+          n1 = nbytes;
+          tmp = malloc (n1 + 1);
+          if (!tmp)
+            goto leave;
+          nbytes = ExpandEnvironmentStrings (result, tmp, n1);
+          if (nbytes && nbytes > n1) {
+            free (tmp); /* Oops - truncated, better don't expand at all. */
+            goto leave;
+          }
+          tmp[nbytes] = 0;
+          free (result);
+          result = tmp;
+        }
+      else if (nbytes)  /* Okay, reduce the length. */
+        {
+          tmp[nbytes] = 0;
+          free (result);
+          result = malloc (strlen (tmp)+1);
+          if (!result)
+            result = tmp;
+          else 
+            {
+              strcpy (result, tmp);
+              free (tmp);
+            }
+        }
+      else  /* Error - don't expand. */
+        {
+          free (tmp);
+        }
+    }
+
+ leave:
+  RegCloseKey( key_handle );
+  return result;
+}
+
+
+/* This is a helper function to load and run a Windows function from
+   either of one DLLs. */
+static HRESULT
+w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
+{
+  static int initialized;
+  static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
+
+  if (!initialized)
+    {
+      static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
+      void *handle;
+      int i;
+
+      initialized = 1;
+
+      for (i=0, handle = NULL; !handle && dllnames[i]; i++)
+        {
+          handle = dlopen (dllnames[i], RTLD_LAZY);
+          if (handle)
+            {
+              func = dlsym (handle, "SHGetFolderPathA");
+              if (!func)
+                {
+                  dlclose (handle);
+                  handle = NULL;
+                }
+            }
+        }
+    }
+
+  if (func)
+    return func (a,b,c,d,e);
+  else
+    return -1;
+}
+
+static char *
+find_program_at_standard_place (const char *name)
+{
+  char path[MAX_PATH];
+  char *result = NULL;
+      
+  if (w32_shgetfolderpath (NULL, CSIDL_PROGRAM_FILES, NULL, 0, path) >= 0) 
+    {
+      result = malloc (strlen (path) + 1 + strlen (name) + 1);
+      if (result)
+        {
+          strcpy (stpcpy (stpcpy (result, path), "\\"), name);
+          if (access (result, F_OK))
+            {
+              free (result);
+              result = NULL;
+            }
+        }
+    }
+  return result;
+}
+#endif
+
+
+const char *
+get_gpgsm_path (void)
+{
+  static const char *pgmname;
+
+#ifdef HAVE_W32_SYSTEM
+  if (!pgmname)
+    pgmname = find_program_at_standard_place ("GNU\\GnuPG\\gpgsm.exe");
+#endif
+  if (!pgmname)
+    pgmname = GPGSM_PATH;
+  return pgmname;
+}
+
+
+const char *
+get_gpg_agent_path (void)
+{
+  static const char *pgmname;
+
+#ifdef HAVE_W32_SYSTEM
+  if (!pgmname)
+    pgmname = find_program_at_standard_place ("GNU\\GnuPG\\gpg-agent.exe");
+#endif
+  if (!pgmname)
+    pgmname = GPG_AGENT_PATH;
+  return pgmname;
+}
+
+
+
+/* Home directory.  */
+
+#ifdef HAVE_W32_SYSTEM
+#ifndef CSIDL_APPDATA
+#define CSIDL_APPDATA 0x001a
+#endif
+#ifndef CSIDL_LOCAL_APPDATA
+#define CSIDL_LOCAL_APPDATA 0x001c
+#endif
+#ifndef CSIDL_COMMON_APPDATA
+#define CSIDL_COMMON_APPDATA 0x0023
+#endif
+#ifndef CSIDL_FLAG_CREATE
+#define CSIDL_FLAG_CREATE 0x8000
+#endif
+#endif /*HAVE_W32_SYSTEM*/
+
+/* Get the standard home directory.  In general this function should
+   not be used as it does not consider a registry value (under W32) or
+   the GNUPGHOME environment variable.  It is better to use
+   default_homedir(). */
+const char *
+standard_homedir (void)
+{
+#ifdef HAVE_W32_SYSTEM
+  static const char *dir;
+
+  if (!dir)
+    {
+      char path[MAX_PATH];
+      
+      /* It might be better to use LOCAL_APPDATA because this is
+         defined as "non roaming" and thus more likely to be kept
+         locally.  For private keys this is desired.  However, given
+         that many users copy private keys anyway forth and back,
+         using a system roaming services might be better than to let
+         them do it manually.  A security conscious user will anyway
+         use the registry entry to have better control.  */
+      if (w32_shgetfolderpath (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, 
+                               NULL, 0, path) >= 0) 
+        {
+          char *tmp = malloc (strlen (path) + 6 +1);
+	  if (tmp)
+	    {
+	      strcpy (stpcpy (tmp, path), "\\gnupg");
+	      dir = tmp;
+          
+	      /* Try to create the directory if it does not yet exists.  */
+	      if (access (dir, F_OK))
+		CreateDirectory (dir, NULL);
+	    }
+        }
+
+      if (!dir)
+        dir = GNUPG_DEFAULT_HOMEDIR;
+    }
+  return dir;
+#else/*!HAVE_W32_SYSTEM*/
+  return GNUPG_DEFAULT_HOMEDIR;
+#endif /*!HAVE_W32_SYSTEM*/
+}
+
+/* Set up the default home directory.  The usual --homedir option
+   should be parsed later. */
+const char *
+default_homedir (void)
+{
+  const char *dir;
+
+  dir = getenv ("GNUPGHOME");
+#ifdef HAVE_W32_SYSTEM
+  if (!dir || !*dir)
+    {
+      static const char *saved_dir;
+      
+      if (!saved_dir)
+        {
+          if (!dir || !*dir)
+            {
+              char *tmp;
+
+              tmp = read_w32_registry_string (NULL, "Software\\GNU\\GnuPG",
+                                              "HomeDir");
+              if (tmp && *tmp)
+                {
+                  free (tmp);
+                  tmp = NULL;
+                }
+               if (tmp)
+                saved_dir = tmp;
+            }
+          
+          if (!saved_dir)
+            saved_dir = standard_homedir ();
+        }
+      dir = saved_dir;
+    }
+#endif /*HAVE_W32_SYSTEM*/
+  if (!dir || !*dir)
+    dir = GNUPG_DEFAULT_HOMEDIR;
+
+  return dir;
+}
+
+
+/* Construct a filename from the NULL terminated list of parts.  Tilde
+   expansion is done here.  */
+char *
+make_filename (const char *first_part, ...)
+{
+  va_list arg_ptr;
+  size_t n;
+  const char *s;
+  char *name;
+  char *home;
+  char *p;
+  
+  va_start (arg_ptr, first_part);
+  n = strlen (first_part) + 1;
+  while ((s = va_arg (arg_ptr, const char *)))
+    n += strlen (s) + 1;
+  va_end (arg_ptr);
+  
+  home = NULL;
+  if (*first_part == '~' && first_part[1] == '/'
+      && (home = getenv("HOME")) && *home)
+    n += strlen (home);
+
+  name = malloc (n);
+  if (! name)
+    return NULL;
+  p = (home 
+       ? stpcpy (stpcpy (name,home), first_part + 1)
+       : stpcpy (name, first_part));
+
+  va_start (arg_ptr, first_part);
+  while ((s = va_arg(arg_ptr, const char *)))
+    p = stpcpy (stpcpy (p,"/"), s);
+  va_end (arg_ptr);
+
+#ifdef HAVE_W32_SYSTEM
+  /* We better avoid mixing slashes and backslashes and prefer
+     backslashes.  There is usual no problem with mixing them, however
+     a very few W32 API calls can't grok plain slashes.  Printing
+     filenames with mixed slashes also looks a bit strange. */
+  if (strchr (name, '\\'))
+    {
+      for (p = name; *p; p++)
+        if (*p == '/')
+          *p = '\\';
+    }
+#endif
+
+  return name;
+}

+ 45 - 0
src/realloc.c

@@ -0,0 +1,45 @@
+/* realloc() function that is glibc compatible.
+
+   Copyright (C) 1997, 2003, 2004, 2006 Free Software Foundation, Inc.
+
+   This program 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, or (at your option)
+   any later version.
+
+   This program 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* written by Jim Meyering */
+
+#include <config.h>
+#undef realloc
+
+#include <stdlib.h>
+
+/* Change the size of an allocated block of memory P to N bytes,
+   with error checking.  If N is zero, change it to 1.  If P is NULL,
+   use malloc.  */
+
+void *
+rpl_realloc (void *p, size_t n)
+{
+  if (n == 0)
+    {
+      n = 1;
+
+      /* In theory realloc might fail, so don't rely on it to free.  */
+      free (p);
+      p = NULL;
+    }
+
+  if (p == NULL)
+    return malloc (n);
+  return realloc (p, n);
+}

+ 47 - 0
src/stpcpy.c

@@ -0,0 +1,47 @@
+/* stpcpy.c -- copy a string and return pointer to end of new string
+   Copyright (C) 1992, 1995, 1997-1998, 2006 Free Software Foundation, Inc.
+
+   NOTE: The canonical source of this file is maintained with the GNU C Library.
+   Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+   This program 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, or (at your option) any
+   later version.
+
+   This program 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+#include <config.h>
+
+#include <string.h>
+
+#undef __stpcpy
+#undef stpcpy
+
+#ifndef weak_alias
+# define __stpcpy stpcpy
+#endif
+
+/* Copy SRC to DEST, returning the address of the terminating '\0' in DEST.  */
+char *
+__stpcpy (char *dest, const char *src)
+{
+  register char *d = dest;
+  register const char *s = src;
+
+  do
+    *d++ = *s;
+  while (*s++ != '\0');
+
+  return d - 1;
+}
+#ifdef weak_alias
+weak_alias (__stpcpy, stpcpy)
+#endif

+ 41 - 0
src/stpcpy.h

@@ -0,0 +1,41 @@
+/* String copying.
+   Copyright (C) 1995, 2001, 2003 Free Software Foundation, Inc.
+
+   This program 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, or (at your option)
+   any later version.
+
+   This program 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+#ifndef _STPCPY_H
+#define _STPCPY_H
+
+#if HAVE_STPCPY
+
+/* Get stpcpy() declaration.  */
+#include <string.h>
+
+#else
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Copy SRC to DST, returning the address of the terminating '\0' in DST.  */
+extern char *stpcpy (char *dst, const char *src);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+#endif /* _STPCPY_H */

+ 29 - 0
src/support.h

@@ -51,5 +51,34 @@ scute_copy_string (char *dest, char *src, int max_len)
   while (i++ < max_len)
   while (i++ < max_len)
     *(dest++) = ' ';
     *(dest++) = ' ';
 }
 }
+
+#ifndef HAVE_STPCPY
+#include "stpcpy.h"
+#endif
+
+/*-- Simple replacement functions. */
+#ifndef HAVE_TTYNAME
+/* Systems without ttyname (W32) will merely return NULL. */
+static inline char *
+ttyname (int fd) 
+{
+  return NULL;
+}
+#endif /* !HAVE_TTYNAME */
+
+
+
+const char *get_gpgsm_path (void);
+const char *get_gpg_agent_path (void);
+
+/* Set up the default home directory.  The usual --homedir option
+   should be parsed later. */
+const char *default_homedir (void);
+
+/* Construct a filename from the NULL terminated list of parts.  Tilde
+   expansion is done here.  */
+char *make_filename (const char *first_part, ...);
+
+
     
     
 #endif	/* !SUPPORT_H */
 #endif	/* !SUPPORT_H */

+ 192 - 0
src/vasprintf.c

@@ -0,0 +1,192 @@
+/* Like vsprintf but provides a pointer to malloc'd storage, which must
+   be freed by the caller.
+   Copyright (C) 1994, 2002 Free Software Foundation, Inc.
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+
+#ifndef va_copy /* According to POSIX, va_copy is a macro. */
+#if defined (__GNUC__) && defined (__PPC__) \
+     && (defined (_CALL_SYSV) || defined (_WIN32))
+#define va_copy(d, s) (*(d) = *(s))
+#elif defined (MUST_COPY_VA_BYVAL)
+#define va_copy(d, s) ((d) = (s))
+#else 
+#define va_copy(d, s) memcpy ((d), (s), sizeof (va_list))
+#endif 
+#endif 
+
+
+#ifdef TEST
+int global_total_width;
+#endif
+
+static int int_vasprintf (char **, const char *, va_list *);
+
+static int
+int_vasprintf (result, format, args)
+     char **result;
+     const char *format;
+     va_list *args;
+{
+  const char *p = format;
+  /* Add one to make sure that it is never zero, which might cause malloc
+     to return NULL.  */
+  int total_width = strlen (format) + 1;
+  va_list ap;
+
+  va_copy (ap, *args);
+
+  while (*p != '\0')
+    {
+      if (*p++ == '%')
+	{
+	  while (strchr ("-+ #0", *p))
+	    ++p;
+	  if (*p == '*')
+	    {
+	      ++p;
+	      total_width += abs (va_arg (ap, int));
+	    }
+	  else
+	    total_width += strtoul (p, (char **) &p, 10);
+	  if (*p == '.')
+	    {
+	      ++p;
+	      if (*p == '*')
+		{
+		  ++p;
+		  total_width += abs (va_arg (ap, int));
+		}
+	      else
+	      total_width += strtoul (p, (char **) &p, 10);
+	    }
+	  while (strchr ("hlL", *p))
+	    ++p;
+	  /* Should be big enough for any format specifier except %s and floats.  */
+	  total_width += 30;
+	  switch (*p)
+	    {
+	    case 'd':
+	    case 'i':
+	    case 'o':
+	    case 'u':
+	    case 'x':
+	    case 'X':
+	    case 'c':
+	      (void) va_arg (ap, int);
+	      break;
+	    case 'f':
+	    case 'e':
+	    case 'E':
+	    case 'g':
+	    case 'G':
+	      (void) va_arg (ap, double);
+	      /* Since an ieee double can have an exponent of 307, we'll
+		 make the buffer wide enough to cover the gross case. */
+	      total_width += 307;
+	      break;
+	    case 's':
+              {
+                char *tmp = va_arg (ap, char *);
+                if (tmp)
+                  total_width += strlen (tmp);
+                else /* in case the vsprintf does prints a text */
+                  total_width += 25; /* e.g. "(null pointer reference)" */
+              }
+	      break;
+	    case 'p':
+	    case 'n':
+	      (void) va_arg (ap, char *);
+	      break;
+	    }
+	  p++;
+	}
+    }
+#ifdef TEST
+  global_total_width = total_width;
+#endif
+  *result = malloc (total_width);
+  if (*result != NULL)
+    return vsprintf (*result, format, *args);
+  else
+    return 0;
+}
+
+int
+vasprintf (result, format, args)
+     char **result;
+     const char *format;
+#if defined (_BSD_VA_LIST_) && defined (__FreeBSD__)
+     _BSD_VA_LIST_ args;
+#else
+     va_list args;
+#endif
+{
+  return int_vasprintf (result, format, &args);
+}
+
+
+int
+asprintf (char **buf, const char *fmt, ...)
+{
+  int status;
+  va_list ap;
+
+  va_start (ap, fmt);
+  status = vasprintf (buf, fmt, ap);
+  va_end (ap);
+  return status;
+}
+
+
+#ifdef TEST
+void
+checkit (const char* format, ...)
+{
+  va_list args;
+  char *result;
+
+  va_start (args, format);
+  vasprintf (&result, format, args);
+  if (strlen (result) < global_total_width)
+    printf ("PASS: ");
+  else
+    printf ("FAIL: ");
+  printf ("%d %s\n", global_total_width, result);
+}
+
+int
+main (void)
+{
+  checkit ("%d", 0x12345678);
+  checkit ("%200d", 5);
+  checkit ("%.300d", 6);
+  checkit ("%100.150d", 7);
+  checkit ("%s", "jjjjjjjjjiiiiiiiiiiiiiiioooooooooooooooooppppppppppppaa\n\
+777777777777777777333333333333366666666666622222222222777777777777733333");
+  checkit ("%f%s%d%s", 1.0, "foo", 77, "asdjffffffffffffffiiiiiiiiiiixxxxx");
+}
+#endif /* TEST */

+ 52 - 0
src/versioninfo.rc.in

@@ -0,0 +1,52 @@
+/* versioninfo.rc.in - for scute
+ *    Copyright (C) 2005 g10 Code GmbH
+ * 
+ * This file is free software; as a special exception the author gives
+ * unlimited permission to copy and/or distribute it, with or without
+ * modifications, as long as this notice is preserved.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+ 
+/* This file is processed by configure to create versioninfo.rc */
+
+#line __LINE__ "versioninfo.rc.in"
+
+#include <afxres.h>
+
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION @LIBSCUTE_LT_CURRENT@,@LIBSCUTE_LT_AGE@,@LIBSCUTE_LT_REVISION@,@BUILD_REVISION@
+ PRODUCTVERSION @BUILD_FILEVERSION@
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x21L
+#else
+ FILEFLAGS 0x20L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "Comments", "Provided under the terms of the GNU Lesser General Public License.\0"
+            VALUE "CompanyName", "g10 Code GmbH\0"
+            VALUE "FileDescription", "SCUTE - GnuPG Made Easy\0"
+            VALUE "FileVersion", "@LIBSCUTE_LT_CURRENT@.@LIBSCUTE_LT_AGE@.@LIBSCUTE_LT_REVISION@.@BUILD_REVISION@\0"
+            VALUE "InternalName", "scute\0"
+            VALUE "LegalCopyright", "Copyright © 2005, 2008 g10 Code GmbH\0"
+            VALUE "LegalTrademarks", "\0"
+            VALUE "OriginalFilename", "scute.dll\0"
+            VALUE "PrivateBuild", "\0"
+            VALUE "ProductName", "SCUTE\0"
+            VALUE "ProductVersion", "@VERSION@\0"
+            VALUE "SpecialBuild", "@BUILD_TIMESTAMP@\0"
+        END
+    END
+END
+

+ 5 - 1
tests/t-getslotinfo.c

@@ -1,5 +1,5 @@
 /* t-getslotinfo.c - Regression test.
 /* t-getslotinfo.c - Regression test.
-   Copyright (C) 2006 g10 Code GmbH
+   Copyright (C) 2006, 2008 g10 Code GmbH
 
 
    This file is part of Scute.
    This file is part of Scute.
  
  
@@ -105,7 +105,11 @@ main (int argc, char *argv[])
       printf ("    Firmware version: %i.%i\n", info.firmwareVersion.major,
       printf ("    Firmware version: %i.%i\n", info.firmwareVersion.major,
 	      info.firmwareVersion.minor);
 	      info.firmwareVersion.minor);
     }
     }
+#ifdef WIN32
+  _sleep (2);
+#else
   sleep (2);
   sleep (2);
+#endif
     }
     }
 
 
   return 0;
   return 0;