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