diff --git a/NEWS b/NEWS index 370d9c4a5..3b2016f3a 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -Noteworthy changes in version 2.1.0beta4 (unreleased) +Noteworthy changes in version 2.1.0-betaN (unreleased) ----------------------------------------------------- * GPG now accepts a space separated fingerprint as a user ID. This @@ -20,6 +20,8 @@ Noteworthy changes in version 2.1.0beta4 (unreleased) * Better support fo CCID readers. Now, internal CCID driver supports readers with no auto configuration feature. + * Support installation as portable application under Windows. + Noteworthy changes in version 2.1.0beta3 (2011-12-20) ----------------------------------------------------- diff --git a/common/homedir.c b/common/homedir.c index 28e5c9a2f..9fec78fc3 100644 --- a/common/homedir.c +++ b/common/homedir.c @@ -1,5 +1,6 @@ /* homedir.c - Setup the home directory. * Copyright (C) 2004, 2006, 2007, 2010 Free Software Foundation, Inc. + * Copyright (C) 2013 Werner Koch * * This file is part of GnuPG. * @@ -33,6 +34,9 @@ #include #ifdef HAVE_W32_SYSTEM +#include /* Due to the stupid mingw64 requirement to + include this header before windows.h which + is often implicitly included. */ #include #ifndef CSIDL_APPDATA #define CSIDL_APPDATA 0x001a @@ -53,6 +57,33 @@ #include "util.h" #include "sysutils.h" +#ifdef HAVE_W32_SYSTEM +/* A flag used to indicate that a control file for gpgconf has been + detected. Under Windows the presence of this file indicates a + portable installations and triggers several changes: + + - The GNUGHOME directory is fixed relative to installation + directory. All other means to set the home directory are ignore. + + - All registry variables will be ignored. + + This flag is not used on Unix systems. + */ +static int w32_portable_app; +#endif /*HAVE_W32_SYSTEM*/ + +#ifdef HAVE_W32_SYSTEM +/* This flag is true if this process' binary has been installed under + bin and not in the root directory. */ +static int w32_bin_is_bin; +#endif /*HAVE_W32_SYSTEM*/ + + +#ifdef HAVE_W32_SYSTEM +static const char *w32_rootdir (void); +#endif + + #ifdef HAVE_W32_SYSTEM static void @@ -124,28 +155,39 @@ standard_homedir (void) if (!dir) { - char path[MAX_PATH]; + const char *rdir; - /* 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) + rdir = w32_rootdir (); + if (w32_portable_app) { - char *tmp = xmalloc (strlen (path) + 6 +1); - strcpy (stpcpy (tmp, path), "\\gnupg"); - dir = tmp; - - /* Try to create the directory if it does not yet exists. */ - if (access (dir, F_OK)) - w32_try_mkdir (dir); + dir = xstrconcat (rdir, DIRSEP_S "home", NULL); } else - dir = GNUPG_DEFAULT_HOMEDIR; + { + 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 = xmalloc (strlen (path) + 6 +1); + strcpy (stpcpy (tmp, path), "\\gnupg"); + dir = tmp; + + /* Try to create the directory if it does not yet exists. */ + if (access (dir, F_OK)) + w32_try_mkdir (dir); + } + else + dir = GNUPG_DEFAULT_HOMEDIR; + } } return dir; #else/*!HAVE_W32_SYSTEM*/ @@ -160,6 +202,13 @@ default_homedir (void) { const char *dir; +#ifdef HAVE_W32_SYSTEM + /* For a portable application we only use the standard homedir. */ + w32_rootdir (); + if (w32_portable_app) + return standard_homedir (); +#endif /*HAVE_W32_SYSTEM*/ + dir = getenv ("GNUPGHOME"); #ifdef HAVE_W32_SYSTEM if (!dir || !*dir) @@ -197,6 +246,37 @@ default_homedir (void) #ifdef HAVE_W32_SYSTEM +/* Check whether gpgconf is installed and if so read the gpgconf.ctl + file. */ +static void +check_portable_app (const char *dir) +{ + char *fname; + + fname = xstrconcat (dir, DIRSEP_S "gpgconf.exe", NULL); + if (access (fname, F_OK)) + log_error ("required binary '%s' is not installed\n", fname); + else + { + strcpy (fname + strlen (fname) - 3, ".ctl"); + if (!access (fname, F_OK)) + { + /* gpgconf.ctl file found. Record this fact. */ + w32_portable_app = 1; + { + unsigned int flags; + log_get_prefix (&flags); + log_set_prefix (NULL, (flags | JNLIB_LOG_NO_REGISTRY)); + } + /* FIXME: We should read the file to detect special flags + and print a warning if we don't understand them */ + } + } + xfree (fname); +} + + +/* Determine the root directory of the gnupg installation on Windows. */ static const char * w32_rootdir (void) { @@ -229,11 +309,17 @@ w32_rootdir (void) if (p) { *p = 0; + + check_portable_app (dir); + /* If we are installed below "bin" we strip that and use the top directory instead. */ p = strrchr (dir, DIRSEP_C); if (p && !strcmp (p+1, "bin")) - *p = 0; + { + *p = 0; + w32_bin_is_bin = 1; + } } if (!p) { @@ -255,8 +341,17 @@ w32_commondir (void) if (!dir) { + const char *rdir; char path[MAX_PATH]; + /* Make sure that w32_rootdir has been called so that we are + able to check the portable application flag. The common dir + is the identical to the rootdir. In that case there is also + no need to strdup its value. */ + rdir = w32_rootdir (); + if (w32_portable_app) + return rdir; + if (w32_shgetfolderpath (NULL, CSIDL_COMMON_APPDATA, NULL, 0, path) >= 0) { @@ -270,7 +365,7 @@ w32_commondir (void) { /* Ooops: Not defined - probably an old Windows version. Use the installation directory instead. */ - dir = xstrdup (w32_rootdir ()); + dir = xstrdup (rdir); } } @@ -315,7 +410,19 @@ gnupg_bindir (void) name = xstrconcat (w32_rootdir (), DIRSEP_S "bin", NULL); return name; #elif defined(HAVE_W32_SYSTEM) - return w32_rootdir (); + const char *rdir; + + rdir = w32_rootdir (); + if (w32_bin_is_bin) + { + static char *name; + + if (!name) + name = xstrconcat (rdir, DIRSEP_S "bin", NULL); + return name; + } + else + return rdir; #else /*!HAVE_W32_SYSTEM*/ return GNUPG_BINDIR; #endif /*!HAVE_W32_SYSTEM*/ @@ -390,41 +497,54 @@ gnupg_cachedir (void) if (!dir) { - char path[MAX_PATH]; - const char *s1[] = { "GNU", "cache", "gnupg", NULL }; - int s1_len; - const char **comp; + const char *rdir; - s1_len = 0; - for (comp = s1; *comp; comp++) - s1_len += 1 + strlen (*comp); - - if (w32_shgetfolderpath (NULL, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, - NULL, 0, path) >= 0) + rdir = w32_rootdir (); + if (w32_portable_app) { - char *tmp = xmalloc (strlen (path) + s1_len + 1); - char *p; - - p = stpcpy (tmp, path); - for (comp = s1; *comp; comp++) - { - p = stpcpy (p, "\\"); - p = stpcpy (p, *comp); - - if (access (tmp, F_OK)) - w32_try_mkdir (tmp); - } - - dir = tmp; + dir = xstrconcat (rdir, + DIRSEP_S, "var", + DIRSEP_S, "cache", + DIRSEP_S, "gnupg", NULL); } else { - dir = "c:\\temp\\cache\\gnupg"; + char path[MAX_PATH]; + const char *s1[] = { "GNU", "cache", "gnupg", NULL }; + int s1_len; + const char **comp; + + s1_len = 0; + for (comp = s1; *comp; comp++) + s1_len += 1 + strlen (*comp); + + if (w32_shgetfolderpath (NULL, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, + NULL, 0, path) >= 0) + { + char *tmp = xmalloc (strlen (path) + s1_len + 1); + char *p; + + p = stpcpy (tmp, path); + for (comp = s1; *comp; comp++) + { + p = stpcpy (p, "\\"); + p = stpcpy (p, *comp); + + if (access (tmp, F_OK)) + w32_try_mkdir (tmp); + } + + dir = tmp; + } + else + { + dir = "c:\\temp\\cache\\gnupg"; #ifdef HAVE_W32CE_SYSTEM - dir += 2; - w32_try_mkdir ("\\temp\\cache"); - w32_try_mkdir ("\\temp\\cache\\gnupg"); + dir += 2; + w32_try_mkdir ("\\temp\\cache"); + w32_try_mkdir ("\\temp\\cache\\gnupg"); #endif + } } } return dir; @@ -449,16 +569,21 @@ dirmngr_socket_name (void) s1 = default_homedir (); # else - char s1[MAX_PATH]; - const char *s2; + char s1buf[MAX_PATH]; + const char *s1, *s2; - /* We need something akin CSIDL_COMMON_PROGRAMS, but local - (non-roaming). This is becuase the file needs to be on the - local machine and makes only sense on that machine. - CSIDL_WINDOWS seems to be the only location which guarantees - that. */ - if (w32_shgetfolderpath (NULL, CSIDL_WINDOWS, NULL, 0, s1) < 0) - strcpy (s1, "C:\\WINDOWS"); + s1 = default_homedir (); + if (!w32_portable_app) + { + /* We need something akin CSIDL_COMMON_PROGRAMS, but local + (non-roaming). This is because the file needs to be on + the local machine and makes only sense on that machine. + CSIDL_WINDOWS seems to be the only location which + guarantees that. */ + if (w32_shgetfolderpath (NULL, CSIDL_WINDOWS, NULL, 0, s1buf) < 0) + strcpy (s1buf, "C:\\WINDOWS"); + s1 = s1buf; + } # endif s2 = DIRSEP_S "S.dirmngr"; name = xmalloc (strlen (s1) + strlen (s2) + 1); diff --git a/common/logging.c b/common/logging.c index 73b0dbe59..f78df9141 100644 --- a/common/logging.c +++ b/common/logging.c @@ -96,6 +96,9 @@ static char prefix_buffer[80]; static int with_time; static int with_prefix; static int with_pid; +#ifdef HAVE_W32_SYSTEM +static int no_registry; +#endif static int (*get_pid_suffix_cb)(unsigned long *r_value); static int running_detached; static int force_prefixes; @@ -561,6 +564,9 @@ log_set_prefix (const char *text, unsigned int flags) with_time = (flags & JNLIB_LOG_WITH_TIME); with_pid = (flags & JNLIB_LOG_WITH_PID); running_detached = (flags & JNLIB_LOG_RUN_DETACHED); +#ifdef HAVE_W32_SYSTEM + no_registry = (flags & JNLIB_LOG_NO_REGISTRY); +#endif } @@ -578,6 +584,10 @@ log_get_prefix (unsigned int *flags) *flags |= JNLIB_LOG_WITH_PID; if (running_detached) *flags |= JNLIB_LOG_RUN_DETACHED; +#ifdef HAVE_W32_SYSTEM + if (no_registry) + *flags |= JNLIB_LOG_NO_REGISTRY; +#endif } return prefix_buffer; } @@ -624,8 +634,10 @@ do_logv (int level, int ignore_arg_ptr, const char *fmt, va_list arg_ptr) #ifdef HAVE_W32_SYSTEM char *tmp; - tmp = read_w32_registry_string (NULL, "Software\\GNU\\GnuPG", - "DefaultLogFile"); + tmp = (no_registry + ? NULL + : read_w32_registry_string (NULL, "Software\\GNU\\GnuPG", + "DefaultLogFile")); log_set_file (tmp && *tmp? tmp : NULL); jnlib_free (tmp); #else diff --git a/common/logging.h b/common/logging.h index 89913e6e5..3b38f7370 100644 --- a/common/logging.h +++ b/common/logging.h @@ -42,6 +42,7 @@ #define JNLIB_LOG_WITH_TIME 2 #define JNLIB_LOG_WITH_PID 4 #define JNLIB_LOG_RUN_DETACHED 256 +#define JNLIB_LOG_NO_REGISTRY 512 int log_get_errorcount (int clear); void log_inc_errorcount (void); diff --git a/doc/opt-homedir.texi b/doc/opt-homedir.texi index e382f6368..033a9016b 100644 --- a/doc/opt-homedir.texi +++ b/doc/opt-homedir.texi @@ -5,6 +5,18 @@ Set the name of the home directory to @var{dir}. If this option is not used, the home directory defaults to @file{~/.gnupg}. It is only recognized when given on the command line. It also overrides any home directory stated through the environment variable @env{GNUPGHOME} or -(on W32 systems) by means of the Registry entry +(on Windows systems) by means of the Registry entry @var{HKCU\Software\GNU\GnuPG:HomeDir}. +On Windows systems it is possible to install GnuPG as a portable +application. In this case only this command line option is +considered, all other ways to set a home directory are ignored. + +To install GnuPG as a portable application under Windows, create an +empty file name @file{gpgconf.ctl} in the same directory as the tool +@file{gpgconf.exe}. The root of the installation is than that +directory; or, if @file{gpgconf.exe} has been installed directly below +a directory named @file{bin}, its parent directory. You also need to +make sure that the following directories exist and are writable: +@file{ROOT/home} for the GnuPG home and @file{ROOT/var/cache/gnupg} +for internal cache files.