Imported Upstream version 1.8.0
[platform/upstream/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, 2013 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, see <https://www.gnu.org/licenses/>.
20  **/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <assert.h>
29 #include <errno.h>
30 #include <stdint.h>
31 #ifdef HAVE_SYS_TIME_H
32 # include <sys/time.h>
33 #endif
34 #ifdef HAVE_SYS_TYPES_H
35 # include <sys/types.h>
36 #endif
37 #ifdef HAVE_SYS_STAT_H
38 # include <sys/stat.h>
39 #endif
40 #ifdef HAVE_UNISTD_H
41 # include <unistd.h>
42 #endif
43 #include <fcntl.h>
44 #include <io.h>
45
46 #if __MINGW64_VERSION_MAJOR >= 2
47 # define _WIN32_IE 0x0501 /* Required by mingw64 toolkit.  */
48 #else
49 # define _WIN32_IE 0x0400 /* Required for SHGetSpecialFolderPathA.  */
50 #endif
51
52 /* We need to include the windows stuff here prior to shlobj.h so that
53    we get the right winsock version.  This is usually done in util.h
54    but that header also redefines some Windows functions which we need
55    to avoid unless having included shlobj.h.  */
56 #include <winsock2.h>
57 #include <ws2tcpip.h>
58 #include <windows.h>
59 #include <shlobj.h>
60
61 #include "util.h"
62 #include "ath.h"
63 #include "sema.h"
64 #include "debug.h"
65 #include "sys-util.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 /* The module handle of this DLL.  If we are linked statically,
79    dllmain does not exists and thus the value of my_hmodule will be
80    NULL.  The effect is that a GetModuleFileName always returns the
81    file name of the DLL or executable which contains the gpgme code.  */
82 static HMODULE my_hmodule;
83
84 /* These variables store the malloced name of alternative default
85    binaries.  The are set only once by gpgme_set_global_flag.  */
86 static char *default_gpg_name;
87 static char *default_gpgconf_name;
88 /* If this variable is not NULL the value is assumed to be the
89    installation directory.  The variable may only be set once by
90    gpgme_set_global_flag and accessed by _gpgme_get_inst_dir.  */
91 static char *override_inst_dir;
92
93 #ifdef HAVE_ALLOW_SET_FOREGROUND_WINDOW
94
95 #define RTLD_LAZY 0
96
97 static GPG_ERR_INLINE void *
98 dlopen (const char * name, int flag)
99 {
100   void * hd = LoadLibrary (name);
101
102   (void)flag;
103   return hd;
104 }
105
106 static GPG_ERR_INLINE void *
107 dlsym (void * hd, const char * sym)
108 {
109   if (hd && sym)
110     {
111       void * fnc = GetProcAddress (hd, sym);
112       if (!fnc)
113         return NULL;
114       return fnc;
115     }
116   return NULL;
117 }
118
119 static GPG_ERR_INLINE int
120 dlclose (void * hd)
121 {
122   if (hd)
123     {
124       FreeLibrary (hd);
125       return 0;
126     }
127   return -1;
128 }
129 #endif /* HAVE_ALLOW_SET_FOREGROUND_WINDOW */
130
131
132 /* Return a malloced string encoded in UTF-8 from the wide char input
133    string STRING.  Caller must free this value.  Returns NULL and sets
134    ERRNO on failure.  Calling this function with STRING set to NULL is
135    not defined.  */
136 static char *
137 wchar_to_utf8 (const wchar_t *string)
138 {
139   int n;
140   char *result;
141
142   n = WideCharToMultiByte (CP_UTF8, 0, string, -1, NULL, 0, NULL, NULL);
143   if (n < 0)
144     {
145       gpg_err_set_errno (EINVAL);
146       return NULL;
147     }
148
149   result = malloc (n+1);
150   if (!result)
151     return NULL;
152
153   n = WideCharToMultiByte (CP_UTF8, 0, string, -1, result, n, NULL, NULL);
154   if (n < 0)
155     {
156       free (result);
157       gpg_err_set_errno (EINVAL);
158       result = NULL;
159     }
160   return result;
161 }
162
163
164 /* Replace all forward slashes by backslashes.  */
165 static void
166 replace_slashes (char *string)
167 {
168   for (; *string; string++)
169     if (*string == '/')
170       *string = '\\';
171 }
172
173
174 /* Get the base name of NAME.  Returns a pointer into NAME right after
175    the last slash or backslash or to NAME if no slash or backslash
176    exists.  */
177 static const char *
178 get_basename (const char *name)
179 {
180   const char *mark, *s;
181
182   for (mark=NULL, s=name; *s; s++)
183     if (*s == '/' || *s == '\\')
184       mark = s;
185
186   return mark? mark+1 : name;
187 }
188
189
190 void
191 _gpgme_allow_set_foreground_window (pid_t pid)
192 {
193 #ifdef HAVE_ALLOW_SET_FOREGROUND_WINDOW
194   static int initialized;
195   static BOOL (WINAPI * func)(DWORD);
196   void *handle;
197
198   if (!initialized)
199     {
200       /* Available since W2000; thus we dynload it.  */
201       initialized = 1;
202       handle = dlopen ("user32.dll", RTLD_LAZY);
203       if (handle)
204         {
205           func = dlsym (handle, "AllowSetForegroundWindow");
206           if (!func)
207             {
208               dlclose (handle);
209               handle = NULL;
210             }
211         }
212     }
213
214   if (!pid || pid == (pid_t)(-1))
215     {
216       TRACE1 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
217               "no action for pid %d", (int)pid);
218     }
219   else if (func)
220     {
221       int rc = func (pid);
222       TRACE2 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
223               "called for pid %d; result=%d", (int)pid, rc);
224
225     }
226   else
227     {
228       TRACE0 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
229               "function not available");
230     }
231 #endif /* HAVE_ALLOW_SET_FOREGROUND_WINDOW */
232 }
233
234
235 /* Return a string from the W32 Registry or NULL in case of error.
236    Caller must release the return value.  A NULL for root is an alias
237    for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. */
238 static char *
239 read_w32_registry_string (const char *root, const char *dir, const char *name)
240 {
241   HKEY root_key, key_handle;
242   DWORD n1, nbytes, type;
243   char *result = NULL;
244
245   if (!root)
246     root_key = HKEY_CURRENT_USER;
247   else if (!strcmp( root, "HKEY_CLASSES_ROOT"))
248     root_key = HKEY_CLASSES_ROOT;
249   else if (!strcmp( root, "HKEY_CURRENT_USER"))
250     root_key = HKEY_CURRENT_USER;
251   else if (!strcmp( root, "HKEY_LOCAL_MACHINE"))
252     root_key = HKEY_LOCAL_MACHINE;
253   else if (!strcmp( root, "HKEY_USERS"))
254     root_key = HKEY_USERS;
255   else if (!strcmp( root, "HKEY_PERFORMANCE_DATA"))
256     root_key = HKEY_PERFORMANCE_DATA;
257   else if (!strcmp( root, "HKEY_CURRENT_CONFIG"))
258     root_key = HKEY_CURRENT_CONFIG;
259   else
260     return NULL;
261
262   if (RegOpenKeyExA (root_key, dir, 0, KEY_READ, &key_handle))
263     {
264       if (root)
265         return NULL; /* no need for a RegClose, so return direct */
266       /* It seems to be common practise to fall back to HKLM. */
267       if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle))
268         return NULL; /* still no need for a RegClose, so return direct */
269     }
270
271   nbytes = 1;
272   if (RegQueryValueExA (key_handle, name, 0, NULL, NULL, &nbytes))
273     {
274       if (root)
275         goto leave;
276       /* Try to fallback to HKLM also vor a missing value.  */
277       RegCloseKey (key_handle);
278       if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle))
279         return NULL; /* Nope.  */
280       if (RegQueryValueExA (key_handle, name, 0, NULL, NULL, &nbytes))
281         goto leave;
282     }
283   n1 = nbytes + 1;
284   result = malloc (n1);
285   if (!result)
286     goto leave;
287   if (RegQueryValueExA (key_handle, name, 0, &type, (LPBYTE) result, &n1))
288     {
289       free (result);
290       result = NULL;
291       goto leave;
292     }
293   result[nbytes] = 0; /* Make sure it is really a string.  */
294
295 #ifndef HAVE_W32CE_SYSTEM
296   /* Windows CE does not have an environment.  */
297   if (type == REG_EXPAND_SZ && strchr (result, '%'))
298     {
299       char *tmp;
300
301       n1 += 1000;
302       tmp = malloc (n1 + 1);
303       if (!tmp)
304         goto leave;
305       nbytes = ExpandEnvironmentStrings (result, tmp, n1);
306       if (nbytes && nbytes > n1)
307         {
308           free (tmp);
309           n1 = nbytes;
310           tmp = malloc (n1 + 1);
311           if (!tmp)
312             goto leave;
313           nbytes = ExpandEnvironmentStrings (result, tmp, n1);
314           if (nbytes && nbytes > n1) {
315             free (tmp); /* Oops - truncated, better don't expand at all. */
316             goto leave;
317           }
318           tmp[nbytes] = 0;
319           free (result);
320           result = tmp;
321         }
322       else if (nbytes)  /* Okay, reduce the length. */
323         {
324           tmp[nbytes] = 0;
325           free (result);
326           result = malloc (strlen (tmp)+1);
327           if (!result)
328             result = tmp;
329           else
330             {
331               strcpy (result, tmp);
332               free (tmp);
333             }
334         }
335       else  /* Error - don't expand. */
336         {
337           free (tmp);
338         }
339     }
340 #endif
341
342  leave:
343   RegCloseKey (key_handle);
344   return result;
345 }
346
347
348 /* Return the name of the directory with the gpgme DLL or the EXE (if
349    statically linked).  May return NULL on severe errors. */
350 const char *
351 _gpgme_get_inst_dir (void)
352 {
353   static char *inst_dir;
354
355   if (override_inst_dir)
356     return override_inst_dir;
357
358   LOCK (get_path_lock);
359   if (!inst_dir)
360     {
361       wchar_t *moddir;
362
363       moddir = malloc ((MAX_PATH+5) * sizeof *moddir);
364       if (moddir)
365         {
366           if (!GetModuleFileNameW (my_hmodule, moddir, MAX_PATH))
367             *moddir = 0;
368           if (!*moddir)
369             gpg_err_set_errno (ENOENT);
370           else
371             {
372               inst_dir = wchar_to_utf8 (moddir);
373               if (inst_dir)
374                 {
375                   char *p = strrchr (inst_dir, '\\');
376                   if (p)
377                     *p = 0;
378                 }
379             }
380           free (moddir);
381         }
382     }
383   UNLOCK (get_path_lock);
384   return inst_dir;
385 }
386
387
388 static char *
389 find_program_in_dir (const char *dir, const char *name)
390 {
391   char *result;
392
393   result = _gpgme_strconcat (dir, "\\", name, NULL);
394   if (!result)
395     return NULL;
396
397   if (access (result, F_OK))
398     {
399       free (result);
400       return NULL;
401     }
402
403   return result;
404 }
405
406
407 static char *
408 find_program_at_standard_place (const char *name)
409 {
410   char path[MAX_PATH];
411   char *result = NULL;
412
413   /* See http://wiki.tcl.tk/17492 for details on compatibility.
414
415      We First try the generic place and then fallback to the x86
416      (i.e. 32 bit) place.  This will prefer a 64 bit of the program
417      over a 32 bit version on 64 bit Windows if installed.  */
418   if (SHGetSpecialFolderPathA (NULL, path, CSIDL_PROGRAM_FILES, 0))
419     {
420       result = _gpgme_strconcat (path, "\\", name, NULL);
421       if (result && access (result, F_OK))
422         {
423           free (result);
424           result = NULL;
425         }
426     }
427   if (!result
428       && SHGetSpecialFolderPathA (NULL, path, CSIDL_PROGRAM_FILESX86, 0))
429     {
430       result = _gpgme_strconcat (path, "\\", name, NULL);
431       if (result && access (result, F_OK))
432         {
433           free (result);
434           result = NULL;
435         }
436     }
437   return result;
438 }
439
440
441 /* Set the default name for the gpg binary.  This function may only be
442    called by gpgme_set_global_flag.  Returns 0 on success.  */
443 int
444 _gpgme_set_default_gpg_name (const char *name)
445 {
446   if (!default_gpg_name)
447     {
448       default_gpg_name = _gpgme_strconcat (name, ".exe", NULL);
449       if (default_gpg_name)
450         replace_slashes (default_gpg_name);
451     }
452   return !default_gpg_name;
453 }
454
455 /* Set the default name for the gpgconf binary.  This function may only be
456    called by gpgme_set_global_flag.  Returns 0 on success.  */
457 int
458 _gpgme_set_default_gpgconf_name (const char *name)
459 {
460   if (!default_gpgconf_name)
461     {
462       default_gpgconf_name = _gpgme_strconcat (name, ".exe", NULL);
463       if (default_gpgconf_name)
464         replace_slashes (default_gpgconf_name);
465     }
466   return !default_gpgconf_name;
467 }
468
469
470 /* Set the override installation directory.  This function may only be
471    called by gpgme_set_global_flag.  Returns 0 on success.  */
472 int
473 _gpgme_set_override_inst_dir (const char *dir)
474 {
475   if (!override_inst_dir)
476     {
477       override_inst_dir = strdup (dir);
478       if (override_inst_dir)
479         {
480           replace_slashes (override_inst_dir);
481           /* Remove a trailing slash.  */
482           if (*override_inst_dir
483               && override_inst_dir[strlen (override_inst_dir)-1] == '\\')
484             override_inst_dir[strlen (override_inst_dir)-1] = 0;
485         }
486     }
487   return !override_inst_dir;
488 }
489
490
491 /* Return the full file name of the GPG binary.  This function is used
492    iff gpgconf was not found and thus it can be assumed that gpg2 is
493    not installed.  This function is only called by get_gpgconf_item
494    and may not be called concurrently. */
495 char *
496 _gpgme_get_gpg_path (void)
497 {
498   char *gpg = NULL;
499   const char *name, *inst_dir;
500
501   name = default_gpg_name? get_basename (default_gpg_name) : "gpg.exe";
502
503   /* 1. Try to find gpg.exe in the installation directory of gpgme.  */
504   inst_dir = _gpgme_get_inst_dir ();
505   if (inst_dir)
506     {
507       gpg = find_program_in_dir (inst_dir, name);
508     }
509
510   /* 2. Try to find gpg.exe using that ancient registry key.  */
511   if (!gpg)
512     {
513       char *dir;
514
515       dir = read_w32_registry_string ("HKEY_LOCAL_MACHINE",
516                                       "Software\\GNU\\GnuPG",
517                                       "Install Directory");
518       if (dir)
519         {
520           gpg = find_program_in_dir (dir, name);
521           free (dir);
522         }
523     }
524
525   /* 3. Try to find gpg.exe below CSIDL_PROGRAM_FILES.  */
526   if (!gpg)
527     {
528       name = default_gpg_name? default_gpg_name : "GNU\\GnuPG\\gpg.exe";
529       gpg = find_program_at_standard_place (name);
530     }
531
532   /* 4. Print a debug message if not found.  */
533   if (!gpg)
534     _gpgme_debug (DEBUG_ENGINE, "_gpgme_get_gpg_path: '%s' not found", name);
535
536   return gpg;
537 }
538
539
540 /* This function is only called by get_gpgconf_item and may not be
541    called concurrently.  */
542 char *
543 _gpgme_get_gpgconf_path (void)
544 {
545   char *gpgconf = NULL;
546   const char *inst_dir, *name;
547
548   name = default_gpgconf_name? get_basename(default_gpgconf_name):"gpgconf.exe";
549
550   /* 1. Try to find gpgconf.exe in the installation directory of gpgme.  */
551   inst_dir = _gpgme_get_inst_dir ();
552   if (inst_dir)
553     {
554       gpgconf = find_program_in_dir (inst_dir, name);
555     }
556
557   /* 2. Try to find gpgconf.exe from GnuPG >= 2.1 below CSIDL_PROGRAM_FILES. */
558   if (!gpgconf)
559     {
560       const char *name2 = (default_gpgconf_name ? default_gpgconf_name
561                            /**/                 : "GnuPG\\bin\\gpgconf.exe");
562       gpgconf = find_program_at_standard_place (name2);
563     }
564
565   /* 3. Try to find gpgconf.exe using the Windows registry. */
566   if (!gpgconf)
567     {
568       char *dir;
569
570       dir = read_w32_registry_string (NULL,
571                                       "Software\\GNU\\GnuPG",
572                                       "Install Directory");
573       if (!dir)
574         {
575           char *tmp = read_w32_registry_string (NULL,
576                                                 "Software\\GnuPG",
577                                                 "Install Directory");
578           if (tmp)
579             {
580               if (gpgrt_asprintf (&dir, "%s\\bin", tmp) == -1)
581                 return NULL;
582               free (tmp);
583             }
584         }
585       if (dir)
586         {
587           gpgconf = find_program_in_dir (dir, name);
588           free (dir);
589         }
590     }
591
592   /* 4. Try to find gpgconf.exe from Gpg4win below CSIDL_PROGRAM_FILES.  */
593   if (!gpgconf)
594     {
595       gpgconf = find_program_at_standard_place ("GNU\\GnuPG\\gpgconf.exe");
596     }
597
598   /* 5. Print a debug message if not found.  */
599   if (!gpgconf)
600     _gpgme_debug (DEBUG_ENGINE, "_gpgme_get_gpgconf_path: '%s' not found",name);
601
602   return gpgconf;
603 }
604
605
606 const char *
607 _gpgme_get_w32spawn_path (void)
608 {
609   static char *w32spawn_program;
610   const char *inst_dir;
611
612   inst_dir = _gpgme_get_inst_dir ();
613   LOCK (get_path_lock);
614   if (!w32spawn_program)
615     w32spawn_program = find_program_in_dir (inst_dir, "gpgme-w32spawn.exe");
616   UNLOCK (get_path_lock);
617   return w32spawn_program;
618 }
619
620
621 /* Return an integer value from gpgme specific configuration
622    entries. VALUE receives that value; function returns true if a value
623    has been configured and false if not. */
624 int
625 _gpgme_get_conf_int (const char *key, int *value)
626 {
627   char *tmp = read_w32_registry_string (NULL, "Software\\GNU\\gpgme", key);
628   if (!tmp)
629     return 0;
630   *value = atoi (tmp);
631   free (tmp);
632   return 1;
633 }
634
635 \f
636 #ifdef HAVE_W32CE_SYSTEM
637 int
638 _gpgme_mkstemp (int *fd, char **name)
639 {
640   return -1;
641 }
642 #else
643
644 /* mkstemp extracted from libc/sysdeps/posix/tempname.c.  Copyright
645    (C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc.
646
647    The GNU C Library is free software; you can redistribute it and/or
648    modify it under the terms of the GNU Lesser General Public
649    License as published by the Free Software Foundation; either
650    version 2.1 of the License, or (at your option) any later version.  */
651
652 static const char letters[] =
653 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
654
655 /* Generate a temporary file name based on TMPL.  TMPL must match the
656    rules for mk[s]temp (i.e. end in "XXXXXX").  The name constructed
657    does not exist at the time of the call to mkstemp.  TMPL is
658    overwritten with the result.  */
659 static int
660 my_mkstemp (char *tmpl)
661 {
662   int len;
663   char *XXXXXX;
664   static uint64_t value;
665   uint64_t random_time_bits;
666   unsigned int count;
667   int fd = -1;
668   int save_errno = errno;
669
670   /* A lower bound on the number of temporary files to attempt to
671      generate.  The maximum total number of temporary file names that
672      can exist for a given template is 62**6.  It should never be
673      necessary to try all these combinations.  Instead if a reasonable
674      number of names is tried (we define reasonable as 62**3) fail to
675      give the system administrator the chance to remove the problems.  */
676 #define ATTEMPTS_MIN (62 * 62 * 62)
677
678   /* The number of times to attempt to generate a temporary file.  To
679      conform to POSIX, this must be no smaller than TMP_MAX.  */
680 #if ATTEMPTS_MIN < TMP_MAX
681   unsigned int attempts = TMP_MAX;
682 #else
683   unsigned int attempts = ATTEMPTS_MIN;
684 #endif
685
686   len = strlen (tmpl);
687   if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
688     {
689       gpg_err_set_errno (EINVAL);
690       return -1;
691     }
692
693   /* This is where the Xs start.  */
694   XXXXXX = &tmpl[len - 6];
695
696   /* Get some more or less random data.  */
697   {
698     FILETIME ft;
699
700     GetSystemTimeAsFileTime (&ft);
701     random_time_bits = (((uint64_t)ft.dwHighDateTime << 32)
702                         | (uint64_t)ft.dwLowDateTime);
703   }
704   value += random_time_bits ^ ath_self ();
705
706   for (count = 0; count < attempts; value += 7777, ++count)
707     {
708       uint64_t v = value;
709
710       /* Fill in the random bits.  */
711       XXXXXX[0] = letters[v % 62];
712       v /= 62;
713       XXXXXX[1] = letters[v % 62];
714       v /= 62;
715       XXXXXX[2] = letters[v % 62];
716       v /= 62;
717       XXXXXX[3] = letters[v % 62];
718       v /= 62;
719       XXXXXX[4] = letters[v % 62];
720       v /= 62;
721       XXXXXX[5] = letters[v % 62];
722
723       fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
724       if (fd >= 0)
725         {
726           gpg_err_set_errno (save_errno);
727           return fd;
728         }
729       else if (errno != EEXIST)
730         return -1;
731     }
732
733   /* We got out of the loop because we ran out of combinations to try.  */
734   gpg_err_set_errno (EEXIST);
735   return -1;
736 }
737
738 \f
739 int
740 _gpgme_mkstemp (int *fd, char **name)
741 {
742   char tmp[MAX_PATH + 2];
743   char *tmpname;
744   int err;
745
746   *fd = -1;
747   *name = NULL;
748
749   err = GetTempPathA (MAX_PATH + 1, tmp);
750   if (err == 0 || err > MAX_PATH + 1)
751     strcpy (tmp,"c:\\windows\\temp");
752   else
753     {
754       int len = strlen(tmp);
755
756       /* GetTempPath may return with \ on the end */
757       while(len > 0 && tmp[len - 1] == '\\')
758         {
759           tmp[len-1] = '\0';
760           len--;
761         }
762     }
763
764   tmpname = _gpgme_strconcat (tmp, "\\gpgme-XXXXXX", NULL);
765   if (!tmpname)
766     return -1;
767   *fd = my_mkstemp (tmpname);
768   if (*fd < 0)
769     {
770       free (tmpname);
771       return -1;
772     }
773
774   *name = tmpname;
775   return 0;
776 }
777 #endif
778
779
780 \f
781 #ifdef HAVE_W32CE_SYSTEM
782 /* Return a malloced string with the replacement value for the
783    GPGME_DEBUG envvar.  Caller must release.  Returns NULL if not
784    set.  */
785 char *
786 _gpgme_w32ce_get_debug_envvar (void)
787 {
788   char *tmp;
789
790   tmp = read_w32_registry_string (NULL, "\\Software\\GNU\\gpgme", "debug");
791   if (tmp && !*tmp)
792     {
793       free (tmp);
794       tmp = NULL;
795     }
796   return tmp;
797 }
798 #endif /*HAVE_W32CE_SYSTEM*/
799
800
801 /* Entry point called by the DLL loader.  */
802 #ifdef DLL_EXPORT
803 int WINAPI
804 DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved)
805 {
806   (void)reserved;
807
808   if (reason == DLL_PROCESS_ATTACH)
809     my_hmodule = hinst;
810
811   return TRUE;
812 }
813 #endif /*DLL_EXPORT*/