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