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