a4b713ed6bf2c6a7ea69d79a912346164bd57bbf
[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_foreground_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       TRACE1 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
421               "no action for pid %d", (int)pid);
422     }
423   else if (func)
424     {
425       int rc = func (pid);
426       TRACE2 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
427               "called for pid %d; result=%d", (int)pid, rc);
428
429     }
430   else
431     {
432       TRACE0 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
433               "function not available");
434     }
435
436 }
437
438
439 \f
440 /* mkstemp extracted from libc/sysdeps/posix/tempname.c.  Copyright
441    (C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc.
442
443    The GNU C Library is free software; you can redistribute it and/or
444    modify it under the terms of the GNU Lesser General Public
445    License as published by the Free Software Foundation; either
446    version 2.1 of the License, or (at your option) any later version.  */
447
448 static const char letters[] =
449 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
450
451 /* Generate a temporary file name based on TMPL.  TMPL must match the
452    rules for mk[s]temp (i.e. end in "XXXXXX").  The name constructed
453    does not exist at the time of the call to mkstemp.  TMPL is
454    overwritten with the result.  */
455 static int
456 mkstemp (char *tmpl)
457 {
458   int len;
459   char *XXXXXX;
460   static uint64_t value;
461   uint64_t random_time_bits;
462   unsigned int count;
463   int fd = -1;
464   int save_errno = errno;
465
466   /* A lower bound on the number of temporary files to attempt to
467      generate.  The maximum total number of temporary file names that
468      can exist for a given template is 62**6.  It should never be
469      necessary to try all these combinations.  Instead if a reasonable
470      number of names is tried (we define reasonable as 62**3) fail to
471      give the system administrator the chance to remove the problems.  */
472 #define ATTEMPTS_MIN (62 * 62 * 62)
473
474   /* The number of times to attempt to generate a temporary file.  To
475      conform to POSIX, this must be no smaller than TMP_MAX.  */
476 #if ATTEMPTS_MIN < TMP_MAX
477   unsigned int attempts = TMP_MAX;
478 #else
479   unsigned int attempts = ATTEMPTS_MIN;
480 #endif
481
482   len = strlen (tmpl);
483   if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
484     {
485       errno = EINVAL;
486       return -1;
487     }
488
489   /* This is where the Xs start.  */
490   XXXXXX = &tmpl[len - 6];
491
492   /* Get some more or less random data.  */
493   {
494     FILETIME ft;
495
496     GetSystemTimeAsFileTime (&ft);
497     random_time_bits = (((uint64_t)ft.dwHighDateTime << 32)
498                         | (uint64_t)ft.dwLowDateTime);
499   }
500   value += random_time_bits ^ getpid ();
501
502   for (count = 0; count < attempts; value += 7777, ++count)
503     {
504       uint64_t v = value;
505
506       /* Fill in the random bits.  */
507       XXXXXX[0] = letters[v % 62];
508       v /= 62;
509       XXXXXX[1] = letters[v % 62];
510       v /= 62;
511       XXXXXX[2] = letters[v % 62];
512       v /= 62;
513       XXXXXX[3] = letters[v % 62];
514       v /= 62;
515       XXXXXX[4] = letters[v % 62];
516       v /= 62;
517       XXXXXX[5] = letters[v % 62];
518
519       fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
520       if (fd >= 0)
521         {
522           errno = save_errno;
523           return fd;
524         }
525       else if (errno != EEXIST)
526         return -1;
527     }
528
529   /* We got out of the loop because we ran out of combinations to try.  */
530   errno = EEXIST;
531   return -1;
532 }
533
534 \f
535 int
536 _gpgme_mkstemp (int *fd, char **name)
537 {
538   char tmp[MAX_PATH + 2];
539   char *tmpname;
540   int err;
541
542   *fd = -1;
543   *name = NULL;
544
545   err = GetTempPath (MAX_PATH + 1, tmp);
546   if (err == 0 || err > MAX_PATH + 1)
547     strcpy (tmp,"c:\\windows\\temp");
548   else
549     {
550       int len = strlen(tmp);
551       
552       /* GetTempPath may return with \ on the end */
553       while(len > 0 && tmp[len - 1] == '\\')
554         {
555           tmp[len-1] = '\0';
556           len--;
557         }
558     }
559
560   tmpname = malloc (strlen (tmp) + 13 + 1);
561   if (!tmpname)
562     return -1;
563   strcpy (stpcpy (tmpname, tmp), "\\gpgme-XXXXXX");
564   *fd = mkstemp (tmpname);
565   if (fd < 0)
566     return -1;
567
568   *name = tmpname;
569   return 0;
570 }