2005-10-01 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / w32-util.c
1 /* w32-util.c - Utility functions for the W32 API
2    Copyright (C) 1999 Free Software Foundation, Inc
3    Copyright (C) 2001 Werner Koch (dd9jn)
4    Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
5
6    This file is part of GPGME.
7  
8    GPGME is free software; you can redistribute it and/or modify it
9    under the terms of the GNU Lesser General Public License as
10    published by the Free Software Foundation; either version 2.1 of
11    the License, or (at your option) any later version.
12    
13    GPGME is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17    
18    You should have received a copy of the GNU Lesser General Public
19    License along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21    02111-1307, USA.  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <assert.h>
30 #include <errno.h>
31 #include <sys/time.h>
32 #include <sys/types.h>
33 #include <signal.h>
34 #include <fcntl.h>
35 #include <windows.h>
36 #include <shlobj.h>
37 #include <io.h>
38
39 #include "util.h"
40 #include "sema.h"
41 #include "debug.h"
42
43 DEFINE_STATIC_LOCK (get_path_lock);
44
45
46 #define RTLD_LAZY 0
47
48 static __inline__ void *
49 dlopen (const char * name, int flag)
50 {
51   void * hd = LoadLibrary (name);
52   return hd;
53 }
54
55 static __inline__ void *
56 dlsym (void * hd, const char * sym)
57 {
58   if (hd && sym)
59     {
60       void * fnc = GetProcAddress (hd, sym);
61       if (!fnc)
62         return NULL;
63       return fnc;
64     }
65   return NULL;
66 }
67
68 static __inline__ int
69 dlclose (void * hd)
70 {
71   if (hd)
72     {
73       FreeLibrary (hd);
74       return 0;
75     }
76   return -1;
77 }  
78
79
80 /* Return a string from the W32 Registry or NULL in case of error.
81    Caller must release the return value.  A NULL for root is an alias
82    for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. */
83 static char *
84 read_w32_registry_string (const char *root, const char *dir, const char *name)
85 {
86   HKEY root_key, key_handle;
87   DWORD n1, nbytes, type;
88   char *result = NULL;
89         
90   if ( !root )
91     root_key = HKEY_CURRENT_USER;
92   else if ( !strcmp( root, "HKEY_CLASSES_ROOT" ) )
93     root_key = HKEY_CLASSES_ROOT;
94   else if ( !strcmp( root, "HKEY_CURRENT_USER" ) )
95     root_key = HKEY_CURRENT_USER;
96   else if ( !strcmp( root, "HKEY_LOCAL_MACHINE" ) )
97     root_key = HKEY_LOCAL_MACHINE;
98   else if ( !strcmp( root, "HKEY_USERS" ) )
99     root_key = HKEY_USERS;
100   else if ( !strcmp( root, "HKEY_PERFORMANCE_DATA" ) )
101     root_key = HKEY_PERFORMANCE_DATA;
102   else if ( !strcmp( root, "HKEY_CURRENT_CONFIG" ) )
103     root_key = HKEY_CURRENT_CONFIG;
104   else
105     return NULL;
106         
107   if ( RegOpenKeyEx ( root_key, dir, 0, KEY_READ, &key_handle ) )
108     {
109       if (root)
110         return NULL; /* no need for a RegClose, so return direct */
111       /* It seems to be common practise to fall back to HKLM. */
112       if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
113         return NULL; /* still no need for a RegClose, so return direct */
114     }
115
116   nbytes = 1;
117   if ( RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes ) )
118     {
119       if (root)
120         goto leave;
121       /* Try to fallback to HKLM also vor a missing value.  */
122       RegCloseKey (key_handle);
123       if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
124         return NULL; /* Nope.  */
125       if (RegQueryValueEx ( key_handle, name, 0, NULL, NULL, &nbytes))
126         goto leave;
127     }
128   result = malloc ( (n1=nbytes+1) );
129   if ( !result )
130     goto leave;
131   if ( RegQueryValueEx ( key_handle, name, 0, &type, result, &n1 ) )
132     {
133       free(result); result = NULL;
134       goto leave;
135     }
136   result[nbytes] = 0; /* Make sure it is really a string.  */
137   if (type == REG_EXPAND_SZ && strchr (result, '%')) 
138     {
139       char *tmp;
140         
141       n1 += 1000;
142       tmp = malloc (n1+1);
143       if (!tmp)
144         goto leave;
145       nbytes = ExpandEnvironmentStrings (result, tmp, n1);
146       if (nbytes && nbytes > n1)
147         {
148           free (tmp);
149           n1 = nbytes;
150           tmp = malloc (n1 + 1);
151           if (!tmp)
152             goto leave;
153           nbytes = ExpandEnvironmentStrings (result, tmp, n1);
154           if (nbytes && nbytes > n1) {
155             free (tmp); /* Oops - truncated, better don't expand at all. */
156             goto leave;
157           }
158           tmp[nbytes] = 0;
159           free (result);
160           result = tmp;
161         }
162       else if (nbytes)  /* Okay, reduce the length. */
163         {
164           tmp[nbytes] = 0;
165           free (result);
166           result = malloc (strlen (tmp)+1);
167           if (!result)
168             result = tmp;
169           else 
170             {
171               strcpy (result, tmp);
172               free (tmp);
173             }
174         }
175       else  /* Error - don't expand. */
176         {
177           free (tmp);
178         }
179     }
180
181  leave:
182   RegCloseKey( key_handle );
183   return result;
184 }
185
186
187 /* This is a helper function to load and run a Windows function from
188    either of one DLLs. */
189 static HRESULT
190 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
191 {
192   static int initialized;
193   static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
194
195   if (!initialized)
196     {
197       static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
198       void *handle;
199       int i;
200
201       initialized = 1;
202
203       for (i=0, handle = NULL; !handle && dllnames[i]; i++)
204         {
205           handle = dlopen (dllnames[i], RTLD_LAZY);
206           if (handle)
207             {
208               func = dlsym (handle, "SHGetFolderPathA");
209               if (!func)
210                 {
211                   dlclose (handle);
212                   handle = NULL;
213                 }
214             }
215         }
216     }
217
218   if (func)
219     return func (a,b,c,d,e);
220   else
221     return -1;
222 }
223
224
225 static char *
226 find_program_in_registry (const char *name)
227 {
228   char *program = NULL;
229     
230   program = read_w32_registry_string (NULL, "Software\\GNU\\GnuPG", name);
231   if (program)
232     {
233       int i;
234
235       DEBUG2 ("found %s in registry: `%s'", name, program);
236       for (i = 0; program[i]; i++)
237         {
238           if (program[i] == '/')
239             program[i] = '\\';
240         }
241     }
242   return program;
243 }
244
245
246 static char *
247 find_program_at_standard_place (const char *name)
248 {
249   char path[MAX_PATH];
250   char *result = NULL;
251       
252   if (w32_shgetfolderpath (NULL, CSIDL_PROGRAM_FILES, NULL, 0, path) >= 0) 
253     {
254       result = malloc (strlen (path) + 1 + strlen (name) + 1);
255       if (result)
256         {
257           strcpy (stpcpy (stpcpy (result, path), "\\"), name);
258           if (access (result, F_OK))
259             {
260               free (result);
261               result = NULL;
262             }
263         }
264     }
265   return result;
266 }
267
268
269 const char *
270 _gpgme_get_gpg_path (void)
271 {
272   static char *gpg_program;
273
274   LOCK (get_path_lock);
275   if (!gpg_program)
276     gpg_program = find_program_in_registry ("gpgProgram");
277   if (!gpg_program)
278     gpg_program = find_program_at_standard_place ("GNU\\GnuPG\\gpg.exe");
279 #ifdef GPG_PATH
280   if (!gpg_program)
281     gpg_program = GPG_PATH;
282 #endif
283   UNLOCK (get_path_lock);
284   return gpg_program;
285 }
286
287 const char *
288 _gpgme_get_gpgsm_path (void)
289 {
290   static char *gpgsm_program;
291
292   LOCK (get_path_lock);
293   if (!gpgsm_program)
294     gpgsm_program = find_program_in_registry ("gpgsmProgram");
295   if (!gpgsm_program)
296     gpgsm_program = find_program_at_standard_place ("GNU\\GnuPG\\gpgsm.exe");
297 #ifdef GPGSM_PATH
298   if (!gpgsm_program)
299     gpgsm_program = GPGSM_PATH;
300 #endif
301   UNLOCK (get_path_lock);
302   return gpgsm_program;
303 }