Imported Upstream version 1.7.1
[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 <http://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       || SHGetSpecialFolderPathA (NULL, path, CSIDL_PROGRAM_FILESX86, 0))
420     {
421       result = _gpgme_strconcat (path, "\\", name, NULL);
422       if (result && access (result, F_OK))
423         {
424           free (result);
425           result = NULL;
426         }
427     }
428   return result;
429 }
430
431
432 /* Set the default name for the gpg binary.  This function may only be
433    called by gpgme_set_global_flag.  Returns 0 on success.  */
434 int
435 _gpgme_set_default_gpg_name (const char *name)
436 {
437   if (!default_gpg_name)
438     {
439       default_gpg_name = _gpgme_strconcat (name, ".exe", NULL);
440       if (default_gpg_name)
441         replace_slashes (default_gpg_name);
442     }
443   return !default_gpg_name;
444 }
445
446 /* Set the default name for the gpgconf binary.  This function may only be
447    called by gpgme_set_global_flag.  Returns 0 on success.  */
448 int
449 _gpgme_set_default_gpgconf_name (const char *name)
450 {
451   if (!default_gpgconf_name)
452     {
453       default_gpgconf_name = _gpgme_strconcat (name, ".exe", NULL);
454       if (default_gpgconf_name)
455         replace_slashes (default_gpgconf_name);
456     }
457   return !default_gpgconf_name;
458 }
459
460
461 /* Set the override installation directory.  This function may only be
462    called by gpgme_set_global_flag.  Returns 0 on success.  */
463 int
464 _gpgme_set_override_inst_dir (const char *dir)
465 {
466   if (!override_inst_dir)
467     {
468       override_inst_dir = strdup (dir);
469       if (override_inst_dir)
470         {
471           replace_slashes (override_inst_dir);
472           /* Remove a trailing slash.  */
473           if (*override_inst_dir
474               && override_inst_dir[strlen (override_inst_dir)-1] == '\\')
475             override_inst_dir[strlen (override_inst_dir)-1] = 0;
476         }
477     }
478   return !override_inst_dir;
479 }
480
481
482 /* Return the full file name of the GPG binary.  This function is used
483    iff gpgconf was not found and thus it can be assumed that gpg2 is
484    not installed.  This function is only called by get_gpgconf_item
485    and may not be called concurrently. */
486 char *
487 _gpgme_get_gpg_path (void)
488 {
489   char *gpg = NULL;
490   const char *name, *inst_dir;
491
492   name = default_gpg_name? get_basename (default_gpg_name) : "gpg.exe";
493
494   /* 1. Try to find gpg.exe in the installation directory of gpgme.  */
495   inst_dir = _gpgme_get_inst_dir ();
496   if (inst_dir)
497     {
498       gpg = find_program_in_dir (inst_dir, name);
499     }
500
501   /* 2. Try to find gpg.exe using that ancient registry key.  */
502   if (!gpg)
503     {
504       char *dir;
505
506       dir = read_w32_registry_string ("HKEY_LOCAL_MACHINE",
507                                       "Software\\GNU\\GnuPG",
508                                       "Install Directory");
509       if (dir)
510         {
511           gpg = find_program_in_dir (dir, name);
512           free (dir);
513         }
514     }
515
516   /* 3. Try to find gpg.exe below CSIDL_PROGRAM_FILES.  */
517   if (!gpg)
518     {
519       name = default_gpg_name? default_gpg_name : "GNU\\GnuPG\\gpg.exe";
520       gpg = find_program_at_standard_place (name);
521     }
522
523   /* 4. Print a debug message if not found.  */
524   if (!gpg)
525     _gpgme_debug (DEBUG_ENGINE, "_gpgme_get_gpg_path: '%s' not found", name);
526
527   return gpg;
528 }
529
530
531 /* This function is only called by get_gpgconf_item and may not be
532    called concurrently.  */
533 char *
534 _gpgme_get_gpgconf_path (void)
535 {
536   char *gpgconf = NULL;
537   const char *inst_dir, *name;
538
539   name = default_gpgconf_name? get_basename(default_gpgconf_name):"gpgconf.exe";
540
541   /* 1. Try to find gpgconf.exe in the installation directory of gpgme.  */
542   inst_dir = _gpgme_get_inst_dir ();
543   if (inst_dir)
544     {
545       gpgconf = find_program_in_dir (inst_dir, name);
546     }
547
548   /* 2. Try to find gpgconf.exe from GnuPG >= 2.1 below CSIDL_PROGRAM_FILES. */
549   if (!gpgconf)
550     {
551       const char *name2 = (default_gpgconf_name ? default_gpgconf_name
552                            /**/                 : "GnuPG\\bin\\gpgconf.exe");
553       gpgconf = find_program_at_standard_place (name2);
554     }
555
556   /* 3. Try to find gpgconf.exe using the Windows registry. */
557   if (!gpgconf)
558     {
559       char *dir;
560
561       dir = read_w32_registry_string (NULL,
562                                       "Software\\GNU\\GnuPG",
563                                       "Install Directory");
564       if (!dir)
565         {
566           char *tmp = read_w32_registry_string (NULL,
567                                                 "Software\\GnuPG",
568                                                 "Install Directory");
569           if (tmp)
570             {
571               if (gpgrt_asprintf (&dir, "%s\\bin", tmp) == -1)
572                 return NULL;
573               free (tmp);
574             }
575         }
576       if (dir)
577         {
578           gpgconf = find_program_in_dir (dir, name);
579           free (dir);
580         }
581     }
582
583   /* 4. Try to find gpgconf.exe from Gpg4win below CSIDL_PROGRAM_FILES.  */
584   if (!gpgconf)
585     {
586       gpgconf = find_program_at_standard_place ("GNU\\GnuPG\\gpgconf.exe");
587     }
588
589   /* 5. Print a debug message if not found.  */
590   if (!gpgconf)
591     _gpgme_debug (DEBUG_ENGINE, "_gpgme_get_gpgconf_path: '%s' not found",name);
592
593   return gpgconf;
594 }
595
596
597 const char *
598 _gpgme_get_w32spawn_path (void)
599 {
600   static char *w32spawn_program;
601   const char *inst_dir;
602
603   inst_dir = _gpgme_get_inst_dir ();
604   LOCK (get_path_lock);
605   if (!w32spawn_program)
606     w32spawn_program = find_program_in_dir (inst_dir, "gpgme-w32spawn.exe");
607   UNLOCK (get_path_lock);
608   return w32spawn_program;
609 }
610
611
612 /* Return an integer value from gpgme specific configuration
613    entries. VALUE receives that value; function returns true if a value
614    has been configured and false if not. */
615 int
616 _gpgme_get_conf_int (const char *key, int *value)
617 {
618   char *tmp = read_w32_registry_string (NULL, "Software\\GNU\\gpgme", key);
619   if (!tmp)
620     return 0;
621   *value = atoi (tmp);
622   free (tmp);
623   return 1;
624 }
625
626 \f
627 #ifdef HAVE_W32CE_SYSTEM
628 int
629 _gpgme_mkstemp (int *fd, char **name)
630 {
631   return -1;
632 }
633 #else
634
635 /* mkstemp extracted from libc/sysdeps/posix/tempname.c.  Copyright
636    (C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc.
637
638    The GNU C Library is free software; you can redistribute it and/or
639    modify it under the terms of the GNU Lesser General Public
640    License as published by the Free Software Foundation; either
641    version 2.1 of the License, or (at your option) any later version.  */
642
643 static const char letters[] =
644 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
645
646 /* Generate a temporary file name based on TMPL.  TMPL must match the
647    rules for mk[s]temp (i.e. end in "XXXXXX").  The name constructed
648    does not exist at the time of the call to mkstemp.  TMPL is
649    overwritten with the result.  */
650 static int
651 my_mkstemp (char *tmpl)
652 {
653   int len;
654   char *XXXXXX;
655   static uint64_t value;
656   uint64_t random_time_bits;
657   unsigned int count;
658   int fd = -1;
659   int save_errno = errno;
660
661   /* A lower bound on the number of temporary files to attempt to
662      generate.  The maximum total number of temporary file names that
663      can exist for a given template is 62**6.  It should never be
664      necessary to try all these combinations.  Instead if a reasonable
665      number of names is tried (we define reasonable as 62**3) fail to
666      give the system administrator the chance to remove the problems.  */
667 #define ATTEMPTS_MIN (62 * 62 * 62)
668
669   /* The number of times to attempt to generate a temporary file.  To
670      conform to POSIX, this must be no smaller than TMP_MAX.  */
671 #if ATTEMPTS_MIN < TMP_MAX
672   unsigned int attempts = TMP_MAX;
673 #else
674   unsigned int attempts = ATTEMPTS_MIN;
675 #endif
676
677   len = strlen (tmpl);
678   if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
679     {
680       gpg_err_set_errno (EINVAL);
681       return -1;
682     }
683
684   /* This is where the Xs start.  */
685   XXXXXX = &tmpl[len - 6];
686
687   /* Get some more or less random data.  */
688   {
689     FILETIME ft;
690
691     GetSystemTimeAsFileTime (&ft);
692     random_time_bits = (((uint64_t)ft.dwHighDateTime << 32)
693                         | (uint64_t)ft.dwLowDateTime);
694   }
695   value += random_time_bits ^ ath_self ();
696
697   for (count = 0; count < attempts; value += 7777, ++count)
698     {
699       uint64_t v = value;
700
701       /* Fill in the random bits.  */
702       XXXXXX[0] = letters[v % 62];
703       v /= 62;
704       XXXXXX[1] = letters[v % 62];
705       v /= 62;
706       XXXXXX[2] = letters[v % 62];
707       v /= 62;
708       XXXXXX[3] = letters[v % 62];
709       v /= 62;
710       XXXXXX[4] = letters[v % 62];
711       v /= 62;
712       XXXXXX[5] = letters[v % 62];
713
714       fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
715       if (fd >= 0)
716         {
717           gpg_err_set_errno (save_errno);
718           return fd;
719         }
720       else if (errno != EEXIST)
721         return -1;
722     }
723
724   /* We got out of the loop because we ran out of combinations to try.  */
725   gpg_err_set_errno (EEXIST);
726   return -1;
727 }
728
729 \f
730 int
731 _gpgme_mkstemp (int *fd, char **name)
732 {
733   char tmp[MAX_PATH + 2];
734   char *tmpname;
735   int err;
736
737   *fd = -1;
738   *name = NULL;
739
740   err = GetTempPathA (MAX_PATH + 1, tmp);
741   if (err == 0 || err > MAX_PATH + 1)
742     strcpy (tmp,"c:\\windows\\temp");
743   else
744     {
745       int len = strlen(tmp);
746
747       /* GetTempPath may return with \ on the end */
748       while(len > 0 && tmp[len - 1] == '\\')
749         {
750           tmp[len-1] = '\0';
751           len--;
752         }
753     }
754
755   tmpname = _gpgme_strconcat (tmp, "\\gpgme-XXXXXX", NULL);
756   if (!tmpname)
757     return -1;
758   *fd = my_mkstemp (tmpname);
759   if (*fd < 0)
760     {
761       free (tmpname);
762       return -1;
763     }
764
765   *name = tmpname;
766   return 0;
767 }
768 #endif
769
770
771 \f
772 #ifdef HAVE_W32CE_SYSTEM
773 /* Return a malloced string with the replacement value for the
774    GPGME_DEBUG envvar.  Caller must release.  Returns NULL if not
775    set.  */
776 char *
777 _gpgme_w32ce_get_debug_envvar (void)
778 {
779   char *tmp;
780
781   tmp = read_w32_registry_string (NULL, "\\Software\\GNU\\gpgme", "debug");
782   if (tmp && !*tmp)
783     {
784       free (tmp);
785       tmp = NULL;
786     }
787   return tmp;
788 }
789 #endif /*HAVE_W32CE_SYSTEM*/
790
791
792 /* Entry point called by the DLL loader.  */
793 #ifdef DLL_EXPORT
794 int WINAPI
795 DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved)
796 {
797   (void)reserved;
798
799   if (reason == DLL_PROCESS_ATTACH)
800     my_hmodule = hinst;
801
802   return TRUE;
803 }
804 #endif /*DLL_EXPORT*/