* configure.ac (SEPCONSTANTS): New to define DIRSEP_C et al.
[gpgme.git] / gpgme / w32-util.c
index e4fac8e..fa1a6d7 100644 (file)
@@ -33,6 +33,7 @@
 #include <signal.h>
 #include <fcntl.h>
 #include <windows.h>
+#include <shlobj.h>
 #include <io.h>
 
 #include "util.h"
 
 DEFINE_STATIC_LOCK (get_path_lock);
 
-/* 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
-  for HKEY_CURRENT_USER.  */
+
+#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;
+  DWORD n1, nbytes, type;
   char *result = NULL;
-
-#ifdef HAVE_W32_SYSTEM
-#warning Check that this code matches the one used by gnupg
-#endif
-
-  if (!root)
+       
+  if ( !root )
     root_key = HKEY_CURRENT_USER;
-  else if (!strcmp (root, "HKEY_CLASSES_ROOT"))
+  else if ( !strcmp( root, "HKEY_CLASSES_ROOT" ) )
     root_key = HKEY_CLASSES_ROOT;
-  else if (!strcmp (root, "HKEY_CURRENT_USER"))
+  else if ( !strcmp( root, "HKEY_CURRENT_USER" ) )
     root_key = HKEY_CURRENT_USER;
-  else if (!strcmp (root, "HKEY_LOCAL_MACHINE"))
+  else if ( !strcmp( root, "HKEY_LOCAL_MACHINE" ) )
     root_key = HKEY_LOCAL_MACHINE;
-  else if (!strcmp (root, "HKEY_USERS"))
+  else if ( !strcmp( root, "HKEY_USERS" ) )
     root_key = HKEY_USERS;
-  else if (!strcmp (root, "HKEY_PERFORMANCE_DATA"))
+  else if ( !strcmp( root, "HKEY_PERFORMANCE_DATA" ) )
     root_key = HKEY_PERFORMANCE_DATA;
-  else if (!strcmp (root, "HKEY_CURRENT_CONFIG"))
+  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 directly.  */
+       
+  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))
-    goto leave;
-  n1 = nbytes + 1;
-  result = malloc (n1);
-  if (!result)
+  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, NULL, result, &n1))
+  if ( RegQueryValueEx ( key_handle, name, 0, &type, result, &n1 ) )
     {
-      free (result);
-      result = NULL;
+      free(result); result = NULL;
       goto leave;
     }
-  result[nbytes] = 0;  /* Make sure it is really a string.  */
+  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);
+  RegCloseKey( key_handle );
   return result;
 }
 
 
-static const char *
+/* 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_in_registry (const char *name)
 {
   char *program = NULL;
@@ -117,6 +243,29 @@ find_program_in_registry (const char *name)
 }
 
 
+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;
+}
+
+
 const char *
 _gpgme_get_gpg_path (void)
 {
@@ -125,6 +274,8 @@ _gpgme_get_gpg_path (void)
   LOCK (get_path_lock);
   if (!gpg_program)
     gpg_program = find_program_in_registry ("gpgProgram");
+  if (!gpg_program)
+    gpg_program = find_program_at_standard_place ("GNU\\GnuPG\\gpg.exe");
 #ifdef GPG_PATH
   if (!gpg_program)
     gpg_program = GPG_PATH;
@@ -141,6 +292,8 @@ _gpgme_get_gpgsm_path (void)
   LOCK (get_path_lock);
   if (!gpgsm_program)
     gpgsm_program = find_program_in_registry ("gpgsmProgram");
+  if (!gpgsm_program)
+    gpgsm_program = find_program_at_standard_place ("GNU\\GnuPG\\gpgsm.exe");
 #ifdef GPGSM_PATH
   if (!gpgsm_program)
     gpgsm_program = GPGSM_PATH;