2009-11-05 Marcus Brinkmann <marcus@g10code.de>
[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_g13_path (void)
366 {
367   static char *g13_program;
368
369   LOCK (get_path_lock);
370 #if 0
371   if (!g13_program)
372     g13_program = find_program_in_registry ("g13Program");
373 #endif
374   if (!g13_program)
375     g13_program = find_program_in_inst_dir ("g13.exe");
376   if (!g13_program)
377     g13_program = find_program_at_standard_place ("GNU\\GnuPG\\g13.exe");
378   UNLOCK (get_path_lock);
379   return g13_program;
380 }
381
382
383 const char *
384 _gpgme_get_w32spawn_path (void)
385 {
386   static char *w32spawn_program;
387
388   LOCK (get_path_lock);
389   if (!w32spawn_program)
390     w32spawn_program = find_program_in_inst_dir ("gpgme-w32spawn.exe");
391   if (!w32spawn_program)
392     w32spawn_program
393       = find_program_at_standard_place ("GNU\\GnuPG\\gpgme-w32spawn.exe");
394   UNLOCK (get_path_lock);
395   return w32spawn_program;
396 }
397
398
399 /* Return an integer value from gpgme specific configuration
400    entries. VALUE receives that value; function returns true if a value
401    has been configured and false if not. */
402 int
403 _gpgme_get_conf_int (const char *key, int *value)
404 {
405   char *tmp = read_w32_registry_string (NULL, "Software\\GNU\\gpgme", key);
406   if (!tmp)
407     return 0;
408   *value = atoi (tmp);
409   free (tmp);
410   return 1;
411 }
412
413
414 void 
415 _gpgme_allow_set_foreground_window (pid_t pid)
416 {
417   static int initialized;
418   static BOOL (WINAPI * func)(DWORD);
419   void *handle;
420
421   if (!initialized)
422     {
423       /* Available since W2000; thus we dynload it.  */
424       initialized = 1;
425       handle = dlopen ("user32.dll", RTLD_LAZY);
426       if (handle)
427         {
428           func = dlsym (handle, "AllowSetForegroundWindow");
429           if (!func)
430             {
431               dlclose (handle);
432               handle = NULL;
433             }
434         }
435     }
436
437   if (!pid || pid == (pid_t)(-1))
438     {
439       TRACE1 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
440               "no action for pid %d", (int)pid);
441     }
442   else if (func)
443     {
444       int rc = func (pid);
445       TRACE2 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
446               "called for pid %d; result=%d", (int)pid, rc);
447
448     }
449   else
450     {
451       TRACE0 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
452               "function not available");
453     }
454
455 }
456
457
458 \f
459 /* mkstemp extracted from libc/sysdeps/posix/tempname.c.  Copyright
460    (C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc.
461
462    The GNU C Library is free software; you can redistribute it and/or
463    modify it under the terms of the GNU Lesser General Public
464    License as published by the Free Software Foundation; either
465    version 2.1 of the License, or (at your option) any later version.  */
466
467 static const char letters[] =
468 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
469
470 /* Generate a temporary file name based on TMPL.  TMPL must match the
471    rules for mk[s]temp (i.e. end in "XXXXXX").  The name constructed
472    does not exist at the time of the call to mkstemp.  TMPL is
473    overwritten with the result.  */
474 static int
475 mkstemp (char *tmpl)
476 {
477   int len;
478   char *XXXXXX;
479   static uint64_t value;
480   uint64_t random_time_bits;
481   unsigned int count;
482   int fd = -1;
483   int save_errno = errno;
484
485   /* A lower bound on the number of temporary files to attempt to
486      generate.  The maximum total number of temporary file names that
487      can exist for a given template is 62**6.  It should never be
488      necessary to try all these combinations.  Instead if a reasonable
489      number of names is tried (we define reasonable as 62**3) fail to
490      give the system administrator the chance to remove the problems.  */
491 #define ATTEMPTS_MIN (62 * 62 * 62)
492
493   /* The number of times to attempt to generate a temporary file.  To
494      conform to POSIX, this must be no smaller than TMP_MAX.  */
495 #if ATTEMPTS_MIN < TMP_MAX
496   unsigned int attempts = TMP_MAX;
497 #else
498   unsigned int attempts = ATTEMPTS_MIN;
499 #endif
500
501   len = strlen (tmpl);
502   if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
503     {
504       errno = EINVAL;
505       return -1;
506     }
507
508   /* This is where the Xs start.  */
509   XXXXXX = &tmpl[len - 6];
510
511   /* Get some more or less random data.  */
512   {
513     FILETIME ft;
514
515     GetSystemTimeAsFileTime (&ft);
516     random_time_bits = (((uint64_t)ft.dwHighDateTime << 32)
517                         | (uint64_t)ft.dwLowDateTime);
518   }
519   value += random_time_bits ^ getpid ();
520
521   for (count = 0; count < attempts; value += 7777, ++count)
522     {
523       uint64_t v = value;
524
525       /* Fill in the random bits.  */
526       XXXXXX[0] = letters[v % 62];
527       v /= 62;
528       XXXXXX[1] = letters[v % 62];
529       v /= 62;
530       XXXXXX[2] = letters[v % 62];
531       v /= 62;
532       XXXXXX[3] = letters[v % 62];
533       v /= 62;
534       XXXXXX[4] = letters[v % 62];
535       v /= 62;
536       XXXXXX[5] = letters[v % 62];
537
538       fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
539       if (fd >= 0)
540         {
541           errno = save_errno;
542           return fd;
543         }
544       else if (errno != EEXIST)
545         return -1;
546     }
547
548   /* We got out of the loop because we ran out of combinations to try.  */
549   errno = EEXIST;
550   return -1;
551 }
552
553 \f
554 int
555 _gpgme_mkstemp (int *fd, char **name)
556 {
557   char tmp[MAX_PATH + 2];
558   char *tmpname;
559   int err;
560
561   *fd = -1;
562   *name = NULL;
563
564   err = GetTempPath (MAX_PATH + 1, tmp);
565   if (err == 0 || err > MAX_PATH + 1)
566     strcpy (tmp,"c:\\windows\\temp");
567   else
568     {
569       int len = strlen(tmp);
570       
571       /* GetTempPath may return with \ on the end */
572       while(len > 0 && tmp[len - 1] == '\\')
573         {
574           tmp[len-1] = '\0';
575           len--;
576         }
577     }
578
579   tmpname = malloc (strlen (tmp) + 13 + 1);
580   if (!tmpname)
581     return -1;
582   strcpy (stpcpy (tmpname, tmp), "\\gpgme-XXXXXX");
583   *fd = mkstemp (tmpname);
584   if (fd < 0)
585     return -1;
586
587   *name = tmpname;
588   return 0;
589 }