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