408de426ad26a2d10dd9903047eb9544dcd2a42d
[gpgol.git] / src / main.c
1 /* main.c - DLL entry point
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
22 #include <windows.h>
23 #include <wincrypt.h>
24
25 #include "mymapi.h"
26 #include "mymapitags.h"
27
28 #include "common.h"
29 #include "passcache.h"
30 #include "msgcache.h"
31 #include "mymapi.h"
32
33
34 /* Registry key for this software. */
35 #define REGKEY "Software\\GNU\\GnuPG"
36
37 /* The malloced name of the logfile and the logging stream.  If
38    LOGFILE is NULL, no logging is done. */
39 static char *logfile;
40 static FILE *logfp;
41
42 /* For certain operations we need to acquire a log on the logging
43    functions.  This lock is controlled by this Mutex. */
44 static HANDLE log_mutex;
45
46 /* The session key used to temporary encrypt attachments.  It is
47    initialized at startup.  */
48 static char *the_session_key;
49
50 /* Local function prototypes. */
51 static char *get_locale_dir (void);
52 static void drop_locale_dir (char *locale_dir);
53
54
55 \f
56 /* Initialization of gloabl options.  These are merely the defaults
57    and will get updated later from the Registry.  That is done later
58    at the time Outlook calls its entry point the first time. */
59 static void
60 init_options (void)
61 {
62   opt.passwd_ttl = 10; /* Seconds. Use a small value, so that no
63                           multiple prompts for attachment encryption
64                           are issued. */
65   opt.enc_format = GPG_FMT_CLASSIC;
66 }
67
68
69 /* Early initialization of this module.  This is done right at startup
70    with only one thread running.  Should be called only once. Returns
71    0 on success. */
72 static int
73 initialize_main (void)
74 {
75   SECURITY_ATTRIBUTES sa;
76   
77   memset (&sa, 0, sizeof sa);
78   sa.bInheritHandle = FALSE;
79   sa.lpSecurityDescriptor = NULL;
80   sa.nLength = sizeof sa;
81   log_mutex = CreateMutex (&sa, FALSE, NULL);
82   return log_mutex? 0 : -1;
83 }
84
85 /* Return nbytes of cryptographic strong random.  Caller needs to free
86    the returned buffer.  */
87 static char *
88 get_crypt_random (size_t nbytes) 
89 {
90   HCRYPTPROV prov;
91   char *buffer;
92
93   if (!CryptAcquireContext (&prov, NULL, NULL, PROV_RSA_FULL, 
94                             (CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) )
95     return NULL;
96   
97   buffer = xmalloc (nbytes);
98   if (!CryptGenRandom (prov, nbytes, buffer))
99     {
100       xfree (buffer);
101       buffer = NULL;
102     }
103   CryptReleaseContext (prov, 0);
104   return buffer;
105 }
106
107
108 static int
109 initialize_session_key (void)
110 {
111   the_session_key = get_crypt_random (16+sizeof (unsigned int));
112   if (the_session_key)
113     {
114       /* We use rand() in generate_boundary so we need to seed it. */
115       unsigned int tmp;
116
117       memcpy (&tmp, the_session_key+16, sizeof (unsigned int));
118       srand (tmp);
119     }
120   return !the_session_key;
121 }
122
123
124 static void
125 i18n_init (void)
126 {
127   char *locale_dir;
128
129 #ifdef ENABLE_NLS
130 # ifdef HAVE_LC_MESSAGES
131   setlocale (LC_TIME, "");
132   setlocale (LC_MESSAGES, "");
133 # else
134   setlocale (LC_ALL, "" );
135 # endif
136 #endif
137
138   locale_dir = get_locale_dir ();
139   if (locale_dir)
140     {
141       bindtextdomain (PACKAGE_GT, locale_dir);
142       drop_locale_dir (locale_dir);
143     }
144   textdomain (PACKAGE_GT);
145 }
146
147
148 /* Entry point called by DLL loader. */
149 int WINAPI
150 DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved)
151 {
152   if (reason == DLL_PROCESS_ATTACH)
153     {
154       set_global_hinstance (hinst);
155       /* The next call initializes subsystems of gpgme and should be
156          done as early as possible.  The actual return value (the
157          version string) is not used here.  It may be called at any
158          time later for this. */
159       gpgme_check_version (NULL);
160
161       /* Early initializations of our subsystems. */
162       if (initialize_main ())
163         return FALSE;
164       i18n_init ();
165       if (initialize_session_key ())
166         return FALSE;
167       if (initialize_passcache ())
168         return FALSE;
169       if (initialize_msgcache ())
170         return FALSE;
171       init_options ();
172     }
173   else if (reason == DLL_PROCESS_DETACH)
174     {
175     }
176   
177   return TRUE;
178 }
179
180
181
182 /* Return the static session key we are using for temporary encrypting
183    attachments.  The session key is guaranteed to be available.  */
184 const void *
185 get_128bit_session_key (void)
186 {
187   return the_session_key;
188 }
189
190
191 /* Return a new allocated IV of size NBYTES.  Caller must free it.  On
192    error NULL is returned. */
193 void *
194 create_initialization_vector (size_t nbytes)
195 {
196   return get_crypt_random (nbytes);
197 }
198
199
200 /* Acquire the mutex for logging.  Returns 0 on success. */
201 static int 
202 lock_log (void)
203 {
204   int code = WaitForSingleObject (log_mutex, INFINITE);
205   return code != WAIT_OBJECT_0;
206 }
207
208 /* Release the mutex for logging. No error return is done because this
209    is a fatal error anyway and we have no means for proper
210    notification. */
211 static void
212 unlock_log (void)
213 {
214   ReleaseMutex (log_mutex);
215 }
216
217
218
219 static void
220 do_log (const char *fmt, va_list a, int w32err, int err,
221         const void *buf, size_t buflen)
222 {
223   if (!logfile)
224     return;
225
226   if (lock_log ())
227     return;
228   
229   if (!logfp)
230     logfp = fopen (logfile, "a+");
231   if (!logfp)
232     {
233       unlock_log ();
234       return;
235     }
236   
237   fprintf (logfp, "%lu/", (unsigned long)GetCurrentThreadId ());
238   if (err == 1)
239     fputs ("ERROR/", logfp);
240   vfprintf (logfp, fmt, a);
241   if (w32err) 
242     {
243       char tmpbuf[256];
244       
245       FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32err, 
246                      MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), 
247                      tmpbuf, sizeof (tmpbuf)-1, NULL);
248       fputs (": ", logfp);
249       if (*tmpbuf && tmpbuf[strlen (tmpbuf)-1] == '\n')
250         tmpbuf[strlen (tmpbuf)-1] = 0;
251       if (*tmpbuf && tmpbuf[strlen (tmpbuf)-1] == '\r')
252         tmpbuf[strlen (tmpbuf)-1] = 0;
253       fprintf (logfp, "%s (%d)", tmpbuf, w32err);
254     }
255   if (buf)
256     {
257       const unsigned char *p = (const unsigned char*)buf;
258
259       for ( ; buflen; buflen--, p++)
260         fprintf (logfp, "%02X", *p);
261       putc ('\n', logfp);
262     }
263   else if ( *fmt && fmt[strlen (fmt) - 1] != '\n')
264     putc ('\n', logfp);
265
266   fflush (logfp);
267   unlock_log ();
268 }
269
270
271 void 
272 log_debug (const char *fmt, ...)
273 {
274   va_list a;
275   
276   va_start (a, fmt);
277   do_log (fmt, a, 0, 0, NULL, 0);
278   va_end (a);
279 }
280
281 void 
282 log_error (const char *fmt, ...)
283 {
284   va_list a;
285   
286   va_start (a, fmt);
287   do_log (fmt, a, 0, 1, NULL, 0);
288   va_end (a);
289 }
290
291 void 
292 log_vdebug (const char *fmt, va_list a)
293 {
294   do_log (fmt, a, 0, 0, NULL, 0);
295 }
296
297
298 void 
299 log_debug_w32 (int w32err, const char *fmt, ...)
300 {
301   va_list a;
302
303   if (w32err == -1)
304     w32err = GetLastError ();
305   
306   va_start (a, fmt);
307   do_log (fmt, a, w32err, 0, NULL, 0);
308   va_end (a);
309 }
310
311 void 
312 log_error_w32 (int w32err, const char *fmt, ...)
313 {
314   va_list a;
315
316   if (w32err == -1)
317     w32err = GetLastError ();
318   
319   va_start (a, fmt);
320   do_log (fmt, a, w32err, 1, NULL, 0);
321   va_end (a);
322 }
323
324
325 void 
326 log_hexdump (const void *buf, size_t buflen, const char *fmt, ...)
327 {
328   va_list a;
329
330   va_start (a, fmt);
331   do_log (fmt, a, 0, 2, buf, buflen);
332   va_end (a);
333 }
334
335
336 /* Helper to log_window_hierarchy.  */
337 static HWND
338 do_log_window_hierarchy (HWND parent, int level)
339 {
340   HWND child;
341
342   child = GetWindow (parent, GW_CHILD);
343   while (child)
344     {
345       char buf[1024+1];
346       char name[200];
347       int nname;
348       char *pname;
349       
350       memset (buf, 0, sizeof (buf));
351       GetWindowText (child, buf, sizeof (buf)-1);
352       nname = GetClassName (child, name, sizeof (name)-1);
353       if (nname)
354         pname = name;
355       else
356         pname = NULL;
357       log_debug ("    %*shwnd=%p (%s) `%s'", level*2, "", child,
358                  pname? pname:"", buf);
359       do_log_window_hierarchy (child, level+1);
360       child = GetNextWindow (child, GW_HWNDNEXT);       
361     }
362
363   return NULL;
364 }
365
366
367 /* Print a debug message using the format string FMT followed by the
368    window hierarchy of WINDOW.  */
369 void
370 log_window_hierarchy (HWND window, const char *fmt, ...)
371 {
372   va_list a;
373
374   va_start (a, fmt);
375   do_log (fmt, a, 0, 0, NULL, 0);
376   va_end (a);
377   do_log_window_hierarchy (window, 0);
378 }
379
380
381
382
383 const char *
384 log_srcname (const char *file)
385 {
386   const char *s = strrchr (file, '/');
387   return s? s+1:file;
388 }
389
390 const char *
391 get_log_file (void)
392 {
393   return logfile? logfile : "";
394 }
395
396 void
397 set_log_file (const char *name)
398 {
399   if (!lock_log ())
400     {
401       if (logfp)
402         {
403           fclose (logfp);
404           logfp = NULL;
405         }
406       xfree (logfile);
407       if (!name || *name == '\"' || !*name) 
408         logfile = NULL;
409       else
410         logfile = xstrdup (name);
411       unlock_log ();
412     }
413 }
414
415
416
417 void
418 set_default_key (const char *name)
419 {
420   if (!lock_log ())
421     {
422       if (!name || *name == '\"' || !*name)
423         {
424           xfree (opt.default_key);
425           opt.default_key = NULL;
426         }
427       else
428         {
429           xfree (opt.default_key);
430           opt.default_key = xstrdup (name);;
431         }
432       unlock_log ();
433     }
434 }
435
436
437 static char *
438 get_locale_dir (void)
439 {
440   char *instdir;
441   char *p;
442   char *dname;
443
444   instdir = read_w32_registry_string ("HKEY_LOCAL_MACHINE", REGKEY,
445                                       "Install Directory");
446   if (!instdir)
447     return NULL;
448   
449   /* Build the key: "<instdir>/share/locale".  */
450 #define SLDIR "\\share\\locale"
451   dname = malloc (strlen (instdir) + strlen (SLDIR) + 1);
452   if (!dname)
453     {
454       free (instdir);
455       return NULL;
456     }
457   p = dname;
458   strcpy (p, instdir);
459   p += strlen (instdir);
460   strcpy (p, SLDIR);
461   
462   free (instdir);
463   
464   return dname;
465 }
466
467
468 static void
469 drop_locale_dir (char *locale_dir)
470 {
471   free (locale_dir);
472 }
473
474
475 /* Read option settings from the Registry. */
476 void
477 read_options (void)
478 {
479   char *val = NULL;
480  
481   load_extension_value ("enableSmime", &val);
482   opt.enable_smime = val == NULL || *val != '1' ? 0 : 1;
483   xfree (val); val = NULL;
484   
485   load_extension_value ("smimeDefault", &val);
486   opt.smime_default = val == NULL || *val != '1'? 0 : 1;
487   xfree (val); val = NULL;
488
489   load_extension_value ("encryptDefault", &val);
490   opt.encrypt_default = val == NULL || *val != '1'? 0 : 1;
491   xfree (val); val = NULL;
492
493   load_extension_value ("signDefault", &val);
494   opt.sign_default = val == NULL || *val != '1'? 0 : 1;
495   xfree (val); val = NULL;
496
497   load_extension_value ("previewDecrypt", &val);
498   opt.preview_decrypt = val == NULL || *val != '1'? 0 : 1;
499   xfree (val); val = NULL;
500
501   load_extension_value ("enableDefaultKey", &val);
502   opt.enable_default_key = val == NULL || *val != '1' ? 0 : 1;
503   xfree (val); val = NULL;
504
505   if (load_extension_value ("storePasswdTime", &val) )
506     opt.passwd_ttl = 600; /* Initial default. */
507   else
508     opt.passwd_ttl = val == NULL || *val == '0'? 0 : atol (val);
509   xfree (val); val = NULL;
510
511   load_extension_value ("encodingFormat", &val);
512   opt.enc_format = val == NULL? GPG_FMT_CLASSIC  : atol (val);
513   xfree (val); val = NULL;
514
515   load_extension_value ("logFile", &val);
516   set_log_file (val);
517   xfree (val); val = NULL;
518   
519   load_extension_value ("defaultKey", &val);
520   set_default_key (val);
521   xfree (val); val = NULL;
522
523   load_extension_value ("preferHtml", &val);
524   opt.prefer_html = val == NULL || *val != '1'? 0 : 1;
525   xfree (val); val = NULL;
526
527   /* Note, that on purpose these flags are only Registry changeable.
528      The format of the entry is a string of of "0" and "1" digits; see
529      the switch below for a description. */
530   memset (&opt.compat, 0, sizeof opt.compat);
531   load_extension_value ("compatFlags", &val);
532   if (val)
533     {
534       const char *s = val;
535       int i, x;
536
537       for (s=val, i=0; *s; s++, i++)
538         {
539           x = *s == '1';
540           switch (i)
541             {
542             case 0: opt.compat.no_msgcache = x; break;
543             case 1: opt.compat.no_pgpmime = x; break;
544             case 2: opt.compat.no_oom_write = x; break;
545             case 3: opt.compat.no_preview_info = x; break;
546             case 4: opt.compat.old_reply_hack = x; break;
547             case 5: opt.compat.auto_decrypt = x; break;
548             case 6: opt.compat.no_attestation = x; break;
549             }
550         }
551       log_debug ("Note: using compatibility flags: %s", val);
552     }
553   xfree (val); val = NULL;
554 }
555
556
557 /* Write current options back to the Registry. */
558 int
559 write_options (void)
560 {
561   struct 
562   {
563     const char *name;
564     int  mode;
565     int  value;
566     char *s_val;
567   } table[] = {
568     {"smimeDefault",             0, opt.smime_default},
569     {"encryptDefault",           0, opt.encrypt_default},
570     {"signDefault",              0, opt.sign_default},
571     {"enableSmime",              0, opt.enable_smime},
572     {"previewDecrypt",           0, opt.preview_decrypt},
573     {"storePasswdTime",          1, opt.passwd_ttl},
574     {"encodingFormat",           1, opt.enc_format},
575     {"logFile",                  2, 0, logfile},
576     {"defaultKey",               2, 0, opt.default_key},
577     {"enableDefaultKey",         0, opt.enable_default_key},
578     {"preferHtml",               0, opt.prefer_html},
579     {NULL, 0}
580   };
581   char buf[32];
582   int rc, i;
583
584   for (i=0; table[i].name; i++) 
585     {
586       log_debug ("storing option `%s'\n", table[i].name);
587       switch (table[i].mode)
588         {
589         case 0:
590           rc = store_extension_value (table[i].name, table[i].value? "1": "0");
591           break;
592         case 1:
593           sprintf (buf, "%d", table[i].value);
594           rc = store_extension_value (table[i].name, buf);
595           break;
596         case 2:
597           rc = store_extension_value (table[i].name,
598                                       table[i].s_val? table[i].s_val : "");
599           break;
600         default:
601           rc = -1;
602           break;
603         }
604       if (rc)
605         log_error ("error storing option `%s': rc = %d\n", table[i].name, rc);
606     }
607   
608   return 0;
609 }