Lots of changes to support S/MIME and to revamp most of the old
[gpgol.git] / src / common.c
1 /* common.c - Common routines used bu 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
17  * License along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  * 02110-1301, USA.
20  */
21
22 #include <config.h>
23 #include <windows.h>
24 #include <time.h>
25
26 #include "common.h"
27
28 HINSTANCE glob_hinst = NULL;
29
30
31 /* The reverse base-64 list used for base-64 decoding. */
32 static unsigned char const asctobin[256] = {
33   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
34   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
35   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
36   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, 
37   0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, 
38   0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
39   0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 
40   0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 
41   0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 
42   0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 
43   0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
44   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
45   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
46   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
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, 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, 0xff, 0xff, 0xff, 0xff, 0xff, 
53   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
54   0xff, 0xff, 0xff, 0xff
55 };
56
57
58
59 void
60 set_global_hinstance (HINSTANCE hinst)
61 {
62     glob_hinst = hinst;
63 }
64
65 /* Center the given window with the desktop window as the
66    parent window. */
67 void
68 center_window (HWND childwnd, HWND style) 
69 {     
70     HWND parwnd;
71     RECT rchild, rparent;    
72     HDC hdc;
73     int wchild, hchild, wparent, hparent;
74     int wscreen, hscreen, xnew, ynew;
75     int flags = SWP_NOSIZE | SWP_NOZORDER;
76
77     parwnd = GetDesktopWindow ();
78     GetWindowRect (childwnd, &rchild);     
79     wchild = rchild.right - rchild.left;     
80     hchild = rchild.bottom - rchild.top;
81
82     GetWindowRect (parwnd, &rparent);     
83     wparent = rparent.right - rparent.left;     
84     hparent = rparent.bottom - rparent.top;      
85     
86     hdc = GetDC (childwnd);     
87     wscreen = GetDeviceCaps (hdc, HORZRES);     
88     hscreen = GetDeviceCaps (hdc, VERTRES);     
89     ReleaseDC (childwnd, hdc);      
90     xnew = rparent.left + ((wparent - wchild) / 2);     
91     if (xnew < 0)
92         xnew = 0;
93     else if ((xnew+wchild) > wscreen) 
94         xnew = wscreen - wchild;
95     ynew = rparent.top  + ((hparent - hchild) / 2);
96     if (ynew < 0)
97         ynew = 0;
98     else if ((ynew+hchild) > hscreen)
99         ynew = hscreen - hchild;
100     if (style == HWND_TOPMOST || style == HWND_NOTOPMOST)
101         flags = SWP_NOMOVE | SWP_NOSIZE;
102     SetWindowPos (childwnd, style? style : NULL, xnew, ynew, 0, 0, flags);
103 }
104
105
106
107 /* Return a filename to be used for saving an attachment. Returns a
108    malloced string on success. HWND is the current Window and SRCNAME
109    the filename to be used as suggestion.  On error (i.e. cancel) NULL
110    is returned. */
111 char *
112 get_save_filename (HWND root, const char *srcname)
113 {
114   char filter[] = "All Files (*.*)\0*.*\0\0";
115   char fname[MAX_PATH+1];
116   OPENFILENAME ofn;
117
118   memset (fname, 0, sizeof (fname));
119   strncpy (fname, srcname, MAX_PATH-1);
120   fname[MAX_PATH] = 0;  
121   
122
123   memset (&ofn, 0, sizeof (ofn));
124   ofn.lStructSize = sizeof (ofn);
125   ofn.hwndOwner = root;
126   ofn.lpstrFile = fname;
127   ofn.nMaxFile = MAX_PATH;
128   ofn.lpstrFileTitle = NULL;
129   ofn.nMaxFileTitle = 0;
130   ofn.Flags |= OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
131   ofn.lpstrTitle = _("GPG - Save decrypted attachment");
132   ofn.lpstrFilter = filter;
133
134   if (GetSaveFileName (&ofn))
135     return xstrdup (fname);
136   return NULL;
137 }
138
139
140 void
141 out_of_core (void)
142 {
143     MessageBox (NULL, "Out of core!", "Fatal Error", MB_OK);
144     abort ();
145 }
146
147 void*
148 xmalloc (size_t n)
149 {
150     void *p = malloc (n);
151     if (!p)
152         out_of_core ();
153     return p;
154 }
155
156 void*
157 xcalloc (size_t m, size_t n)
158 {
159     void *p = calloc (m, n);
160     if (!p)
161         out_of_core ();
162     return p;
163 }
164
165 char*
166 xstrdup (const char *s)
167 {
168     char *p = xmalloc (strlen (s)+1);
169     strcpy (p, s);
170     return p;
171 }
172
173 void
174 xfree (void *p)
175 {
176     if (p)
177         free (p);
178 }
179
180
181 /* This is a helper function to load a Windows function from either of
182    one DLLs. */
183 HRESULT
184 w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
185 {
186   static int initialized;
187   static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
188
189   if (!initialized)
190     {
191       static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
192       void *handle;
193       int i;
194
195       initialized = 1;
196
197       for (i=0, handle = NULL; !handle && dllnames[i]; i++)
198         {
199           handle = LoadLibrary (dllnames[i]);
200           if (handle)
201             {
202               func = (HRESULT (WINAPI *)(HWND,int,HANDLE,DWORD,LPSTR))
203                      GetProcAddress (handle, "SHGetFolderPathA");
204               if (!func)
205                 {
206                   FreeLibrary (handle);
207                   handle = NULL;
208                 }
209             }
210         }
211     }
212
213   if (func)
214     return func (a,b,c,d,e);
215   else
216     return -1;
217 }
218
219
220
221 /* Same as above, but only convert the first LEN wchars.  */
222 char *
223 wchar_to_utf8_2 (const wchar_t *string, size_t len)
224 {
225   int n;
226   char *result;
227
228   /* Note, that CP_UTF8 is not defined in Windows versions earlier
229      than NT.*/
230   n = WideCharToMultiByte (CP_UTF8, 0, string, len, NULL, 0, NULL, NULL);
231   if (n < 0)
232     return NULL;
233
234   result = xmalloc (n+1);
235   n = WideCharToMultiByte (CP_UTF8, 0, string, len, result, n, NULL, NULL);
236   if (n < 0)
237     {
238       xfree (result);
239       return NULL;
240     }
241   return result;
242 }
243
244
245 /* Same as above but convert only the first LEN characters.  STRING
246    must be at least LEN characters long. */
247 wchar_t *
248 utf8_to_wchar2 (const char *string, size_t len)
249 {
250   int n;
251   wchar_t *result;
252
253   n = MultiByteToWideChar (CP_UTF8, 0, string, len, NULL, 0);
254   if (n < 0)
255     return NULL;
256
257   result = xmalloc ((n+1) * sizeof *result);
258   n = MultiByteToWideChar (CP_UTF8, 0, string, len, result, n);
259   if (n < 0)
260     {
261       xfree (result);
262       return NULL;
263     }
264   result[n] = 0;
265   return result;
266 }
267
268
269 /* Assume STRING is a Latin-1 encoded and convert it to utf-8.
270    Returns a newly malloced UTF-8 string. */
271 char *
272 latin1_to_utf8 (const char *string)
273 {
274   const char *s;
275   char *buffer, *p;
276   size_t n;
277
278   for (s=string, n=0; *s; s++) 
279     {
280       n++;
281       if (*s & 0x80)
282         n++;
283     }
284   buffer = xmalloc (n + 1);
285   for (s=string, p=buffer; *s; s++)
286     {
287       if (*s & 0x80)
288         {
289           *p++ = 0xc0 | ((*s >> 6) & 3);
290           *p++ = 0x80 | (*s & 0x3f);
291         }
292       else
293         *p++ = *s;
294     }
295   *p = 0;
296   return buffer;
297 }
298
299
300 /* Strip off trailing white spaces from STRING.  Returns STRING. */
301 char *
302 trim_trailing_spaces (char *string)
303 {
304   char *p, *mark;
305
306   for (mark=NULL, p=string; *p; p++)
307     {
308       if (strchr (" \t\r\n", *p ))
309         {
310           if (!mark)
311             mark = p;
312         }
313         else
314           mark = NULL;
315     }
316
317   if (mark)
318     *mark = 0;
319   return string;
320 }
321
322
323 /* Helper for read_w32_registry_string(). */
324 static HKEY
325 get_root_key(const char *root)
326 {
327   HKEY root_key;
328
329   if( !root )
330     root_key = HKEY_CURRENT_USER;
331   else if( !strcmp( root, "HKEY_CLASSES_ROOT" ) )
332     root_key = HKEY_CLASSES_ROOT;
333   else if( !strcmp( root, "HKEY_CURRENT_USER" ) )
334     root_key = HKEY_CURRENT_USER;
335   else if( !strcmp( root, "HKEY_LOCAL_MACHINE" ) )
336     root_key = HKEY_LOCAL_MACHINE;
337   else if( !strcmp( root, "HKEY_USERS" ) )
338     root_key = HKEY_USERS;
339   else if( !strcmp( root, "HKEY_PERFORMANCE_DATA" ) )
340     root_key = HKEY_PERFORMANCE_DATA;
341   else if( !strcmp( root, "HKEY_CURRENT_CONFIG" ) )
342     root_key = HKEY_CURRENT_CONFIG;
343   else
344     return NULL;
345   return root_key;
346 }
347
348 /* Return a string from the Win32 Registry or NULL in case of error.
349    Caller must release the return value.  A NULL for root is an alias
350    for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn.  NOTE: The value
351    is allocated with a plain malloc() - use free() and not the usual
352    xfree(). */
353 char *
354 read_w32_registry_string (const char *root, const char *dir, const char *name)
355 {
356   HKEY root_key, key_handle;
357   DWORD n1, nbytes, type;
358   char *result = NULL;
359
360   if ( !(root_key = get_root_key(root) ) )
361     return NULL;
362
363   if( RegOpenKeyEx( root_key, dir, 0, KEY_READ, &key_handle ) )
364     {
365       if (root)
366         return NULL; /* no need for a RegClose, so return direct */
367       /* It seems to be common practise to fall back to HKLM. */
368       if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
369         return NULL; /* still no need for a RegClose, so return direct */
370     }
371
372   nbytes = 1;
373   if( RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes ) ) {
374     if (root)
375       goto leave;
376     /* Try to fallback to HKLM also vor a missing value.  */
377     RegCloseKey (key_handle);
378     if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
379       return NULL; /* Nope.  */
380     if (RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes))
381       goto leave;
382   }
383   result = malloc( (n1=nbytes+1) );
384   if( !result )
385     goto leave;
386   if( RegQueryValueEx( key_handle, name, 0, &type, result, &n1 ) ) {
387     free(result); result = NULL;
388     goto leave;
389   }
390   result[nbytes] = 0; /* make sure it is really a string  */
391   if (type == REG_EXPAND_SZ && strchr (result, '%')) {
392     char *tmp;
393
394     n1 += 1000;
395     tmp = malloc (n1+1);
396     if (!tmp)
397       goto leave;
398     nbytes = ExpandEnvironmentStrings (result, tmp, n1);
399     if (nbytes && nbytes > n1) {
400       free (tmp);
401       n1 = nbytes;
402       tmp = malloc (n1 + 1);
403       if (!tmp)
404         goto leave;
405       nbytes = ExpandEnvironmentStrings (result, tmp, n1);
406       if (nbytes && nbytes > n1) {
407         free (tmp); /* oops - truncated, better don't expand at all */
408         goto leave;
409       }
410       tmp[nbytes] = 0;
411       free (result);
412       result = tmp;
413     }
414     else if (nbytes) { /* okay, reduce the length */
415       tmp[nbytes] = 0;
416       free (result);
417       result = malloc (strlen (tmp)+1);
418       if (!result)
419         result = tmp;
420       else {
421         strcpy (result, tmp);
422         free (tmp);
423       }
424     }
425     else {  /* error - don't expand */
426       free (tmp);
427     }
428   }
429
430  leave:
431   RegCloseKey( key_handle );
432   return result;
433 }
434
435
436
437
438 /* Do in-place decoding of quoted-printable data of LENGTH in BUFFER.
439    Returns the new length of the buffer. */
440 size_t
441 qp_decode (char *buffer, size_t length)
442 {
443   char *d, *s;
444
445   for (s=d=buffer; length; length--)
446     if (*s == '=' && length > 2 && hexdigitp (s+1) && hexdigitp (s+2))
447       {
448         s++;
449         *(unsigned char*)d++ = xtoi_2 (s);
450         s += 2;
451         length -= 2;
452       }
453     else
454       *d++ = *s++;
455   
456   return d - buffer;
457 }
458
459
460 /* Initialize the Base 64 decoder state.  */
461 void b64_init (b64_state_t *state)
462 {
463   state->idx = 0;
464   state->val = 0;
465   state->stop_seen = 0;
466   state->invalid_encoding = 0;
467 }
468
469
470 /* Do in-place decoding of base-64 data of LENGTH in BUFFER.  Returns
471    the new length of the buffer. STATE is required to return errors and
472    to maintain the state of the decoder.  */
473 size_t
474 b64_decode (b64_state_t *state, char *buffer, size_t length)
475 {
476   int idx = state->idx;
477   unsigned char val = state->val;
478   int c;
479   char *d, *s;
480
481   if (state->stop_seen)
482     return 0;
483
484   for (s=d=buffer; length; length--, s++)
485     {
486       if (*s == '\n' || *s == ' ' || *s == '\r' || *s == '\t')
487         continue;
488       if (*s == '=')
489         { 
490           /* Pad character: stop */
491           if (idx == 1)
492             *d++ = val; 
493           state->stop_seen = 1;
494           break;
495         }
496
497       if ((c = asctobin[*(unsigned char *)s]) == 255) 
498         {
499           if (!state->invalid_encoding)
500             log_debug ("%s: invalid base64 character %02X at pos %d skipped\n",
501                        __func__, *(unsigned char*)s, (int)(s-buffer));
502           state->invalid_encoding = 1;
503           continue;
504         }
505
506       switch (idx) 
507         {
508         case 0: 
509           val = c << 2;
510           break;
511         case 1: 
512           val |= (c>>4)&3;
513           *d++ = val;
514           val = (c<<4)&0xf0;
515           break;
516         case 2: 
517           val |= (c>>2)&15;
518           *d++ = val;
519           val = (c<<6)&0xc0;
520           break;
521         case 3: 
522           val |= c&0x3f;
523           *d++ = val;
524           break;
525         }
526       idx = (idx+1) % 4;
527     }
528
529   
530   state->idx = idx;
531   state->val = val;
532   return d - buffer;
533 }
534