spelling: fix misspellings
[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 #define HAVE_ALLOW_SET_FOREGROUND_WINDOW 1
69 #ifndef F_OK
70 # define F_OK 0
71 #endif
72
73 /* The Registry key used by GNUPG.  */
74 #ifdef _WIN64
75 # define GNUPG_REGKEY_2  "Software\\Wow6432Node\\GNU\\GnuPG"
76 #else
77 # define GNUPG_REGKEY_2  "Software\\GNU\\GnuPG"
78 #endif
79 #ifdef _WIN64
80 # define GNUPG_REGKEY_3  "Software\\Wow6432Node\\GnuPG"
81 #else
82 # define GNUPG_REGKEY_3  "Software\\GnuPG"
83 #endif
84
85 DEFINE_STATIC_LOCK (get_path_lock);
86
87 /* The module handle of this DLL.  If we are linked statically,
88    dllmain does not exists and thus the value of my_hmodule will be
89    NULL.  The effect is that a GetModuleFileName always returns the
90    file name of the DLL or executable which contains the gpgme code.  */
91 static HMODULE my_hmodule;
92
93 /* These variables store the malloced name of alternative default
94    binaries.  The are set only once by gpgme_set_global_flag.  */
95 static char *default_gpg_name;
96 static char *default_gpgconf_name;
97 /* If this variable is not NULL the value is assumed to be the
98    installation directory.  The variable may only be set once by
99    gpgme_set_global_flag and accessed by _gpgme_get_inst_dir.  */
100 static char *override_inst_dir;
101
102 #define RTLD_LAZY 0
103
104 static GPG_ERR_INLINE void *
105 dlopen (const char * name, int flag)
106 {
107   void * hd = LoadLibrary (name);
108
109   (void)flag;
110   return hd;
111 }
112
113 static GPG_ERR_INLINE void *
114 dlsym (void * hd, const char * sym)
115 {
116   if (hd && sym)
117     {
118       void * fnc = GetProcAddress (hd, sym);
119       if (!fnc)
120         return NULL;
121       return fnc;
122     }
123   return NULL;
124 }
125
126 static GPG_ERR_INLINE int
127 dlclose (void * hd)
128 {
129   if (hd)
130     {
131       FreeLibrary (hd);
132       return 0;
133     }
134   return -1;
135 }
136
137
138 /* Return a malloced string encoded in UTF-8 from the wide char input
139    string STRING.  Caller must free this value.  Returns NULL and sets
140    ERRNO on failure.  Calling this function with STRING set to NULL is
141    not defined.  */
142 static char *
143 wchar_to_utf8 (const wchar_t *string)
144 {
145   int n;
146   char *result;
147
148   n = WideCharToMultiByte (CP_UTF8, 0, string, -1, NULL, 0, NULL, NULL);
149   if (n < 0)
150     {
151       gpg_err_set_errno (EINVAL);
152       return NULL;
153     }
154
155   result = malloc (n+1);
156   if (!result)
157     return NULL;
158
159   n = WideCharToMultiByte (CP_UTF8, 0, string, -1, result, n, NULL, NULL);
160   if (n < 0)
161     {
162       free (result);
163       gpg_err_set_errno (EINVAL);
164       result = NULL;
165     }
166   return result;
167 }
168
169
170 /* Replace all forward slashes by backslashes.  */
171 static void
172 replace_slashes (char *string)
173 {
174   for (; *string; string++)
175     if (*string == '/')
176       *string = '\\';
177 }
178
179
180 /* Get the base name of NAME.  Returns a pointer into NAME right after
181    the last slash or backslash or to NAME if no slash or backslash
182    exists.  */
183 static const char *
184 get_basename (const char *name)
185 {
186   const char *mark, *s;
187
188   for (mark=NULL, s=name; *s; s++)
189     if (*s == '/' || *s == '\\')
190       mark = s;
191
192   return mark? mark+1 : name;
193 }
194
195
196 void
197 _gpgme_allow_set_foreground_window (pid_t pid)
198 {
199 #ifdef HAVE_ALLOW_SET_FOREGROUND_WINDOW
200   static int initialized;
201   static BOOL (WINAPI * func)(DWORD);
202   void *handle;
203
204   if (!initialized)
205     {
206       /* Available since W2000; thus we dynload it.  */
207       initialized = 1;
208       handle = dlopen ("user32.dll", RTLD_LAZY);
209       if (handle)
210         {
211           func = dlsym (handle, "AllowSetForegroundWindow");
212           if (!func)
213             {
214               dlclose (handle);
215               handle = NULL;
216             }
217         }
218     }
219
220   if (!pid || pid == (pid_t)(-1))
221     {
222       TRACE1 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
223               "no action for pid %d", (int)pid);
224     }
225   else if (func)
226     {
227       int rc = func (pid);
228       TRACE2 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
229               "called for pid %d; result=%d", (int)pid, rc);
230
231     }
232   else
233     {
234       TRACE0 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
235               "function not available");
236     }
237 #endif /* HAVE_ALLOW_SET_FOREGROUND_WINDOW */
238 }
239
240
241 /* Wrapper around CancelSynchronousIo which is only available since
242  * Vista.  */
243 void
244 _gpgme_w32_cancel_synchronous_io (HANDLE thread)
245 {
246   static int initialized;
247   static BOOL (WINAPI * func)(DWORD);
248   void *handle;
249
250   if (!initialized)
251     {
252       /* Available since Vista; thus we dynload it.  */
253       initialized = 1;
254       handle = dlopen ("kernel32.dll", RTLD_LAZY);
255       if (handle)
256         {
257           func = dlsym (handle, "CancelSynchronousIo");
258           if (!func)
259             {
260               dlclose (handle);
261               handle = NULL;
262             }
263         }
264     }
265
266   if (func)
267     {
268       if (!func (thread) && GetLastError() != ERROR_NOT_FOUND)
269         {
270           TRACE2 (DEBUG_ENGINE, "gpgme:CancelSynchronousIo", 0,
271                   "called for thread %p: ec=%d", thread, GetLastError ());
272         }
273     }
274   else
275     {
276       TRACE0 (DEBUG_ENGINE, "gpgme:CancelSynchronousIo", 0,
277               "function not available");
278     }
279 }
280
281
282 /* Return a string from the W32 Registry or NULL in case of error.
283    Caller must release the return value.  A NULL for root is an alias
284    for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. */
285 static char *
286 read_w32_registry_string (const char *root, const char *dir, const char *name)
287 {
288   HKEY root_key, key_handle;
289   DWORD n1, nbytes, type;
290   char *result = NULL;
291
292   if (!root)
293     root_key = HKEY_CURRENT_USER;
294   else if (!strcmp( root, "HKEY_CLASSES_ROOT"))
295     root_key = HKEY_CLASSES_ROOT;
296   else if (!strcmp( root, "HKEY_CURRENT_USER"))
297     root_key = HKEY_CURRENT_USER;
298   else if (!strcmp( root, "HKEY_LOCAL_MACHINE"))
299     root_key = HKEY_LOCAL_MACHINE;
300   else if (!strcmp( root, "HKEY_USERS"))
301     root_key = HKEY_USERS;
302   else if (!strcmp( root, "HKEY_PERFORMANCE_DATA"))
303     root_key = HKEY_PERFORMANCE_DATA;
304   else if (!strcmp( root, "HKEY_CURRENT_CONFIG"))
305     root_key = HKEY_CURRENT_CONFIG;
306   else
307     return NULL;
308
309   if (RegOpenKeyExA (root_key, dir, 0, KEY_READ, &key_handle))
310     {
311       if (root)
312         return NULL; /* no need for a RegClose, so return direct */
313       /* It seems to be common practise to fall back to HKLM. */
314       if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle))
315         return NULL; /* still no need for a RegClose, so return direct */
316     }
317
318   nbytes = 1;
319   if (RegQueryValueExA (key_handle, name, 0, NULL, NULL, &nbytes))
320     {
321       if (root)
322         goto leave;
323       /* Try to fallback to HKLM also vor a missing value.  */
324       RegCloseKey (key_handle);
325       if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle))
326         return NULL; /* Nope.  */
327       if (RegQueryValueExA (key_handle, name, 0, NULL, NULL, &nbytes))
328         goto leave;
329     }
330   n1 = nbytes + 1;
331   result = malloc (n1);
332   if (!result)
333     goto leave;
334   if (RegQueryValueExA (key_handle, name, 0, &type, (LPBYTE) result, &n1))
335     {
336       free (result);
337       result = NULL;
338       goto leave;
339     }
340   result[nbytes] = 0; /* Make sure it is really a string.  */
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 https://wiki.tcl-lang.org/page/Getting+Windows+%22special+folders%22+with+Ffidl 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                                       GNUPG_REGKEY_2,
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                                       GNUPG_REGKEY_2,
572                                       "Install Directory");
573       if (!dir)
574         {
575           char *tmp = read_w32_registry_string (NULL,
576                                                 GNUPG_REGKEY_3,
577                                                 "Install Directory");
578           if (tmp)
579             {
580               dir = _gpgme_strconcat (tmp, "\\bin", NULL);
581               free (tmp);
582               if (!dir)
583                 return NULL;
584             }
585         }
586       if (dir)
587         {
588           gpgconf = find_program_in_dir (dir, name);
589           free (dir);
590         }
591     }
592
593   /* 4. Try to find gpgconf.exe from Gpg4win below CSIDL_PROGRAM_FILES.  */
594   if (!gpgconf)
595     {
596       gpgconf = find_program_at_standard_place ("GNU\\GnuPG\\gpgconf.exe");
597     }
598
599   /* 5. Try to find gpgconf.exe relative to us.  */
600   if (!gpgconf && inst_dir)
601     {
602       char *dir = _gpgme_strconcat (inst_dir, "\\..\\..\\GnuPG\\bin");
603       gpgconf = find_program_in_dir (dir, name);
604       free (dir);
605     }
606
607   /* 5. Print a debug message if not found.  */
608   if (!gpgconf)
609     _gpgme_debug (DEBUG_ENGINE, "_gpgme_get_gpgconf_path: '%s' not found",name);
610
611   return gpgconf;
612 }
613
614
615 const char *
616 _gpgme_get_w32spawn_path (void)
617 {
618   static char *w32spawn_program;
619   const char *inst_dir;
620
621   inst_dir = _gpgme_get_inst_dir ();
622   LOCK (get_path_lock);
623   if (!w32spawn_program)
624     w32spawn_program = find_program_in_dir (inst_dir, "gpgme-w32spawn.exe");
625   UNLOCK (get_path_lock);
626   return w32spawn_program;
627 }
628
629
630 /* Return an integer value from gpgme specific configuration
631    entries. VALUE receives that value; function returns true if a value
632    has been configured and false if not. */
633 int
634 _gpgme_get_conf_int (const char *key, int *value)
635 {
636   char *tmp = read_w32_registry_string (NULL, "Software\\GNU\\gpgme", key);
637   if (!tmp)
638     return 0;
639   *value = atoi (tmp);
640   free (tmp);
641   return 1;
642 }
643
644
645 \f
646 /* mkstemp extracted from libc/sysdeps/posix/tempname.c.  Copyright
647    (C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc.
648
649    The GNU C Library is free software; you can redistribute it and/or
650    modify it under the terms of the GNU Lesser General Public
651    License as published by the Free Software Foundation; either
652    version 2.1 of the License, or (at your option) any later version.  */
653
654 static const char letters[] =
655 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
656
657 /* Generate a temporary file name based on TMPL.  TMPL must match the
658    rules for mk[s]temp (i.e. end in "XXXXXX").  The name constructed
659    does not exist at the time of the call to mkstemp.  TMPL is
660    overwritten with the result.  */
661 static int
662 my_mkstemp (char *tmpl)
663 {
664   int len;
665   char *XXXXXX;
666   static uint64_t value;
667   uint64_t random_time_bits;
668   unsigned int count;
669   int fd = -1;
670   int save_errno = errno;
671
672   /* A lower bound on the number of temporary files to attempt to
673      generate.  The maximum total number of temporary file names that
674      can exist for a given template is 62**6.  It should never be
675      necessary to try all these combinations.  Instead if a reasonable
676      number of names is tried (we define reasonable as 62**3) fail to
677      give the system administrator the chance to remove the problems.  */
678 #define ATTEMPTS_MIN (62 * 62 * 62)
679
680   /* The number of times to attempt to generate a temporary file.  To
681      conform to POSIX, this must be no smaller than TMP_MAX.  */
682 #if ATTEMPTS_MIN < TMP_MAX
683   unsigned int attempts = TMP_MAX;
684 #else
685   unsigned int attempts = ATTEMPTS_MIN;
686 #endif
687
688   len = strlen (tmpl);
689   if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
690     {
691       gpg_err_set_errno (EINVAL);
692       return -1;
693     }
694
695   /* This is where the Xs start.  */
696   XXXXXX = &tmpl[len - 6];
697
698   /* Get some more or less random data.  */
699   {
700     FILETIME ft;
701
702     GetSystemTimeAsFileTime (&ft);
703     random_time_bits = (((uint64_t)ft.dwHighDateTime << 32)
704                         | (uint64_t)ft.dwLowDateTime);
705   }
706   value += random_time_bits ^ ath_self ();
707
708   for (count = 0; count < attempts; value += 7777, ++count)
709     {
710       uint64_t v = value;
711
712       /* Fill in the random bits.  */
713       XXXXXX[0] = letters[v % 62];
714       v /= 62;
715       XXXXXX[1] = letters[v % 62];
716       v /= 62;
717       XXXXXX[2] = letters[v % 62];
718       v /= 62;
719       XXXXXX[3] = letters[v % 62];
720       v /= 62;
721       XXXXXX[4] = letters[v % 62];
722       v /= 62;
723       XXXXXX[5] = letters[v % 62];
724
725       fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
726       if (fd >= 0)
727         {
728           gpg_err_set_errno (save_errno);
729           return fd;
730         }
731       else if (errno != EEXIST)
732         return -1;
733     }
734
735   /* We got out of the loop because we ran out of combinations to try.  */
736   gpg_err_set_errno (EEXIST);
737   return -1;
738 }
739
740 \f
741 int
742 _gpgme_mkstemp (int *fd, char **name)
743 {
744   char tmp[MAX_PATH + 2];
745   char *tmpname;
746   int err;
747
748   *fd = -1;
749   *name = NULL;
750
751   err = GetTempPathA (MAX_PATH + 1, tmp);
752   if (err == 0 || err > MAX_PATH + 1)
753     strcpy (tmp,"c:\\windows\\temp");
754   else
755     {
756       int len = strlen(tmp);
757
758       /* GetTempPath may return with \ on the end */
759       while(len > 0 && tmp[len - 1] == '\\')
760         {
761           tmp[len-1] = '\0';
762           len--;
763         }
764     }
765
766   tmpname = _gpgme_strconcat (tmp, "\\gpgme-XXXXXX", NULL);
767   if (!tmpname)
768     return -1;
769   *fd = my_mkstemp (tmpname);
770   if (*fd < 0)
771     {
772       free (tmpname);
773       return -1;
774     }
775
776   *name = tmpname;
777   return 0;
778 }
779
780
781 \f
782 /* Entry point called by the DLL loader.  */
783 #ifdef DLL_EXPORT
784 int WINAPI
785 DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved)
786 {
787   (void)reserved;
788
789   if (reason == DLL_PROCESS_ATTACH)
790     my_hmodule = hinst;
791
792   return TRUE;
793 }
794 #endif /*DLL_EXPORT*/