assuan/
[gpgme.git] / src / 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 <stdint.h>
32 #include <sys/time.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36 #include <signal.h>
37 #include <fcntl.h>
38 #include <windows.h>
39 #include <shlobj.h>
40 #include <io.h>
41
42 #include "util.h"
43 #include "sema.h"
44 #include "debug.h"
45
46 DEFINE_STATIC_LOCK (get_path_lock);
47
48
49 #define RTLD_LAZY 0
50
51 static __inline__ void *
52 dlopen (const char * name, int flag)
53 {
54   void * hd = LoadLibrary (name);
55   return hd;
56 }
57
58 static __inline__ void *
59 dlsym (void * hd, const char * sym)
60 {
61   if (hd && sym)
62     {
63       void * fnc = GetProcAddress (hd, sym);
64       if (!fnc)
65         return NULL;
66       return fnc;
67     }
68   return NULL;
69 }
70
71 static __inline__ int
72 dlclose (void * hd)
73 {
74   if (hd)
75     {
76       FreeLibrary (hd);
77       return 0;
78     }
79   return -1;
80 }  
81
82
83 /* Return a string from the W32 Registry or NULL in case of error.
84    Caller must release the return value.  A NULL for root is an alias
85    for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. */
86 static char *
87 read_w32_registry_string (const char *root, const char *dir, const char *name)
88 {
89   HKEY root_key, key_handle;
90   DWORD n1, nbytes, type;
91   char *result = NULL;
92         
93   if ( !root )
94     root_key = HKEY_CURRENT_USER;
95   else if ( !strcmp( root, "HKEY_CLASSES_ROOT" ) )
96     root_key = HKEY_CLASSES_ROOT;
97   else if ( !strcmp( root, "HKEY_CURRENT_USER" ) )
98     root_key = HKEY_CURRENT_USER;
99   else if ( !strcmp( root, "HKEY_LOCAL_MACHINE" ) )
100     root_key = HKEY_LOCAL_MACHINE;
101   else if ( !strcmp( root, "HKEY_USERS" ) )
102     root_key = HKEY_USERS;
103   else if ( !strcmp( root, "HKEY_PERFORMANCE_DATA" ) )
104     root_key = HKEY_PERFORMANCE_DATA;
105   else if ( !strcmp( root, "HKEY_CURRENT_CONFIG" ) )
106     root_key = HKEY_CURRENT_CONFIG;
107   else
108     return NULL;
109         
110   if ( RegOpenKeyEx ( root_key, dir, 0, KEY_READ, &key_handle ) )
111     {
112       if (root)
113         return NULL; /* no need for a RegClose, so return direct */
114       /* It seems to be common practise to fall back to HKLM. */
115       if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
116         return NULL; /* still no need for a RegClose, so return direct */
117     }
118
119   nbytes = 1;
120   if ( RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes ) )
121     {
122       if (root)
123         goto leave;
124       /* Try to fallback to HKLM also vor a missing value.  */
125       RegCloseKey (key_handle);
126       if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
127         return NULL; /* Nope.  */
128       if (RegQueryValueEx ( key_handle, name, 0, NULL, NULL, &nbytes))
129         goto leave;
130     }
131   result = malloc ( (n1=nbytes+1) );
132   if ( !result )
133     goto leave;
134   if ( RegQueryValueEx ( key_handle, name, 0, &type, result, &n1 ) )
135     {
136       free(result); result = NULL;
137       goto leave;
138     }
139   result[nbytes] = 0; /* Make sure it is really a string.  */
140   if (type == REG_EXPAND_SZ && strchr (result, '%')) 
141     {
142       char *tmp;
143         
144       n1 += 1000;
145       tmp = malloc (n1+1);
146       if (!tmp)
147         goto leave;
148       nbytes = ExpandEnvironmentStrings (result, tmp, n1);
149       if (nbytes && nbytes > n1)
150         {
151           free (tmp);
152           n1 = nbytes;
153           tmp = malloc (n1 + 1);
154           if (!tmp)
155             goto leave;
156           nbytes = ExpandEnvironmentStrings (result, tmp, n1);
157           if (nbytes && nbytes > n1) {
158             free (tmp); /* Oops - truncated, better don't expand at all. */
159             goto leave;
160           }
161           tmp[nbytes] = 0;
162           free (result);
163           result = tmp;
164         }
165       else if (nbytes)  /* Okay, reduce the length. */
166         {
167           tmp[nbytes] = 0;
168           free (result);
169           result = malloc (strlen (tmp)+1);
170           if (!result)
171             result = tmp;
172           else 
173             {
174               strcpy (result, tmp);
175               free (tmp);
176             }
177         }
178       else  /* Error - don't expand. */
179         {
180           free (tmp);
181         }
182     }
183
184  leave:
185   RegCloseKey( key_handle );
186   return result;
187 }
188
189
190 /* This is a helper function to load and run a Windows function from
191    either of one DLLs. */
192 static HRESULT
193 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
194 {
195   static int initialized;
196   static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
197
198   if (!initialized)
199     {
200       static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
201       void *handle;
202       int i;
203
204       initialized = 1;
205
206       for (i=0, handle = NULL; !handle && dllnames[i]; i++)
207         {
208           handle = dlopen (dllnames[i], RTLD_LAZY);
209           if (handle)
210             {
211               func = dlsym (handle, "SHGetFolderPathA");
212               if (!func)
213                 {
214                   dlclose (handle);
215                   handle = NULL;
216                 }
217             }
218         }
219     }
220
221   if (func)
222     return func (a,b,c,d,e);
223   else
224     return -1;
225 }
226
227
228 #if 0
229 static char *
230 find_program_in_registry (const char *name)
231 {
232   char *program = NULL;
233     
234   program = read_w32_registry_string (NULL, "Software\\GNU\\GnuPG", name);
235   if (program)
236     {
237       int i;
238
239       TRACE2 (DEBUG_CTX, "gpgme:find_program_in_registry", 0,
240               "found %s in registry: `%s'", name, program);
241       for (i = 0; program[i]; i++)
242         {
243           if (program[i] == '/')
244             program[i] = '\\';
245         }
246     }
247   return program;
248 }
249 #endif
250
251
252 static char *
253 find_program_in_inst_dir (const char *name)
254 {
255   char *result = NULL;
256   char *tmp;
257
258   tmp = read_w32_registry_string ("HKEY_LOCAL_MACHINE",
259                                   "Software\\GNU\\GnuPG",
260                                   "Install Directory");
261   if (!tmp)
262     return NULL;
263
264   result = malloc (strlen (tmp) + 1 + strlen (name) + 1);
265   if (!result)
266     {
267       free (tmp);
268       return NULL;
269     }
270
271   strcpy (stpcpy (stpcpy (result, tmp), "\\"), name);
272   free (tmp);
273   if (access (result, F_OK))
274     {
275       free (result);
276       return NULL;
277     }
278
279   return result;
280 }
281
282
283 static char *
284 find_program_at_standard_place (const char *name)
285 {
286   char path[MAX_PATH];
287   char *result = NULL;
288       
289   if (w32_shgetfolderpath (NULL, CSIDL_PROGRAM_FILES, NULL, 0, path) >= 0) 
290     {
291       result = malloc (strlen (path) + 1 + strlen (name) + 1);
292       if (result)
293         {
294           strcpy (stpcpy (stpcpy (result, path), "\\"), name);
295           if (access (result, F_OK))
296             {
297               free (result);
298               result = NULL;
299             }
300         }
301     }
302   return result;
303 }
304
305
306 const char *
307 _gpgme_get_gpg_path (void)
308 {
309   static char *gpg_program;
310
311   LOCK (get_path_lock);
312 #if 0
313   if (!gpg_program)
314     gpg_program = find_program_in_registry ("gpgProgram");
315 #endif
316   if (!gpg_program)
317     gpg_program = find_program_in_inst_dir ("gpg.exe");
318   if (!gpg_program)
319     gpg_program = find_program_at_standard_place ("GNU\\GnuPG\\gpg.exe");
320   UNLOCK (get_path_lock);
321   return gpg_program;
322 }
323
324
325 const char *
326 _gpgme_get_gpgsm_path (void)
327 {
328   static char *gpgsm_program;
329
330   LOCK (get_path_lock);
331 #if 0
332   if (!gpgsm_program)
333     gpgsm_program = find_program_in_registry ("gpgsmProgram");
334 #endif
335   if (!gpgsm_program)
336     gpgsm_program = find_program_in_inst_dir ("gpgsm.exe");
337   if (!gpgsm_program)
338     gpgsm_program = find_program_at_standard_place ("GNU\\GnuPG\\gpgsm.exe");
339   UNLOCK (get_path_lock);
340   return gpgsm_program;
341 }
342
343
344 const char *
345 _gpgme_get_gpgconf_path (void)
346 {
347   static char *gpgconf_program;
348
349   LOCK (get_path_lock);
350 #if 0
351   if (!gpgconf_program)
352     gpgconf_program = find_program_in_registry ("gpgconfProgram");
353 #endif
354   if (!gpgconf_program)
355     gpgconf_program = find_program_in_inst_dir ("gpgconf.exe");
356   if (!gpgconf_program)
357     gpgconf_program
358       = find_program_at_standard_place ("GNU\\GnuPG\\gpgconf.exe");
359   UNLOCK (get_path_lock);
360   return gpgconf_program;
361 }
362
363
364 const char *
365 _gpgme_get_w32spawn_path (void)
366 {
367   static char *w32spawn_program;
368
369   LOCK (get_path_lock);
370   if (!w32spawn_program)
371     w32spawn_program = find_program_in_inst_dir ("gpgme-w32spawn.exe");
372   if (!w32spawn_program)
373     w32spawn_program
374       = find_program_at_standard_place ("GNU\\GnuPG\\gpgme-w32spawn.exe");
375   UNLOCK (get_path_lock);
376   return w32spawn_program;
377 }
378
379
380 /* Return an integer value from gpgme specific configuration
381    entries. VALUE receives that value; function returns true if a value
382    has been configured and false if not. */
383 int
384 _gpgme_get_conf_int (const char *key, int *value)
385 {
386   char *tmp = read_w32_registry_string (NULL, "Software\\GNU\\gpgme", key);
387   if (!tmp)
388     return 0;
389   *value = atoi (tmp);
390   free (tmp);
391   return 1;
392 }
393
394
395 void 
396 _gpgme_allow_set_foregound_window (pid_t pid)
397 {
398   static int initialized;
399   static BOOL (WINAPI * func)(DWORD);
400   void *handle;
401
402   if (!initialized)
403     {
404       /* Available since W2000; thus we dynload it.  */
405       initialized = 1;
406       handle = dlopen ("user32.dll", RTLD_LAZY);
407       if (handle)
408         {
409           func = dlsym (handle, "AllowSetForegroundWindow");
410           if (!func)
411             {
412               dlclose (handle);
413               handle = NULL;
414             }
415         }
416     }
417
418   if (!pid || pid == (pid_t)(-1))
419     ;
420   else if (func)
421     func (pid);
422
423 }
424
425
426 \f
427 /* mkstemp extracted from libc/sysdeps/posix/tempname.c.  Copyright
428    (C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc.
429
430    The GNU C Library is free software; you can redistribute it and/or
431    modify it under the terms of the GNU Lesser General Public
432    License as published by the Free Software Foundation; either
433    version 2.1 of the License, or (at your option) any later version.  */
434
435 static const char letters[] =
436 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
437
438 /* Generate a temporary file name based on TMPL.  TMPL must match the
439    rules for mk[s]temp (i.e. end in "XXXXXX").  The name constructed
440    does not exist at the time of the call to mkstemp.  TMPL is
441    overwritten with the result.  */
442 static int
443 mkstemp (char *tmpl)
444 {
445   int len;
446   char *XXXXXX;
447   static uint64_t value;
448   uint64_t random_time_bits;
449   unsigned int count;
450   int fd = -1;
451   int save_errno = errno;
452
453   /* A lower bound on the number of temporary files to attempt to
454      generate.  The maximum total number of temporary file names that
455      can exist for a given template is 62**6.  It should never be
456      necessary to try all these combinations.  Instead if a reasonable
457      number of names is tried (we define reasonable as 62**3) fail to
458      give the system administrator the chance to remove the problems.  */
459 #define ATTEMPTS_MIN (62 * 62 * 62)
460
461   /* The number of times to attempt to generate a temporary file.  To
462      conform to POSIX, this must be no smaller than TMP_MAX.  */
463 #if ATTEMPTS_MIN < TMP_MAX
464   unsigned int attempts = TMP_MAX;
465 #else
466   unsigned int attempts = ATTEMPTS_MIN;
467 #endif
468
469   len = strlen (tmpl);
470   if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
471     {
472       errno = EINVAL;
473       return -1;
474     }
475
476   /* This is where the Xs start.  */
477   XXXXXX = &tmpl[len - 6];
478
479   /* Get some more or less random data.  */
480   {
481     FILETIME ft;
482
483     GetSystemTimeAsFileTime (&ft);
484     random_time_bits = (((uint64_t)ft.dwHighDateTime << 32)
485                         | (uint64_t)ft.dwLowDateTime);
486   }
487   value += random_time_bits ^ getpid ();
488
489   for (count = 0; count < attempts; value += 7777, ++count)
490     {
491       uint64_t v = value;
492
493       /* Fill in the random bits.  */
494       XXXXXX[0] = letters[v % 62];
495       v /= 62;
496       XXXXXX[1] = letters[v % 62];
497       v /= 62;
498       XXXXXX[2] = letters[v % 62];
499       v /= 62;
500       XXXXXX[3] = letters[v % 62];
501       v /= 62;
502       XXXXXX[4] = letters[v % 62];
503       v /= 62;
504       XXXXXX[5] = letters[v % 62];
505
506       fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
507       if (fd >= 0)
508         {
509           errno = save_errno;
510           return fd;
511         }
512       else if (errno != EEXIST)
513         return -1;
514     }
515
516   /* We got out of the loop because we ran out of combinations to try.  */
517   errno = EEXIST;
518   return -1;
519 }
520
521 \f
522 int
523 _gpgme_mkstemp (int *fd, char **name)
524 {
525   char tmp[MAX_PATH + 2];
526   char *tmpname;
527   int err;
528
529   *fd = -1;
530   *name = NULL;
531
532   err = GetTempPath (MAX_PATH + 1, tmp);
533   if (err == 0 || err > MAX_PATH + 1)
534     strcpy (tmp,"c:\\windows\\temp");
535   else
536     {
537       int len = strlen(tmp);
538       
539       /* GetTempPath may return with \ on the end */
540       while(len > 0 && tmp[len - 1] == '\\')
541         {
542           tmp[len-1] = '\0';
543           len--;
544         }
545     }
546
547   tmpname = malloc (strlen (tmp) + 13 + 1);
548   if (!tmpname)
549     return -1;
550   strcpy (stpcpy (tmpname, tmp), "\\gpgme-XXXXXX");
551   *fd = mkstemp (tmpname);
552   if (fd < 0)
553     return -1;
554
555   *name = tmpname;
556   return 0;
557 }