Try harder to match outlooks internal filename enc
[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 #define OEMRESOURCE    /* Required for OBM_CHECKBOXES.  */
22 #include <windows.h>
23 #include <shlobj.h>
24 #ifndef CSIDL_APPDATA
25 #define CSIDL_APPDATA 0x001a
26 #endif
27 #ifndef CSIDL_LOCAL_APPDATA
28 #define CSIDL_LOCAL_APPDATA 0x001c
29 #endif
30 #ifndef CSIDL_FLAG_CREATE
31 #define CSIDL_FLAG_CREATE 0x8000
32 #endif
33 #include <time.h>
34 #include <fcntl.h>
35 #include <ctype.h>
36
37 #include "common.h"
38
39 HINSTANCE glob_hinst = NULL;
40
41
42 /* The base-64 list used for base64 encoding. */
43 static unsigned char bintoasc[64+1] = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
44                                        "abcdefghijklmnopqrstuvwxyz"
45                                        "0123456789+/");
46
47 /* The reverse base-64 list used for base-64 decoding. */
48 static unsigned char const asctobin[256] = {
49   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
50   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
51   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
52   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
53   0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
54   0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
55   0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
56   0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
57   0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
58   0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
59   0x31, 0x32, 0x33, 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, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
69   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
70   0xff, 0xff, 0xff, 0xff
71 };
72
73
74
75 void
76 set_global_hinstance (HINSTANCE hinst)
77 {
78     glob_hinst = hinst;
79 }
80
81 /* Center the given window with the desktop window as the
82    parent window. */
83 void
84 center_window (HWND childwnd, HWND style)
85 {
86     HWND parwnd;
87     RECT rchild, rparent;
88     HDC hdc;
89     int wchild, hchild, wparent, hparent;
90     int wscreen, hscreen, xnew, ynew;
91     int flags = SWP_NOSIZE | SWP_NOZORDER;
92
93     parwnd = GetDesktopWindow ();
94     GetWindowRect (childwnd, &rchild);
95     wchild = rchild.right - rchild.left;
96     hchild = rchild.bottom - rchild.top;
97
98     GetWindowRect (parwnd, &rparent);
99     wparent = rparent.right - rparent.left;
100     hparent = rparent.bottom - rparent.top;
101
102     hdc = GetDC (childwnd);
103     wscreen = GetDeviceCaps (hdc, HORZRES);
104     hscreen = GetDeviceCaps (hdc, VERTRES);
105     ReleaseDC (childwnd, hdc);
106     xnew = rparent.left + ((wparent - wchild) / 2);
107     if (xnew < 0)
108         xnew = 0;
109     else if ((xnew+wchild) > wscreen)
110         xnew = wscreen - wchild;
111     ynew = rparent.top  + ((hparent - hchild) / 2);
112     if (ynew < 0)
113         ynew = 0;
114     else if ((ynew+hchild) > hscreen)
115         ynew = hscreen - hchild;
116     if (style == HWND_TOPMOST || style == HWND_NOTOPMOST)
117         flags = SWP_NOMOVE | SWP_NOSIZE;
118     SetWindowPos (childwnd, style? style : NULL, xnew, ynew, 0, 0, flags);
119 }
120
121
122 /* Return the system's bitmap of the check bar used which check boxes.
123    If CHECKED is set, this check mark is returned; if it is not set,
124    the one used for not-checked is returned.  May return NULL on
125    error.  Taken from an example in the platform reference. 
126
127    Not used as of now. */
128 HBITMAP
129 get_system_check_bitmap (int checked)
130 {
131   COLORREF bg_color;
132   HBRUSH bg_brush, saved_dst_brush;
133   HDC src_dc, dst_dc;
134   WORD xsize, ysize;
135   HBITMAP result, saved_dst_bitmap, saved_src_bitmap, checkboxes;
136   BITMAP bitmap;
137   RECT rect;
138
139   bg_color = GetSysColor (COLOR_MENU);
140   bg_brush = CreateSolidBrush (bg_color);
141
142   src_dc = CreateCompatibleDC (NULL);
143   dst_dc = CreateCompatibleDC (src_dc);
144
145   xsize = GetSystemMetrics (SM_CXMENUCHECK);
146   ysize = GetSystemMetrics (SM_CYMENUCHECK);
147   result = CreateCompatibleBitmap(src_dc, xsize, ysize);
148
149   saved_dst_brush  = SelectObject (dst_dc, bg_brush);
150   saved_dst_bitmap = SelectObject (dst_dc, result);
151
152   PatBlt (dst_dc, 0, 0, xsize, ysize, PATCOPY);
153
154   checkboxes = LoadBitmap (NULL, (LPTSTR)OBM_CHECKBOXES);
155
156   saved_src_bitmap = SelectObject (src_dc, checkboxes);
157
158   GetObject (checkboxes, sizeof (BITMAP), &bitmap);
159   rect.top = 0;
160   rect.bottom = (bitmap.bmHeight / 3);
161   if (checked)
162     {
163       /* Select row 1, column 1.  */
164       rect.left  = 0;
165       rect.right = (bitmap.bmWidth / 4);
166     }
167   else
168     {
169       /* Select row 1, column 2. */ 
170       rect.left  = (bitmap.bmWidth / 4);
171       rect.right = (bitmap.bmWidth / 4) * 2;
172     }
173
174   if ( ((rect.right - rect.left) > (int)xsize)
175        || ((rect.bottom - rect.top) > (int)ysize) )
176     StretchBlt (dst_dc, 0, 0, xsize, ysize, src_dc, rect.left, rect.top,
177                 rect.right - rect.left, rect.bottom - rect.top, SRCCOPY);
178   else
179     BitBlt (dst_dc, 0, 0, rect.right - rect.left, rect.bottom - rect.top,
180             src_dc, rect.left, rect.top, SRCCOPY);
181
182   SelectObject (src_dc, saved_src_bitmap);
183   SelectObject (dst_dc, saved_dst_brush);
184   result = SelectObject (dst_dc, saved_dst_bitmap);
185
186   DeleteObject (bg_brush);
187   DeleteObject (src_dc);
188   DeleteObject (dst_dc);
189   return result;
190 }
191
192 /* Return the path to a file that should be worked with.
193    Returns a malloced string (UTF-8) on success.
194    HWND is the current Window.
195    Title is a UTF-8 encoded string containing the
196    dialog title and may be NULL.
197    On error (i.e. cancel) NULL is returned. */
198 char *
199 get_open_filename (HWND root, const char *title)
200 {
201   OPENFILENAMEW ofn;
202   wchar_t fname[MAX_PATH+1];
203   wchar_t *wTitle = NULL;
204
205   if (title)
206     {
207       wTitle = utf8_to_wchar2 (title, strlen(title));
208     }
209   memset (fname, 0, sizeof (fname));
210
211   /* Set up the ofn structure */
212   memset (&ofn, 0, sizeof (ofn));
213   ofn.lStructSize = sizeof (ofn);
214   ofn.hwndOwner = root;
215   ofn.lpstrFile = fname;
216   ofn.nMaxFile = MAX_PATH;
217   ofn.lpstrTitle = wTitle;
218   ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
219
220   if (GetOpenFileNameW (&ofn))
221     {
222       xfree (wTitle);
223       return wchar_to_utf8_2 (fname, MAX_PATH);
224     }
225   xfree (wTitle);
226   return NULL;
227 }
228
229
230 /* Return a filename to be used for saving an attachment. Returns a
231    malloced string on success. HWND is the current Window and SRCNAME
232    the filename to be used as suggestion.  On error (i.e. cancel) NULL
233    is returned. */
234 char *
235 get_save_filename (HWND root, const char *srcname)
236 {
237   char filter[21] = "All Files (*.*)\0*.*\0\0";
238   char fname[MAX_PATH+1];
239   char filterBuf[32];
240   char* extSep;
241   OPENFILENAME ofn;
242
243   memset (fname, 0, sizeof (fname));
244   memset (filterBuf, 0, sizeof (filterBuf));
245   strncpy (fname, srcname, MAX_PATH-1);
246   fname[MAX_PATH] = 0;
247
248   if ((extSep = strrchr (srcname, '.')) && strlen (extSep) <= 4)
249     {
250       /* Windows removes the file extension by default so we
251          need to set the first filter to the file extension.
252       */
253       strcpy (filterBuf, extSep);
254       strcpy (filterBuf + strlen (filterBuf) + 1, extSep);
255       memcpy (filterBuf + strlen (extSep) * 2 + 2, filter, 21);
256     }
257   else
258     memcpy (filterBuf, filter, 21);
259
260
261   memset (&ofn, 0, sizeof (ofn));
262   ofn.lStructSize = sizeof (ofn);
263   ofn.hwndOwner = root;
264   ofn.lpstrFile = fname;
265   ofn.nMaxFile = MAX_PATH;
266   ofn.lpstrFileTitle = NULL;
267   ofn.nMaxFileTitle = 0;
268   ofn.Flags |= OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
269   ofn.lpstrTitle = _("GpgOL - Save attachment");
270   ofn.lpstrFilter = filterBuf;
271
272   if (GetSaveFileName (&ofn))
273     return xstrdup (fname);
274   return NULL;
275 }
276
277
278 void
279 fatal_error (const char *format, ...)
280 {
281   va_list arg_ptr;
282   char buf[512];
283
284   va_start (arg_ptr, format);
285   vsnprintf (buf, sizeof buf -1, format, arg_ptr);
286   buf[sizeof buf - 1] = 0;
287   va_end (arg_ptr);
288   MessageBox (NULL, buf, "Fatal Error", MB_OK);
289   abort ();
290 }
291
292
293 void
294 out_of_core (void)
295 {
296   MessageBox (NULL, "Out of core!", "Fatal Error", MB_OK);
297   abort ();
298 }
299
300 void*
301 xmalloc (size_t n)
302 {
303     void *p = malloc (n);
304     if (!p)
305         out_of_core ();
306     return p;
307 }
308
309 void*
310 xcalloc (size_t m, size_t n)
311 {
312     void *p = calloc (m, n);
313     if (!p)
314         out_of_core ();
315     return p;
316 }
317
318 void *
319 xrealloc (void *a, size_t n)
320 {
321   void *p = realloc (a, n);
322   if (!p)
323     out_of_core ();
324   return p;
325 }
326
327 char*
328 xstrdup (const char *s)
329 {
330     char *p = xmalloc (strlen (s)+1);
331     strcpy (p, s);
332     return p;
333 }
334
335 void
336 xfree (void *p)
337 {
338     if (p)
339         free (p);
340 }
341
342
343 /* This is a helper function to load a Windows function from either of
344    one DLLs. */
345 static HRESULT
346 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
347 {
348   static int initialized;
349   static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
350
351   if (!initialized)
352     {
353       static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
354       void *handle;
355       int i;
356
357       initialized = 1;
358
359       for (i=0, handle = NULL; !handle && dllnames[i]; i++)
360         {
361           handle = LoadLibrary (dllnames[i]);
362           if (handle)
363             {
364               func = (HRESULT (WINAPI *)(HWND,int,HANDLE,DWORD,LPSTR))
365                      GetProcAddress (handle, "SHGetFolderPathA");
366               if (!func)
367                 {
368                   FreeLibrary (handle);
369                   handle = NULL;
370                 }
371             }
372         }
373     }
374
375   if (func)
376     return func (a,b,c,d,e);
377   else
378     return -1;
379 }
380
381
382
383 /* Same as above, but only convert the first LEN wchars.  */
384 char *
385 wchar_to_utf8_2 (const wchar_t *string, size_t len)
386 {
387   int n;
388   char *result;
389
390   /* Note, that CP_UTF8 is not defined in Windows versions earlier
391      than NT.*/
392   n = WideCharToMultiByte (CP_UTF8, 0, string, len, NULL, 0, NULL, NULL);
393   if (n < 0)
394     return NULL;
395
396   result = xmalloc (n+1);
397   n = WideCharToMultiByte (CP_UTF8, 0, string, len, result, n, NULL, NULL);
398   if (n < 0)
399     {
400       xfree (result);
401       return NULL;
402     }
403   return result;
404 }
405
406
407 /* Same as above but convert only the first LEN characters.  STRING
408    must be at least LEN characters long. */
409 wchar_t *
410 utf8_to_wchar2 (const char *string, size_t len)
411 {
412   int n;
413   wchar_t *result;
414
415   n = MultiByteToWideChar (CP_UTF8, 0, string, len, NULL, 0);
416   if (n < 0)
417     return NULL;
418
419   result = xmalloc ((n+1) * sizeof *result);
420   n = MultiByteToWideChar (CP_UTF8, 0, string, len, result, n);
421   if (n < 0)
422     {
423       xfree (result);
424       return NULL;
425     }
426   result[n] = 0;
427   return result;
428 }
429
430
431 /* Assume STRING is a Latin-1 encoded and convert it to utf-8.
432    Returns a newly malloced UTF-8 string. */
433 char *
434 latin1_to_utf8 (const char *string)
435 {
436   const char *s;
437   char *buffer, *p;
438   size_t n;
439
440   for (s=string, n=0; *s; s++)
441     {
442       n++;
443       if (*s & 0x80)
444         n++;
445     }
446   buffer = xmalloc (n + 1);
447   for (s=string, p=buffer; *s; s++)
448     {
449       if (*s & 0x80)
450         {
451           *p++ = 0xc0 | ((*s >> 6) & 3);
452           *p++ = 0x80 | (*s & 0x3f);
453         }
454       else
455         *p++ = *s;
456     }
457   *p = 0;
458   return buffer;
459 }
460
461
462 /* This function is similar to strncpy().  However it won't copy more
463    than N - 1 characters and makes sure that a Nul is appended. With N
464    given as 0, nothing will happen.  With DEST given as NULL, memory
465    will be allocated using xmalloc (i.e. if it runs out of core the
466    function terminates).  Returns DEST or a pointer to the allocated
467    memory.  */
468 char *
469 mem2str (char *dest, const void *src, size_t n)
470 {
471   char *d;
472   const char *s;
473   
474   if (n)
475     {
476       if (!dest)
477         dest = xmalloc (n);
478       d = dest;
479       s = src ;
480       for (n--; n && *s; n--)
481         *d++ = *s++;
482       *d = 0;
483     }
484   else if (!dest)
485     {
486       dest = xmalloc (1);
487       *dest = 0;
488     }
489   
490   return dest;
491 }
492
493
494 /* Strip off trailing white spaces from STRING.  Returns STRING. */
495 char *
496 trim_trailing_spaces (char *string)
497 {
498   char *p, *mark;
499
500   for (mark=NULL, p=string; *p; p++)
501     {
502       if (strchr (" \t\r\n", *p ))
503         {
504           if (!mark)
505             mark = p;
506         }
507         else
508           mark = NULL;
509     }
510
511   if (mark)
512     *mark = 0;
513   return string;
514 }
515
516
517 /* Strip off leading and trailing white spaces from STRING.  Returns
518    STRING. */
519 char *
520 trim_spaces (char *arg_string)
521 {
522   char *string = arg_string;
523   char *p, *mark;
524
525   /* Find first non space character. */
526   for (p = string; *p && isascii (*p) && isspace (*p) ; p++ )
527     ;
528   /* Move characters. */
529   for (mark = NULL; (*string = *p); string++, p++ )
530     {
531       if (isascii (*p) && isspace (*p))
532         {
533           if (!mark)
534           mark = string;
535         }
536       else
537         mark = NULL ;
538     }
539   if (mark)
540     *mark = 0;
541   
542   return arg_string;
543 }
544
545
546
547 /* Helper for read_w32_registry_string(). */
548 static HKEY
549 get_root_key(const char *root)
550 {
551   HKEY root_key;
552
553   if( !root )
554     root_key = HKEY_CURRENT_USER;
555   else if( !strcmp( root, "HKEY_CLASSES_ROOT" ) )
556     root_key = HKEY_CLASSES_ROOT;
557   else if( !strcmp( root, "HKEY_CURRENT_USER" ) )
558     root_key = HKEY_CURRENT_USER;
559   else if( !strcmp( root, "HKEY_LOCAL_MACHINE" ) )
560     root_key = HKEY_LOCAL_MACHINE;
561   else if( !strcmp( root, "HKEY_USERS" ) )
562     root_key = HKEY_USERS;
563   else if( !strcmp( root, "HKEY_PERFORMANCE_DATA" ) )
564     root_key = HKEY_PERFORMANCE_DATA;
565   else if( !strcmp( root, "HKEY_CURRENT_CONFIG" ) )
566     root_key = HKEY_CURRENT_CONFIG;
567   else
568     return NULL;
569   return root_key;
570 }
571
572 /* Return a string from the Win32 Registry or NULL in case of error.
573    Caller must release the return value.  A NULL for root is an alias
574    for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn.  NOTE: The value
575    is allocated with a plain malloc() - use free() and not the usual
576    xfree(). */
577 char *
578 read_w32_registry_string (const char *root, const char *dir, const char *name)
579 {
580   HKEY root_key, key_handle;
581   DWORD n1, nbytes, type;
582   char *result = NULL;
583
584   if ( !(root_key = get_root_key(root) ) )
585     return NULL;
586
587   if( RegOpenKeyEx( root_key, dir, 0, KEY_READ, &key_handle ) )
588     {
589       if (root)
590         return NULL; /* no need for a RegClose, so return direct */
591       /* It seems to be common practise to fall back to HKLM. */
592       if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
593         return NULL; /* still no need for a RegClose, so return direct */
594     }
595
596   nbytes = 1;
597   if( RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes ) ) {
598     if (root)
599       goto leave;
600     /* Try to fallback to HKLM also vor a missing value.  */
601     RegCloseKey (key_handle);
602     if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
603       return NULL; /* Nope.  */
604     if (RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes))
605       goto leave;
606   }
607   result = malloc( (n1=nbytes+1) );
608   if( !result )
609     goto leave;
610   if( RegQueryValueEx( key_handle, name, 0, &type, result, &n1 ) ) {
611     free(result); result = NULL;
612     goto leave;
613   }
614   result[nbytes] = 0; /* make sure it is really a string  */
615   if (type == REG_EXPAND_SZ && strchr (result, '%')) {
616     char *tmp;
617
618     n1 += 1000;
619     tmp = malloc (n1+1);
620     if (!tmp)
621       goto leave;
622     nbytes = ExpandEnvironmentStrings (result, tmp, n1);
623     if (nbytes && nbytes > n1) {
624       free (tmp);
625       n1 = nbytes;
626       tmp = malloc (n1 + 1);
627       if (!tmp)
628         goto leave;
629       nbytes = ExpandEnvironmentStrings (result, tmp, n1);
630       if (nbytes && nbytes > n1) {
631         free (tmp); /* oops - truncated, better don't expand at all */
632         goto leave;
633       }
634       tmp[nbytes] = 0;
635       free (result);
636       result = tmp;
637     }
638     else if (nbytes) { /* okay, reduce the length */
639       tmp[nbytes] = 0;
640       free (result);
641       result = malloc (strlen (tmp)+1);
642       if (!result)
643         result = tmp;
644       else {
645         strcpy (result, tmp);
646         free (tmp);
647       }
648     }
649     else {  /* error - don't expand */
650       free (tmp);
651     }
652   }
653
654  leave:
655   RegCloseKey( key_handle );
656   return result;
657 }
658
659
660 /* Get the standard home directory.  In general this function should
661    not be used as it does not consider a registry value or the
662    GNUPGHOME environment variable.  Please use default_homedir(). */
663 static const char *
664 standard_homedir (void)
665 {
666   static char *dir;
667
668   if (!dir)
669     {
670       char path[MAX_PATH];
671
672       /* It might be better to use LOCAL_APPDATA because this is
673          defined as "non roaming" and thus more likely to be kept
674          locally.  For private keys this is desired.  However, given
675          that many users copy private keys anyway forth and back,
676          using a system roaming services might be better than to let
677          them do it manually.  A security conscious user will anyway
678          use the registry entry to have better control.  */
679       if (w32_shgetfolderpath (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
680                                NULL, 0, path) >= 0)
681         {
682           char *tmp = malloc (strlen (path) + 6 + 1);
683
684           strcpy (tmp, path);
685           strcat (tmp, "\\gnupg");
686
687           dir = tmp;
688
689           /* Try to create the directory if it does not yet exists.  */
690           if (access (dir, F_OK))
691             CreateDirectory (dir, NULL);
692         }
693       else
694         dir = xstrdup ("C:\\gnupg");
695     }
696   return dir;
697 }
698
699
700 /* Retrieve the default home directory.  */
701 const char *
702 default_homedir (void)
703 {
704   static char *dir;
705
706   if (!dir)
707     {
708       dir = getenv ("GNUPGHOME");
709       if (!dir || !*dir)
710         {
711           char *tmp;
712
713           tmp = read_w32_registry_string (NULL, GNUPG_REGKEY, "HomeDir");
714           if (tmp && !*tmp)
715             {
716               free (tmp);
717               tmp = NULL;
718             }
719           if (tmp)
720             dir = tmp;
721           else
722             dir = xstrdup (standard_homedir ());
723         }
724       else
725         dir = xstrdup (dir);
726     }
727
728   return dir;
729 }
730
731 /* Return the data dir used for forms etc.   Returns NULL on error. */
732 char *
733 get_data_dir (void)
734 {
735   char *instdir;
736   char *p;
737   char *dname;
738
739   instdir = read_w32_registry_string ("HKEY_LOCAL_MACHINE", GNUPG_REGKEY,
740                                       "Install Directory");
741   if (!instdir)
742     return NULL;
743   
744   /* Build the key: "<instdir>/share/gpgol".  */
745 #define SDDIR "\\share\\gpgol"
746   dname = malloc (strlen (instdir) + strlen (SDDIR) + 1);
747   if (!dname)
748     {
749       free (instdir);
750       return NULL;
751     }
752   p = dname;
753   strcpy (p, instdir);
754   p += strlen (instdir);
755   strcpy (p, SDDIR);
756   
757   free (instdir);
758   
759 #undef SDDIR
760   return dname;
761 }
762
763
764
765 /* Do in-place decoding of quoted-printable data of LENGTH in BUFFER.
766    Returns the new length of the buffer and stores true at R_SLBRK if
767    the line ended with a soft line break; false is stored if not.
768    This fucntion asssumes that a complete line is passed in
769    buffer.  */
770 size_t
771 qp_decode (char *buffer, size_t length, int *r_slbrk)
772 {
773   char *d, *s;
774
775   if (r_slbrk)
776     *r_slbrk = 0;
777
778   /* Fixme:  We should remove trailing white space first.  */
779   for (s=d=buffer; length; length--)
780     if (*s == '=')
781       {
782         if (length > 2 && hexdigitp (s+1) && hexdigitp (s+2))
783           {
784             s++;
785             *(unsigned char*)d++ = xtoi_2 (s);
786             s += 2;
787             length -= 2;
788           }
789         else if (length > 2 && s[1] == '\r' && s[2] == '\n')
790           {
791             /* Soft line break.  */
792             s += 3;
793             length -= 2;
794             if (r_slbrk && length == 1)
795               *r_slbrk = 1;
796           }
797         else if (length > 1 && s[1] == '\n')
798           {
799             /* Soft line break with only a Unix line terminator. */
800             s += 2;
801             length -= 1;
802             if (r_slbrk && length == 1)
803               *r_slbrk = 1;
804           }
805         else if (length == 1)
806           {
807             /* Soft line break at the end of the line. */
808             s += 1;
809             if (r_slbrk)
810               *r_slbrk = 1;
811           }
812         else
813           *d++ = *s++;
814       }
815     else
816       *d++ = *s++;
817
818   return d - buffer;
819 }
820
821 /* Return the a quoted printable encoded version of the
822    input string. If outlen is not null the size of the
823    quoted printable string is returned. String will be
824    malloced and zero terminated. Aborts if the output
825    is more then three times the size of the input.
826    This is only basic and does not handle mutliline data. */
827 char *
828 qp_encode (const char *input, size_t inlen, size_t *r_outlen)
829 {
830   size_t max_len = inlen * 3 +1;
831   char *outbuf = xmalloc (max_len);
832   size_t outlen = 0;
833   const unsigned char *p;
834
835   memset (outbuf, 0, max_len);
836
837   for (p = input; inlen; p++, inlen--)
838     {
839       if (*p >= '!' && *p <= '~' && *p != '=')
840         {
841           outbuf[outlen++] = *p;
842         }
843       else if (*p == ' ')
844         {
845           /* Outlook does it this way */
846           outbuf[outlen++] = '_';
847         }
848       else
849         {
850           outbuf[outlen++] = '=';
851           outbuf[outlen++] = tohex ((*p>>4)&15);
852           outbuf[outlen++] = tohex (*p&15);
853         }
854       if (outlen == max_len -1)
855         {
856           log_error ("Quoted printable too long. Bug.");
857           r_outlen = NULL;
858           return NULL;
859         }
860     }
861   if (r_outlen)
862     *r_outlen = outlen;
863   return outbuf;
864 }
865
866
867 /* Initialize the Base 64 decoder state.  */
868 void b64_init (b64_state_t *state)
869 {
870   state->idx = 0;
871   state->val = 0;
872   state->stop_seen = 0;
873   state->invalid_encoding = 0;
874 }
875
876
877 /* Do in-place decoding of base-64 data of LENGTH in BUFFER.  Returns
878    the new length of the buffer. STATE is required to return errors and
879    to maintain the state of the decoder.  */
880 size_t
881 b64_decode (b64_state_t *state, char *buffer, size_t length)
882 {
883   int idx = state->idx;
884   unsigned char val = state->val;
885   int c;
886   char *d, *s;
887
888   if (state->stop_seen)
889     return 0;
890
891   for (s=d=buffer; length; length--, s++)
892     {
893       if (*s == '\n' || *s == ' ' || *s == '\r' || *s == '\t')
894         continue;
895       if (*s == '=')
896         {
897           /* Pad character: stop */
898           if (idx == 1)
899             *d++ = val;
900           state->stop_seen = 1;
901           break;
902         }
903
904       if ((c = asctobin[*(unsigned char *)s]) == 255)
905         {
906           if (!state->invalid_encoding)
907             log_debug ("%s: invalid base64 character %02X at pos %d skipped\n",
908                        __func__, *(unsigned char*)s, (int)(s-buffer));
909           state->invalid_encoding = 1;
910           continue;
911         }
912
913       switch (idx)
914         {
915         case 0:
916           val = c << 2;
917           break;
918         case 1:
919           val |= (c>>4)&3;
920           *d++ = val;
921           val = (c<<4)&0xf0;
922           break;
923         case 2:
924           val |= (c>>2)&15;
925           *d++ = val;
926           val = (c<<6)&0xc0;
927           break;
928         case 3:
929           val |= c&0x3f;
930           *d++ = val;
931           break;
932         }
933       idx = (idx+1) % 4;
934     }
935
936
937   state->idx = idx;
938   state->val = val;
939   return d - buffer;
940 }
941
942
943 /* Base 64 encode the input. If input is null returns NULL otherwise
944    a pointer to the malloced encoded string. */
945 char *
946 b64_encode (const char *input, size_t length)
947 {
948   size_t out_len = 4 * ((length + 2) / 3);
949   char *ret;
950   int i, j;
951
952   if (!length || !input)
953     {
954       return NULL;
955     }
956   ret = xmalloc (out_len);
957   memset (ret, 0, out_len);
958
959   for (i = 0, j = 0; i < length;)
960     {
961       unsigned int a = i < length ? (unsigned char)input[i++] : 0;
962       unsigned int b = i < length ? (unsigned char)input[i++] : 0;
963       unsigned int c = i < length ? (unsigned char)input[i++] : 0;
964
965       unsigned int triple = (a << 0x10) + (b << 0x08) + c;
966
967       ret[j++] = bintoasc[(triple >> 3 * 6) & 0x3F];
968       ret[j++] = bintoasc[(triple >> 2 * 6) & 0x3F];
969       ret[j++] = bintoasc[(triple >> 1 * 6) & 0x3F];
970       ret[j++] = bintoasc[(triple >> 0 * 6) & 0x3F];
971     }
972
973   if (length % 3)
974     {
975       ret [j - 1] = '=';
976     }
977   if (length % 3 == 1)
978     {
979       ret [j - 2] = '=';
980     }
981
982   return ret;
983 }
984
985 /* Create a boundary.  Note that mimemaker.c knows about the structure
986    of the boundary (i.e. that it starts with "=-=") so that it can
987    protect against accidently used boundaries within the content.  */
988 char *
989 generate_boundary (char *buffer)
990 {
991   char *p = buffer;
992   int i;
993
994 #if RAND_MAX < (64*2*BOUNDARYSIZE)
995 #error RAND_MAX is way too small
996 #endif
997
998   *p++ = '=';
999   *p++ = '-';
1000   *p++ = '=';
1001   for (i=0; i < BOUNDARYSIZE-6; i++)
1002     *p++ = bintoasc[rand () % 64];
1003   *p++ = '=';
1004   *p++ = '-';
1005   *p++ = '=';
1006   *p = 0;
1007
1008   return buffer;
1009 }
1010
1011
1012 /* Percent-escape the string STR by replacing colons with '%3a'.  If
1013    EXTRA is not NULL all characters in it are also escaped. */
1014 char *
1015 percent_escape (const char *str, const char *extra)
1016 {
1017   int i, j;
1018   char *ptr;
1019
1020   if (!str)
1021     return NULL;
1022
1023   for (i=j=0; str[i]; i++)
1024     if (str[i] == ':' || str[i] == '%' || (extra && strchr (extra, str[i])))
1025       j++;
1026   ptr = (char *) malloc (i + 2 * j + 1);
1027   i = 0;
1028   while (*str)
1029     {
1030       /* FIXME: Work around a bug in Kleo.  */
1031       if (*str == ':')
1032         {
1033           ptr[i++] = '%';
1034           ptr[i++] = '3';
1035           ptr[i++] = 'a';
1036         }
1037       else
1038         {
1039           if (*str == '%')
1040             {
1041               ptr[i++] = '%';
1042               ptr[i++] = '2';
1043               ptr[i++] = '5';
1044             }
1045           else if (extra && strchr (extra, *str))
1046             {
1047               ptr[i++] = '%';
1048               ptr[i++] = tohex_lower ((*str >> 4) & 15);
1049               ptr[i++] = tohex_lower (*str & 15);
1050             }
1051           else
1052             ptr[i++] = *str;
1053         }
1054       str++;
1055     }
1056   ptr[i] = '\0';
1057
1058   return ptr;
1059 }
1060
1061 /* Fix linebreaks.
1062    This replaces all consecutive \r or \n characters
1063    by a single \n.
1064    There can be extremly weird combinations of linebreaks
1065    like \r\r\n\r\r\n at the end of each line when
1066    getting the body of a mail message.
1067 */
1068 void
1069 fix_linebreaks (char *str, int *len)
1070 {
1071   char *src;
1072   char *dst;
1073
1074   src = str;
1075   dst = str;
1076   while (*src)
1077     {
1078       if (*src == '\r' || *src == '\n')
1079         {
1080           do
1081             src++;
1082           while (*src == '\r' || *src == '\n');
1083           *(dst++) = '\n';
1084         }
1085       else
1086         {
1087           *(dst++) = *(src++);
1088         }
1089     }
1090   *dst = '\0';
1091   *len = dst - str;
1092 }
1093
1094 /* Get a pretty name for the file at path path. File extension
1095    will be set to work for the protocol as provided in protocol and
1096    depends on the signature setting. Set signature to 0 if the
1097    extension should not be a signature extension.
1098    Returns NULL on success.
1099    Caller must free result. */
1100 wchar_t *
1101 get_pretty_attachment_name (wchar_t *path, protocol_t protocol,
1102                             int signature)
1103 {
1104   wchar_t* pretty;
1105   wchar_t* buf;
1106
1107   if (!path || !wcslen (path))
1108     {
1109       log_error("%s:%s: No path given", SRCNAME, __func__);
1110       return NULL;
1111     }
1112
1113   pretty = (wchar_t*) xmalloc ((MAX_PATH + 1) * sizeof (wchar_t));
1114   memset (pretty, 0, (MAX_PATH + 1) * sizeof (wchar_t));
1115
1116   buf = wcsrchr (path, '\\') + 1;
1117
1118   if (!buf || !*buf)
1119     {
1120       log_error("%s:%s: No filename found in path", SRCNAME, __func__);
1121       xfree (pretty);
1122       return NULL;
1123     }
1124
1125   wcscpy (pretty, buf);
1126
1127   buf = pretty + wcslen(pretty);
1128   if (signature)
1129     {
1130       if (protocol == PROTOCOL_SMIME)
1131         {
1132           *(buf++) = '.';
1133           *(buf++) = 'p';
1134           *(buf++) = '7';
1135           *(buf++) = 's';
1136         }
1137       else
1138         {
1139           *(buf++) = '.';
1140           *(buf++) = 's';
1141           *(buf++) = 'i';
1142           *(buf++) = 'g';
1143         }
1144     }
1145   else
1146     {
1147       if (protocol == PROTOCOL_SMIME)
1148         {
1149           *(buf++) = '.';
1150           *(buf++) = 'p';
1151           *(buf++) = '7';
1152           *(buf++) = 'm';
1153         }
1154       else
1155         {
1156           *(buf++) = '.';
1157           *(buf++) = 'g';
1158           *(buf++) = 'p';
1159           *(buf++) = 'g';
1160         }
1161     }
1162
1163   return pretty;
1164 }
1165
1166 /* Open a file in a temporary directory, take name as a
1167    suggestion and put the open Handle in outHandle.
1168    Returns the actually used file name in case there
1169    were other files with that name. */
1170 wchar_t*
1171 get_tmp_outfile (wchar_t *name, HANDLE *outHandle)
1172 {
1173   wchar_t tmpPath[MAX_PATH];
1174   wchar_t *outName;
1175   wchar_t *fileExt = NULL;
1176   int tries = 1;
1177
1178   if (!name || !wcslen(name))
1179     {
1180       log_error ("%s:%s: Needs a name.",
1181                  SRCNAME, __func__);
1182       return NULL;
1183     }
1184
1185   /* We should probably use the unicode variants here
1186      but this would mean adding OpenStreamOnFileW to
1187      out mapi */
1188
1189   if (!GetTempPathW (MAX_PATH, tmpPath))
1190     {
1191       log_error ("%s:%s: Could not get tmp path.",
1192                  SRCNAME, __func__);
1193       return NULL;
1194     }
1195
1196   outName = (wchar_t*) xmalloc ((MAX_PATH + 1) * sizeof(wchar_t));
1197   memset (outName, 0, (MAX_PATH + 1) * sizeof (wchar_t));
1198
1199   snwprintf (outName, MAX_PATH, L"%s%s", tmpPath, name);
1200   fileExt = wcschr (wcschr(outName, '\\'), '.');
1201
1202   while ((*outHandle = CreateFileW (outName,
1203                                     GENERIC_WRITE | GENERIC_READ,
1204                                     FILE_SHARE_READ,
1205                                     NULL,
1206                                     CREATE_NEW,
1207                                     FILE_ATTRIBUTE_TEMPORARY,
1208                                     NULL)) == INVALID_HANDLE_VALUE)
1209     {
1210       wchar_t fnameBuf[MAX_PATH + 1];
1211       wchar_t origName[MAX_PATH + 1];
1212       memset (fnameBuf, 0, MAX_PATH + 1);
1213       memset (origName, 0, MAX_PATH + 1);
1214
1215       snwprintf (origName, MAX_PATH, L"%s%s", tmpPath, name);
1216       fileExt = wcschr (wcsrchr(origName, '\\'), '.');
1217       wcsncpy (fnameBuf, origName, fileExt - origName);
1218       snwprintf (outName, MAX_PATH, L"%s%i%s", fnameBuf, tries++, fileExt);
1219       if (tries > 100)
1220         {
1221           /* You have to know when to give up,.. */
1222           log_error ("%s:%s: Could not get a name out of 100 tries",
1223                      SRCNAME, __func__);
1224           xfree (outName);
1225           return NULL;
1226         }
1227     }
1228
1229   return outName;
1230 }