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