00aec6a376d1b371c452f360ad8a6d6499c8db5f
[gpgol.git] / src / olflange.cpp
1 /* olflange.cpp - Connect GpgOL to Outlook
2  *      Copyright (C) 2001 G Data Software AG, http://www.gdata.de
3  *      Copyright (C) 2004, 2005, 2007, 2008 g10 Code GmbH
4  * 
5  * This file is part of GpgOL.
6  * 
7  * GpgOL is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  * 
12  * GpgOL is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU Lesser General Public License for more details.
16  * 
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <windows.h>
26
27 #ifndef INITGUID
28 #define INITGUID
29 #endif
30
31 #include <initguid.h>
32 #include "mymapi.h"
33 #include "mymapitags.h"
34 #include "myexchext.h"
35
36 #include "common.h"
37 #include "display.h"
38 #include "msgcache.h"
39 #include "engine.h"
40 #include "mapihelp.h"
41
42 #include "olflange-def.h"
43 #include "olflange.h"
44 #include "ext-commands.h"
45 #include "user-events.h"
46 #include "session-events.h"
47 #include "message-events.h"
48 #include "property-sheets.h"
49 #include "attached-file-events.h"
50 #include "item-events.h"
51 #include "ol-ext-callback.h"
52
53 /* The GUID for this plugin.  */
54 #define CLSIDSTR_GPGOL   "{42d30988-1a3a-11da-c687-000d6080e735}"
55 DEFINE_GUID(CLSID_GPGOL, 0x42d30988, 0x1a3a, 0x11da, 
56             0xc6, 0x87, 0x00, 0x0d, 0x60, 0x80, 0xe7, 0x35);
57
58 /* For documentation: The GUID used for our custom properties: 
59    {31805ab8-3e92-11dc-879c-00061b031004}
60  */
61
62
63 #define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
64                                      SRCNAME, __func__, __LINE__); \
65                         } while (0)
66
67
68 static bool g_initdll = FALSE;
69
70 static void install_forms (void);
71
72 static char *olversion;
73
74
75 \f
76 /* Return a string for the context NO.  This never return NULL. */
77 const char *
78 ext_context_name (unsigned long no)
79 {
80   switch (no)
81     {
82     case EECONTEXT_SESSION:           return "Session";
83     case EECONTEXT_VIEWER:            return "Viewer";
84     case EECONTEXT_REMOTEVIEWER:      return "RemoteViewer";
85     case EECONTEXT_SEARCHVIEWER:      return "SearchViewer";
86     case EECONTEXT_ADDRBOOK:          return "AddrBook";
87     case EECONTEXT_SENDNOTEMESSAGE:   return "SendNoteMessage";
88     case EECONTEXT_READNOTEMESSAGE:   return "ReadNoteMessage";
89     case EECONTEXT_SENDPOSTMESSAGE:   return "SendPostMessage";
90     case EECONTEXT_READPOSTMESSAGE:   return "ReadPostMessage";
91     case EECONTEXT_READREPORTMESSAGE: return "ReadReportMessage";
92     case EECONTEXT_SENDRESENDMESSAGE: return "SendResendMessage";
93     case EECONTEXT_PROPERTYSHEETS:    return "PropertySheets";
94     case EECONTEXT_ADVANCEDCRITERIA:  return "AdvancedCriteria";
95     case EECONTEXT_TASK:              return "Task";
96     default: return "?";
97     }
98 }
99
100
101 EXTERN_C int
102 get_ol_main_version (void)
103 {
104   return olversion? atoi (olversion): 0;
105 }
106
107
108 /* Wrapper around UlRelease with error checking. */
109 // static void 
110 // ul_release (LPVOID punk, const char *func)
111 // {
112 //   ULONG res;
113   
114 //   if (!punk)
115 //     return;
116 //   res = UlRelease (punk);
117 //   if (opt.enable_debug & DBG_MEMORY)
118 //     log_debug ("%s:%s: UlRelease(%p) had %lu references\n", 
119 //                SRCNAME, func, punk, res);
120 // }
121
122
123 \f
124 /* Registers this module as an Exchange extension. This basically updates
125    some Registry entries. */
126 STDAPI 
127 DllRegisterServer (void)
128 {    
129   HKEY hkey, hkey2;
130   CHAR szKeyBuf[MAX_PATH+1024];
131   CHAR szEntry[MAX_PATH+512];
132   TCHAR szModuleFileName[MAX_PATH];
133   DWORD dwTemp = 0;
134   long ec;
135
136   /* Get server location. */
137   if (!GetModuleFileName(glob_hinst, szModuleFileName, MAX_PATH))
138     return E_FAIL;
139
140   lstrcpy (szKeyBuf, "Software\\Microsoft\\Exchange\\Client\\Extensions");
141   lstrcpy (szEntry, "4.0;");
142   lstrcat (szEntry, szModuleFileName);
143   lstrcat (szEntry, ";1"); /* Entry point ordinal. */
144   /* Context information string:
145      pos       context
146      1  EECONTEXT_SESSION
147      2  EECONTEXT_VIEWER
148      3  EECONTEXT_REMOTEVIEWER
149      4  EECONTEXT_SEARCHVIEWER
150      5  EECONTEXT_ADDRBOOK
151      6  EECONTEXT_SENDNOTEMESSAGE
152      7  EECONTEXT_READNOTEMESSAGE
153      8  EECONTEXT_SENDPOSTMESSAGE
154      9  EECONTEXT_READPOSTMESSAGE
155      a  EECONTEXT_READREPORTMESSAGE
156      b  EECONTEXT_SENDRESENDMESSAGE
157      c  EECONTEXT_PROPERTYSHEETS
158      d  EECONTEXT_ADVANCEDCRITERIA
159      e  EECONTEXT_TASK
160                    ___123456789abcde___ */                 
161   lstrcat (szEntry, ";11000111111100"); 
162   /* Interfaces to we want to hook into:
163      pos  interface
164      1    IExchExtCommands            
165      2    IExchExtUserEvents          
166      3    IExchExtSessionEvents       
167      4    IExchExtMessageEvents       
168      5    IExchExtAttachedFileEvents  
169      6    IExchExtPropertySheets      
170      7    IExchExtAdvancedCriteria    
171      -    IExchExt              
172      -    IExchExtModeless
173      -    IExchExtModelessCallback
174                    ___1234567___ */
175   lstrcat (szEntry, ";11111101"); 
176   ec = RegCreateKeyEx (HKEY_LOCAL_MACHINE, szKeyBuf, 0, NULL, 
177                        REG_OPTION_NON_VOLATILE,
178                        KEY_ALL_ACCESS, NULL, &hkey, NULL);
179   if (ec != ERROR_SUCCESS) 
180     {
181       log_debug ("DllRegisterServer failed\n");
182       return E_ACCESSDENIED;
183     }
184     
185   dwTemp = lstrlen (szEntry) + 1;
186   RegSetValueEx (hkey, "GpgOL", 0, REG_SZ, (BYTE*) szEntry, dwTemp);
187
188   /* To avoid conflicts with the old G-DATA plugin and older versions
189      of this Plugin, we remove the key used by these versions. */
190   RegDeleteValue (hkey, "GPG Exchange");
191
192   /* Set outlook update flag. */
193   /* Fixme: We have not yet implemented this hint from Microsoft:
194
195        In order for .ecf-based ECEs to be detected by Outlook on Vista,
196        one needs to delete if present:
197
198        [HKEY_CURRENT_USER\Software\Microsoft\Office\[Office Version]\Outlook]
199        "Exchange Client Extension"=
200              "4.0;Outxxx.dll;7;000000000000000;0000000000;OutXXX"
201
202        [Office Version] is 11.0 ( for OL 03 ), 12.0 ( OL 07 )...
203
204      Obviously due to HKCU, that also requires to run this code at
205      startup.  However, we don't use an ECF right now.  */
206   strcpy (szEntry, "4.0;Outxxx.dll;7;000000000000000;0000000000;OutXXX");
207   dwTemp = lstrlen (szEntry) + 1;
208   RegSetValueEx (hkey, "Outlook Setup Extension",
209                  0, REG_SZ, (BYTE*) szEntry, dwTemp);
210   RegCloseKey (hkey);
211     
212   hkey = NULL;
213   lstrcpy (szKeyBuf, "Software\\GNU\\GpgOL");
214   RegCreateKeyEx (HKEY_CURRENT_USER, szKeyBuf, 0, NULL,
215                   REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL);
216   if (hkey != NULL)
217     RegCloseKey (hkey);
218
219   hkey = NULL;
220   strcpy (szKeyBuf, "CLSID\\" CLSIDSTR_GPGOL );
221   ec = RegCreateKeyEx (HKEY_CLASSES_ROOT, szKeyBuf, 0, NULL,
222                   REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL);
223   if (ec != ERROR_SUCCESS) 
224     {
225       fprintf (stderr, "creating key `%s' failed: ec=%#lx\n", szKeyBuf, ec);
226       return E_ACCESSDENIED;
227     }
228
229   strcpy (szEntry, "GpgOL - The GnuPG Outlook Plugin");
230   dwTemp = strlen (szEntry) + 1;
231   RegSetValueEx (hkey, NULL, 0, REG_SZ, (BYTE*)szEntry, dwTemp);
232
233   strcpy (szKeyBuf, "InprocServer32");
234   ec = RegCreateKeyEx (hkey, szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE,
235                        KEY_ALL_ACCESS, NULL, &hkey2, NULL);
236   if (ec != ERROR_SUCCESS) 
237     {
238       fprintf (stderr, "creating key `%s' failed: ec=%#lx\n", szKeyBuf, ec);
239       RegCloseKey (hkey);
240       return E_ACCESSDENIED;
241     }
242   strcpy (szEntry, szModuleFileName);
243   dwTemp = strlen (szEntry) + 1;
244   RegSetValueEx (hkey2, NULL, 0, REG_SZ, (BYTE*)szEntry, dwTemp);
245
246   strcpy (szEntry, "Neutral");
247   dwTemp = strlen (szEntry) + 1;
248   RegSetValueEx (hkey2, "ThreadingModel", 0, REG_SZ, (BYTE*)szEntry, dwTemp);
249
250   RegCloseKey (hkey2);
251   RegCloseKey (hkey);
252
253
254   log_debug ("DllRegisterServer succeeded\n");
255   return S_OK;
256 }
257
258
259 /* Unregisters this module as an Exchange extension. */
260 STDAPI 
261 DllUnregisterServer (void)
262 {
263   HKEY hkey;
264   CHAR buf[MAX_PATH+1024];
265   DWORD ntemp;
266   long res;
267
268   strcpy (buf, "Software\\Microsoft\\Exchange\\Client\\Extensions");
269   /* Create and open key and subkey. */
270   res = RegCreateKeyEx (HKEY_LOCAL_MACHINE, buf, 0, NULL, 
271                         REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 
272                         NULL, &hkey, NULL);
273   if (res != ERROR_SUCCESS) 
274     {
275       log_debug ("DllUnregisterServer: access denied.\n");
276       return E_ACCESSDENIED;
277     }
278   RegDeleteValue (hkey, "GpgOL");
279   
280   /* Set outlook update flag.  */  
281   strcpy (buf, "4.0;Outxxx.dll;7;000000000000000;0000000000;OutXXX");
282   ntemp = strlen (buf) + 1;
283   RegSetValueEx (hkey, "Outlook Setup Extension", 0, 
284                  REG_SZ, (BYTE*) buf, ntemp);
285   RegCloseKey (hkey);
286
287   /* Delete CLSIDs. */
288   strcpy (buf, "CLSID\\" CLSIDSTR_GPGOL "\\InprocServer32");
289   RegDeleteKey (HKEY_CLASSES_ROOT, buf);
290   strcpy (buf, "CLSID\\" CLSIDSTR_GPGOL);
291   RegDeleteKey (HKEY_CLASSES_ROOT, buf);
292   
293   return S_OK;
294 }
295
296
297 static const char*
298 parse_version_number (const char *s, int *number)
299 {
300   int val = 0;
301
302   if (*s == '0' && digitp (s+1))
303     return NULL;  /* Leading zeros are not allowed.  */
304   for (; digitp (s); s++)
305     {
306       val *= 10;
307       val += *s - '0';
308     }
309   *number = val;
310   return val < 0 ? NULL : s;
311 }
312
313 static const char *
314 parse_version_string (const char *s, int *major, int *minor, int *micro)
315 {
316   s = parse_version_number (s, major);
317   if (!s || *s != '.')
318     return NULL;
319   s++;
320   s = parse_version_number (s, minor);
321   if (!s || *s != '.')
322     return NULL;
323   s++;
324   s = parse_version_number (s, micro);
325   if (!s)
326     return NULL;
327   return s;  /* Patchlevel.  */
328 }
329
330 static const char *
331 compare_versions (const char *my_version, const char *req_version)
332 {
333   int my_major, my_minor, my_micro;
334   int rq_major, rq_minor, rq_micro;
335   const char *my_plvl, *rq_plvl;
336
337   if (!req_version)
338     return my_version;
339   if (!my_version)
340     return NULL;
341
342   my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro);
343   if (!my_plvl)
344     return NULL;        /* Very strange: our own version is bogus.  */
345   rq_plvl = parse_version_string(req_version,
346                                  &rq_major, &rq_minor, &rq_micro);
347   if (!rq_plvl)
348     return NULL;        /* Requested version string is invalid.  */
349
350   if (my_major > rq_major
351         || (my_major == rq_major && my_minor > rq_minor)
352       || (my_major == rq_major && my_minor == rq_minor 
353           && my_micro > rq_micro)
354       || (my_major == rq_major && my_minor == rq_minor
355           && my_micro == rq_micro
356           && strcmp( my_plvl, rq_plvl ) >= 0))
357     {
358       return my_version;
359     }
360   return NULL;
361 }
362
363 /* Check that the the version of GpgOL is at minimum the requested one
364  * and return GpgOL's version string; return NULL if that condition is
365  * not met.  If a NULL is passed to this function, no check is done
366  * and the version string is simply returned.  */
367 EXTERN_C const char * __stdcall
368 gpgol_check_version (const char *req_version)
369 {
370   return compare_versions (PACKAGE_VERSION, req_version);
371 }
372
373
374
375 \f
376 /* The entry point which Exchange/Outlook calls.  This is called for
377    each context entry.  Creates a new GpgolExt object every time so
378    each context will get its own GpgolExt interface. */
379 EXTERN_C LPEXCHEXT __stdcall
380 ExchEntryPoint (void)
381 {
382   log_debug ("%s:%s: creating new GpgolExt object\n", SRCNAME, __func__);
383   return new GpgolExt;
384 }
385
386
387
388 /* Constructor of GpgolExt
389
390    Initializes members and creates the interface objects for the new
391    context.  Does the DLL initialization if it has not been done
392    before. */
393 GpgolExt::GpgolExt (void)
394
395   m_lRef = 1;
396   m_lContext = 0;
397   m_hWndExchange = 0;
398   m_protoSelection = PROTOCOL_UNKNOWN;
399   m_gpgEncrypt = FALSE;
400   m_gpgSign = FALSE;
401
402   m_pExchExtCommands           = new GpgolExtCommands (this);
403   m_pExchExtUserEvents         = new GpgolUserEvents (this);
404   m_pExchExtSessionEvents      = new GpgolSessionEvents (this);
405   m_pExchExtMessageEvents      = new GpgolMessageEvents (this);
406   m_pExchExtAttachedFileEvents = new GpgolAttachedFileEvents (this);
407   m_pExchExtPropertySheets     = new GpgolPropertySheets (this);
408 //   m_pOutlookExtItemEvents      = new GpgolItemEvents (this);
409   if (!m_pExchExtCommands
410       || !m_pExchExtUserEvents
411       || !m_pExchExtSessionEvents
412       || !m_pExchExtMessageEvents
413       || !m_pExchExtAttachedFileEvents
414       || !m_pExchExtPropertySheets
415       /*|| !m_pOutlookExtItemEvents*/)
416     out_of_core ();
417
418   /* For this class we need to bump the reference counter intially.
419      The question is why it works at all with the other stuff.  */
420 //   m_pOutlookExtItemEvents->AddRef ();
421
422   if (!g_initdll)
423     {
424       read_options ();
425       log_debug ("%s:%s: this is GpgOL %s\n", 
426                  SRCNAME, __func__, PACKAGE_VERSION);
427       log_debug ("%s:%s:   using GPGME %s\n", 
428                  SRCNAME, __func__, gpgme_check_version (NULL));
429       engine_init ();
430       g_initdll = TRUE;
431       log_debug ("%s:%s: first time initialization done\n",
432                  SRCNAME, __func__);
433       if ( SVN_REVISION > opt.svn_revision )
434         MessageBox (NULL,
435                     _("You have installed a new version of GpgOL.\n"
436                       "\n"
437                       "Please open the option dialog and confirm that"
438                       " the settings are correct for your needs.  The option"
439                       " dialog can be found in the main menu at:"
440                       " Extras->Options->GpgOL.\n"),
441                       "GpgOL", MB_ICONINFORMATION|MB_OK);
442       if ( SVN_REVISION > opt.forms_revision )
443         install_forms ();
444     }
445 }
446
447
448 /*  Uninitializes the DLL in the session context. */
449 GpgolExt::~GpgolExt (void) 
450 {
451   log_debug ("%s:%s: cleaning up GpgolExt object; context=%s\n",
452              SRCNAME, __func__, ext_context_name (m_lContext));
453     
454 //   if (m_pOutlookExtItemEvents)
455 //     m_pOutlookExtItemEvents->Release ();
456   
457   if (m_lContext == EECONTEXT_SESSION)
458     {
459       if (g_initdll)
460         {
461           engine_deinit ();
462           write_options ();
463           g_initdll = FALSE;
464           log_debug ("%s:%s: DLL closed down\n", SRCNAME, __func__);
465         }       
466     }
467   
468
469 }
470
471
472 /* Called by Exchange to retrieve an object pointer for a named
473    interface.  This is a standard COM method.  REFIID is the ID of the
474    interface and PPVOBJ will get the address of the object pointer if
475    this class defines the requested interface.  Return value: S_OK if
476    the interface is supported, otherwise E_NOINTERFACE. */
477 STDMETHODIMP 
478 GpgolExt::QueryInterface(REFIID riid, LPVOID *ppvObj)
479 {
480   HRESULT hr = S_OK;
481   
482   *ppvObj = NULL;
483   
484   if ((riid == IID_IUnknown) || (riid == IID_IExchExt)) 
485     {
486       *ppvObj = (LPUNKNOWN) this;
487     }
488   else if (riid == IID_IExchExtCommands) 
489     {
490       *ppvObj = (LPUNKNOWN)m_pExchExtCommands;
491       m_pExchExtCommands->SetContext (m_lContext);
492     }
493   else if (riid == IID_IExchExtUserEvents) 
494     {
495       *ppvObj = (LPUNKNOWN) m_pExchExtUserEvents;
496       m_pExchExtUserEvents->SetContext (m_lContext);
497     }
498   else if (riid == IID_IExchExtSessionEvents) 
499     {
500       *ppvObj = (LPUNKNOWN) m_pExchExtSessionEvents;
501       m_pExchExtSessionEvents->SetContext (m_lContext);
502     }
503   else if (riid == IID_IExchExtMessageEvents) 
504     {
505       *ppvObj = (LPUNKNOWN) m_pExchExtMessageEvents;
506       m_pExchExtMessageEvents->SetContext (m_lContext);
507     }
508   else if (riid == IID_IExchExtAttachedFileEvents)
509     {
510       *ppvObj = (LPUNKNOWN)m_pExchExtAttachedFileEvents;
511     }  
512   else if (riid == IID_IExchExtPropertySheets) 
513     {
514       if (m_lContext != EECONTEXT_PROPERTYSHEETS)
515         return E_NOINTERFACE;
516       *ppvObj = (LPUNKNOWN) m_pExchExtPropertySheets;
517     }
518 //   else if (riid == IID_IOutlookExtItemEvents)
519 //     {
520 //       *ppvObj = (LPUNKNOWN)m_pOutlookExtItemEvents;
521 //     }  
522   else
523     hr = E_NOINTERFACE;
524   
525   /* On success we need to bump up the reference counter for the
526      requested object. */
527   if (*ppvObj)
528     ((LPUNKNOWN)*ppvObj)->AddRef();
529   
530   return hr;
531 }
532
533
534 /* Called once for each new context. Checks the Exchange extension
535    version number and the context.  Returns: S_OK if the extension
536    should used in the requested context, otherwise S_FALSE.  PEECB is
537    a pointer to Exchange extension callback function.  LCONTEXT is the
538    context code at time of being called. LFLAGS carries flags to
539    indicate whether the extension should be installed modal.
540 */
541 STDMETHODIMP 
542 GpgolExt::Install(LPEXCHEXTCALLBACK pEECB, ULONG lContext, ULONG lFlags)
543 {
544   static int version_shown;
545   ULONG lBuildVersion;
546   ULONG lActualVersion;
547   ULONG lVirtualVersion;
548
549
550   /* Save the context in an instance variable. */
551   m_lContext = lContext;
552
553   log_debug ("%s:%s: context=%s flags=0x%lx\n", SRCNAME, __func__,
554              ext_context_name (lContext), lFlags);
555   
556   /* Check version.  This install method is called by Outlook even
557      before the OOM interface is available, thus we need to keep on
558      checking for the olversion until we get one.  Only then we can
559      display the complete version and do a final test to see whether
560      this is a supported version. */
561   if (!olversion)
562     olversion = get_outlook_property (pEECB, "Application.Version");
563   pEECB->GetVersion (&lBuildVersion, EECBGV_GETBUILDVERSION);
564   pEECB->GetVersion (&lActualVersion, EECBGV_GETACTUALVERSION);
565   pEECB->GetVersion (&lVirtualVersion, EECBGV_GETVIRTUALVERSION);
566   if (!version_shown)
567     {
568       log_debug ("%s:%s: detected Outlook build version 0x%lx (%lu.%lu)\n",
569                  SRCNAME, __func__, lBuildVersion,
570                  (lBuildVersion & EECBGV_BUILDVERSION_MAJOR_MASK) >> 16,
571                  (lBuildVersion & EECBGV_BUILDVERSION_MINOR_MASK));
572       log_debug ("%s:%s:                 actual version 0x%lx (%u.%u.%u.%u)\n",
573                  SRCNAME, __func__, lActualVersion, 
574                  (unsigned int)((lActualVersion >> 24) & 0xff),
575                  (unsigned int)((lActualVersion >> 16) & 0xff),
576              (unsigned int)((lActualVersion >> 8) & 0xff),
577                  (unsigned int)(lActualVersion & 0xff));
578       log_debug ("%s:%s:                virtual version 0x%lx (%u.%u.%u.%u)\n",
579                  SRCNAME, __func__, lVirtualVersion, 
580                  (unsigned int)((lVirtualVersion >> 24) & 0xff),
581                  (unsigned int)((lVirtualVersion >> 16) & 0xff),
582              (unsigned int)((lVirtualVersion >> 8) & 0xff),
583                  (unsigned int)(lVirtualVersion & 0xff));
584       if (olversion)
585         {
586           log_debug ("%s:%s:                    OOM version %s\n",
587                      SRCNAME, __func__, olversion);
588           version_shown = 1;
589         }
590     }
591   
592   if (EECBGV_BUILDVERSION_MAJOR
593       != (lBuildVersion & EECBGV_BUILDVERSION_MAJOR_MASK))
594     {
595       log_debug ("%s:%s: invalid version 0x%lx\n",
596                  SRCNAME, __func__, lBuildVersion);
597       return S_FALSE;
598     }
599   
600   /* The version numbers as returned by GetVersion are the same for
601      OL2003 as well as for recent OL2002.  My guess is that this
602      version comes from the Exchange Client Extension API and that has
603      been updated in all version of OL.  Thus we also need to check
604      the version number as taken from the Outlook Object Model.  */
605   if ( (lBuildVersion & EECBGV_BUILDVERSION_MAJOR_MASK) < 13
606        || (lBuildVersion & EECBGV_BUILDVERSION_MINOR_MASK) < 1573
607        || (olversion && atoi (olversion) < 11) )
608     {
609       static int shown;
610       HWND hwnd;
611       
612       if (!shown)
613         {
614           shown = 1;
615           
616           if (FAILED(pEECB->GetWindow (&hwnd)))
617             hwnd = NULL;
618           MessageBox (hwnd,
619                       _("This version of Outlook is too old!\n\n"
620                         "At least versions of Outlook 2003 older than SP2 "
621                         "exhibit crashes when sending messages and messages "
622                         "might get stuck in the outgoing queue.\n\n"
623                         "Please update at least to SP2 before trying to send "
624                         "a message"),
625                       "GpgOL", MB_ICONSTOP|MB_OK);
626         }
627     }
628   
629
630   /* Check context. */
631   if (   lContext == EECONTEXT_PROPERTYSHEETS
632       || lContext == EECONTEXT_SENDNOTEMESSAGE
633       || lContext == EECONTEXT_SENDPOSTMESSAGE
634       || lContext == EECONTEXT_SENDRESENDMESSAGE
635       || lContext == EECONTEXT_READNOTEMESSAGE
636       || lContext == EECONTEXT_READPOSTMESSAGE
637       || lContext == EECONTEXT_READREPORTMESSAGE
638       || lContext == EECONTEXT_VIEWER
639       || lContext == EECONTEXT_SESSION)
640     {
641       return S_OK;
642     }
643   
644   log_debug ("%s:%s: can't handle this context\n", SRCNAME, __func__);
645   return S_FALSE;
646 }
647
648
649 static void
650 install_forms (void)
651 {
652   HRESULT hr;
653   LPMAPIFORMCONTAINER formcontainer = NULL;
654   static char *forms[] = 
655     {
656       "gpgol",
657       "gpgol-ms",
658       "gpgol-cs",
659       NULL,
660     };
661   int formidx;
662   LANGID langid;
663   const char *langsuffix;
664   char buffer[MAX_PATH+10];
665   char *datadir;
666   int any_error = 0;
667
668   langid = PRIMARYLANGID (LANGIDFROMLCID (GetThreadLocale ()));
669   switch (langid)
670     {
671     case LANG_GERMAN: langsuffix = "de"; break;
672     default: 
673       log_debug ("%s:%s: No forms available for primary language %d\n",
674                  SRCNAME, __func__, (int)langid);
675       /* Don't try again.  */
676       opt.forms_revision = SVN_REVISION;
677       write_options ();
678       return;
679     }
680
681   MAPIOpenLocalFormContainer (&formcontainer);
682   if (!formcontainer)
683     {
684       log_error ("%s:%s: error getting local form container\n",
685                  SRCNAME, __func__);
686       return;
687     }
688
689   datadir = get_data_dir ();
690   if (!datadir)
691     {
692       log_error ("%s:%s: error getting data directory\n",
693                  SRCNAME, __func__);
694       return;
695     }
696
697   for (formidx=0; forms[formidx]; formidx++)
698     {
699
700       snprintf (buffer, MAX_PATH, "%s\\%s_%s.cfg",
701                 datadir, forms[formidx], langsuffix);
702       hr = formcontainer->InstallForm (0, MAPIFORM_INSTALL_OVERWRITEONCONFLICT,
703                                        buffer);
704       if (hr)
705         {
706           any_error = 1;
707           log_error ("%s:%s: installing form `%s' failed: hr=%#lx\n",
708                      SRCNAME, __func__, buffer, hr);
709         }
710       else
711         log_debug ("%s:%s: form `%s' installed\n",  SRCNAME, __func__, buffer);
712     }
713
714   xfree (datadir);
715
716   if (!any_error)
717     {
718       opt.forms_revision = SVN_REVISION;
719       write_options ();
720     }
721 }
722
723