Minor GUI fixes.
[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         {
435           MessageBox (NULL,
436                     _("You have installed a new version of GpgOL.\n"
437                       "\n"
438                       "Please open the option dialog and confirm that"
439                       " the settings are correct for your needs.  The option"
440                       " dialog can be found in the main menu at:"
441                       " Extras->Options->GpgOL.\n"),
442                       "GpgOL", MB_ICONINFORMATION|MB_OK);
443           /* Show this warning only once.  */
444           opt.svn_revision = SVN_REVISION;
445           write_options ();
446         }
447       if ( SVN_REVISION > opt.forms_revision )
448         install_forms ();
449     }
450 }
451
452
453 /*  Uninitializes the DLL in the session context. */
454 GpgolExt::~GpgolExt (void) 
455 {
456   log_debug ("%s:%s: cleaning up GpgolExt object; context=%s\n",
457              SRCNAME, __func__, ext_context_name (m_lContext));
458     
459 //   if (m_pOutlookExtItemEvents)
460 //     m_pOutlookExtItemEvents->Release ();
461   
462   if (m_lContext == EECONTEXT_SESSION)
463     {
464       if (g_initdll)
465         {
466           engine_deinit ();
467           write_options ();
468           g_initdll = FALSE;
469           log_debug ("%s:%s: DLL closed down\n", SRCNAME, __func__);
470         }       
471     }
472   
473
474 }
475
476
477 /* Called by Exchange to retrieve an object pointer for a named
478    interface.  This is a standard COM method.  REFIID is the ID of the
479    interface and PPVOBJ will get the address of the object pointer if
480    this class defines the requested interface.  Return value: S_OK if
481    the interface is supported, otherwise E_NOINTERFACE. */
482 STDMETHODIMP 
483 GpgolExt::QueryInterface(REFIID riid, LPVOID *ppvObj)
484 {
485   HRESULT hr = S_OK;
486   
487   *ppvObj = NULL;
488   
489   if ((riid == IID_IUnknown) || (riid == IID_IExchExt)) 
490     {
491       *ppvObj = (LPUNKNOWN) this;
492     }
493   else if (riid == IID_IExchExtCommands) 
494     {
495       *ppvObj = (LPUNKNOWN)m_pExchExtCommands;
496       m_pExchExtCommands->SetContext (m_lContext);
497     }
498   else if (riid == IID_IExchExtUserEvents) 
499     {
500       *ppvObj = (LPUNKNOWN) m_pExchExtUserEvents;
501       m_pExchExtUserEvents->SetContext (m_lContext);
502     }
503   else if (riid == IID_IExchExtSessionEvents) 
504     {
505       *ppvObj = (LPUNKNOWN) m_pExchExtSessionEvents;
506       m_pExchExtSessionEvents->SetContext (m_lContext);
507     }
508   else if (riid == IID_IExchExtMessageEvents) 
509     {
510       *ppvObj = (LPUNKNOWN) m_pExchExtMessageEvents;
511       m_pExchExtMessageEvents->SetContext (m_lContext);
512     }
513   else if (riid == IID_IExchExtAttachedFileEvents)
514     {
515       *ppvObj = (LPUNKNOWN)m_pExchExtAttachedFileEvents;
516     }  
517   else if (riid == IID_IExchExtPropertySheets) 
518     {
519       if (m_lContext != EECONTEXT_PROPERTYSHEETS)
520         return E_NOINTERFACE;
521       *ppvObj = (LPUNKNOWN) m_pExchExtPropertySheets;
522     }
523 //   else if (riid == IID_IOutlookExtItemEvents)
524 //     {
525 //       *ppvObj = (LPUNKNOWN)m_pOutlookExtItemEvents;
526 //     }  
527   else
528     hr = E_NOINTERFACE;
529   
530   /* On success we need to bump up the reference counter for the
531      requested object. */
532   if (*ppvObj)
533     ((LPUNKNOWN)*ppvObj)->AddRef();
534   
535   return hr;
536 }
537
538
539 /* Called once for each new context. Checks the Exchange extension
540    version number and the context.  Returns: S_OK if the extension
541    should used in the requested context, otherwise S_FALSE.  PEECB is
542    a pointer to Exchange extension callback function.  LCONTEXT is the
543    context code at time of being called. LFLAGS carries flags to
544    indicate whether the extension should be installed modal.
545 */
546 STDMETHODIMP 
547 GpgolExt::Install(LPEXCHEXTCALLBACK pEECB, ULONG lContext, ULONG lFlags)
548 {
549   static int version_shown;
550   ULONG lBuildVersion;
551   ULONG lActualVersion;
552   ULONG lVirtualVersion;
553
554
555   /* Save the context in an instance variable. */
556   m_lContext = lContext;
557
558   log_debug ("%s:%s: context=%s flags=0x%lx\n", SRCNAME, __func__,
559              ext_context_name (lContext), lFlags);
560   
561   /* Check version.  This install method is called by Outlook even
562      before the OOM interface is available, thus we need to keep on
563      checking for the olversion until we get one.  Only then we can
564      display the complete version and do a final test to see whether
565      this is a supported version. */
566   if (!olversion)
567     olversion = get_outlook_property (pEECB, "Application.Version");
568   pEECB->GetVersion (&lBuildVersion, EECBGV_GETBUILDVERSION);
569   pEECB->GetVersion (&lActualVersion, EECBGV_GETACTUALVERSION);
570   pEECB->GetVersion (&lVirtualVersion, EECBGV_GETVIRTUALVERSION);
571   if (!version_shown)
572     {
573       log_debug ("%s:%s: detected Outlook build version 0x%lx (%lu.%lu)\n",
574                  SRCNAME, __func__, lBuildVersion,
575                  (lBuildVersion & EECBGV_BUILDVERSION_MAJOR_MASK) >> 16,
576                  (lBuildVersion & EECBGV_BUILDVERSION_MINOR_MASK));
577       log_debug ("%s:%s:                 actual version 0x%lx (%u.%u.%u.%u)\n",
578                  SRCNAME, __func__, lActualVersion, 
579                  (unsigned int)((lActualVersion >> 24) & 0xff),
580                  (unsigned int)((lActualVersion >> 16) & 0xff),
581              (unsigned int)((lActualVersion >> 8) & 0xff),
582                  (unsigned int)(lActualVersion & 0xff));
583       log_debug ("%s:%s:                virtual version 0x%lx (%u.%u.%u.%u)\n",
584                  SRCNAME, __func__, lVirtualVersion, 
585                  (unsigned int)((lVirtualVersion >> 24) & 0xff),
586                  (unsigned int)((lVirtualVersion >> 16) & 0xff),
587              (unsigned int)((lVirtualVersion >> 8) & 0xff),
588                  (unsigned int)(lVirtualVersion & 0xff));
589       if (olversion)
590         {
591           log_debug ("%s:%s:                    OOM version %s\n",
592                      SRCNAME, __func__, olversion);
593           version_shown = 1;
594         }
595     }
596   
597   if (EECBGV_BUILDVERSION_MAJOR
598       != (lBuildVersion & EECBGV_BUILDVERSION_MAJOR_MASK))
599     {
600       log_debug ("%s:%s: invalid version 0x%lx\n",
601                  SRCNAME, __func__, lBuildVersion);
602       return S_FALSE;
603     }
604   
605   /* The version numbers as returned by GetVersion are the same for
606      OL2003 as well as for recent OL2002.  My guess is that this
607      version comes from the Exchange Client Extension API and that has
608      been updated in all version of OL.  Thus we also need to check
609      the version number as taken from the Outlook Object Model.  */
610   if ( (lBuildVersion & EECBGV_BUILDVERSION_MAJOR_MASK) < 13
611        || (lBuildVersion & EECBGV_BUILDVERSION_MINOR_MASK) < 1573
612        || (olversion && atoi (olversion) < 11) )
613     {
614       static int shown;
615       HWND hwnd;
616       
617       if (!shown)
618         {
619           shown = 1;
620           
621           if (FAILED(pEECB->GetWindow (&hwnd)))
622             hwnd = NULL;
623           MessageBox (hwnd,
624                       _("This version of Outlook is too old!\n\n"
625                         "At least versions of Outlook 2003 older than SP2 "
626                         "exhibit crashes when sending messages and messages "
627                         "might get stuck in the outgoing queue.\n\n"
628                         "Please update at least to SP2 before trying to send "
629                         "a message"),
630                       "GpgOL", MB_ICONSTOP|MB_OK);
631         }
632     }
633   
634
635   /* Check context. */
636   if (   lContext == EECONTEXT_PROPERTYSHEETS
637       || lContext == EECONTEXT_SENDNOTEMESSAGE
638       || lContext == EECONTEXT_SENDPOSTMESSAGE
639       || lContext == EECONTEXT_SENDRESENDMESSAGE
640       || lContext == EECONTEXT_READNOTEMESSAGE
641       || lContext == EECONTEXT_READPOSTMESSAGE
642       || lContext == EECONTEXT_READREPORTMESSAGE
643       || lContext == EECONTEXT_VIEWER
644       || lContext == EECONTEXT_SESSION)
645     {
646       return S_OK;
647     }
648   
649   log_debug ("%s:%s: can't handle this context\n", SRCNAME, __func__);
650   return S_FALSE;
651 }
652
653
654 static void
655 install_forms (void)
656 {
657   HRESULT hr;
658   LPMAPIFORMCONTAINER formcontainer = NULL;
659   static char *forms[] = 
660     {
661       "gpgol",
662       "gpgol-ms",
663       "gpgol-cs",
664       NULL,
665     };
666   int formidx;
667   LANGID langid;
668   const char *langsuffix;
669   char buffer[MAX_PATH+10];
670   char *datadir;
671   int any_error = 0;
672
673   langid = PRIMARYLANGID (LANGIDFROMLCID (GetThreadLocale ()));
674   switch (langid)
675     {
676     case LANG_GERMAN: langsuffix = "de"; break;
677     default: 
678       log_debug ("%s:%s: No forms available for primary language %d\n",
679                  SRCNAME, __func__, (int)langid);
680       /* Don't try again.  */
681       opt.forms_revision = SVN_REVISION;
682       write_options ();
683       return;
684     }
685
686   MAPIOpenLocalFormContainer (&formcontainer);
687   if (!formcontainer)
688     {
689       log_error ("%s:%s: error getting local form container\n",
690                  SRCNAME, __func__);
691       return;
692     }
693
694   datadir = get_data_dir ();
695   if (!datadir)
696     {
697       log_error ("%s:%s: error getting data directory\n",
698                  SRCNAME, __func__);
699       return;
700     }
701
702   for (formidx=0; forms[formidx]; formidx++)
703     {
704
705       snprintf (buffer, MAX_PATH, "%s\\%s_%s.cfg",
706                 datadir, forms[formidx], langsuffix);
707       hr = formcontainer->InstallForm (0, MAPIFORM_INSTALL_OVERWRITEONCONFLICT,
708                                        buffer);
709       if (hr)
710         {
711           any_error = 1;
712           log_error ("%s:%s: installing form `%s' failed: hr=%#lx\n",
713                      SRCNAME, __func__, buffer, hr);
714         }
715       else
716         log_debug ("%s:%s: form `%s' installed\n",  SRCNAME, __func__, buffer);
717     }
718
719   xfree (datadir);
720
721   if (!any_error)
722     {
723       opt.forms_revision = SVN_REVISION;
724       write_options ();
725     }
726 }
727
728