Tweak for some opaque S/MIME messages.
[gpgol.git] / src / common.c
1 /* common.c - Common routines used by GpgOL
2  *      Copyright (C) 2005, 2007, 2008 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 and stores true at R_SLBRK if
603    the line ended with a soft line break; false is stored if not.
604    This fucntion asssumes that a complete line is passed in
605    buffer.  */
606 size_t
607 qp_decode (char *buffer, size_t length, int *r_slbrk)
608 {
609   char *d, *s;
610
611   if (r_slbrk)
612     *r_slbrk = 0;
613
614   /* Fixme:  We should remove trailing white space first.  */
615   for (s=d=buffer; length; length--)
616     if (*s == '=')
617       {
618         if (length > 2 && hexdigitp (s+1) && hexdigitp (s+2))
619           {
620             s++;
621             *(unsigned char*)d++ = xtoi_2 (s);
622             s += 2;
623             length -= 2;
624           }
625         else if (length > 2 && s[1] == '\r' && s[2] == '\n')
626           {
627             /* Soft line break.  */
628             s += 3;
629             length -= 2;
630             if (r_slbrk && length == 1)
631               *r_slbrk = 1;
632           }
633         else if (length > 1 && s[1] == '\n')
634           {
635             /* Soft line break with only a Unix line terminator. */
636             s += 2;
637             length -= 1;
638             if (r_slbrk && length == 1)
639               *r_slbrk = 1;
640           }
641         else if (length == 1)
642           {
643             /* Soft line break at the end of the line. */
644             s += 1;
645             if (r_slbrk)
646               *r_slbrk = 1;
647           }
648         else
649           *d++ = *s++;
650       }
651     else
652       *d++ = *s++;
653
654   return d - buffer;
655 }
656
657
658 /* Initialize the Base 64 decoder state.  */
659 void b64_init (b64_state_t *state)
660 {
661   state->idx = 0;
662   state->val = 0;
663   state->stop_seen = 0;
664   state->invalid_encoding = 0;
665 }
666
667
668 /* Do in-place decoding of base-64 data of LENGTH in BUFFER.  Returns
669    the new length of the buffer. STATE is required to return errors and
670    to maintain the state of the decoder.  */
671 size_t
672 b64_decode (b64_state_t *state, char *buffer, size_t length)
673 {
674   int idx = state->idx;
675   unsigned char val = state->val;
676   int c;
677   char *d, *s;
678
679   if (state->stop_seen)
680     return 0;
681
682   for (s=d=buffer; length; length--, s++)
683     {
684       if (*s == '\n' || *s == ' ' || *s == '\r' || *s == '\t')
685         continue;
686       if (*s == '=')
687         {
688           /* Pad character: stop */
689           if (idx == 1)
690             *d++ = val;
691           state->stop_seen = 1;
692           break;
693         }
694
695       if ((c = asctobin[*(unsigned char *)s]) == 255)
696         {
697           if (!state->invalid_encoding)
698             log_debug ("%s: invalid base64 character %02X at pos %d skipped\n",
699                        __func__, *(unsigned char*)s, (int)(s-buffer));
700           state->invalid_encoding = 1;
701           continue;
702         }
703
704       switch (idx)
705         {
706         case 0:
707           val = c << 2;
708           break;
709         case 1:
710           val |= (c>>4)&3;
711           *d++ = val;
712           val = (c<<4)&0xf0;
713           break;
714         case 2:
715           val |= (c>>2)&15;
716           *d++ = val;
717           val = (c<<6)&0xc0;
718           break;
719         case 3:
720           val |= c&0x3f;
721           *d++ = val;
722           break;
723         }
724       idx = (idx+1) % 4;
725     }
726
727
728   state->idx = idx;
729   state->val = val;
730   return d - buffer;
731 }
732
733
734 /* Create a boundary.  Note that mimemaker.c knows about the structure
735    of the boundary (i.e. that it starts with "=-=") so that it can
736    protect against accidently used boundaries within the content.  */
737 char *
738 generate_boundary (char *buffer)
739 {
740   char *p = buffer;
741   int i;
742
743 #if RAND_MAX < (64*2*BOUNDARYSIZE)
744 #error RAND_MAX is way too small
745 #endif
746
747   *p++ = '=';
748   *p++ = '-';
749   *p++ = '=';
750   for (i=0; i < BOUNDARYSIZE-6; i++)
751     *p++ = bintoasc[rand () % 64];
752   *p++ = '=';
753   *p++ = '-';
754   *p++ = '=';
755   *p = 0;
756
757   return buffer;
758 }
759
760
761 /* Fork and exec the program gioven in CMDLINE with /dev/null as
762    stdin, stdout and stderr.  Returns 0 on success.  */
763 int
764 gpgol_spawn_detached (const char *cmdline)
765 {
766   int rc;
767   SECURITY_ATTRIBUTES sec_attr;
768   PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
769   STARTUPINFO si;
770   int cr_flags;
771   char *cmdline_copy;
772
773   memset (&sec_attr, 0, sizeof sec_attr);
774   sec_attr.nLength = sizeof sec_attr;
775   
776   memset (&si, 0, sizeof si);
777   si.cb = sizeof (si);
778   si.dwFlags = STARTF_USESHOWWINDOW;
779   si.wShowWindow = SW_SHOW;
780
781   cr_flags = (CREATE_DEFAULT_ERROR_MODE
782               | GetPriorityClass (GetCurrentProcess ())
783               | CREATE_NEW_PROCESS_GROUP
784               | DETACHED_PROCESS); 
785
786   cmdline_copy = xstrdup (cmdline);
787   rc = CreateProcess (NULL,          /* No appliactionname, use CMDLINE.  */
788                       cmdline_copy,  /* Command line arguments.  */
789                       &sec_attr,     /* Process security attributes.  */
790                       &sec_attr,     /* Thread security attributes.  */
791                       TRUE,          /* Inherit handles.  */
792                       cr_flags,      /* Creation flags.  */
793                       NULL,          /* Environment.  */
794                       NULL,          /* Use current drive/directory.  */
795                       &si,           /* Startup information. */
796                       &pi            /* Returns process information.  */
797                       );
798   xfree (cmdline_copy);
799   if (!rc)
800     {
801       log_error_w32 (-1, "%s:%s: CreateProcess failed", SRCNAME, __func__);
802       return -1;
803     }
804
805   CloseHandle (pi.hThread); 
806   CloseHandle (pi.hProcess);
807   return 0;
808 }
809
810
811
812
813 /* Simple but pretty complete ASN.1 BER parser.  Parse the data at the
814    address of BUFFER with a length given at the address of SIZE.  On
815    success return 0 and update BUFFER and SIZE to point to the value.
816    Do not update them on error.  The information about the object are
817    stored in the caller allocated TI structure.  */
818 int
819 parse_tlv (char const **buffer, size_t *size, tlvinfo_t *ti)
820 {
821   int c;
822   unsigned long tag;
823   const unsigned char *buf = (const unsigned char *)(*buffer);
824   size_t length = *size;
825
826   ti->cls = 0;
827   ti->tag = 0;
828   ti->is_cons = 0;
829   ti->is_ndef = 0;
830   ti->length = 0;
831   ti->nhdr = 0;
832
833   if (!length)
834     return -1;
835   c = *buf++; length--; ++ti->nhdr;
836
837   ti->cls = (c & 0xc0) >> 6;
838   ti->is_cons = !!(c & 0x20);
839   tag = c & 0x1f;
840
841   if (tag == 0x1f)
842     {
843       tag = 0;
844       do
845         {
846           tag <<= 7;
847           if (!length)
848             return -1;
849           c = *buf++; length--; ++ti->nhdr;
850           tag |= c & 0x7f;
851         }
852       while (c & 0x80);
853     }
854   ti->tag = tag;
855
856   if (!length)
857     return -1;
858   c = *buf++; length--; ++ti->nhdr;
859
860   if ( !(c & 0x80) )
861     ti->length = c;
862   else if (c == 0x80)
863     ti->is_ndef = 1;
864   else if (c == 0xff)
865     return -1;
866   else
867     {
868       unsigned long len = 0;
869       int count = (c & 0x7f);
870
871       if (count > sizeof (len) || count > sizeof (size_t))
872         return -1;
873
874       for (; count; count--)
875         {
876           len <<= 8;
877           if (!length)
878             return -1;
879           c = *buf++; length--; ++ti->nhdr;
880           len |= c & 0xff;
881         }
882       ti->length = len;
883     }
884   
885   *buffer = buf;
886   *size = length;
887   return 0;
888 }
889
890