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