New code to install forms so that appropriate icons are shown in the Viewer.
[gpgol.git] / src / main.c
1 /* main.c - DLL entry point
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
22 #include <windows.h>
23 #include <wincrypt.h>
24 #include <ctype.h>
25
26 #include "mymapi.h"
27 #include "mymapitags.h"
28
29 #include "common.h"
30 #include "passcache.h"
31 #include "msgcache.h"
32 #include "mymapi.h"
33
34
35 /* The malloced name of the logfile and the logging stream.  If
36    LOGFILE is NULL, no logging is done. */
37 static char *logfile;
38 static FILE *logfp;
39
40 /* For certain operations we need to acquire a log on the logging
41    functions.  This lock is controlled by this Mutex. */
42 static HANDLE log_mutex;
43
44 /* The session key used to temporary encrypt attachments.  It is
45    initialized at startup.  */
46 static char *the_session_key;
47
48 /* The session marker to identify this session.  Its value is not
49   confidential.  It is initialized at startup.  */
50 static char *the_session_marker;
51
52 /* Local function prototypes. */
53 static char *get_locale_dir (void);
54 static void drop_locale_dir (char *locale_dir);
55
56
57 \f
58 /* Initialization of gloabl options.  These are merely the defaults
59    and will get updated later from the Registry.  That is done later
60    at the time Outlook calls its entry point the first time. */
61 static void
62 init_options (void)
63 {
64   opt.passwd_ttl = 10; /* Seconds. Use a small value, so that no
65                           multiple prompts for attachment encryption
66                           are issued. */
67   opt.enc_format = GPG_FMT_CLASSIC;
68 }
69
70
71 /* Early initialization of this module.  This is done right at startup
72    with only one thread running.  Should be called only once. Returns
73    0 on success. */
74 static int
75 initialize_main (void)
76 {
77   SECURITY_ATTRIBUTES sa;
78   
79   memset (&sa, 0, sizeof sa);
80   sa.bInheritHandle = FALSE;
81   sa.lpSecurityDescriptor = NULL;
82   sa.nLength = sizeof sa;
83   log_mutex = CreateMutex (&sa, FALSE, NULL);
84   return log_mutex? 0 : -1;
85 }
86
87 /* Return nbytes of cryptographic strong random.  Caller needs to free
88    the returned buffer.  */
89 static char *
90 get_crypt_random (size_t nbytes) 
91 {
92   HCRYPTPROV prov;
93   char *buffer;
94
95   if (!CryptAcquireContext (&prov, NULL, NULL, PROV_RSA_FULL, 
96                             (CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) )
97     return NULL;
98   
99   buffer = xmalloc (nbytes);
100   if (!CryptGenRandom (prov, nbytes, buffer))
101     {
102       xfree (buffer);
103       buffer = NULL;
104     }
105   CryptReleaseContext (prov, 0);
106   return buffer;
107 }
108
109
110 /* Initialize the session key and the session marker.  */
111 static int
112 initialize_session_key (void)
113 {
114   the_session_key = get_crypt_random (16+sizeof (unsigned int)+8);
115   if (the_session_key)
116     {
117       /* We use rand() in generate_boundary so we need to seed it. */
118       unsigned int tmp;
119
120       memcpy (&tmp, the_session_key+16, sizeof (unsigned int));
121       srand (tmp);
122
123       /* And save the session marker. */
124       the_session_marker = the_session_key + 16 + sizeof (unsigned int);
125     }
126   return !the_session_key;
127 }
128
129
130 static void
131 i18n_init (void)
132 {
133   char *locale_dir;
134
135 #ifdef ENABLE_NLS
136 # ifdef HAVE_LC_MESSAGES
137   setlocale (LC_TIME, "");
138   setlocale (LC_MESSAGES, "");
139 # else
140   setlocale (LC_ALL, "" );
141 # endif
142 #endif
143
144   locale_dir = get_locale_dir ();
145   if (locale_dir)
146     {
147       bindtextdomain (PACKAGE_GT, locale_dir);
148       drop_locale_dir (locale_dir);
149     }
150   textdomain (PACKAGE_GT);
151 }
152
153
154 /* Entry point called by DLL loader. */
155 int WINAPI
156 DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved)
157 {
158   if (reason == DLL_PROCESS_ATTACH)
159     {
160       set_global_hinstance (hinst);
161       /* The next call initializes subsystems of gpgme and should be
162          done as early as possible.  The actual return value (the
163          version string) is not used here.  It may be called at any
164          time later for this. */
165       gpgme_check_version (NULL);
166
167       /* Early initializations of our subsystems. */
168       if (initialize_main ())
169         return FALSE;
170       i18n_init ();
171       if (initialize_session_key ())
172         return FALSE;
173       if (initialize_passcache ())
174         return FALSE;
175       if (initialize_msgcache ())
176         return FALSE;
177       init_options ();
178     }
179   else if (reason == DLL_PROCESS_DETACH)
180     {
181     }
182   
183   return TRUE;
184 }
185
186
187
188 /* Return the static session key we are using for temporary encrypting
189    attachments.  The session key is guaranteed to be available.  */
190 const void *
191 get_128bit_session_key (void)
192 {
193   return the_session_key;
194 }
195
196
197 const void *
198 get_64bit_session_marker (void)
199 {
200   return the_session_marker;
201 }
202
203
204 /* Return a new allocated IV of size NBYTES.  Caller must free it.  On
205    error NULL is returned. */
206 void *
207 create_initialization_vector (size_t nbytes)
208 {
209   return get_crypt_random (nbytes);
210 }
211
212
213 /* Acquire the mutex for logging.  Returns 0 on success. */
214 static int 
215 lock_log (void)
216 {
217   int code = WaitForSingleObject (log_mutex, INFINITE);
218   return code != WAIT_OBJECT_0;
219 }
220
221 /* Release the mutex for logging. No error return is done because this
222    is a fatal error anyway and we have no means for proper
223    notification. */
224 static void
225 unlock_log (void)
226 {
227   ReleaseMutex (log_mutex);
228 }
229
230
231
232 static void
233 do_log (const char *fmt, va_list a, int w32err, int err,
234         const void *buf, size_t buflen)
235 {
236   if (!logfile)
237     return;
238
239   if (lock_log ())
240     return;
241   
242   if (!logfp)
243     logfp = fopen (logfile, "a+");
244   if (!logfp)
245     {
246       unlock_log ();
247       return;
248     }
249   
250   fprintf (logfp, "%05lu/%lu/", 
251            ((unsigned long)GetTickCount () % 100000),
252            (unsigned long)GetCurrentThreadId ());
253   if (err == 1)
254     fputs ("ERROR/", logfp);
255   vfprintf (logfp, fmt, a);
256   if (w32err) 
257     {
258       char tmpbuf[256];
259       
260       FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32err, 
261                      MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), 
262                      tmpbuf, sizeof (tmpbuf)-1, NULL);
263       fputs (": ", logfp);
264       if (*tmpbuf && tmpbuf[strlen (tmpbuf)-1] == '\n')
265         tmpbuf[strlen (tmpbuf)-1] = 0;
266       if (*tmpbuf && tmpbuf[strlen (tmpbuf)-1] == '\r')
267         tmpbuf[strlen (tmpbuf)-1] = 0;
268       fprintf (logfp, "%s (%d)", tmpbuf, w32err);
269     }
270   if (buf)
271     {
272       const unsigned char *p = (const unsigned char*)buf;
273
274       for ( ; buflen; buflen--, p++)
275         fprintf (logfp, "%02X", *p);
276       putc ('\n', logfp);
277     }
278   else if ( *fmt && fmt[strlen (fmt) - 1] != '\n')
279     putc ('\n', logfp);
280
281   fflush (logfp);
282   unlock_log ();
283 }
284
285
286 void 
287 log_debug (const char *fmt, ...)
288 {
289   va_list a;
290   
291   va_start (a, fmt);
292   do_log (fmt, a, 0, 0, NULL, 0);
293   va_end (a);
294 }
295
296 void 
297 log_error (const char *fmt, ...)
298 {
299   va_list a;
300   
301   va_start (a, fmt);
302   do_log (fmt, a, 0, 1, NULL, 0);
303   va_end (a);
304 }
305
306 void 
307 log_vdebug (const char *fmt, va_list a)
308 {
309   do_log (fmt, a, 0, 0, NULL, 0);
310 }
311
312
313 void 
314 log_debug_w32 (int w32err, const char *fmt, ...)
315 {
316   va_list a;
317
318   if (w32err == -1)
319     w32err = GetLastError ();
320   
321   va_start (a, fmt);
322   do_log (fmt, a, w32err, 0, NULL, 0);
323   va_end (a);
324 }
325
326 void 
327 log_error_w32 (int w32err, const char *fmt, ...)
328 {
329   va_list a;
330
331   if (w32err == -1)
332     w32err = GetLastError ();
333   
334   va_start (a, fmt);
335   do_log (fmt, a, w32err, 1, NULL, 0);
336   va_end (a);
337 }
338
339
340 void 
341 log_hexdump (const void *buf, size_t buflen, const char *fmt, ...)
342 {
343   va_list a;
344
345   va_start (a, fmt);
346   do_log (fmt, a, 0, 2, buf, buflen);
347   va_end (a);
348 }
349
350
351 static void
352 do_log_window_info (HWND window, int level)
353 {
354   char buf[1024+1];
355   char name[200];
356   int nname;
357   char *pname;
358   DWORD pid;
359
360   if (!window)
361     return;
362
363   GetWindowThreadProcessId (window, &pid);
364   if (pid != GetCurrentProcessId ())
365     return;
366  
367   memset (buf, 0, sizeof (buf));
368   GetWindowText (window, buf, sizeof (buf)-1);
369   nname = GetClassName (window, name, sizeof (name)-1);
370   if (nname)
371     pname = name;
372   else
373     pname = NULL;
374
375   if (level == -1)
376     log_debug ("  parent=%p/%lu (%s) `%s'", window, (unsigned long)pid,
377                pname? pname:"", buf);
378   else
379     log_debug ("    %*shwnd=%p/%lu (%s) `%s'", level*2, "", window,
380                (unsigned long)pid, pname? pname:"", buf);
381 }
382
383
384 /* Helper to log_window_hierarchy.  */
385 static HWND
386 do_log_window_hierarchy (HWND parent, int level)
387 {
388   HWND child;
389
390   child = GetWindow (parent, GW_CHILD);
391   while (child)
392     {
393       do_log_window_info (child, level);
394       do_log_window_hierarchy (child, level+1);
395       child = GetNextWindow (child, GW_HWNDNEXT);       
396     }
397
398   return NULL;
399 }
400
401
402 /* Print a debug message using the format string FMT followed by the
403    window hierarchy of WINDOW.  */
404 void
405 log_window_hierarchy (HWND window, const char *fmt, ...)
406 {
407   va_list a;
408
409   va_start (a, fmt);
410   do_log (fmt, a, 0, 0, NULL, 0);
411   va_end (a);
412   if (window)
413     {
414       do_log_window_info (window, -1);
415       do_log_window_hierarchy (window, 0);
416     }
417 }
418
419
420 const char *
421 log_srcname (const char *file)
422 {
423   const char *s = strrchr (file, '/');
424   return s? s+1:file;
425 }
426
427 const char *
428 get_log_file (void)
429 {
430   return logfile? logfile : "";
431 }
432
433 void
434 set_log_file (const char *name)
435 {
436   if (!lock_log ())
437     {
438       if (logfp)
439         {
440           fclose (logfp);
441           logfp = NULL;
442         }
443       xfree (logfile);
444       if (!name || *name == '\"' || !*name) 
445         logfile = NULL;
446       else
447         logfile = xstrdup (name);
448       unlock_log ();
449     }
450 }
451
452
453
454 void
455 set_default_key (const char *name)
456 {
457   if (!lock_log ())
458     {
459       if (!name || *name == '\"' || !*name)
460         {
461           xfree (opt.default_key);
462           opt.default_key = NULL;
463         }
464       else
465         {
466           xfree (opt.default_key);
467           opt.default_key = xstrdup (name);;
468         }
469       unlock_log ();
470     }
471 }
472
473
474 static char *
475 get_locale_dir (void)
476 {
477   char *instdir;
478   char *p;
479   char *dname;
480
481   instdir = read_w32_registry_string ("HKEY_LOCAL_MACHINE", GNUPG_REGKEY,
482                                       "Install Directory");
483   if (!instdir)
484     return NULL;
485   
486   /* Build the key: "<instdir>/share/locale".  */
487 #define SLDIR "\\share\\locale"
488   dname = malloc (strlen (instdir) + strlen (SLDIR) + 1);
489   if (!dname)
490     {
491       free (instdir);
492       return NULL;
493     }
494   p = dname;
495   strcpy (p, instdir);
496   p += strlen (instdir);
497   strcpy (p, SLDIR);
498   
499   free (instdir);
500   
501   return dname;
502 }
503
504
505 static void
506 drop_locale_dir (char *locale_dir)
507 {
508   free (locale_dir);
509 }
510
511
512 /* Read option settings from the Registry. */
513 void
514 read_options (void)
515 {
516   static int warnings_shown;
517   char *val = NULL;
518
519   /* Set the log file first so that output from this function is
520      logged too.  */
521   load_extension_value ("logFile", &val);
522   set_log_file (val);
523   xfree (val); val = NULL;
524   
525   /* Parse the debug flags.  */
526   load_extension_value ("enableDebug", &val);
527   opt.enable_debug = 0;
528   if (val)
529     {
530       char *p, *pend;
531
532       trim_spaces (val);
533       for (p = val; p; p = pend)
534         {
535           pend = strpbrk (p, ", \t\n\r\f");
536           if (pend)
537             {
538               *pend++ = 0;
539               pend += strspn (pend, ", \t\n\r\f");
540             }
541           if (isascii (*p) && isdigit (*p))
542             opt.enable_debug |= strtoul (p, NULL, 0);
543           else if (!strcmp (p, "ioworker"))
544             opt.enable_debug |= DBG_IOWORKER;
545           else if (!strcmp (p, "ioworker-extra"))
546             opt.enable_debug |= DBG_IOWORKER_EXTRA;
547           else if (!strcmp (p, "filter"))
548             opt.enable_debug |= DBG_FILTER;
549           else if (!strcmp (p, "filter-extra"))
550             opt.enable_debug |= DBG_FILTER_EXTRA;
551           else if (!strcmp (p, "memory"))
552             opt.enable_debug |= DBG_MEMORY;
553           else if (!strcmp (p, "commands"))
554             opt.enable_debug |= DBG_COMMANDS;
555           else if (!strcmp (p, "mime-parser"))
556             opt.enable_debug |= DBG_MIME_PARSER;
557           else if (!strcmp (p, "mime-data"))
558             opt.enable_debug |= DBG_MIME_DATA;
559           else
560             log_debug ("invalid debug flag `%s' ignored", p);
561         }
562     }
563   else
564     {
565       /* To help the user enable debugging make sure that the registry
566          key exists.  Note that the other registry keys are stored
567          after using the configuration dialog.  */
568       store_extension_value ("enableDebug", "0");
569     }
570   xfree (val); val = NULL;
571   if (opt.enable_debug)
572     log_debug ("enabled debug flags:%s%s%s%s%s%s%s%s\n",
573                (opt.enable_debug & DBG_IOWORKER)? " ioworker":"",
574                (opt.enable_debug & DBG_IOWORKER_EXTRA)? " ioworker-extra":"",
575                (opt.enable_debug & DBG_FILTER)? " filter":"",
576                (opt.enable_debug & DBG_FILTER_EXTRA)? " filter-extra":"",
577                (opt.enable_debug & DBG_MEMORY)? " memory":"",
578                (opt.enable_debug & DBG_COMMANDS)? " commands":"",
579                (opt.enable_debug & DBG_MIME_PARSER)? " mime-parser":"",
580                (opt.enable_debug & DBG_MIME_DATA)? " mime-data":""
581                );
582
583
584   load_extension_value ("enableSmime", &val);
585   opt.enable_smime = val == NULL || *val != '1' ? 0 : 1;
586   xfree (val); val = NULL;
587   
588   load_extension_value ("defaultProtocol", &val);
589   switch ((!val || *val == '0')? 0 : atol (val))
590     {
591     case 1: opt.default_protocol = PROTOCOL_OPENPGP; break;
592     case 2: opt.default_protocol = PROTOCOL_SMIME; break;
593     case 0:
594     default: opt.default_protocol = PROTOCOL_UNKNOWN /*(auto*)*/; break;
595     }
596   xfree (val); val = NULL;
597
598   load_extension_value ("encryptDefault", &val);
599   opt.encrypt_default = val == NULL || *val != '1'? 0 : 1;
600   xfree (val); val = NULL;
601
602   load_extension_value ("signDefault", &val);
603   opt.sign_default = val == NULL || *val != '1'? 0 : 1;
604   xfree (val); val = NULL;
605
606   load_extension_value ("previewDecrypt", &val);
607   opt.preview_decrypt = val == NULL || *val != '1'? 0 : 1;
608   xfree (val); val = NULL;
609
610   load_extension_value ("enableDefaultKey", &val);
611   opt.enable_default_key = val == NULL || *val != '1' ? 0 : 1;
612   xfree (val); val = NULL;
613
614   if (load_extension_value ("storePasswdTime", &val) )
615     opt.passwd_ttl = 600; /* Initial default. */
616   else
617     opt.passwd_ttl = val == NULL || *val == '0'? 0 : atol (val);
618   xfree (val); val = NULL;
619
620   load_extension_value ("encodingFormat", &val);
621   opt.enc_format = val == NULL? GPG_FMT_CLASSIC  : atol (val);
622   xfree (val); val = NULL;
623
624   load_extension_value ("defaultKey", &val);
625   set_default_key (val);
626   xfree (val); val = NULL;
627
628   load_extension_value ("preferHtml", &val);
629   opt.prefer_html = val == NULL || *val != '1'? 0 : 1;
630   xfree (val); val = NULL;
631
632   load_extension_value ("svnRevision", &val);
633   opt.svn_revision = val? atol (val) : 0;
634   xfree (val); val = NULL;
635
636   load_extension_value ("formsRevision", &val);
637   opt.forms_revision = val? atol (val) : 0;
638   xfree (val); val = NULL;
639
640   load_extension_value ("bodyAsAttachment", &val);
641   opt.body_as_attachment = val == NULL || *val != '1'? 0 : 1;
642   xfree (val); val = NULL;
643
644   /* Note, that on purpose these flags are only Registry changeable.
645      The format of the entry is a string of of "0" and "1" digits; see
646      the switch below for a description. */
647   memset (&opt.compat, 0, sizeof opt.compat);
648   load_extension_value ("compatFlags", &val);
649   if (val)
650     {
651       const char *s = val;
652       int i, x;
653
654       for (s=val, i=0; *s; s++, i++)
655         {
656           x = *s == '1';
657           switch (i)
658             {
659             case 0: opt.compat.no_msgcache = x; break;
660             case 1: opt.compat.no_pgpmime = x; break;
661             case 2: opt.compat.no_oom_write = x; break;
662             case 3: opt.compat.no_preview_info = x; break;
663             case 4: opt.compat.old_reply_hack = x; break;
664             case 5: opt.compat.auto_decrypt = x; break;
665             case 6: opt.compat.no_attestation = x; break;
666             }
667         }
668       log_debug ("Note: using compatibility flags: %s", val);
669     }
670
671   if (!warnings_shown)
672     {
673       char tmpbuf[512];
674           
675       warnings_shown = 1;
676       if (val && *val)
677         {
678           snprintf (tmpbuf, sizeof tmpbuf,
679                     _("Note: Using compatibility flags: %s"), val);
680           MessageBox (NULL, tmpbuf, _("GpgOL"), MB_ICONWARNING|MB_OK);
681         }
682       if (logfile && !opt.enable_debug)
683         {
684           snprintf (tmpbuf, sizeof tmpbuf,
685                     _("Note: Writing debug logs to\n\n\"%s\""), logfile);
686           MessageBox (NULL, tmpbuf, _("GpgOL"), MB_ICONWARNING|MB_OK);
687         }
688     }
689   xfree (val); val = NULL;
690
691 }
692
693
694 /* Write current options back to the Registry. */
695 int
696 write_options (void)
697 {
698   struct 
699   {
700     const char *name;
701     int  mode;
702     int  value;
703     char *s_val;
704   } table[] = {
705     {"enableSmime",              0, opt.enable_smime},
706     {"defaultProtocol",          3, opt.default_protocol},
707     {"encryptDefault",           0, opt.encrypt_default},
708     {"signDefault",              0, opt.sign_default},
709     {"previewDecrypt",           0, opt.preview_decrypt},
710     {"storePasswdTime",          1, opt.passwd_ttl},
711     {"encodingFormat",           1, opt.enc_format},
712     {"logFile",                  2, 0, logfile},
713     {"defaultKey",               2, 0, opt.default_key},
714     {"enableDefaultKey",         0, opt.enable_default_key},
715     {"preferHtml",               0, opt.prefer_html},
716     {"svnRevision",              1, opt.svn_revision},
717     {"formsRevision",            1, opt.forms_revision},
718     {"bodyAsAttachment",         0, opt.body_as_attachment},
719     {NULL, 0}
720   };
721   char buf[32];
722   int rc, i;
723   const char *string;
724
725   for (i=0; table[i].name; i++) 
726     {
727       switch (table[i].mode)
728         {
729         case 0:
730           string = table[i].value? "1": "0";
731           log_debug ("storing option `%s' value=`%s'\n",
732                      table[i].name, string);
733           rc = store_extension_value (table[i].name, string);
734           break;
735         case 1:
736           sprintf (buf, "%d", table[i].value);
737           log_debug ("storing option `%s' value=`%s'\n",
738                      table[i].name, buf);
739           rc = store_extension_value (table[i].name, buf);
740           break;
741         case 2:
742           string = table[i].s_val? table[i].s_val : "";
743           log_debug ("storing option `%s' value=`%s'\n",
744                      table[i].name, string);
745           rc = store_extension_value (table[i].name, string);
746           break;
747         case 3:
748           buf[0] = '0';
749           buf[1] = 0;
750           switch (opt.default_protocol)
751             {
752             case PROTOCOL_UNKNOWN: buf[0] = '0'; /* auto */ break;
753             case PROTOCOL_OPENPGP: buf[0] = '1'; break;
754             case PROTOCOL_SMIME:   buf[0] = '2'; break;
755             }
756           log_debug ("storing option `%s' value=`%s'\n",
757                      table[i].name, buf);
758           rc = store_extension_value (table[i].name, buf);
759           break;  
760
761         default:
762           rc = -1;
763           break;
764         }
765       if (rc)
766         log_error ("error storing option `%s': rc = %d\n", table[i].name, rc);
767     }
768   
769   return 0;
770 }