Code cleanups.
[gpgol.git] / src / common.c
1 /* common.c - Common routines used by GpgOL
2  *      Copyright (C) 2005, 2007 g10 Code GmbH
3  *
4  * This file is part of GpgOL.
5  *
6  * GpgOL is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * as published by the Free Software Foundation; either version 2.1
9  * of the License, or (at your option) any later version.
10  *
11  * GpgOL is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <windows.h>
22 #include <shlobj.h>
23 #ifndef CSIDL_APPDATA
24 #define CSIDL_APPDATA 0x001a
25 #endif
26 #ifndef CSIDL_LOCAL_APPDATA
27 #define CSIDL_LOCAL_APPDATA 0x001c
28 #endif
29 #ifndef CSIDL_FLAG_CREATE
30 #define CSIDL_FLAG_CREATE 0x8000
31 #endif
32 #include <time.h>
33 #include <fcntl.h>
34
35 #include "common.h"
36
37 HINSTANCE glob_hinst = NULL;
38
39
40 /* The base-64 list used for base64 encoding. */
41 static unsigned char bintoasc[64+1] = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
42                                        "abcdefghijklmnopqrstuvwxyz"
43                                        "0123456789+/");
44
45 /* The reverse base-64 list used for base-64 decoding. */
46 static unsigned char const asctobin[256] = {
47   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
48   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
49   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
50   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
51   0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
52   0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
53   0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
54   0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
55   0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
56   0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
57   0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
58   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
59   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
60   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
61   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
62   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
63   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
64   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
65   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
66   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
67   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
68   0xff, 0xff, 0xff, 0xff
69 };
70
71
72
73 void
74 set_global_hinstance (HINSTANCE hinst)
75 {
76     glob_hinst = hinst;
77 }
78
79 /* Center the given window with the desktop window as the
80    parent window. */
81 void
82 center_window (HWND childwnd, HWND style)
83 {
84     HWND parwnd;
85     RECT rchild, rparent;
86     HDC hdc;
87     int wchild, hchild, wparent, hparent;
88     int wscreen, hscreen, xnew, ynew;
89     int flags = SWP_NOSIZE | SWP_NOZORDER;
90
91     parwnd = GetDesktopWindow ();
92     GetWindowRect (childwnd, &rchild);
93     wchild = rchild.right - rchild.left;
94     hchild = rchild.bottom - rchild.top;
95
96     GetWindowRect (parwnd, &rparent);
97     wparent = rparent.right - rparent.left;
98     hparent = rparent.bottom - rparent.top;
99
100     hdc = GetDC (childwnd);
101     wscreen = GetDeviceCaps (hdc, HORZRES);
102     hscreen = GetDeviceCaps (hdc, VERTRES);
103     ReleaseDC (childwnd, hdc);
104     xnew = rparent.left + ((wparent - wchild) / 2);
105     if (xnew < 0)
106         xnew = 0;
107     else if ((xnew+wchild) > wscreen)
108         xnew = wscreen - wchild;
109     ynew = rparent.top  + ((hparent - hchild) / 2);
110     if (ynew < 0)
111         ynew = 0;
112     else if ((ynew+hchild) > hscreen)
113         ynew = hscreen - hchild;
114     if (style == HWND_TOPMOST || style == HWND_NOTOPMOST)
115         flags = SWP_NOMOVE | SWP_NOSIZE;
116     SetWindowPos (childwnd, style? style : NULL, xnew, ynew, 0, 0, flags);
117 }
118
119
120 /* Return the system's bitmap of the check bar used which check boxes.
121    If CHECKED is set, this check mark is returned; if it is not set,
122    the one used for not-checked is returned.  May return NULL on
123    error.  Taken from an example in the platform reference. 
124
125    Not used as of now. */
126 HBITMAP
127 get_system_check_bitmap (int checked)
128 {
129   COLORREF bg_color;
130   HBRUSH bg_brush, saved_dst_brush;
131   HDC src_dc, dst_dc;
132   WORD xsize, ysize;
133   HBITMAP result, saved_dst_bitmap, saved_src_bitmap, checkboxes;
134   BITMAP bitmap;
135   RECT rect;
136
137   bg_color = GetSysColor (COLOR_MENU);
138   bg_brush = CreateSolidBrush (bg_color);
139
140   src_dc = CreateCompatibleDC (NULL);
141   dst_dc = CreateCompatibleDC (src_dc);
142
143   xsize = GetSystemMetrics (SM_CXMENUCHECK);
144   ysize = GetSystemMetrics (SM_CYMENUCHECK);
145   result = CreateCompatibleBitmap(src_dc, xsize, ysize);
146
147   saved_dst_brush  = SelectObject (dst_dc, bg_brush);
148   saved_dst_bitmap = SelectObject (dst_dc, result);
149
150   PatBlt (dst_dc, 0, 0, xsize, ysize, PATCOPY);
151
152   checkboxes = LoadBitmap (NULL, (LPTSTR)OBM_CHECKBOXES);
153
154   saved_src_bitmap = SelectObject (src_dc, checkboxes);
155
156   GetObject (checkboxes, sizeof (BITMAP), &bitmap);
157   rect.top = 0;
158   rect.bottom = (bitmap.bmHeight / 3);
159   if (checked)
160     {
161       /* Select row 1, column 1.  */
162       rect.left  = 0;
163       rect.right = (bitmap.bmWidth / 4);
164     }
165   else
166     {
167       /* Select row 1, column 2. */ 
168       rect.left  = (bitmap.bmWidth / 4);
169       rect.right = (bitmap.bmWidth / 4) * 2;
170     }
171
172   if ( ((rect.right - rect.left) > (int)xsize)
173        || ((rect.bottom - rect.top) > (int)ysize) )
174     StretchBlt (dst_dc, 0, 0, xsize, ysize, src_dc, rect.left, rect.top,
175                 rect.right - rect.left, rect.bottom - rect.top, SRCCOPY);
176   else
177     BitBlt (dst_dc, 0, 0, rect.right - rect.left, rect.bottom - rect.top,
178             src_dc, rect.left, rect.top, SRCCOPY);
179
180   SelectObject (src_dc, saved_src_bitmap);
181   SelectObject (dst_dc, saved_dst_brush);
182   result = SelectObject (dst_dc, saved_dst_bitmap);
183
184   DeleteObject (bg_brush);
185   DeleteObject (src_dc);
186   DeleteObject (dst_dc);
187   return result;
188 }
189
190
191 /* Return a filename to be used for saving an attachment. Returns a
192    malloced string on success. HWND is the current Window and SRCNAME
193    the filename to be used as suggestion.  On error (i.e. cancel) NULL
194    is returned. */
195 char *
196 get_save_filename (HWND root, const char *srcname)
197 {
198   char filter[] = "All Files (*.*)\0*.*\0\0";
199   char fname[MAX_PATH+1];
200   OPENFILENAME ofn;
201
202   memset (fname, 0, sizeof (fname));
203   strncpy (fname, srcname, MAX_PATH-1);
204   fname[MAX_PATH] = 0;
205
206
207   memset (&ofn, 0, sizeof (ofn));
208   ofn.lStructSize = sizeof (ofn);
209   ofn.hwndOwner = root;
210   ofn.lpstrFile = fname;
211   ofn.nMaxFile = MAX_PATH;
212   ofn.lpstrFileTitle = NULL;
213   ofn.nMaxFileTitle = 0;
214   ofn.Flags |= OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
215   ofn.lpstrTitle = _("GpgOL - Save decrypted attachment");
216   ofn.lpstrFilter = filter;
217
218   if (GetSaveFileName (&ofn))
219     return xstrdup (fname);
220   return NULL;
221 }
222
223
224 void
225 out_of_core (void)
226 {
227     MessageBox (NULL, "Out of core!", "Fatal Error", MB_OK);
228     abort ();
229 }
230
231 void*
232 xmalloc (size_t n)
233 {
234     void *p = malloc (n);
235     if (!p)
236         out_of_core ();
237     return p;
238 }
239
240 void*
241 xcalloc (size_t m, size_t n)
242 {
243     void *p = calloc (m, n);
244     if (!p)
245         out_of_core ();
246     return p;
247 }
248
249 void *
250 xrealloc (void *a, size_t n)
251 {
252   void *p = realloc (a, n);
253   if (!p)
254     out_of_core ();
255   return p;
256 }
257
258 char*
259 xstrdup (const char *s)
260 {
261     char *p = xmalloc (strlen (s)+1);
262     strcpy (p, s);
263     return p;
264 }
265
266 void
267 xfree (void *p)
268 {
269     if (p)
270         free (p);
271 }
272
273
274 /* This is a helper function to load a Windows function from either of
275    one DLLs. */
276 static HRESULT
277 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
278 {
279   static int initialized;
280   static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
281
282   if (!initialized)
283     {
284       static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
285       void *handle;
286       int i;
287
288       initialized = 1;
289
290       for (i=0, handle = NULL; !handle && dllnames[i]; i++)
291         {
292           handle = LoadLibrary (dllnames[i]);
293           if (handle)
294             {
295               func = (HRESULT (WINAPI *)(HWND,int,HANDLE,DWORD,LPSTR))
296                      GetProcAddress (handle, "SHGetFolderPathA");
297               if (!func)
298                 {
299                   FreeLibrary (handle);
300                   handle = NULL;
301                 }
302             }
303         }
304     }
305
306   if (func)
307     return func (a,b,c,d,e);
308   else
309     return -1;
310 }
311
312
313
314 /* Same as above, but only convert the first LEN wchars.  */
315 char *
316 wchar_to_utf8_2 (const wchar_t *string, size_t len)
317 {
318   int n;
319   char *result;
320
321   /* Note, that CP_UTF8 is not defined in Windows versions earlier
322      than NT.*/
323   n = WideCharToMultiByte (CP_UTF8, 0, string, len, NULL, 0, NULL, NULL);
324   if (n < 0)
325     return NULL;
326
327   result = xmalloc (n+1);
328   n = WideCharToMultiByte (CP_UTF8, 0, string, len, result, n, NULL, NULL);
329   if (n < 0)
330     {
331       xfree (result);
332       return NULL;
333     }
334   return result;
335 }
336
337
338 /* Same as above but convert only the first LEN characters.  STRING
339    must be at least LEN characters long. */
340 wchar_t *
341 utf8_to_wchar2 (const char *string, size_t len)
342 {
343   int n;
344   wchar_t *result;
345
346   n = MultiByteToWideChar (CP_UTF8, 0, string, len, NULL, 0);
347   if (n < 0)
348     return NULL;
349
350   result = xmalloc ((n+1) * sizeof *result);
351   n = MultiByteToWideChar (CP_UTF8, 0, string, len, result, n);
352   if (n < 0)
353     {
354       xfree (result);
355       return NULL;
356     }
357   result[n] = 0;
358   return result;
359 }
360
361
362 /* Assume STRING is a Latin-1 encoded and convert it to utf-8.
363    Returns a newly malloced UTF-8 string. */
364 char *
365 latin1_to_utf8 (const char *string)
366 {
367   const char *s;
368   char *buffer, *p;
369   size_t n;
370
371   for (s=string, n=0; *s; s++)
372     {
373       n++;
374       if (*s & 0x80)
375         n++;
376     }
377   buffer = xmalloc (n + 1);
378   for (s=string, p=buffer; *s; s++)
379     {
380       if (*s & 0x80)
381         {
382           *p++ = 0xc0 | ((*s >> 6) & 3);
383           *p++ = 0x80 | (*s & 0x3f);
384         }
385       else
386         *p++ = *s;
387     }
388   *p = 0;
389   return buffer;
390 }
391
392
393 /* Strip off trailing white spaces from STRING.  Returns STRING. */
394 char *
395 trim_trailing_spaces (char *string)
396 {
397   char *p, *mark;
398
399   for (mark=NULL, p=string; *p; p++)
400     {
401       if (strchr (" \t\r\n", *p ))
402         {
403           if (!mark)
404             mark = p;
405         }
406         else
407           mark = NULL;
408     }
409
410   if (mark)
411     *mark = 0;
412   return string;
413 }
414
415
416 /* Helper for read_w32_registry_string(). */
417 static HKEY
418 get_root_key(const char *root)
419 {
420   HKEY root_key;
421
422   if( !root )
423     root_key = HKEY_CURRENT_USER;
424   else if( !strcmp( root, "HKEY_CLASSES_ROOT" ) )
425     root_key = HKEY_CLASSES_ROOT;
426   else if( !strcmp( root, "HKEY_CURRENT_USER" ) )
427     root_key = HKEY_CURRENT_USER;
428   else if( !strcmp( root, "HKEY_LOCAL_MACHINE" ) )
429     root_key = HKEY_LOCAL_MACHINE;
430   else if( !strcmp( root, "HKEY_USERS" ) )
431     root_key = HKEY_USERS;
432   else if( !strcmp( root, "HKEY_PERFORMANCE_DATA" ) )
433     root_key = HKEY_PERFORMANCE_DATA;
434   else if( !strcmp( root, "HKEY_CURRENT_CONFIG" ) )
435     root_key = HKEY_CURRENT_CONFIG;
436   else
437     return NULL;
438   return root_key;
439 }
440
441 /* Return a string from the Win32 Registry or NULL in case of error.
442    Caller must release the return value.  A NULL for root is an alias
443    for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn.  NOTE: The value
444    is allocated with a plain malloc() - use free() and not the usual
445    xfree(). */
446 char *
447 read_w32_registry_string (const char *root, const char *dir, const char *name)
448 {
449   HKEY root_key, key_handle;
450   DWORD n1, nbytes, type;
451   char *result = NULL;
452
453   if ( !(root_key = get_root_key(root) ) )
454     return NULL;
455
456   if( RegOpenKeyEx( root_key, dir, 0, KEY_READ, &key_handle ) )
457     {
458       if (root)
459         return NULL; /* no need for a RegClose, so return direct */
460       /* It seems to be common practise to fall back to HKLM. */
461       if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
462         return NULL; /* still no need for a RegClose, so return direct */
463     }
464
465   nbytes = 1;
466   if( RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes ) ) {
467     if (root)
468       goto leave;
469     /* Try to fallback to HKLM also vor a missing value.  */
470     RegCloseKey (key_handle);
471     if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
472       return NULL; /* Nope.  */
473     if (RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes))
474       goto leave;
475   }
476   result = malloc( (n1=nbytes+1) );
477   if( !result )
478     goto leave;
479   if( RegQueryValueEx( key_handle, name, 0, &type, result, &n1 ) ) {
480     free(result); result = NULL;
481     goto leave;
482   }
483   result[nbytes] = 0; /* make sure it is really a string  */
484   if (type == REG_EXPAND_SZ && strchr (result, '%')) {
485     char *tmp;
486
487     n1 += 1000;
488     tmp = malloc (n1+1);
489     if (!tmp)
490       goto leave;
491     nbytes = ExpandEnvironmentStrings (result, tmp, n1);
492     if (nbytes && nbytes > n1) {
493       free (tmp);
494       n1 = nbytes;
495       tmp = malloc (n1 + 1);
496       if (!tmp)
497         goto leave;
498       nbytes = ExpandEnvironmentStrings (result, tmp, n1);
499       if (nbytes && nbytes > n1) {
500         free (tmp); /* oops - truncated, better don't expand at all */
501         goto leave;
502       }
503       tmp[nbytes] = 0;
504       free (result);
505       result = tmp;
506     }
507     else if (nbytes) { /* okay, reduce the length */
508       tmp[nbytes] = 0;
509       free (result);
510       result = malloc (strlen (tmp)+1);
511       if (!result)
512         result = tmp;
513       else {
514         strcpy (result, tmp);
515         free (tmp);
516       }
517     }
518     else {  /* error - don't expand */
519       free (tmp);
520     }
521   }
522
523  leave:
524   RegCloseKey( key_handle );
525   return result;
526 }
527
528
529 /* Get the standard home directory.  In general this function should
530    not be used as it does not consider a registry value or the
531    GNUPGHOME environment variable.  Please use default_homedir(). */
532 static const char *
533 standard_homedir (void)
534 {
535   static char *dir;
536
537   if (!dir)
538     {
539       char path[MAX_PATH];
540
541       /* It might be better to use LOCAL_APPDATA because this is
542          defined as "non roaming" and thus more likely to be kept
543          locally.  For private keys this is desired.  However, given
544          that many users copy private keys anyway forth and back,
545          using a system roaming services might be better than to let
546          them do it manually.  A security conscious user will anyway
547          use the registry entry to have better control.  */
548       if (w32_shgetfolderpath (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
549                                NULL, 0, path) >= 0)
550         {
551           char *tmp = malloc (strlen (path) + 6 + 1);
552
553           strcpy (tmp, path);
554           strcat (tmp, "\\gnupg");
555
556           dir = tmp;
557
558           /* Try to create the directory if it does not yet exists.  */
559           if (access (dir, F_OK))
560             CreateDirectory (dir, NULL);
561         }
562       else
563         dir = xstrdup ("C:\\gnupg");
564     }
565   return dir;
566 }
567
568
569 /* Retrieve the default home directory.  */
570 const char *
571 default_homedir (void)
572 {
573   static char *dir;
574
575   if (!dir)
576     {
577       dir = getenv ("GNUPGHOME");
578       if (!dir || !*dir)
579         {
580           char *tmp;
581
582           tmp = read_w32_registry_string (NULL, GNUPG_REGKEY, "HomeDir");
583           if (tmp && !*tmp)
584             {
585               free (tmp);
586               tmp = NULL;
587             }
588           if (tmp)
589             dir = tmp;
590           else
591             dir = xstrdup (standard_homedir ());
592         }
593       else
594         dir = xstrdup (dir);
595     }
596
597   return dir;
598 }
599
600
601 /* Do in-place decoding of quoted-printable data of LENGTH in BUFFER.
602    Returns the new length of the buffer. */
603 size_t
604 qp_decode (char *buffer, size_t length)
605 {
606   char *d, *s;
607
608   for (s=d=buffer; length; length--)
609     if (*s == '=')
610       {
611         if (length > 2 && hexdigitp (s+1) && hexdigitp (s+2))
612           {
613             s++;
614             *(unsigned char*)d++ = xtoi_2 (s);
615             s += 2;
616             length -= 2;
617           }
618         else if (length > 2 && s[1] == '\r' && s[2] == '\n')
619           {
620             /* Soft line break.  */
621             s += 3;
622             length -= 2;
623           }
624         else if (length > 1 && s[1] == '\n')
625           {
626             /* Soft line break with only a Unix line terminator. */
627             s += 2;
628             length -= 1;
629           }
630         else
631           *d++ = *s++;
632       }
633     else
634       *d++ = *s++;
635
636   return d - buffer;
637 }
638
639
640 /* Initialize the Base 64 decoder state.  */
641 void b64_init (b64_state_t *state)
642 {
643   state->idx = 0;
644   state->val = 0;
645   state->stop_seen = 0;
646   state->invalid_encoding = 0;
647 }
648
649
650 /* Do in-place decoding of base-64 data of LENGTH in BUFFER.  Returns
651    the new length of the buffer. STATE is required to return errors and
652    to maintain the state of the decoder.  */
653 size_t
654 b64_decode (b64_state_t *state, char *buffer, size_t length)
655 {
656   int idx = state->idx;
657   unsigned char val = state->val;
658   int c;
659   char *d, *s;
660
661   if (state->stop_seen)
662     return 0;
663
664   for (s=d=buffer; length; length--, s++)
665     {
666       if (*s == '\n' || *s == ' ' || *s == '\r' || *s == '\t')
667         continue;
668       if (*s == '=')
669         {
670           /* Pad character: stop */
671           if (idx == 1)
672             *d++ = val;
673           state->stop_seen = 1;
674           break;
675         }
676
677       if ((c = asctobin[*(unsigned char *)s]) == 255)
678         {
679           if (!state->invalid_encoding)
680             log_debug ("%s: invalid base64 character %02X at pos %d skipped\n",
681                        __func__, *(unsigned char*)s, (int)(s-buffer));
682           state->invalid_encoding = 1;
683           continue;
684         }
685
686       switch (idx)
687         {
688         case 0:
689           val = c << 2;
690           break;
691         case 1:
692           val |= (c>>4)&3;
693           *d++ = val;
694           val = (c<<4)&0xf0;
695           break;
696         case 2:
697           val |= (c>>2)&15;
698           *d++ = val;
699           val = (c<<6)&0xc0;
700           break;
701         case 3:
702           val |= c&0x3f;
703           *d++ = val;
704           break;
705         }
706       idx = (idx+1) % 4;
707     }
708
709
710   state->idx = idx;
711   state->val = val;
712   return d - buffer;
713 }
714
715
716 /* Create a boundary.  Note that mimemaker.c knows about the structure
717    of the boundary (i.e. that it starts with "=-=") so that it can
718    protect against accidently used boundaries within the content.  */
719 char *
720 generate_boundary (char *buffer)
721 {
722   char *p = buffer;
723   int i;
724
725 #if RAND_MAX < (64*2*BOUNDARYSIZE)
726 #error RAND_MAX is way too small
727 #endif
728
729   *p++ = '=';
730   *p++ = '-';
731   *p++ = '=';
732   for (i=0; i < BOUNDARYSIZE-6; i++)
733     *p++ = bintoasc[rand () % 64];
734   *p++ = '=';
735   *p++ = '-';
736   *p++ = '=';
737   *p = 0;
738
739   return buffer;
740 }
741
742
743 /* Fork and exec the program gioven in CMDLINE with /dev/null as
744    stdin, stdout and stderr.  Returns 0 on success.  */
745 int
746 gpgol_spawn_detached (const char *cmdline)
747 {
748   int rc;
749   SECURITY_ATTRIBUTES sec_attr;
750   PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
751   STARTUPINFO si;
752   int cr_flags;
753   char *cmdline_copy;
754
755   memset (&sec_attr, 0, sizeof sec_attr);
756   sec_attr.nLength = sizeof sec_attr;
757   
758   memset (&si, 0, sizeof si);
759   si.cb = sizeof (si);
760   si.dwFlags = STARTF_USESHOWWINDOW;
761   si.wShowWindow = SW_SHOW;
762
763   cr_flags = (CREATE_DEFAULT_ERROR_MODE
764               | GetPriorityClass (GetCurrentProcess ())
765               | CREATE_NEW_PROCESS_GROUP
766               | DETACHED_PROCESS); 
767
768   cmdline_copy = xstrdup (cmdline);
769   rc = CreateProcess (NULL,          /* No appliactionname, use CMDLINE.  */
770                       cmdline_copy,  /* Command line arguments.  */
771                       &sec_attr,     /* Process security attributes.  */
772                       &sec_attr,     /* Thread security attributes.  */
773                       TRUE,          /* Inherit handles.  */
774                       cr_flags,      /* Creation flags.  */
775                       NULL,          /* Environment.  */
776                       NULL,          /* Use current drive/directory.  */
777                       &si,           /* Startup information. */
778                       &pi            /* Returns process information.  */
779                       );
780   xfree (cmdline_copy);
781   if (!rc)
782     {
783       log_error_w32 (-1, "%s:%s: CreateProcess failed", SRCNAME, __func__);
784       return -1;
785     }
786
787   CloseHandle (pi.hThread); 
788   CloseHandle (pi.hProcess);
789   return 0;
790 }