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