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