Chnages to supporta pinnetry notification
[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, 2007 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       TRACE2 (DEBUG_CTX, "gpgme:find_program_in_registry", 0,
236               "found %s in registry: `%s'", name, program);
237       for (i = 0; program[i]; i++)
238         {
239           if (program[i] == '/')
240             program[i] = '\\';
241         }
242     }
243   return program;
244 }
245
246
247 static char *
248 find_program_in_inst_dir (const char *name)
249 {
250   char *result = NULL;
251   char *tmp;
252
253   tmp = read_w32_registry_string ("HKEY_LOCAL_MACHINE",
254                                         "Software\\GNU\\GnuPG",
255                                         "Install Directory");
256   if (!tmp)
257     return NULL;
258
259   result = malloc (strlen (tmp) + 1 + strlen (name) + 1);
260   if (!result)
261     {
262       free (tmp);
263       return NULL;
264     }
265
266   strcpy (stpcpy (stpcpy (result, tmp), "\\"), name);
267   free (tmp);
268   if (access (result, F_OK))
269     {
270       free (result);
271       return NULL;
272     }
273
274   return result;
275 }
276
277
278 static char *
279 find_program_at_standard_place (const char *name)
280 {
281   char path[MAX_PATH];
282   char *result = NULL;
283       
284   if (w32_shgetfolderpath (NULL, CSIDL_PROGRAM_FILES, NULL, 0, path) >= 0) 
285     {
286       result = malloc (strlen (path) + 1 + strlen (name) + 1);
287       if (result)
288         {
289           strcpy (stpcpy (stpcpy (result, path), "\\"), name);
290           if (access (result, F_OK))
291             {
292               free (result);
293               result = NULL;
294             }
295         }
296     }
297   return result;
298 }
299
300
301 const char *
302 _gpgme_get_gpg_path (void)
303 {
304   static char *gpg_program;
305
306   LOCK (get_path_lock);
307   if (!gpg_program)
308     gpg_program = find_program_in_registry ("gpgProgram");
309   if (!gpg_program)
310     gpg_program = find_program_in_inst_dir ("gpg.exe");
311   if (!gpg_program)
312     gpg_program = find_program_at_standard_place ("GNU\\GnuPG\\gpg.exe");
313   UNLOCK (get_path_lock);
314   return gpg_program;
315 }
316
317
318 const char *
319 _gpgme_get_gpgsm_path (void)
320 {
321   static char *gpgsm_program;
322
323   LOCK (get_path_lock);
324   if (!gpgsm_program)
325     gpgsm_program = find_program_in_registry ("gpgsmProgram");
326   if (!gpgsm_program)
327     gpgsm_program = find_program_in_inst_dir ("gpgsm.exe");
328   if (!gpgsm_program)
329     gpgsm_program = find_program_at_standard_place ("GNU\\GnuPG\\gpgsm.exe");
330   UNLOCK (get_path_lock);
331   return gpgsm_program;
332 }
333
334
335 const char *
336 _gpgme_get_gpgconf_path (void)
337 {
338   static char *gpgconf_program;
339
340   LOCK (get_path_lock);
341   if (!gpgconf_program)
342     gpgconf_program = find_program_in_registry ("gpgconfProgram");
343   if (!gpgconf_program)
344     gpgconf_program = find_program_in_inst_dir ("gpgconf.exe");
345   if (!gpgconf_program)
346     gpgconf_program
347       = find_program_at_standard_place ("GNU\\GnuPG\\gpgconf.exe");
348   UNLOCK (get_path_lock);
349   return gpgconf_program;
350 }
351
352
353 /* Return an integer value from gpgme specific configuration
354    entries. VALUE receives that value; function returns true if a value
355    has been configured and false if not. */
356 int
357 _gpgme_get_conf_int (const char *key, int *value)
358 {
359   char *tmp = read_w32_registry_string (NULL, "Software\\GNU\\gpgme", key);
360   if (!tmp)
361     return 0;
362   *value = atoi (tmp);
363   free (tmp);
364   return 1;
365 }
366
367
368 void 
369 _gpgme_allow_set_foregound_window (pid_t pid)
370 {
371   static int initialized;
372   static BOOL (WINAPI * func)(DWORD);
373   void *handle;
374
375   if (!initialized)
376     {
377       /* Available since W2000; thus we dynload it.  */
378       initialized = 1;
379       handle = dlopen ("user32.dll", RTLD_LAZY);
380       if (handle)
381         {
382           func = dlsym (handle, "AllowSetForegroundWindow");
383           if (!func)
384             {
385               dlclose (handle);
386               handle = NULL;
387             }
388         }
389     }
390
391   if (!pid || pid == (pid_t)(-1))
392     ;
393   else if (func)
394     func (pid);
395
396 }
397