5de2d31124113cd01075b81481c16df9a1ee05c1
[gpgol.git] / src / olflange.cpp
1 /* olflange.cpp - Flange between Outlook and the GpgMsg class
2  *      Copyright (C) 2001 G Data Software AG, http://www.gdata.de
3  *      Copyright (C) 2004, 2005 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 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
18  * License along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  * 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <windows.h>
28
29 #ifndef INITGUID
30 #define INITGUID
31 #endif
32
33 #include <initguid.h>
34 #include "mymapi.h"
35 #include "mymapitags.h"
36 #include "myexchext.h"
37 #include "display.h"
38 #include "intern.h"
39 #include "gpgmsg.hh"
40 #include "msgcache.h"
41 #include "engine.h"
42
43 #include "olflange-ids.h"
44 #include "olflange-def.h"
45 #include "olflange.h"
46 #include "attach.h"
47
48 #define CLSIDSTR_GPGOL   "{42d30988-1a3a-11da-c687-000d6080e735}"
49 DEFINE_GUID(CLSID_GPGOL, 0x42d30988, 0x1a3a, 0x11da, 
50             0xc6, 0x87, 0x00, 0x0d, 0x60, 0x80, 0xe7, 0x35);
51
52
53 #define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
54                                      __FILE__, __func__, __LINE__); \
55                         } while (0)
56
57
58 bool g_initdll = FALSE;
59
60 static HWND show_window_hierarchy (HWND parent, int level);
61
62
63 /* Registers this module as an Exchange extension. This basically updates
64    some Registry entries. */
65 STDAPI 
66 DllRegisterServer (void)
67 {    
68   HKEY hkey, hkey2;
69   CHAR szKeyBuf[MAX_PATH+1024];
70   CHAR szEntry[MAX_PATH+512];
71   TCHAR szModuleFileName[MAX_PATH];
72   DWORD dwTemp = 0;
73   long ec;
74
75   /* Get server location. */
76   if (!GetModuleFileName(glob_hinst, szModuleFileName, MAX_PATH))
77     return E_FAIL;
78
79   lstrcpy (szKeyBuf, "Software\\Microsoft\\Exchange\\Client\\Extensions");
80   lstrcpy (szEntry, "4.0;");
81   lstrcat (szEntry, szModuleFileName);
82   lstrcat (szEntry, ";1"); /* Entry point ordinal. */
83   /* Context information string:
84      pos       context
85      1  EECONTEXT_SESSION
86      2  EECONTEXT_VIEWER
87      3  EECONTEXT_REMOTEVIEWER
88      4  EECONTEXT_SEARCHVIEWER
89      5  EECONTEXT_ADDRBOOK
90      6  EECONTEXT_SENDNOTEMESSAGE
91      7  EECONTEXT_READNOTEMESSAGE
92      8  EECONTEXT_SENDPOSTMESSAGE
93      9  EECONTEXT_READPOSTMESSAGE
94      10 EECONTEXT_READREPORTMESSAGE
95      11 EECONTEXT_SENDRESENDMESSAGE
96      12 EECONTEXT_PROPERTYSHEETS
97      13 EECONTEXT_ADVANCEDCRITERIA
98      14 EECONTEXT_TASK
99   */
100   lstrcat (szEntry, ";11000111111100"); 
101   ec = RegCreateKeyEx (HKEY_LOCAL_MACHINE, szKeyBuf, 0, NULL, 
102                        REG_OPTION_NON_VOLATILE,
103                        KEY_ALL_ACCESS, NULL, &hkey, NULL);
104   if (ec != ERROR_SUCCESS) 
105     {
106       log_debug ("DllRegisterServer failed\n");
107       return E_ACCESSDENIED;
108     }
109     
110   dwTemp = lstrlen (szEntry) + 1;
111   RegSetValueEx (hkey, "GPGol", 0, REG_SZ, (BYTE*) szEntry, dwTemp);
112
113   /* To avoid conflicts with the old G-DATA plugin and older vesions
114      of this Plugin, we remove the key used by these versions. */
115   RegDeleteValue (hkey, "GPG Exchange");
116
117   /* Set outlook update flag. */
118   strcpy (szEntry, "4.0;Outxxx.dll;7;000000000000000;0000000000;OutXXX");
119   dwTemp = lstrlen (szEntry) + 1;
120   RegSetValueEx (hkey, "Outlook Setup Extension",
121                  0, REG_SZ, (BYTE*) szEntry, dwTemp);
122   RegCloseKey (hkey);
123     
124   hkey = NULL;
125   lstrcpy (szKeyBuf, "Software\\GNU\\GPGol");
126   RegCreateKeyEx (HKEY_CURRENT_USER, szKeyBuf, 0, NULL,
127                   REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL);
128   if (hkey != NULL)
129     RegCloseKey (hkey);
130
131   hkey = NULL;
132   strcpy (szKeyBuf, "CLSID\\" CLSIDSTR_GPGOL );
133   ec = RegCreateKeyEx (HKEY_CLASSES_ROOT, szKeyBuf, 0, NULL,
134                   REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL);
135   if (ec != ERROR_SUCCESS) 
136     {
137       fprintf (stderr, "creating key `%s' failed: ec=%#lx\n", szKeyBuf, ec);
138       return E_ACCESSDENIED;
139     }
140
141   strcpy (szEntry, "GPGol - The GPG Outlook Plugin");
142   dwTemp = strlen (szEntry) + 1;
143   RegSetValueEx (hkey, NULL, 0, REG_SZ, (BYTE*)szEntry, dwTemp);
144
145   strcpy (szKeyBuf, "InprocServer32");
146   ec = RegCreateKeyEx (hkey, szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE,
147                        KEY_ALL_ACCESS, NULL, &hkey2, NULL);
148   if (ec != ERROR_SUCCESS) 
149     {
150       fprintf (stderr, "creating key `%s' failed: ec=%#lx\n", szKeyBuf, ec);
151       RegCloseKey (hkey);
152       return E_ACCESSDENIED;
153     }
154   strcpy (szEntry, szModuleFileName);
155   dwTemp = strlen (szEntry) + 1;
156   RegSetValueEx (hkey2, NULL, 0, REG_SZ, (BYTE*)szEntry, dwTemp);
157
158   strcpy (szEntry, "Neutral");
159   dwTemp = strlen (szEntry) + 1;
160   RegSetValueEx (hkey2, "ThreadingModel", 0, REG_SZ, (BYTE*)szEntry, dwTemp);
161
162   RegCloseKey (hkey2);
163   RegCloseKey (hkey);
164
165
166   log_debug ("DllRegisterServer succeeded\n");
167   return S_OK;
168 }
169
170
171 /* Unregisters this module as an Exchange extension. */
172 STDAPI 
173 DllUnregisterServer (void)
174 {
175   HKEY hkey;
176   CHAR buf[512];
177   DWORD ntemp;
178   long res;
179
180   strcpy (buf, "Software\\Microsoft\\Exchange\\Client\\Extensions");
181   /* create and open key and subkey */
182   res = RegCreateKeyEx (HKEY_LOCAL_MACHINE, buf, 0, NULL, 
183                         REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 
184                         NULL, &hkey, NULL);
185   if (res != ERROR_SUCCESS) 
186     {
187       log_debug ("DllUnregisterServer: access denied.\n");
188       return E_ACCESSDENIED;
189     }
190   RegDeleteValue (hkey, "GPGol");
191   
192   /* set outlook update flag */  
193   strcpy (buf, "4.0;Outxxx.dll;7;000000000000000;0000000000;OutXXX");
194   ntemp = strlen (buf) + 1;
195   RegSetValueEx (hkey, "Outlook Setup Extension", 0, 
196                  REG_SZ, (BYTE*) buf, ntemp);
197   RegCloseKey (hkey);
198   
199   return S_OK;
200 }
201
202 /* Wrapper around UlRelease with error checking. */
203 static void 
204 ul_release (LPVOID punk)
205 {
206   ULONG res;
207   
208   if (!punk)
209     return;
210   res = UlRelease (punk);
211   log_debug ("%s UlRelease(%p) had %lu references\n", __func__, punk, res);
212 }
213
214
215
216 /* DISPLAY a MAPI property. */
217 static void
218 show_mapi_property (LPMESSAGE message, ULONG prop, const char *propname)
219 {
220   HRESULT hr;
221   LPSPropValue lpspvFEID = NULL;
222   size_t keylen;
223   void *key;
224
225   if (!message)
226     return; /* No message: Nop. */
227
228   hr = HrGetOneProp ((LPMAPIPROP)message, prop, &lpspvFEID);
229   if (FAILED (hr))
230     {
231       log_debug ("%s: HrGetOneProp(%s) failed: hr=%#lx\n",
232                  __func__, propname, hr);
233       return;
234     }
235     
236   if ( PROP_TYPE (lpspvFEID->ulPropTag) != PT_BINARY )
237     {
238       log_debug ("%s: HrGetOneProp(%s) returned unexpected property type\n",
239                  __func__, propname);
240       MAPIFreeBuffer (lpspvFEID);
241       return;
242     }
243   keylen = lpspvFEID->Value.bin.cb;
244   key = lpspvFEID->Value.bin.lpb;
245   log_hexdump (key, keylen, "%s: %20s=", __func__, propname);
246   MAPIFreeBuffer (lpspvFEID);
247 }
248
249
250
251 /* Locate a property using the current callback LPEECB and traverse
252    down to the last element in the dot delimited NAME.  Returns the
253    Dispatch object and if R_DISPID is not NULL, the dispatch-id of the
254    last part.  Returns NULL on error.  The traversal implictly starts
255    at the object returned by the outlook application callback. */
256 static LPDISPATCH
257 find_outlook_property (LPEXCHEXTCALLBACK lpeecb,
258                        const char *name, DISPID *r_dispid)
259 {
260   HRESULT hr;
261   LPOUTLOOKEXTCALLBACK pCb;
262   LPUNKNOWN pObj;
263   LPDISPATCH pDisp;
264   DISPID dispid;
265   wchar_t *wname;
266   const char *s;
267
268   log_debug ("%s:%s: looking for `%s'\n", __FILE__, __func__, name);
269
270   pCb = NULL;
271   pObj = NULL;
272   lpeecb->QueryInterface (IID_IOutlookExtCallback, (LPVOID*)&pCb);
273   if (pCb)
274     pCb->GetObject (&pObj);
275   for (; pObj && (s = strchr (name, '.')) && s != name; name = s + 1)
276     {
277       DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
278       VARIANT vtResult;
279
280       /* Our loop expects that all objects except for the last one are
281          of class IDispatch.  This is pretty reasonable. */
282       pObj->QueryInterface (IID_IDispatch, (LPVOID*)&pDisp);
283       if (!pDisp)
284         return NULL;
285       
286       wname = utf8_to_wchar2 (name, s-name);
287       if (!wname)
288         return NULL;
289
290       hr = pDisp->GetIDsOfNames(IID_NULL, &wname, 1,
291                                 LOCALE_SYSTEM_DEFAULT, &dispid);
292       xfree (wname);
293       //log_debug ("   dispid(%.*s)=%d  (hr=0x%x)\n",
294       //           (int)(s-name), name, dispid, hr);
295       vtResult.pdispVal = NULL;
296       hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
297                           DISPATCH_METHOD, &dispparamsNoArgs,
298                           &vtResult, NULL, NULL);
299       pObj = vtResult.pdispVal;
300       /* FIXME: Check that the class of the returned object is as
301          expected.  To do this we better let GetIdsOfNames also return
302          the ID of "Class". */
303       //log_debug ("%s:%s: %.*s=%p  (hr=0x%x)\n",
304       //           __FILE__, __func__, (int)(s-name), name, pObj, hr);
305       pDisp->Release ();
306       pDisp = NULL;
307       /* Fixme: Do we need to release pObj? */
308     }
309   if (!pObj || !*name)
310     return NULL;
311
312   pObj->QueryInterface (IID_IDispatch, (LPVOID*)&pDisp);
313   if (!pDisp)
314     return NULL;
315   wname = utf8_to_wchar (name);
316   if (!wname)
317     {
318       pDisp->Release ();
319       return NULL;
320     }
321       
322   hr = pDisp->GetIDsOfNames (IID_NULL, &wname, 1,
323                              LOCALE_SYSTEM_DEFAULT, &dispid);
324   xfree (wname);
325   //log_debug ("   dispid(%s)=%d  (hr=0x%x)\n", name, dispid, hr);
326   if (r_dispid)
327     *r_dispid = dispid;
328
329   log_debug ("%s:%s:    got IDispatch=%p dispid=%u\n",
330              __FILE__, __func__, pDisp, (unsigned int)dispid);
331   return pDisp;
332 }
333
334
335 /* Return Outlook's Application object. */
336 /* FIXME: We should be able to fold most of the code into
337    find_outlook_property. */
338 #if 0 /* Not used as of now. */
339 static LPUNKNOWN
340 get_outlook_application_object (LPEXCHEXTCALLBACK lpeecb)
341 {
342   LPOUTLOOKEXTCALLBACK pCb = NULL;
343   LPDISPATCH pDisp = NULL;
344   LPUNKNOWN pUnk = NULL;
345
346   lpeecb->QueryInterface (IID_IOutlookExtCallback, (LPVOID*)&pCb);
347   if (pCb)
348     pCb->GetObject (&pUnk);
349   if (pUnk)
350     {
351       pUnk->QueryInterface (IID_IDispatch, (LPVOID*)&pDisp);
352       pUnk->Release();
353       pUnk = NULL;
354     }
355
356   if (pDisp)
357     {
358       WCHAR *name = L"Class";
359       DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
360       DISPID dispid;
361       VARIANT vtResult;
362
363       pDisp->GetIDsOfNames(IID_NULL, &name, 1,
364                            LOCALE_SYSTEM_DEFAULT, &dispid);
365       vtResult.pdispVal = NULL;
366       pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
367                     DISPATCH_PROPERTYGET, &dispparamsNoArgs,
368                     &vtResult, NULL, NULL);
369       log_debug ("%s:%s: Outlookcallback returned object of class=%d\n",
370                    __FILE__, __func__, vtResult.intVal);
371     }
372   if (pDisp)
373     {
374       WCHAR *name = L"Application";
375       DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
376       DISPID dispid;
377       VARIANT vtResult;
378       
379       pDisp->GetIDsOfNames(IID_NULL, &name, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
380       //log_debug ("   dispid(Application)=%d\n", dispid);
381       vtResult.pdispVal = NULL;
382       pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
383                      DISPATCH_METHOD, &dispparamsNoArgs,
384                      &vtResult, NULL, NULL);
385       pUnk = vtResult.pdispVal;
386       //log_debug ("%s:%s: Outlook.Application=%p\n",
387       //             __FILE__, __func__, pUnk);
388       pDisp->Release();
389       pDisp = NULL;
390     }
391   return pUnk;
392 }
393 #endif /* commented */
394
395
396 int
397 put_outlook_property (void *pEECB, const char *key, const char *value)
398 {
399   int result = -1;
400   HRESULT hr;
401   LPMDB pMDB = NULL;
402   LPMESSAGE pMessage = NULL;
403   LPDISPATCH pDisp;
404   DISPID dispid;
405   DISPID dispid_put = DISPID_PROPERTYPUT;
406   DISPPARAMS dispparams;
407   VARIANT aVariant;
408
409   if (!pEECB)
410     return -1;
411
412   hr = ((LPEXCHEXTCALLBACK)pEECB)->GetObject (&pMDB, (LPMAPIPROP *)&pMessage);
413   if (FAILED (hr))
414     log_debug ("%s:%s: getObject failed: hr=%#lx\n", __FILE__, __func__, hr);
415   else if ( (pDisp = find_outlook_property ((LPEXCHEXTCALLBACK)pEECB,
416                                             key, &dispid)))
417     {
418       dispparams.cNamedArgs = 1;
419       dispparams.rgdispidNamedArgs = &dispid_put;
420       dispparams.cArgs = 1;
421       dispparams.rgvarg = &aVariant;
422       dispparams.rgvarg[0].vt = VT_LPWSTR;
423       dispparams.rgvarg[0].bstrVal = utf8_to_wchar (value);
424       hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
425                           DISPATCH_PROPERTYPUT, &dispparams,
426                           NULL, NULL, NULL);
427       xfree (dispparams.rgvarg[0].bstrVal);
428       log_debug ("%s:%s: PROPERTYPUT(%s) result -> %#lx\n",
429                  __FILE__, __func__, key, hr);
430
431       pDisp->Release ();
432       pDisp = NULL;
433       result = 0;
434     }
435
436   ul_release (pMessage);
437   ul_release (pMDB);
438   return result;
439 }
440
441
442
443 /* The entry point which Exchange calls.  This is called for each
444    context entry. Creates a new CGPGExchExt object every time so each
445    context will get its own CGPGExchExt interface. */
446 EXTERN_C LPEXCHEXT __stdcall
447 ExchEntryPoint (void)
448 {
449   log_debug ("%s:%s: creating new CGPGExchExt object\n", __FILE__, __func__);
450   return new CGPGExchExt;
451 }
452
453
454 /* Constructor of CGPGExchExt
455
456    Initializes members and creates the interface objects for the new
457    context.  Does the DLL initialization if it has not been done
458    before. */
459 CGPGExchExt::CGPGExchExt (void)
460
461   m_lRef = 1;
462   m_lContext = 0;
463   m_hWndExchange = 0;
464   m_gpgEncrypt = FALSE;
465   m_gpgSign = FALSE;
466   m_pExchExtMessageEvents = new CGPGExchExtMessageEvents (this);
467   m_pExchExtCommands = new CGPGExchExtCommands (this);
468   m_pExchExtPropertySheets = new CGPGExchExtPropertySheets (this);
469   m_pExchExtAttachedFileEvents = new CGPGExchExtAttachedFileEvents (this);
470   if (!m_pExchExtMessageEvents || !m_pExchExtCommands
471       || !m_pExchExtPropertySheets || !m_pExchExtAttachedFileEvents)
472     out_of_core ();
473   
474   if (!g_initdll)
475     {
476       if (opt.compat.auto_decrypt)
477         watcher_init_hook ();
478       read_options ();
479       op_init ();
480       g_initdll = TRUE;
481       log_debug ("%s:%s: first time initialization done\n",
482                  __FILE__, __func__);
483     }
484 }
485
486
487 /*  Uninitializes the DLL in the session context. */
488 CGPGExchExt::~CGPGExchExt (void) 
489 {
490   log_debug ("%s:%s: cleaning up CGPGExchExt object; "
491              "context=0x%lx (%s)\n", __FILE__, __func__, 
492              m_lContext,
493              (m_lContext == EECONTEXT_SESSION?           "Session":
494               m_lContext == EECONTEXT_VIEWER?            "Viewer":
495               m_lContext == EECONTEXT_REMOTEVIEWER?      "RemoteViewer":
496               m_lContext == EECONTEXT_SEARCHVIEWER?      "SearchViewer":
497               m_lContext == EECONTEXT_ADDRBOOK?          "AddrBook" :
498               m_lContext == EECONTEXT_SENDNOTEMESSAGE?   "SendNoteMessage" :
499               m_lContext == EECONTEXT_READNOTEMESSAGE?   "ReadNoteMessage" :
500               m_lContext == EECONTEXT_SENDPOSTMESSAGE?   "SendPostMessage" :
501               m_lContext == EECONTEXT_READPOSTMESSAGE?   "ReadPostMessage" :
502               m_lContext == EECONTEXT_READREPORTMESSAGE? "ReadReportMessage" :
503               m_lContext == EECONTEXT_SENDRESENDMESSAGE? "SendResendMessage" :
504               m_lContext == EECONTEXT_PROPERTYSHEETS?    "PropertySheets" :
505               m_lContext == EECONTEXT_ADVANCEDCRITERIA?  "AdvancedCriteria" :
506               m_lContext == EECONTEXT_TASK?              "Task" : ""));
507   
508   if (m_lContext == EECONTEXT_SESSION)
509     {
510       if (g_initdll)
511         {
512           watcher_free_hook ();
513           op_deinit ();
514           write_options ();
515           g_initdll = FALSE;
516           log_debug ("%s:%s: DLL closed down\n", __FILE__, __func__);
517         }       
518     }
519
520 }
521
522
523 /* Called by Exchange to retrieve an object pointer for a named
524    interface.  This is a standard COM method.  REFIID is the ID of the
525    interface and PPVOBJ will get the address of the object pointer if
526    this class defines the requested interface.  Return value: S_OK if
527    the interface is supported, otherwise E_NOINTERFACE. */
528 STDMETHODIMP 
529 CGPGExchExt::QueryInterface(REFIID riid, LPVOID *ppvObj)
530 {
531   HRESULT hr = S_OK;
532   
533   *ppvObj = NULL;
534   
535   if ((riid == IID_IUnknown) || (riid == IID_IExchExt)) 
536     {
537       *ppvObj = (LPUNKNOWN) this;
538     }
539   else if (riid == IID_IExchExtMessageEvents) 
540     {
541       *ppvObj = (LPUNKNOWN) m_pExchExtMessageEvents;
542       m_pExchExtMessageEvents->SetContext (m_lContext);
543     }
544   else if (riid == IID_IExchExtCommands) 
545     {
546       *ppvObj = (LPUNKNOWN)m_pExchExtCommands;
547       m_pExchExtCommands->SetContext (m_lContext);
548     }
549   else if (riid == IID_IExchExtPropertySheets) 
550     {
551       if (m_lContext != EECONTEXT_PROPERTYSHEETS)
552         return E_NOINTERFACE;
553       *ppvObj = (LPUNKNOWN) m_pExchExtPropertySheets;
554     }
555   else if (riid == IID_IExchExtAttachedFileEvents)
556     {
557       *ppvObj = (LPUNKNOWN)m_pExchExtAttachedFileEvents;
558     }  
559   else
560     hr = E_NOINTERFACE;
561   
562   /* On success we need to bump up the reference counter for the
563    requested object. */
564   if (*ppvObj)
565     ((LPUNKNOWN)*ppvObj)->AddRef();
566   
567   /*log_debug("QueryInterface %d\n", __LINE__);*/
568     return hr;
569 }
570
571
572 /* Called once for each new context. Checks the Exchange extension
573    version number and the context.  Returns: S_OK if the extension
574    should used in the requested context, otherwise S_FALSE.  PEECB is
575    a pointer to Exchange extension callback function.  LCONTEXT is the
576    context code at time of being called. LFLAGS carries flags to
577    indicate whether the extension should be installed modal.
578 */
579 STDMETHODIMP 
580 CGPGExchExt::Install(LPEXCHEXTCALLBACK pEECB, ULONG lContext, ULONG lFlags)
581 {
582   ULONG lBuildVersion;
583
584   /* Save the context in an instance variable. */
585   m_lContext = lContext;
586
587   log_debug ("%s:%s: context=0x%lx (%s) flags=0x%lx\n", __FILE__, __func__, 
588                lContext,
589                (lContext == EECONTEXT_SESSION?           "Session":
590                 lContext == EECONTEXT_VIEWER?            "Viewer":
591                 lContext == EECONTEXT_REMOTEVIEWER?      "RemoteViewer":
592                 lContext == EECONTEXT_SEARCHVIEWER?      "SearchViewer":
593                 lContext == EECONTEXT_ADDRBOOK?          "AddrBook" :
594                 lContext == EECONTEXT_SENDNOTEMESSAGE?   "SendNoteMessage" :
595                 lContext == EECONTEXT_READNOTEMESSAGE?   "ReadNoteMessage" :
596                 lContext == EECONTEXT_SENDPOSTMESSAGE?   "SendPostMessage" :
597                 lContext == EECONTEXT_READPOSTMESSAGE?   "ReadPostMessage" :
598                 lContext == EECONTEXT_READREPORTMESSAGE? "ReadReportMessage" :
599                 lContext == EECONTEXT_SENDRESENDMESSAGE? "SendResendMessage" :
600                 lContext == EECONTEXT_PROPERTYSHEETS?    "PropertySheets" :
601                 lContext == EECONTEXT_ADVANCEDCRITERIA?  "AdvancedCriteria" :
602                 lContext == EECONTEXT_TASK?              "Task" : ""),
603                lFlags);
604   
605   /* Check version. */
606   pEECB->GetVersion (&lBuildVersion, EECBGV_GETBUILDVERSION);
607   if (EECBGV_BUILDVERSION_MAJOR
608       != (lBuildVersion & EECBGV_BUILDVERSION_MAJOR_MASK))
609     {
610       log_debug ("%s:%s: invalid version 0x%lx\n",
611                    __FILE__, __func__, lBuildVersion);
612       return S_FALSE;
613     }
614   
615
616   /* Check context. */
617   if (   lContext == EECONTEXT_PROPERTYSHEETS
618       || lContext == EECONTEXT_SENDNOTEMESSAGE
619       || lContext == EECONTEXT_SENDPOSTMESSAGE
620       || lContext == EECONTEXT_SENDRESENDMESSAGE
621       || lContext == EECONTEXT_READNOTEMESSAGE
622       || lContext == EECONTEXT_READPOSTMESSAGE
623       || lContext == EECONTEXT_READREPORTMESSAGE
624       || lContext == EECONTEXT_VIEWER)
625     {
626 //       LPUNKNOWN pApplication = get_outlook_application_object (pEECB);
627 //       log_debug ("%s:%s: pApplication=%p\n",
628 //                    __FILE__, __func__, pApplication);
629       return S_OK;
630     }
631   
632   log_debug ("%s:%s: can't handle this context\n", __FILE__, __func__);
633   return S_FALSE;
634 }
635
636
637
638 CGPGExchExtMessageEvents::CGPGExchExtMessageEvents 
639                                               (CGPGExchExt *pParentInterface)
640
641   m_pExchExt = pParentInterface;
642   m_lRef = 0; 
643   m_bOnSubmitActive = FALSE;
644 }
645
646
647 STDMETHODIMP 
648 CGPGExchExtMessageEvents::QueryInterface (REFIID riid, LPVOID FAR *ppvObj)
649 {   
650     *ppvObj = NULL;
651     if (riid == IID_IExchExtMessageEvents) {
652         *ppvObj = (LPVOID)this;
653         AddRef();
654         return S_OK;
655     }
656     if (riid == IID_IUnknown) {
657         *ppvObj = (LPVOID)m_pExchExt;  
658         m_pExchExt->AddRef();
659         return S_OK;
660     }
661     return E_NOINTERFACE;
662 }
663
664
665 /* Called from Exchange on reading a message.  Returns: S_FALSE to
666    signal Exchange to continue calling extensions.  PEECB is a pointer
667    to the IExchExtCallback interface. */
668 STDMETHODIMP 
669 CGPGExchExtMessageEvents::OnRead (LPEXCHEXTCALLBACK pEECB) 
670 {
671   LPMDB pMDB = NULL;
672   LPMESSAGE pMessage = NULL;
673
674   log_debug ("%s:%s: received\n", __FILE__, __func__);
675   pEECB->GetObject (&pMDB, (LPMAPIPROP *)&pMessage);
676   show_mapi_property (pMessage, PR_CONVERSATION_INDEX,"PR_CONVERSATION_INDEX");
677   ul_release (pMessage);
678   ul_release (pMDB);
679
680   return S_FALSE;
681 }
682
683
684 /* Called by Exchange after a message has been read.  Returns: S_FALSE
685    to signal Exchange to continue calling extensions.  PEECB is a
686    pointer to the IExchExtCallback interface. LFLAGS are some flags. */
687 STDMETHODIMP 
688 CGPGExchExtMessageEvents::OnReadComplete (LPEXCHEXTCALLBACK pEECB,
689                                           ULONG lFlags)
690 {
691   log_debug ("%s:%s: received\n", __FILE__, __func__);
692   if (opt.compat.preview_decryption)
693     {
694       HRESULT hr;
695       HWND hWnd = NULL;
696       LPMESSAGE pMessage = NULL;
697       LPMDB pMDB = NULL;
698
699       if (FAILED (pEECB->GetWindow (&hWnd)))
700         hWnd = NULL;
701       hr = pEECB->GetObject (&pMDB, (LPMAPIPROP *)&pMessage);
702       if (SUCCEEDED (hr))
703         {
704           GpgMsg *m = CreateGpgMsg (pMessage);
705           m->setExchangeCallback ((void*)pEECB);
706           m->setSilent (1);
707           m->decrypt (hWnd);
708           delete m;
709         }
710       ul_release (pMessage);
711       ul_release (pMDB);
712     }
713 #if 0
714   else
715     {
716       HWND hWnd = NULL;
717
718       if (FAILED (pEECB->GetWindow (&hWnd)))
719         hWnd = NULL;
720       else
721         show_window_hierarchy (hWnd, 0);
722     }
723 #endif
724
725   return S_FALSE;
726 }
727
728
729 /* Called by Exchange when a message will be written. Returns: S_FALSE
730    to signal Exchange to continue calling extensions.  PEECB is a
731    pointer to the IExchExtCallback interface. */
732 STDMETHODIMP 
733 CGPGExchExtMessageEvents::OnWrite (LPEXCHEXTCALLBACK pEECB)
734 {
735   log_debug ("%s:%s: received\n", __FILE__, __func__);
736
737   HRESULT hr;
738   LPDISPATCH pDisp;
739   DISPID dispid;
740   VARIANT aVariant;
741   DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
742   HWND hWnd = NULL;
743
744
745   /* If we are going to encrypt, check that the BodyFormat is
746      something we support.  This helps avoiding surprise by sending
747      out unencrypted messages. */
748   if (m_pExchExt->m_gpgEncrypt)
749     {
750       pDisp = find_outlook_property (pEECB, "BodyFormat", &dispid);
751       if (!pDisp)
752         {
753           log_debug ("%s:%s: BodyFormat not found\n", __FILE__, __func__);
754           m_bWriteFailed = TRUE;        
755           return E_FAIL;
756         }
757       
758       aVariant.bstrVal = NULL;
759       hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
760                           DISPATCH_PROPERTYGET, &dispparamsNoArgs,
761                           &aVariant, NULL, NULL);
762       if (hr != S_OK)
763         {
764           log_debug ("%s:%s: retrieving BodyFormat failed: %#lx",
765                      __FILE__, __func__, hr);
766           m_bWriteFailed = TRUE;        
767           pDisp->Release();
768           return E_FAIL;
769         }
770   
771       if (aVariant.vt != VT_INT && aVariant.vt != VT_I4)
772         {
773           log_debug ("%s:%s: BodyFormat is not an integer (%d)",
774                      __FILE__, __func__, aVariant.vt);
775           m_bWriteFailed = TRUE;        
776           pDisp->Release();
777           return E_FAIL;
778         }
779   
780       if (aVariant.intVal != 1)
781         {
782
783           log_debug ("%s:%s: BodyFormat is %d",
784                      __FILE__, __func__, aVariant.intVal);
785           
786           if (FAILED(pEECB->GetWindow (&hWnd)))
787             hWnd = NULL;
788           MessageBox (hWnd,
789                       "Sorry, we can only encrypt plain text messages and no\n"
790                       "HTML or RTF messages. Please make sure that only the\n"
791                       "text format has been selected.",
792                       "GPGol", MB_ICONERROR|MB_OK);
793
794           m_bWriteFailed = TRUE;        
795           pDisp->Release();
796           return E_FAIL;
797         }
798       pDisp->Release();
799       
800     }
801   
802   
803   return S_FALSE;
804 }
805
806
807 /* Called by Exchange when the data has been written to the message.
808    Encrypts and signs the message if the options are set.  PEECB is a
809    pointer to the IExchExtCallback interface.  Returns: S_FALSE to
810    signal Exchange to continue calling extensions.  We return E_FAIL
811    to signals Exchange an error; the message will not be sent.  Note
812    that we don't return S_OK because this would mean that we rolled
813    back the write operation and that no further extensions should be
814    called. */
815 STDMETHODIMP 
816 CGPGExchExtMessageEvents::OnWriteComplete (LPEXCHEXTCALLBACK pEECB,
817                                            ULONG lFlags)
818 {
819   log_debug ("%s:%s: received\n", __FILE__, __func__);
820
821   HRESULT hrReturn = S_FALSE;
822   LPMESSAGE msg = NULL;
823   LPMDB pMDB = NULL;
824   HWND hWnd = NULL;
825   int rc;
826
827   if (lFlags & (EEME_FAILED|EEME_COMPLETE_FAILED))
828     return S_FALSE; /* We don't need to rollback anything in case
829                        other extensions flagged a failire. */
830           
831   if (!m_bOnSubmitActive) /* The user is just saving the message. */
832     return S_FALSE;
833   
834   if (m_bWriteFailed)     /* Operation failed already. */
835     return S_FALSE;
836   
837   if (FAILED(pEECB->GetWindow (&hWnd)))
838     hWnd = NULL;
839
840   HRESULT hr = pEECB->GetObject (&pMDB, (LPMAPIPROP *)&msg);
841   if (SUCCEEDED (hr))
842     {
843       GpgMsg *m = CreateGpgMsg (msg);
844       m->setExchangeCallback ((void*)pEECB);
845       if (m_pExchExt->m_gpgEncrypt && m_pExchExt->m_gpgSign)
846         rc = m->signEncrypt (hWnd);
847       if (m_pExchExt->m_gpgEncrypt && !m_pExchExt->m_gpgSign)
848         rc = m->encrypt (hWnd);
849       if (!m_pExchExt->m_gpgEncrypt && m_pExchExt->m_gpgSign)
850         rc = m->sign (hWnd);
851       else
852         rc = 0;
853       delete m;
854       
855       if (rc)
856         {
857           hrReturn = E_FAIL;
858           m_bWriteFailed = TRUE;        
859         }
860     }
861
862   ul_release (msg);
863   ul_release (pMDB);
864
865   return hrReturn;
866 }
867
868 /* Called by Exchange when the user selects the "check names" command.
869    PEECB is a pointer to the IExchExtCallback interface.  Returns
870    S_FALSE to signal Exchange to continue calling extensions. */
871 STDMETHODIMP 
872 CGPGExchExtMessageEvents::OnCheckNames(LPEXCHEXTCALLBACK pEECB)
873 {
874   log_debug ("%s:%s: received\n", __FILE__, __func__);
875   return S_FALSE;
876 }
877
878
879 /* Called by Exchange when "check names" command is complete.
880    PEECB is a pointer to the IExchExtCallback interface.  Returns
881    S_FALSE to signal Exchange to continue calling extensions. */
882 STDMETHODIMP 
883 CGPGExchExtMessageEvents::OnCheckNamesComplete (LPEXCHEXTCALLBACK pEECB,
884                                                 ULONG lFlags)
885 {
886   log_debug ("%s:%s: received\n", __FILE__, __func__);
887   return S_FALSE;
888 }
889
890
891 /* Called by Exchange before the message data will be written and
892    submitted to MAPI.  PEECB is a pointer to the IExchExtCallback
893    interface.  Returns S_FALSE to signal Exchange to continue calling
894    extensions. */
895 STDMETHODIMP 
896 CGPGExchExtMessageEvents::OnSubmit (LPEXCHEXTCALLBACK pEECB)
897 {
898   log_debug ("%s:%s: received\n", __FILE__, __func__);
899   m_bOnSubmitActive = TRUE;
900   m_bWriteFailed = FALSE;
901   return S_FALSE;
902 }
903
904
905 /* Called by Echange after the message has been submitted to MAPI.
906    PEECB is a pointer to the IExchExtCallback interface. */
907 STDMETHODIMP_ (VOID) 
908 CGPGExchExtMessageEvents::OnSubmitComplete (LPEXCHEXTCALLBACK pEECB,
909                                             ULONG lFlags)
910 {
911   log_debug ("%s:%s: received\n", __FILE__, __func__);
912   m_bOnSubmitActive = FALSE; 
913 }
914
915
916
917 CGPGExchExtCommands::CGPGExchExtCommands (CGPGExchExt* pParentInterface)
918
919   m_pExchExt = pParentInterface; 
920   m_lRef = 0; 
921   m_lContext = 0; 
922   m_nCmdEncrypt = 0;  
923   m_nCmdSign = 0; 
924   m_nToolbarButtonID1 = 0; 
925   m_nToolbarButtonID2 = 0; 
926   m_nToolbarBitmap1 = 0;
927   m_nToolbarBitmap2 = 0; 
928   m_hWnd = NULL; 
929 }
930
931
932
933 STDMETHODIMP 
934 CGPGExchExtCommands::QueryInterface (REFIID riid, LPVOID FAR * ppvObj)
935 {
936     *ppvObj = NULL;
937     if ((riid == IID_IExchExtCommands) || (riid == IID_IUnknown)) {
938         *ppvObj = (LPVOID)this;
939         AddRef ();
940         return S_OK;
941     }
942     return E_NOINTERFACE;
943 }
944
945
946 static HWND
947 show_window_hierarchy (HWND parent, int level)
948 {
949   HWND child;
950
951   child = GetWindow (parent, GW_CHILD);
952   while (child)
953     {
954       char buf[1024+1];
955       char name[200];
956       int nname;
957       char *pname;
958       
959       memset (buf, 0, sizeof (buf));
960       GetWindowText (child, buf, sizeof (buf)-1);
961       nname = GetClassName (child, name, sizeof (name)-1);
962       if (nname)
963         pname = name;
964       else
965         pname = NULL;
966       log_debug ("XXX %*shwnd=%p (%s) `%s'", level*2, "", child,
967                  pname? pname:"", buf);
968       show_window_hierarchy (child, level+1);
969       child = GetNextWindow (child, GW_HWNDNEXT);       
970     }
971
972   return NULL;
973 }
974
975
976 /* Called by Exchange to install commands and toolbar buttons.  Returns
977    S_FALSE to signal Exchange to continue calling extensions. */
978 STDMETHODIMP 
979 CGPGExchExtCommands::InstallCommands (
980         LPEXCHEXTCALLBACK pEECB, // The Exchange Callback Interface.
981         HWND hWnd,               // The window handle to the main window
982                                  // of context.
983         HMENU hMenu,             // The menu handle to main menu of context.
984         UINT FAR * pnCommandIDBase,  // The base command id.
985         LPTBENTRY pTBEArray,     // The array of toolbar button entries.
986         UINT nTBECnt,            // The count of button entries in array.
987         ULONG lFlags)            // reserved
988 {
989   HRESULT hr;
990   HMENU hMenuTools;
991   m_hWnd = hWnd;
992   LPDISPATCH pDisp;
993   DISPID dispid;
994   DISPID dispid_put = DISPID_PROPERTYPUT;
995   DISPPARAMS dispparams;
996   VARIANT aVariant;
997   int force_encrypt = 0;
998   
999   log_debug ("%s:%s: context=0x%lx (%s) flags=0x%lx\n", __FILE__, __func__, 
1000              m_lContext,
1001              (m_lContext == EECONTEXT_SESSION?           "Session"          :
1002               m_lContext == EECONTEXT_VIEWER?            "Viewer"           :
1003               m_lContext == EECONTEXT_REMOTEVIEWER?      "RemoteViewer"     :
1004               m_lContext == EECONTEXT_SEARCHVIEWER?      "SearchViewer"     :
1005               m_lContext == EECONTEXT_ADDRBOOK?          "AddrBook"         :
1006               m_lContext == EECONTEXT_SENDNOTEMESSAGE?   "SendNoteMessage"  :
1007               m_lContext == EECONTEXT_READNOTEMESSAGE?   "ReadNoteMessage"  :
1008               m_lContext == EECONTEXT_SENDPOSTMESSAGE?   "SendPostMessage"  :
1009               m_lContext == EECONTEXT_READPOSTMESSAGE?   "ReadPostMessage"  :
1010               m_lContext == EECONTEXT_READREPORTMESSAGE? "ReadReportMessage":
1011               m_lContext == EECONTEXT_SENDRESENDMESSAGE? "SendResendMessage":
1012               m_lContext == EECONTEXT_PROPERTYSHEETS?    "PropertySheets"   :
1013               m_lContext == EECONTEXT_ADVANCEDCRITERIA?  "AdvancedCriteria" :
1014               m_lContext == EECONTEXT_TASK?              "Task" : ""),
1015              lFlags);
1016
1017
1018   /* Outlook 2003 sometimes displays the plaintext sometimes the
1019      orginal undecrypted text when doing a Reply.  This seems to
1020      depend on the size of the message; my guess it that only short
1021      messages are locally saved in the process and larger ones are
1022      fetyched again from the backend - or the other way around.
1023      Anyway, we can't rely on that and thus me make sure to update the
1024      Body object right here with our own copy of the plaintext.  To
1025      match the text we use the ConversationIndex property.
1026
1027      Unfortunately there seems to be no way of resetting the Saved
1028      property after updating the body, thus even without entering a
1029      single byte the user will be asked when cancelling a reply
1030      whether he really wants to do that.  
1031
1032      Note, that we can't optimize the code here by first reading the
1033      body because this would pop up the securiy window, telling the
1034      user that someone is trying to read this data.
1035   */
1036   if (m_lContext == EECONTEXT_SENDNOTEMESSAGE)
1037     {
1038       LPMDB pMDB = NULL;
1039       LPMESSAGE pMessage = NULL;
1040       
1041       /*  Note that for read and send the object returned by the
1042           outlook extension callback is of class 43 (MailItem) so we
1043           only need to ask for Body then. */
1044       hr = pEECB->GetObject (&pMDB, (LPMAPIPROP *)&pMessage);
1045       if (FAILED(hr))
1046         log_debug ("%s:%s: getObject failed: hr=%#lx\n", __FILE__,__func__,hr);
1047       else if ( !opt.compat.no_msgcache)
1048         {
1049           const char *body;
1050           char *key = NULL;
1051           size_t keylen = 0;
1052           void *refhandle = NULL;
1053      
1054           pDisp = find_outlook_property (pEECB, "ConversationIndex", &dispid);
1055           if (pDisp)
1056             {
1057               DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
1058
1059               aVariant.bstrVal = NULL;
1060               hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
1061                                   DISPATCH_PROPERTYGET, &dispparamsNoArgs,
1062                                   &aVariant, NULL, NULL);
1063               if (hr != S_OK)
1064                 log_debug ("%s:%s: retrieving ConversationIndex failed: %#lx",
1065                            __FILE__, __func__, hr);
1066               else if (aVariant.vt != VT_BSTR)
1067                 log_debug ("%s:%s: ConversationIndex is not a string (%d)",
1068                            __FILE__, __func__, aVariant.vt);
1069               else if (aVariant.bstrVal)
1070                 {
1071                   char *p;
1072
1073                   key = wchar_to_utf8 (aVariant.bstrVal);
1074                   log_debug ("%s:%s: ConversationIndex is `%s'",
1075                            __FILE__, __func__, key);
1076                   /* The keyis a hex string.  Convert it to binary. */
1077                   for (keylen=0,p=key; hexdigitp(p) && hexdigitp(p+1); p += 2)
1078                     ((unsigned char*)key)[keylen++] = xtoi_2 (p);
1079                   
1080                   /* FIXME: Do we need to free the string returned in
1081                      AVARIANT?  Check at other places too. */
1082                 }
1083
1084               pDisp->Release();
1085               pDisp = NULL;
1086             }
1087           
1088           if (key && keylen
1089               && (body = msgcache_get (key, keylen, &refhandle)) 
1090               && (pDisp = find_outlook_property (pEECB, "Body", &dispid)))
1091             {
1092 #if 1
1093               dispparams.cNamedArgs = 1;
1094               dispparams.rgdispidNamedArgs = &dispid_put;
1095               dispparams.cArgs = 1;
1096               dispparams.rgvarg = &aVariant;
1097               dispparams.rgvarg[0].vt = VT_LPWSTR;
1098               dispparams.rgvarg[0].bstrVal = utf8_to_wchar (body);
1099               hr = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
1100                                  DISPATCH_PROPERTYPUT, &dispparams,
1101                                  NULL, NULL, NULL);
1102               xfree (dispparams.rgvarg[0].bstrVal);
1103               log_debug ("%s:%s: PROPERTYPUT(body) result -> %#lx\n",
1104                          __FILE__, __func__, hr);
1105 #else
1106               log_debug ("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
1107               show_window_hierarchy (hWnd, 0);
1108 #endif
1109               pDisp->Release();
1110               pDisp = NULL;
1111               
1112               /* Because we found the plaintext in the cache we can assume
1113                  that the orginal message has been encrypted and thus we
1114                  now set a flag to make sure that by default the reply
1115                  gets encrypted too. */
1116               force_encrypt = 1;
1117             }
1118           msgcache_unref (refhandle);
1119           xfree (key);
1120         }
1121       
1122       ul_release (pMessage);
1123       ul_release (pMDB);
1124     }
1125
1126
1127
1128   /* XXX: factor out common code */
1129   if (m_lContext == EECONTEXT_READNOTEMESSAGE)
1130     {
1131       int nTBIndex;
1132       HWND hwndToolbar = NULL;
1133       CHAR szBuffer[128];
1134
1135       if (opt.compat.auto_decrypt)
1136         watcher_set_callback_ctx ((void *)pEECB);
1137       pEECB->GetMenuPos (EECMDID_ToolsCustomizeToolbar, &hMenuTools,
1138                          NULL, NULL, 0);
1139       AppendMenu (hMenuTools, MF_SEPARATOR, 0, NULL);
1140         
1141       LoadString (glob_hinst, IDS_DECRYPT_MENU_ITEM, szBuffer, 128);
1142       AppendMenu (hMenuTools, MF_BYPOSITION | MF_STRING,
1143                   *pnCommandIDBase, szBuffer);
1144
1145       m_nCmdEncrypt = *pnCommandIDBase;
1146       (*pnCommandIDBase)++;
1147         
1148       for (nTBIndex = nTBECnt-1; nTBIndex > -1; --nTBIndex)
1149         {       
1150           if (EETBID_STANDARD == pTBEArray[nTBIndex].tbid)
1151             {
1152               hwndToolbar = pTBEArray[nTBIndex].hwnd;           
1153               m_nToolbarButtonID1 = pTBEArray[nTBIndex].itbbBase;
1154               pTBEArray[nTBIndex].itbbBase++;
1155               break;            
1156             }   
1157         }
1158
1159       if (hwndToolbar)
1160         {
1161           TBADDBITMAP tbab;
1162           tbab.hInst = glob_hinst;
1163           tbab.nID = IDB_DECRYPT;
1164           m_nToolbarBitmap1 = SendMessage(hwndToolbar, TB_ADDBITMAP,
1165                                           1, (LPARAM)&tbab);
1166           m_nToolbarButtonID2 = pTBEArray[nTBIndex].itbbBase;
1167           pTBEArray[nTBIndex].itbbBase++;
1168         }
1169     }
1170
1171   if (m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
1172     {
1173       CHAR szBuffer[128];
1174       int nTBIndex;
1175       HWND hwndToolbar = NULL;
1176
1177       pEECB->GetMenuPos(EECMDID_ToolsCustomizeToolbar, &hMenuTools,
1178                         NULL, NULL, 0);
1179       AppendMenu(hMenuTools, MF_SEPARATOR, 0, NULL);
1180         
1181       LoadString(glob_hinst, IDS_ENCRYPT_MENU_ITEM, szBuffer, 128);
1182       AppendMenu(hMenuTools, MF_STRING,
1183                  *pnCommandIDBase, szBuffer);
1184
1185       m_nCmdEncrypt = *pnCommandIDBase;
1186       (*pnCommandIDBase)++;
1187
1188       LoadString(glob_hinst, IDS_SIGN_MENU_ITEM, szBuffer, 128);
1189       AppendMenu(hMenuTools, MF_STRING,
1190                  *pnCommandIDBase, szBuffer);
1191
1192       m_nCmdSign = *pnCommandIDBase;
1193       (*pnCommandIDBase)++;
1194
1195       for (nTBIndex = nTBECnt-1; nTBIndex > -1; --nTBIndex)
1196         {
1197           if (EETBID_STANDARD == pTBEArray[nTBIndex].tbid)
1198             {
1199               hwndToolbar = pTBEArray[nTBIndex].hwnd;
1200               m_nToolbarButtonID1 = pTBEArray[nTBIndex].itbbBase;
1201               pTBEArray[nTBIndex].itbbBase++;
1202               break;    
1203             }
1204         }
1205
1206       if (hwndToolbar) 
1207         {
1208           TBADDBITMAP tbab;
1209           tbab.hInst = glob_hinst;
1210           tbab.nID = IDB_ENCRYPT;
1211           m_nToolbarBitmap1 = SendMessage (hwndToolbar, TB_ADDBITMAP,
1212                                            1, (LPARAM)&tbab);
1213           m_nToolbarButtonID2 = pTBEArray[nTBIndex].itbbBase;
1214           pTBEArray[nTBIndex].itbbBase++;
1215           tbab.nID = IDB_SIGN;
1216           m_nToolbarBitmap2 = SendMessage (hwndToolbar, TB_ADDBITMAP,
1217                                            1, (LPARAM)&tbab);
1218         }
1219
1220       m_pExchExt->m_gpgEncrypt = opt.encrypt_default;
1221       m_pExchExt->m_gpgSign    = opt.sign_default;
1222       if (force_encrypt)
1223         m_pExchExt->m_gpgEncrypt = true;
1224     }
1225
1226   if (m_lContext == EECONTEXT_VIEWER) 
1227     {
1228       CHAR szBuffer[128];
1229       int nTBIndex;
1230       HWND hwndToolbar = NULL;
1231       
1232       pEECB->GetMenuPos (EECMDID_ToolsCustomizeToolbar, &hMenuTools,
1233                          NULL, NULL, 0);
1234       AppendMenu (hMenuTools, MF_SEPARATOR, 0, NULL);
1235       
1236       LoadString (glob_hinst, IDS_KEY_MANAGER, szBuffer, 128);
1237       AppendMenu (hMenuTools, MF_BYPOSITION | MF_STRING,
1238                   *pnCommandIDBase, szBuffer);
1239
1240       m_nCmdEncrypt = *pnCommandIDBase;
1241       (*pnCommandIDBase)++;     
1242       
1243       for (nTBIndex = nTBECnt-1; nTBIndex > -1; --nTBIndex)
1244         {
1245           if (EETBID_STANDARD == pTBEArray[nTBIndex].tbid) 
1246             {
1247               hwndToolbar = pTBEArray[nTBIndex].hwnd;
1248               m_nToolbarButtonID1 = pTBEArray[nTBIndex].itbbBase;
1249               pTBEArray[nTBIndex].itbbBase++;
1250               break;    
1251             }
1252         }
1253       if (hwndToolbar)
1254         {
1255           TBADDBITMAP tbab;
1256           tbab.hInst = glob_hinst;
1257           tbab.nID = IDB_KEY_MANAGER;
1258           m_nToolbarBitmap1 = SendMessage(hwndToolbar, TB_ADDBITMAP,
1259                                           1, (LPARAM)&tbab);
1260         }       
1261     }
1262   return S_FALSE;
1263 }
1264
1265
1266 /* Called by Exchange when a user selects a command.  Return value:
1267    S_OK if command is handled, otherwise S_FALSE. */
1268 STDMETHODIMP 
1269 CGPGExchExtCommands::DoCommand (
1270                   LPEXCHEXTCALLBACK pEECB, // The Exchange Callback Interface.
1271                   UINT nCommandID)         // The command id.
1272 {
1273   HRESULT hr;
1274
1275   log_debug ("%s:%s: commandID=%u (%#x)\n",
1276              __FILE__, __func__, nCommandID, nCommandID);
1277   if (nCommandID == SC_CLOSE && m_lContext == EECONTEXT_READNOTEMESSAGE)
1278     {
1279       /* This is the system close command. Replace it with our own to
1280          avoid the "save changes" query, apparently induced by OL
1281          internal syncronisation of our SetWindowText message with its
1282          own OOM (in this case Body). */
1283       LPDISPATCH pDisp;
1284       DISPID dispid;
1285       DISPPARAMS dispparams;
1286       VARIANT aVariant;
1287       
1288       pDisp = find_outlook_property (pEECB, "Close", &dispid);
1289       if (pDisp)
1290         {
1291           dispparams.rgvarg = &aVariant;
1292           dispparams.rgvarg[0].vt = VT_INT;
1293           dispparams.rgvarg[0].intVal = 1; /* olDiscard */
1294           dispparams.cArgs = 1;
1295           dispparams.cNamedArgs = 0;
1296           hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
1297                               DISPATCH_METHOD, &dispparams,
1298                               NULL, NULL, NULL);
1299           pDisp->Release();
1300           pDisp = NULL;
1301           if (hr == S_OK)
1302             {
1303               log_debug ("%s:%s: invoking Close succeeded", __FILE__,__func__);
1304               return S_OK; /* We handled the close command. */
1305             }
1306
1307           log_debug ("%s:%s: invoking Close failed: %#lx",
1308                      __FILE__, __func__, hr);
1309         }
1310
1311       /* We are not interested in the close command - pass it on. */
1312       return S_FALSE; 
1313     }
1314   else if (nCommandID == 154)
1315     {
1316       log_debug ("%s:%s: command Reply called\n", __FILE__, __func__);
1317       /* What we might want to do is to call Reply, then GetInspector
1318          and then Activate - this allows us to get full control over
1319          the quoted message and avoids the ugly msgcache. */
1320     }
1321   else if (nCommandID == 155)
1322     {
1323       log_debug ("%s:%s: command ReplyAll called\n", __FILE__, __func__);
1324     }
1325   else if (nCommandID == 156)
1326     {
1327       log_debug ("%s:%s: command Forward called\n", __FILE__, __func__);
1328     }
1329   
1330
1331
1332   if ((nCommandID != m_nCmdEncrypt) 
1333       && (nCommandID != m_nCmdSign))
1334     return S_FALSE; 
1335
1336   if (m_lContext == EECONTEXT_READNOTEMESSAGE) 
1337     {
1338       HWND hWnd = NULL;
1339       LPMESSAGE pMessage = NULL;
1340       LPMDB pMDB = NULL;
1341
1342       if (FAILED (pEECB->GetWindow (&hWnd)))
1343         hWnd = NULL;
1344 //       else
1345 //         show_window_hierarchy (hWnd, 0);
1346
1347       hr = pEECB->GetObject (&pMDB, (LPMAPIPROP *)&pMessage);
1348       if (SUCCEEDED (hr))
1349         {
1350           if (nCommandID == m_nCmdEncrypt)
1351             {
1352               GpgMsg *m = CreateGpgMsg (pMessage);
1353               m->setExchangeCallback ((void*)pEECB);
1354               m->decrypt (hWnd);
1355               delete m;
1356             }
1357         }
1358       ul_release (pMessage);
1359       ul_release (pMDB);
1360     }
1361   else if (m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
1362     {
1363       if (nCommandID == m_nCmdEncrypt)
1364         m_pExchExt->m_gpgEncrypt = !m_pExchExt->m_gpgEncrypt;
1365       if (nCommandID == m_nCmdSign)
1366         m_pExchExt->m_gpgSign = !m_pExchExt->m_gpgSign;
1367     }
1368   else if (m_lContext == EECONTEXT_VIEWER)
1369     {
1370       if (start_key_manager ())
1371         MessageBox (NULL, "Could not start Key-Manager",
1372                     "GPGol", MB_ICONERROR|MB_OK);
1373     }
1374
1375   return S_OK; 
1376 }
1377
1378 /* Called by Exchange when it receives a WM_INITMENU message, allowing
1379    the extension object to enable, disable, or update its menu
1380    commands before the user sees them. This method is called
1381    frequently and should be written in a very efficient manner. */
1382 STDMETHODIMP_(VOID) 
1383 CGPGExchExtCommands::InitMenu(LPEXCHEXTCALLBACK pEECB) 
1384 {
1385 #if 0
1386   log_debug ("%s:%s: context=0x%lx (%s)\n", __FILE__, __func__, 
1387              m_lContext,
1388              (m_lContext == EECONTEXT_SESSION?           "Session"          :
1389               m_lContext == EECONTEXT_VIEWER?            "Viewer"           :
1390               m_lContext == EECONTEXT_REMOTEVIEWER?      "RemoteViewer"     :
1391               m_lContext == EECONTEXT_SEARCHVIEWER?      "SearchViewer"     :
1392               m_lContext == EECONTEXT_ADDRBOOK?          "AddrBook"         :
1393               m_lContext == EECONTEXT_SENDNOTEMESSAGE?   "SendNoteMessage"  :
1394               m_lContext == EECONTEXT_READNOTEMESSAGE?   "ReadNoteMessage"  :
1395               m_lContext == EECONTEXT_SENDPOSTMESSAGE?   "SendPostMessage"  :
1396               m_lContext == EECONTEXT_READPOSTMESSAGE?   "ReadPostMessage"  :
1397               m_lContext == EECONTEXT_READREPORTMESSAGE? "ReadReportMessage":
1398               m_lContext == EECONTEXT_SENDRESENDMESSAGE? "SendResendMessage":
1399               m_lContext == EECONTEXT_PROPERTYSHEETS?    "PropertySheets"   :
1400               m_lContext == EECONTEXT_ADVANCEDCRITERIA?  "AdvancedCriteria" :
1401               m_lContext == EECONTEXT_TASK?              "Task" : ""));
1402 #endif
1403 }
1404
1405
1406 /* Called by Exhange when the user requests help for a menu item.
1407    Return value: S_OK when it is a menu item of this plugin and the
1408    help was shown; otherwise S_FALSE.  */
1409 STDMETHODIMP 
1410 CGPGExchExtCommands::Help (
1411         LPEXCHEXTCALLBACK pEECB, // The pointer to Exchange Callback Interface.
1412         UINT nCommandID)         // The command id.
1413 {
1414     if (m_lContext == EECONTEXT_READNOTEMESSAGE) {
1415         if (nCommandID == m_nCmdEncrypt) {
1416             CHAR szBuffer[512];
1417             CHAR szAppName[128];
1418
1419             LoadString (glob_hinst, IDS_DECRYPT_HELP, szBuffer, 511);
1420             LoadString (glob_hinst, IDS_APP_NAME, szAppName, 127);
1421             MessageBox (m_hWnd, szBuffer, szAppName, MB_OK);
1422             return S_OK;
1423         }
1424     }
1425     if (m_lContext == EECONTEXT_SENDNOTEMESSAGE) {
1426         if (nCommandID == m_nCmdEncrypt) {
1427             CHAR szBuffer[512];
1428             CHAR szAppName[128];
1429             LoadString(glob_hinst, IDS_ENCRYPT_HELP, szBuffer, 511);
1430             LoadString(glob_hinst, IDS_APP_NAME, szAppName, 127);
1431             MessageBox(m_hWnd, szBuffer, szAppName, MB_OK);     
1432             return S_OK;
1433         } 
1434         if (nCommandID == m_nCmdSign) {
1435             CHAR szBuffer[512]; 
1436             CHAR szAppName[128];        
1437             LoadString(glob_hinst, IDS_SIGN_HELP, szBuffer, 511);       
1438             LoadString(glob_hinst, IDS_APP_NAME, szAppName, 127);       
1439             MessageBox(m_hWnd, szBuffer, szAppName, MB_OK);     
1440             return S_OK;
1441         } 
1442     }
1443
1444     if (m_lContext == EECONTEXT_VIEWER) {
1445         if (nCommandID == m_nCmdEncrypt) {
1446                 CHAR szBuffer[512];
1447                 CHAR szAppName[128];
1448                 LoadString(glob_hinst, IDS_KEY_MANAGER_HELP, szBuffer, 511);
1449                 LoadString(glob_hinst, IDS_APP_NAME, szAppName, 127);
1450                 MessageBox(m_hWnd, szBuffer, szAppName, MB_OK);
1451                 return S_OK;
1452         } 
1453     }
1454
1455     return S_FALSE;
1456 }
1457
1458
1459 /* Called by Exhange to get the status bar text or the tooltip of a
1460    menu item.  Returns S_OK when it is a menu item of this plugin and
1461    the text was set; otherwise S_FALSE. */
1462 STDMETHODIMP 
1463 CGPGExchExtCommands::QueryHelpText(
1464           UINT nCommandID,  // The command id corresponding to the
1465                             //  menu item activated.
1466           ULONG lFlags,     // Identifies either EECQHT_STATUS
1467                             //  or EECQHT_TOOLTIP.
1468           LPTSTR pszText,   // A pointer to buffer to be populated 
1469                             //  with text to display.
1470           UINT nCharCnt)    // The count of characters available in psz buffer.
1471 {
1472         
1473     if (m_lContext == EECONTEXT_READNOTEMESSAGE) {
1474         if (nCommandID == m_nCmdEncrypt) {
1475             if (lFlags == EECQHT_STATUS)
1476                 LoadString (glob_hinst, IDS_DECRYPT_STATUSBAR,
1477                             pszText, nCharCnt);
1478             if (lFlags == EECQHT_TOOLTIP)
1479                 LoadString (glob_hinst, IDS_DECRYPT_TOOLTIP,
1480                             pszText, nCharCnt);
1481             return S_OK;
1482         }
1483     }
1484     if (m_lContext == EECONTEXT_SENDNOTEMESSAGE) {
1485         if (nCommandID == m_nCmdEncrypt) {
1486             if (lFlags == EECQHT_STATUS)
1487                 LoadString (glob_hinst, IDS_ENCRYPT_STATUSBAR,
1488                             pszText, nCharCnt);
1489             if (lFlags == EECQHT_TOOLTIP)
1490                 LoadString (glob_hinst, IDS_ENCRYPT_TOOLTIP,
1491                             pszText, nCharCnt);
1492             return S_OK;
1493         }
1494         if (nCommandID == m_nCmdSign) {
1495             if (lFlags == EECQHT_STATUS)
1496                 LoadString (glob_hinst, IDS_SIGN_STATUSBAR, pszText, nCharCnt);
1497             if (lFlags == EECQHT_TOOLTIP)
1498                 LoadString (glob_hinst, IDS_SIGN_TOOLTIP, pszText, nCharCnt);
1499             return S_OK;
1500         }
1501     }
1502     if (m_lContext == EECONTEXT_VIEWER) {
1503         if (nCommandID == m_nCmdEncrypt) {
1504             if (lFlags == EECQHT_STATUS)
1505                 LoadString (glob_hinst, IDS_KEY_MANAGER_STATUSBAR,
1506                             pszText, nCharCnt);
1507             if (lFlags == EECQHT_TOOLTIP)
1508                 LoadString (glob_hinst, IDS_KEY_MANAGER_TOOLTIP,
1509                             pszText, nCharCnt);
1510             return S_OK;
1511         }       
1512     }
1513     return S_FALSE;
1514 }
1515
1516
1517 /* Called by Exchange to get toolbar button infos.  Returns S_OK when
1518    it is a button of this plugin and the requested info was delivered;
1519    otherwise S_FALSE. */
1520 STDMETHODIMP 
1521 CGPGExchExtCommands::QueryButtonInfo (
1522         ULONG lToolbarID,       // The toolbar identifier.
1523         UINT nToolbarButtonID,  // The toolbar button index.
1524         LPTBBUTTON pTBB,        // A pointer to toolbar button structure
1525                                 //  (see TBBUTTON structure).
1526         LPTSTR lpszDescription, // A pointer to string describing button.
1527         UINT nCharCnt,          // The maximum size of lpsz buffer.
1528         ULONG lFlags)           // EXCHEXT_UNICODE may be specified
1529 {
1530         if (m_lContext == EECONTEXT_READNOTEMESSAGE)
1531         {
1532                 if (nToolbarButtonID == m_nToolbarButtonID1)
1533                 {
1534                         pTBB->iBitmap = m_nToolbarBitmap1;             
1535                         pTBB->idCommand = m_nCmdEncrypt;
1536                         pTBB->fsState = TBSTATE_ENABLED;
1537                         pTBB->fsStyle = TBSTYLE_BUTTON;
1538                         pTBB->dwData = 0;
1539                         pTBB->iString = -1;
1540                         LoadString(glob_hinst, IDS_DECRYPT_TOOLTIP,
1541                                    lpszDescription, nCharCnt);
1542                         return S_OK;
1543                 }
1544         }
1545         if (m_lContext == EECONTEXT_SENDNOTEMESSAGE)
1546         {
1547                 if (nToolbarButtonID == m_nToolbarButtonID1)
1548                 {
1549                         pTBB->iBitmap = m_nToolbarBitmap1;             
1550                         pTBB->idCommand = m_nCmdEncrypt;
1551                         pTBB->fsState = TBSTATE_ENABLED;
1552                         if (m_pExchExt->m_gpgEncrypt)
1553                                 pTBB->fsState |= TBSTATE_CHECKED;
1554                         pTBB->fsStyle = TBSTYLE_BUTTON | TBSTYLE_CHECK;
1555                         pTBB->dwData = 0;
1556                         pTBB->iString = -1;
1557                         LoadString(glob_hinst, IDS_ENCRYPT_TOOLTIP,
1558                                    lpszDescription, nCharCnt);
1559                         return S_OK;
1560                 }
1561                 if (nToolbarButtonID == m_nToolbarButtonID2)
1562                 {
1563                         pTBB->iBitmap = m_nToolbarBitmap2;             
1564                         pTBB->idCommand = m_nCmdSign;
1565                         pTBB->fsState = TBSTATE_ENABLED;
1566                         if (m_pExchExt->m_gpgSign)
1567                                 pTBB->fsState |= TBSTATE_CHECKED;
1568                         pTBB->fsStyle = TBSTYLE_BUTTON | TBSTYLE_CHECK;
1569                         pTBB->dwData = 0;
1570                         pTBB->iString = -1;
1571                         LoadString(glob_hinst, IDS_SIGN_TOOLTIP,
1572                                    lpszDescription, nCharCnt);
1573                         return S_OK;
1574                 }
1575         }
1576         if (m_lContext == EECONTEXT_VIEWER)
1577         {
1578                 if (nToolbarButtonID == m_nToolbarButtonID1)
1579                 {
1580                         pTBB->iBitmap = m_nToolbarBitmap1;             
1581                         pTBB->idCommand = m_nCmdEncrypt;
1582                         pTBB->fsState = TBSTATE_ENABLED;
1583                         pTBB->fsStyle = TBSTYLE_BUTTON;
1584                         pTBB->dwData = 0;
1585                         pTBB->iString = -1;
1586                         LoadString(glob_hinst, IDS_KEY_MANAGER_TOOLTIP,
1587                                    lpszDescription, nCharCnt);
1588                         return S_OK;
1589                 }
1590         }
1591
1592         return S_FALSE;
1593 }
1594
1595
1596
1597 STDMETHODIMP 
1598 CGPGExchExtCommands::ResetToolbar (ULONG lToolbarID, ULONG lFlags)
1599 {       
1600     return S_OK;
1601 }
1602
1603
1604 CGPGExchExtPropertySheets::CGPGExchExtPropertySheets (
1605                                     CGPGExchExt* pParentInterface)
1606
1607     m_pExchExt = pParentInterface;
1608     m_lRef = 0; 
1609 }
1610
1611
1612 STDMETHODIMP 
1613 CGPGExchExtPropertySheets::QueryInterface(REFIID riid, LPVOID FAR * ppvObj)
1614 {   
1615     *ppvObj = NULL;
1616     if (riid == IID_IExchExtPropertySheets) {
1617         *ppvObj = (LPVOID)this;
1618         AddRef();
1619         return S_OK;
1620     }
1621     if (riid == IID_IUnknown) {
1622         *ppvObj = (LPVOID)m_pExchExt;
1623         m_pExchExt->AddRef();
1624         return S_OK;
1625     }
1626
1627     return E_NOINTERFACE;
1628 }
1629
1630
1631 /* Called by Echange to get the maximum number of property pages which
1632    are to be added. LFLAGS is a bitmask indicating what type of
1633    property sheet is being displayed. Return value: The maximum number
1634    of custom pages for the property sheet.  */
1635 STDMETHODIMP_ (ULONG) 
1636 CGPGExchExtPropertySheets::GetMaxPageCount(ULONG lFlags)
1637 {
1638     if (lFlags == EEPS_TOOLSOPTIONS)
1639         return 1;       
1640     return 0;
1641 }
1642
1643
1644 /* Called by Exchange to request information about the property page.
1645    Return value: S_OK to signal Echange to use the pPSP
1646    information. */
1647 STDMETHODIMP 
1648 CGPGExchExtPropertySheets::GetPages(
1649         LPEXCHEXTCALLBACK pEECB, // A pointer to Exchange callback interface.
1650         ULONG lFlags,            // A  bitmask indicating what type of
1651                                  //  property sheet is being displayed.
1652         LPPROPSHEETPAGE pPSP,    // The output parm pointing to pointer
1653                                  //  to list of property sheets.
1654         ULONG FAR * plPSP)       // The output parm pointing to buffer 
1655                                  //  containing the number of property 
1656                                  //  sheets actually used.
1657 {
1658     int resid = 0;
1659
1660     switch (GetUserDefaultLangID ()) {
1661     case 0x0407:    resid = IDD_GPG_OPTIONS_DE;break;
1662     default:        resid = IDD_GPG_OPTIONS; break;
1663     }
1664
1665     pPSP[0].dwSize = sizeof (PROPSHEETPAGE);
1666     pPSP[0].dwFlags = PSP_DEFAULT | PSP_HASHELP;
1667     pPSP[0].hInstance = glob_hinst;
1668     pPSP[0].pszTemplate = MAKEINTRESOURCE (resid);
1669     pPSP[0].hIcon = NULL;     
1670     pPSP[0].pszTitle = NULL;  
1671     pPSP[0].pfnDlgProc = (DLGPROC) GPGOptionsDlgProc;
1672     pPSP[0].lParam = 0;     
1673     pPSP[0].pfnCallback = NULL;
1674     pPSP[0].pcRefParent = NULL; 
1675
1676     *plPSP = 1;
1677
1678     return S_OK;
1679 }
1680
1681
1682 STDMETHODIMP_ (VOID) 
1683 CGPGExchExtPropertySheets::FreePages (LPPROPSHEETPAGE pPSP,
1684                                       ULONG lFlags, ULONG lPSP)
1685 {
1686 }
1687
1688
1689
1690