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