8a20bafaabd29a6aadef6054613afae253738f04
[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   m_want_html = FALSE;
645 }
646
647
648 STDMETHODIMP 
649 CGPGExchExtMessageEvents::QueryInterface (REFIID riid, LPVOID FAR *ppvObj)
650 {   
651     *ppvObj = NULL;
652     if (riid == IID_IExchExtMessageEvents) {
653         *ppvObj = (LPVOID)this;
654         AddRef();
655         return S_OK;
656     }
657     if (riid == IID_IUnknown) {
658         *ppvObj = (LPVOID)m_pExchExt;  
659         m_pExchExt->AddRef();
660         return S_OK;
661     }
662     return E_NOINTERFACE;
663 }
664
665
666 /* Called from Exchange on reading a message.  Returns: S_FALSE to
667    signal Exchange to continue calling extensions.  PEECB is a pointer
668    to the IExchExtCallback interface. */
669 STDMETHODIMP 
670 CGPGExchExtMessageEvents::OnRead (LPEXCHEXTCALLBACK pEECB) 
671 {
672   LPMDB pMDB = NULL;
673   LPMESSAGE pMessage = NULL;
674
675   log_debug ("%s:%s: received\n", __FILE__, __func__);
676   pEECB->GetObject (&pMDB, (LPMAPIPROP *)&pMessage);
677   show_mapi_property (pMessage, PR_CONVERSATION_INDEX,"PR_CONVERSATION_INDEX");
678   ul_release (pMessage);
679   ul_release (pMDB);
680
681   return S_FALSE;
682 }
683
684
685 /* Called by Exchange after a message has been read.  Returns: S_FALSE
686    to signal Exchange to continue calling extensions.  PEECB is a
687    pointer to the IExchExtCallback interface. LFLAGS are some flags. */
688 STDMETHODIMP 
689 CGPGExchExtMessageEvents::OnReadComplete (LPEXCHEXTCALLBACK pEECB,
690                                           ULONG lFlags)
691 {
692   log_debug ("%s:%s: received\n", __FILE__, __func__);
693   if (opt.compat.preview_decryption)
694     {
695       HRESULT hr;
696       HWND hWnd = NULL;
697       LPMESSAGE pMessage = NULL;
698       LPMDB pMDB = NULL;
699
700       if (FAILED (pEECB->GetWindow (&hWnd)))
701         hWnd = NULL;
702       hr = pEECB->GetObject (&pMDB, (LPMAPIPROP *)&pMessage);
703       if (SUCCEEDED (hr))
704         {
705           GpgMsg *m = CreateGpgMsg (pMessage);
706           m->setExchangeCallback ((void*)pEECB);
707           m->setSilent (1);
708           m->decrypt (hWnd);
709           delete m;
710         }
711       ul_release (pMessage);
712       ul_release (pMDB);
713     }
714 #if 0
715   else
716     {
717       HWND hWnd = NULL;
718
719       if (FAILED (pEECB->GetWindow (&hWnd)))
720         hWnd = NULL;
721       else
722         show_window_hierarchy (hWnd, 0);
723     }
724 #endif
725
726   return S_FALSE;
727 }
728
729
730 /* Called by Exchange when a message will be written. Returns: S_FALSE
731    to signal Exchange to continue calling extensions.  PEECB is a
732    pointer to the IExchExtCallback interface. */
733 STDMETHODIMP 
734 CGPGExchExtMessageEvents::OnWrite (LPEXCHEXTCALLBACK pEECB)
735 {
736   log_debug ("%s:%s: received\n", __FILE__, __func__);
737
738   HRESULT hr;
739   LPDISPATCH pDisp;
740   DISPID dispid;
741   VARIANT aVariant;
742   DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
743   HWND hWnd = NULL;
744
745
746   /* If we are going to encrypt, check that the BodyFormat is
747      something we support.  This helps avoiding surprise by sending
748      out unencrypted messages. */
749   if (m_pExchExt->m_gpgEncrypt)
750     {
751       pDisp = find_outlook_property (pEECB, "BodyFormat", &dispid);
752       if (!pDisp)
753         {
754           log_debug ("%s:%s: BodyFormat not found\n", __FILE__, __func__);
755           m_bWriteFailed = TRUE;        
756           return E_FAIL;
757         }
758       
759       aVariant.bstrVal = NULL;
760       hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
761                           DISPATCH_PROPERTYGET, &dispparamsNoArgs,
762                           &aVariant, NULL, NULL);
763       if (hr != S_OK)
764         {
765           log_debug ("%s:%s: retrieving BodyFormat failed: %#lx",
766                      __FILE__, __func__, hr);
767           m_bWriteFailed = TRUE;        
768           pDisp->Release();
769           return E_FAIL;
770         }
771   
772       if (aVariant.vt != VT_INT && aVariant.vt != VT_I4)
773         {
774           log_debug ("%s:%s: BodyFormat is not an integer (%d)",
775                      __FILE__, __func__, aVariant.vt);
776           m_bWriteFailed = TRUE;        
777           pDisp->Release();
778           return E_FAIL;
779         }
780   
781       if (aVariant.intVal == 1)
782         m_want_html = 0;
783       else if (aVariant.intVal == 2)
784         m_want_html = 1;
785       else
786         {
787
788           log_debug ("%s:%s: BodyFormat is %d",
789                      __FILE__, __func__, aVariant.intVal);
790           
791           if (FAILED(pEECB->GetWindow (&hWnd)))
792             hWnd = NULL;
793           MessageBox (hWnd,
794                       "Sorry, we can only encrypt plain text messages and\n"
795                       "no RTF messages. Please make sure that only the text\n"
796                       "format has been selected.",
797                       "GPGol", MB_ICONERROR|MB_OK);
798
799           m_bWriteFailed = TRUE;        
800           pDisp->Release();
801           return E_FAIL;
802         }
803       pDisp->Release();
804       
805     }
806   
807   
808   return S_FALSE;
809 }
810
811
812 /* Called by Exchange when the data has been written to the message.
813    Encrypts and signs the message if the options are set.  PEECB is a
814    pointer to the IExchExtCallback interface.  Returns: S_FALSE to
815    signal Exchange to continue calling extensions.  We return E_FAIL
816    to signals Exchange an error; the message will not be sent.  Note
817    that we don't return S_OK because this would mean that we rolled
818    back the write operation and that no further extensions should be
819    called. */
820 STDMETHODIMP 
821 CGPGExchExtMessageEvents::OnWriteComplete (LPEXCHEXTCALLBACK pEECB,
822                                            ULONG lFlags)
823 {
824   log_debug ("%s:%s: received\n", __FILE__, __func__);
825
826   HRESULT hrReturn = S_FALSE;
827   LPMESSAGE msg = NULL;
828   LPMDB pMDB = NULL;
829   HWND hWnd = NULL;
830   int rc;
831
832   if (lFlags & (EEME_FAILED|EEME_COMPLETE_FAILED))
833     return S_FALSE; /* We don't need to rollback anything in case
834                        other extensions flagged a failure. */
835           
836   if (!m_bOnSubmitActive) /* The user is just saving the message. */
837     return S_FALSE;
838   
839   if (m_bWriteFailed)     /* Operation failed already. */
840     return S_FALSE;
841   
842   if (FAILED(pEECB->GetWindow (&hWnd)))
843     hWnd = NULL;
844
845   HRESULT hr = pEECB->GetObject (&pMDB, (LPMAPIPROP *)&msg);
846   if (SUCCEEDED (hr))
847     {
848       SPropTagArray proparray;
849
850       GpgMsg *m = CreateGpgMsg (msg);
851       m->setExchangeCallback ((void*)pEECB);
852       if (m_pExchExt->m_gpgEncrypt && m_pExchExt->m_gpgSign)
853         rc = m->signEncrypt (hWnd, m_want_html);
854       if (m_pExchExt->m_gpgEncrypt && !m_pExchExt->m_gpgSign)
855         rc = m->encrypt (hWnd, m_want_html);
856       if (!m_pExchExt->m_gpgEncrypt && m_pExchExt->m_gpgSign)
857         rc = m->sign (hWnd);
858       else
859         rc = 0;
860       delete m;
861
862       /* If we are encrypting we need to make sure that the other
863          format gets deleted and is not actually sent in the clear. */
864       if (m_pExchExt->m_gpgEncrypt)
865         {
866           proparray.cValues = 1;
867           proparray.aulPropTag[0] = m_want_html? PR_BODY : PR_BODY_HTML;
868           msg->DeleteProps (&proparray, NULL);
869         }
870       
871       if (rc)
872         {
873           hrReturn = E_FAIL;
874           m_bWriteFailed = TRUE;        
875
876           /* Due to an error in Outlook the error is ignored and the
877              message sent out anyway.  Thus we better delete the stuff
878              now. */
879           if (m_pExchExt->m_gpgEncrypt)
880             {
881               proparray.cValues = 1;
882               proparray.aulPropTag[0] = m_want_html? PR_BODY_HTML : PR_BODY;
883               hr = msg->DeleteProps (&proparray, NULL);
884               if (hr != S_OK)
885                 log_debug ("%s:%s: DeleteProps failed: hr=%#lx\n",
886                            __FILE__, __func__, hr);
887               /* FIXME: We should delete the atatchments too. */
888             }
889           
890         }
891     }
892
893   ul_release (msg);
894   ul_release (pMDB);
895
896   return hrReturn;
897 }
898
899 /* Called by Exchange when the user selects the "check names" command.
900    PEECB is a pointer to the IExchExtCallback interface.  Returns
901    S_FALSE to signal Exchange to continue calling extensions. */
902 STDMETHODIMP 
903 CGPGExchExtMessageEvents::OnCheckNames(LPEXCHEXTCALLBACK pEECB)
904 {
905   log_debug ("%s:%s: received\n", __FILE__, __func__);
906   return S_FALSE;
907 }
908
909
910 /* Called by Exchange when "check names" command is complete.
911    PEECB is a pointer to the IExchExtCallback interface.  Returns
912    S_FALSE to signal Exchange to continue calling extensions. */
913 STDMETHODIMP 
914 CGPGExchExtMessageEvents::OnCheckNamesComplete (LPEXCHEXTCALLBACK pEECB,
915                                                 ULONG lFlags)
916 {
917   log_debug ("%s:%s: received\n", __FILE__, __func__);
918   return S_FALSE;
919 }
920
921
922 /* Called by Exchange before the message data will be written and
923    submitted to MAPI.  PEECB is a pointer to the IExchExtCallback
924    interface.  Returns S_FALSE to signal Exchange to continue calling
925    extensions. */
926 STDMETHODIMP 
927 CGPGExchExtMessageEvents::OnSubmit (LPEXCHEXTCALLBACK pEECB)
928 {
929   log_debug ("%s:%s: received\n", __FILE__, __func__);
930   m_bOnSubmitActive = TRUE;
931   m_bWriteFailed = FALSE;
932   return S_FALSE;
933 }
934
935
936 /* Called by Echange after the message has been submitted to MAPI.
937    PEECB is a pointer to the IExchExtCallback interface. */
938 STDMETHODIMP_ (VOID) 
939 CGPGExchExtMessageEvents::OnSubmitComplete (LPEXCHEXTCALLBACK pEECB,
940                                             ULONG lFlags)
941 {
942   log_debug ("%s:%s: received\n", __FILE__, __func__);
943   m_bOnSubmitActive = FALSE; 
944 }
945
946
947
948 CGPGExchExtCommands::CGPGExchExtCommands (CGPGExchExt* pParentInterface)
949
950   m_pExchExt = pParentInterface; 
951   m_lRef = 0; 
952   m_lContext = 0; 
953   m_nCmdEncrypt = 0;  
954   m_nCmdSign = 0; 
955   m_nToolbarButtonID1 = 0; 
956   m_nToolbarButtonID2 = 0; 
957   m_nToolbarBitmap1 = 0;
958   m_nToolbarBitmap2 = 0; 
959   m_hWnd = NULL; 
960 }
961
962
963
964 STDMETHODIMP 
965 CGPGExchExtCommands::QueryInterface (REFIID riid, LPVOID FAR * ppvObj)
966 {
967     *ppvObj = NULL;
968     if ((riid == IID_IExchExtCommands) || (riid == IID_IUnknown)) {
969         *ppvObj = (LPVOID)this;
970         AddRef ();
971         return S_OK;
972     }
973     return E_NOINTERFACE;
974 }
975
976
977 static HWND
978 show_window_hierarchy (HWND parent, int level)
979 {
980   HWND child;
981
982   child = GetWindow (parent, GW_CHILD);
983   while (child)
984     {
985       char buf[1024+1];
986       char name[200];
987       int nname;
988       char *pname;
989       
990       memset (buf, 0, sizeof (buf));
991       GetWindowText (child, buf, sizeof (buf)-1);
992       nname = GetClassName (child, name, sizeof (name)-1);
993       if (nname)
994         pname = name;
995       else
996         pname = NULL;
997       log_debug ("XXX %*shwnd=%p (%s) `%s'", level*2, "", child,
998                  pname? pname:"", buf);
999       show_window_hierarchy (child, level+1);
1000       child = GetNextWindow (child, GW_HWNDNEXT);       
1001     }
1002
1003   return NULL;
1004 }
1005
1006
1007 /* Called by Exchange to install commands and toolbar buttons.  Returns
1008    S_FALSE to signal Exchange to continue calling extensions. */
1009 STDMETHODIMP 
1010 CGPGExchExtCommands::InstallCommands (
1011         LPEXCHEXTCALLBACK pEECB, // The Exchange Callback Interface.
1012         HWND hWnd,               // The window handle to the main window
1013                                  // of context.
1014         HMENU hMenu,             // The menu handle to main menu of context.
1015         UINT FAR * pnCommandIDBase,  // The base command id.
1016         LPTBENTRY pTBEArray,     // The array of toolbar button entries.
1017         UINT nTBECnt,            // The count of button entries in array.
1018         ULONG lFlags)            // reserved
1019 {
1020   HRESULT hr;
1021   HMENU hMenuTools;
1022   m_hWnd = hWnd;
1023   LPDISPATCH pDisp;
1024   DISPID dispid;
1025   DISPID dispid_put = DISPID_PROPERTYPUT;
1026   DISPPARAMS dispparams;
1027   VARIANT aVariant;
1028   int force_encrypt = 0;
1029   
1030   log_debug ("%s:%s: context=0x%lx (%s) flags=0x%lx\n", __FILE__, __func__, 
1031              m_lContext,
1032              (m_lContext == EECONTEXT_SESSION?           "Session"          :
1033               m_lContext == EECONTEXT_VIEWER?            "Viewer"           :
1034               m_lContext == EECONTEXT_REMOTEVIEWER?      "RemoteViewer"     :
1035               m_lContext == EECONTEXT_SEARCHVIEWER?      "SearchViewer"     :
1036               m_lContext == EECONTEXT_ADDRBOOK?          "AddrBook"         :
1037               m_lContext == EECONTEXT_SENDNOTEMESSAGE?   "SendNoteMessage"  :
1038               m_lContext == EECONTEXT_READNOTEMESSAGE?   "ReadNoteMessage"  :
1039               m_lContext == EECONTEXT_SENDPOSTMESSAGE?   "SendPostMessage"  :
1040               m_lContext == EECONTEXT_READPOSTMESSAGE?   "ReadPostMessage"  :
1041               m_lContext == EECONTEXT_READREPORTMESSAGE? "ReadReportMessage":
1042               m_lContext == EECONTEXT_SENDRESENDMESSAGE? "SendResendMessage":
1043               m_lContext == EECONTEXT_PROPERTYSHEETS?    "PropertySheets"   :
1044               m_lContext == EECONTEXT_ADVANCEDCRITERIA?  "AdvancedCriteria" :
1045               m_lContext == EECONTEXT_TASK?              "Task" : ""),
1046              lFlags);
1047
1048
1049   /* Outlook 2003 sometimes displays the plaintext sometimes the
1050      orginal undecrypted text when doing a Reply.  This seems to
1051      depend on the size of the message; my guess it that only short
1052      messages are locally saved in the process and larger ones are
1053      fetyched again from the backend - or the other way around.
1054      Anyway, we can't rely on that and thus me make sure to update the
1055      Body object right here with our own copy of the plaintext.  To
1056      match the text we use the ConversationIndex property.
1057
1058      Unfortunately there seems to be no way of resetting the Saved
1059      property after updating the body, thus even without entering a
1060      single byte the user will be asked when cancelling a reply
1061      whether he really wants to do that.  
1062
1063      Note, that we can't optimize the code here by first reading the
1064      body because this would pop up the securiy window, telling the
1065      user that someone is trying to read this data.
1066   */
1067   if (m_lContext == EECONTEXT_SENDNOTEMESSAGE)
1068     {
1069       LPMDB pMDB = NULL;
1070       LPMESSAGE pMessage = NULL;
1071       
1072       /*  Note that for read and send the object returned by the
1073           outlook extension callback is of class 43 (MailItem) so we
1074           only need to ask for Body then. */
1075       hr = pEECB->GetObject (&pMDB, (LPMAPIPROP *)&pMessage);
1076       if (FAILED(hr))
1077         log_debug ("%s:%s: getObject failed: hr=%#lx\n", __FILE__,__func__,hr);
1078       else if ( !opt.compat.no_msgcache)
1079         {
1080           const char *body;
1081           char *key = NULL;
1082           size_t keylen = 0;
1083           void *refhandle = NULL;
1084      
1085           pDisp = find_outlook_property (pEECB, "ConversationIndex", &dispid);
1086           if (pDisp)
1087             {
1088               DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
1089
1090               aVariant.bstrVal = NULL;
1091               hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
1092                                   DISPATCH_PROPERTYGET, &dispparamsNoArgs,
1093                                   &aVariant, NULL, NULL);
1094               if (hr != S_OK)
1095                 log_debug ("%s:%s: retrieving ConversationIndex failed: %#lx",
1096                            __FILE__, __func__, hr);
1097               else if (aVariant.vt != VT_BSTR)
1098                 log_debug ("%s:%s: ConversationIndex is not a string (%d)",
1099                            __FILE__, __func__, aVariant.vt);
1100               else if (aVariant.bstrVal)
1101                 {
1102                   char *p;
1103
1104                   key = wchar_to_utf8 (aVariant.bstrVal);
1105                   log_debug ("%s:%s: ConversationIndex is `%s'",
1106                            __FILE__, __func__, key);
1107                   /* The keyis a hex string.  Convert it to binary. */
1108                   for (keylen=0,p=key; hexdigitp(p) && hexdigitp(p+1); p += 2)
1109                     ((unsigned char*)key)[keylen++] = xtoi_2 (p);
1110                   
1111                   /* FIXME: Do we need to free the string returned in
1112                      AVARIANT?  Check at other places too. */
1113                 }
1114
1115               pDisp->Release();
1116               pDisp = NULL;
1117             }
1118           
1119           if (key && keylen
1120               && (body = msgcache_get (key, keylen, &refhandle)) 
1121               && (pDisp = find_outlook_property (pEECB, "Body", &dispid)))
1122             {
1123 #if 1
1124               dispparams.cNamedArgs = 1;
1125               dispparams.rgdispidNamedArgs = &dispid_put;
1126               dispparams.cArgs = 1;
1127               dispparams.rgvarg = &aVariant;
1128               dispparams.rgvarg[0].vt = VT_LPWSTR;
1129               dispparams.rgvarg[0].bstrVal = utf8_to_wchar (body);
1130               hr = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
1131                                  DISPATCH_PROPERTYPUT, &dispparams,
1132                                  NULL, NULL, NULL);
1133               xfree (dispparams.rgvarg[0].bstrVal);
1134               log_debug ("%s:%s: PROPERTYPUT(body) result -> %#lx\n",
1135                          __FILE__, __func__, hr);
1136 #else
1137               log_debug ("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
1138               show_window_hierarchy (hWnd, 0);
1139 #endif
1140               pDisp->Release();
1141               pDisp = NULL;
1142               
1143               /* Because we found the plaintext in the cache we can assume
1144                  that the orginal message has been encrypted and thus we
1145                  now set a flag to make sure that by default the reply
1146                  gets encrypted too. */
1147               force_encrypt = 1;
1148             }
1149           msgcache_unref (refhandle);
1150           xfree (key);
1151         }
1152       
1153       ul_release (pMessage);
1154       ul_release (pMDB);
1155     }
1156
1157
1158
1159   /* XXX: factor out common code */
1160   if (m_lContext == EECONTEXT_READNOTEMESSAGE)
1161     {
1162       int nTBIndex;
1163       HWND hwndToolbar = NULL;
1164       CHAR szBuffer[128];
1165
1166       if (opt.compat.auto_decrypt)
1167         watcher_set_callback_ctx ((void *)pEECB);
1168       pEECB->GetMenuPos (EECMDID_ToolsCustomizeToolbar, &hMenuTools,
1169                          NULL, NULL, 0);
1170       AppendMenu (hMenuTools, MF_SEPARATOR, 0, NULL);
1171         
1172       LoadString (glob_hinst, IDS_DECRYPT_MENU_ITEM, szBuffer, 128);
1173       AppendMenu (hMenuTools, MF_BYPOSITION | MF_STRING,
1174                   *pnCommandIDBase, szBuffer);
1175
1176       m_nCmdEncrypt = *pnCommandIDBase;
1177       (*pnCommandIDBase)++;
1178         
1179       for (nTBIndex = nTBECnt-1; nTBIndex > -1; --nTBIndex)
1180         {       
1181           if (EETBID_STANDARD == pTBEArray[nTBIndex].tbid)
1182             {
1183               hwndToolbar = pTBEArray[nTBIndex].hwnd;           
1184               m_nToolbarButtonID1 = pTBEArray[nTBIndex].itbbBase;
1185               pTBEArray[nTBIndex].itbbBase++;
1186               break;            
1187             }   
1188         }
1189
1190       if (hwndToolbar)
1191         {
1192           TBADDBITMAP tbab;
1193           tbab.hInst = glob_hinst;
1194           tbab.nID = IDB_DECRYPT;
1195           m_nToolbarBitmap1 = SendMessage(hwndToolbar, TB_ADDBITMAP,
1196                                           1, (LPARAM)&tbab);
1197           m_nToolbarButtonID2 = pTBEArray[nTBIndex].itbbBase;
1198           pTBEArray[nTBIndex].itbbBase++;
1199         }
1200     }
1201
1202   if (m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
1203     {
1204       CHAR szBuffer[128];
1205       int nTBIndex;
1206       HWND hwndToolbar = NULL;
1207
1208       pEECB->GetMenuPos(EECMDID_ToolsCustomizeToolbar, &hMenuTools,
1209                         NULL, NULL, 0);
1210       AppendMenu(hMenuTools, MF_SEPARATOR, 0, NULL);
1211         
1212       LoadString(glob_hinst, IDS_ENCRYPT_MENU_ITEM, szBuffer, 128);
1213       AppendMenu(hMenuTools, MF_STRING,
1214                  *pnCommandIDBase, szBuffer);
1215
1216       m_nCmdEncrypt = *pnCommandIDBase;
1217       (*pnCommandIDBase)++;
1218
1219       LoadString(glob_hinst, IDS_SIGN_MENU_ITEM, szBuffer, 128);
1220       AppendMenu(hMenuTools, MF_STRING,
1221                  *pnCommandIDBase, szBuffer);
1222
1223       m_nCmdSign = *pnCommandIDBase;
1224       (*pnCommandIDBase)++;
1225
1226       for (nTBIndex = nTBECnt-1; nTBIndex > -1; --nTBIndex)
1227         {
1228           if (EETBID_STANDARD == pTBEArray[nTBIndex].tbid)
1229             {
1230               hwndToolbar = pTBEArray[nTBIndex].hwnd;
1231               m_nToolbarButtonID1 = pTBEArray[nTBIndex].itbbBase;
1232               pTBEArray[nTBIndex].itbbBase++;
1233               break;    
1234             }
1235         }
1236
1237       if (hwndToolbar) 
1238         {
1239           TBADDBITMAP tbab;
1240           tbab.hInst = glob_hinst;
1241           tbab.nID = IDB_ENCRYPT;
1242           m_nToolbarBitmap1 = SendMessage (hwndToolbar, TB_ADDBITMAP,
1243                                            1, (LPARAM)&tbab);
1244           m_nToolbarButtonID2 = pTBEArray[nTBIndex].itbbBase;
1245           pTBEArray[nTBIndex].itbbBase++;
1246           tbab.nID = IDB_SIGN;
1247           m_nToolbarBitmap2 = SendMessage (hwndToolbar, TB_ADDBITMAP,
1248                                            1, (LPARAM)&tbab);
1249         }
1250
1251       m_pExchExt->m_gpgEncrypt = opt.encrypt_default;
1252       m_pExchExt->m_gpgSign    = opt.sign_default;
1253       if (force_encrypt)
1254         m_pExchExt->m_gpgEncrypt = true;
1255     }
1256
1257   if (m_lContext == EECONTEXT_VIEWER) 
1258     {
1259       CHAR szBuffer[128];
1260       int nTBIndex;
1261       HWND hwndToolbar = NULL;
1262       
1263       pEECB->GetMenuPos (EECMDID_ToolsCustomizeToolbar, &hMenuTools,
1264                          NULL, NULL, 0);
1265       AppendMenu (hMenuTools, MF_SEPARATOR, 0, NULL);
1266       
1267       LoadString (glob_hinst, IDS_KEY_MANAGER, szBuffer, 128);
1268       AppendMenu (hMenuTools, MF_BYPOSITION | MF_STRING,
1269                   *pnCommandIDBase, szBuffer);
1270
1271       m_nCmdEncrypt = *pnCommandIDBase;
1272       (*pnCommandIDBase)++;     
1273       
1274       for (nTBIndex = nTBECnt-1; nTBIndex > -1; --nTBIndex)
1275         {
1276           if (EETBID_STANDARD == pTBEArray[nTBIndex].tbid) 
1277             {
1278               hwndToolbar = pTBEArray[nTBIndex].hwnd;
1279               m_nToolbarButtonID1 = pTBEArray[nTBIndex].itbbBase;
1280               pTBEArray[nTBIndex].itbbBase++;
1281               break;    
1282             }
1283         }
1284       if (hwndToolbar)
1285         {
1286           TBADDBITMAP tbab;
1287           tbab.hInst = glob_hinst;
1288           tbab.nID = IDB_KEY_MANAGER;
1289           m_nToolbarBitmap1 = SendMessage(hwndToolbar, TB_ADDBITMAP,
1290                                           1, (LPARAM)&tbab);
1291         }       
1292     }
1293   return S_FALSE;
1294 }
1295
1296
1297 /* Called by Exchange when a user selects a command.  Return value:
1298    S_OK if command is handled, otherwise S_FALSE. */
1299 STDMETHODIMP 
1300 CGPGExchExtCommands::DoCommand (
1301                   LPEXCHEXTCALLBACK pEECB, // The Exchange Callback Interface.
1302                   UINT nCommandID)         // The command id.
1303 {
1304   HRESULT hr;
1305
1306   log_debug ("%s:%s: commandID=%u (%#x)\n",
1307              __FILE__, __func__, nCommandID, nCommandID);
1308   if (nCommandID == SC_CLOSE && m_lContext == EECONTEXT_READNOTEMESSAGE)
1309     {
1310       /* This is the system close command. Replace it with our own to
1311          avoid the "save changes" query, apparently induced by OL
1312          internal syncronisation of our SetWindowText message with its
1313          own OOM (in this case Body). */
1314       LPDISPATCH pDisp;
1315       DISPID dispid;
1316       DISPPARAMS dispparams;
1317       VARIANT aVariant;
1318       
1319       pDisp = find_outlook_property (pEECB, "Close", &dispid);
1320       if (pDisp)
1321         {
1322           dispparams.rgvarg = &aVariant;
1323           dispparams.rgvarg[0].vt = VT_INT;
1324           dispparams.rgvarg[0].intVal = 1; /* olDiscard */
1325           dispparams.cArgs = 1;
1326           dispparams.cNamedArgs = 0;
1327           hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
1328                               DISPATCH_METHOD, &dispparams,
1329                               NULL, NULL, NULL);
1330           pDisp->Release();
1331           pDisp = NULL;
1332           if (hr == S_OK)
1333             {
1334               log_debug ("%s:%s: invoking Close succeeded", __FILE__,__func__);
1335               return S_OK; /* We handled the close command. */
1336             }
1337
1338           log_debug ("%s:%s: invoking Close failed: %#lx",
1339                      __FILE__, __func__, hr);
1340         }
1341
1342       /* We are not interested in the close command - pass it on. */
1343       return S_FALSE; 
1344     }
1345   else if (nCommandID == 154)
1346     {
1347       log_debug ("%s:%s: command Reply called\n", __FILE__, __func__);
1348       /* What we might want to do is to call Reply, then GetInspector
1349          and then Activate - this allows us to get full control over
1350          the quoted message and avoids the ugly msgcache. */
1351     }
1352   else if (nCommandID == 155)
1353     {
1354       log_debug ("%s:%s: command ReplyAll called\n", __FILE__, __func__);
1355     }
1356   else if (nCommandID == 156)
1357     {
1358       log_debug ("%s:%s: command Forward called\n", __FILE__, __func__);
1359     }
1360   
1361
1362
1363   if ((nCommandID != m_nCmdEncrypt) 
1364       && (nCommandID != m_nCmdSign))
1365     return S_FALSE; 
1366
1367   if (m_lContext == EECONTEXT_READNOTEMESSAGE) 
1368     {
1369       HWND hWnd = NULL;
1370       LPMESSAGE pMessage = NULL;
1371       LPMDB pMDB = NULL;
1372
1373       if (FAILED (pEECB->GetWindow (&hWnd)))
1374         hWnd = NULL;
1375 //       else
1376 //         show_window_hierarchy (hWnd, 0);
1377
1378       hr = pEECB->GetObject (&pMDB, (LPMAPIPROP *)&pMessage);
1379       if (SUCCEEDED (hr))
1380         {
1381           if (nCommandID == m_nCmdEncrypt)
1382             {
1383               GpgMsg *m = CreateGpgMsg (pMessage);
1384               m->setExchangeCallback ((void*)pEECB);
1385               m->decrypt (hWnd);
1386               delete m;
1387             }
1388         }
1389       ul_release (pMessage);
1390       ul_release (pMDB);
1391     }
1392   else if (m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
1393     {
1394       if (nCommandID == m_nCmdEncrypt)
1395         m_pExchExt->m_gpgEncrypt = !m_pExchExt->m_gpgEncrypt;
1396       if (nCommandID == m_nCmdSign)
1397         m_pExchExt->m_gpgSign = !m_pExchExt->m_gpgSign;
1398     }
1399   else if (m_lContext == EECONTEXT_VIEWER)
1400     {
1401       if (start_key_manager ())
1402         MessageBox (NULL, "Could not start Key-Manager",
1403                     "GPGol", MB_ICONERROR|MB_OK);
1404     }
1405
1406   return S_OK; 
1407 }
1408
1409 /* Called by Exchange when it receives a WM_INITMENU message, allowing
1410    the extension object to enable, disable, or update its menu
1411    commands before the user sees them. This method is called
1412    frequently and should be written in a very efficient manner. */
1413 STDMETHODIMP_(VOID) 
1414 CGPGExchExtCommands::InitMenu(LPEXCHEXTCALLBACK pEECB) 
1415 {
1416 #if 0
1417   log_debug ("%s:%s: context=0x%lx (%s)\n", __FILE__, __func__, 
1418              m_lContext,
1419              (m_lContext == EECONTEXT_SESSION?           "Session"          :
1420               m_lContext == EECONTEXT_VIEWER?            "Viewer"           :
1421               m_lContext == EECONTEXT_REMOTEVIEWER?      "RemoteViewer"     :
1422               m_lContext == EECONTEXT_SEARCHVIEWER?      "SearchViewer"     :
1423               m_lContext == EECONTEXT_ADDRBOOK?          "AddrBook"         :
1424               m_lContext == EECONTEXT_SENDNOTEMESSAGE?   "SendNoteMessage"  :
1425               m_lContext == EECONTEXT_READNOTEMESSAGE?   "ReadNoteMessage"  :
1426               m_lContext == EECONTEXT_SENDPOSTMESSAGE?   "SendPostMessage"  :
1427               m_lContext == EECONTEXT_READPOSTMESSAGE?   "ReadPostMessage"  :
1428               m_lContext == EECONTEXT_READREPORTMESSAGE? "ReadReportMessage":
1429               m_lContext == EECONTEXT_SENDRESENDMESSAGE? "SendResendMessage":
1430               m_lContext == EECONTEXT_PROPERTYSHEETS?    "PropertySheets"   :
1431               m_lContext == EECONTEXT_ADVANCEDCRITERIA?  "AdvancedCriteria" :
1432               m_lContext == EECONTEXT_TASK?              "Task" : ""));
1433 #endif
1434 }
1435
1436
1437 /* Called by Exhange when the user requests help for a menu item.
1438    Return value: S_OK when it is a menu item of this plugin and the
1439    help was shown; otherwise S_FALSE.  */
1440 STDMETHODIMP 
1441 CGPGExchExtCommands::Help (
1442         LPEXCHEXTCALLBACK pEECB, // The pointer to Exchange Callback Interface.
1443         UINT nCommandID)         // The command id.
1444 {
1445     if (m_lContext == EECONTEXT_READNOTEMESSAGE) {
1446         if (nCommandID == m_nCmdEncrypt) {
1447             CHAR szBuffer[512];
1448             CHAR szAppName[128];
1449
1450             LoadString (glob_hinst, IDS_DECRYPT_HELP, szBuffer, 511);
1451             LoadString (glob_hinst, IDS_APP_NAME, szAppName, 127);
1452             MessageBox (m_hWnd, szBuffer, szAppName, MB_OK);
1453             return S_OK;
1454         }
1455     }
1456     if (m_lContext == EECONTEXT_SENDNOTEMESSAGE) {
1457         if (nCommandID == m_nCmdEncrypt) {
1458             CHAR szBuffer[512];
1459             CHAR szAppName[128];
1460             LoadString(glob_hinst, IDS_ENCRYPT_HELP, szBuffer, 511);
1461             LoadString(glob_hinst, IDS_APP_NAME, szAppName, 127);
1462             MessageBox(m_hWnd, szBuffer, szAppName, MB_OK);     
1463             return S_OK;
1464         } 
1465         if (nCommandID == m_nCmdSign) {
1466             CHAR szBuffer[512]; 
1467             CHAR szAppName[128];        
1468             LoadString(glob_hinst, IDS_SIGN_HELP, szBuffer, 511);       
1469             LoadString(glob_hinst, IDS_APP_NAME, szAppName, 127);       
1470             MessageBox(m_hWnd, szBuffer, szAppName, MB_OK);     
1471             return S_OK;
1472         } 
1473     }
1474
1475     if (m_lContext == EECONTEXT_VIEWER) {
1476         if (nCommandID == m_nCmdEncrypt) {
1477                 CHAR szBuffer[512];
1478                 CHAR szAppName[128];
1479                 LoadString(glob_hinst, IDS_KEY_MANAGER_HELP, szBuffer, 511);
1480                 LoadString(glob_hinst, IDS_APP_NAME, szAppName, 127);
1481                 MessageBox(m_hWnd, szBuffer, szAppName, MB_OK);
1482                 return S_OK;
1483         } 
1484     }
1485
1486     return S_FALSE;
1487 }
1488
1489
1490 /* Called by Exhange to get the status bar text or the tooltip of a
1491    menu item.  Returns S_OK when it is a menu item of this plugin and
1492    the text was set; otherwise S_FALSE. */
1493 STDMETHODIMP 
1494 CGPGExchExtCommands::QueryHelpText(
1495           UINT nCommandID,  // The command id corresponding to the
1496                             //  menu item activated.
1497           ULONG lFlags,     // Identifies either EECQHT_STATUS
1498                             //  or EECQHT_TOOLTIP.
1499           LPTSTR pszText,   // A pointer to buffer to be populated 
1500                             //  with text to display.
1501           UINT nCharCnt)    // The count of characters available in psz buffer.
1502 {
1503         
1504     if (m_lContext == EECONTEXT_READNOTEMESSAGE) {
1505         if (nCommandID == m_nCmdEncrypt) {
1506             if (lFlags == EECQHT_STATUS)
1507                 LoadString (glob_hinst, IDS_DECRYPT_STATUSBAR,
1508                             pszText, nCharCnt);
1509             if (lFlags == EECQHT_TOOLTIP)
1510                 LoadString (glob_hinst, IDS_DECRYPT_TOOLTIP,
1511                             pszText, nCharCnt);
1512             return S_OK;
1513         }
1514     }
1515     if (m_lContext == EECONTEXT_SENDNOTEMESSAGE) {
1516         if (nCommandID == m_nCmdEncrypt) {
1517             if (lFlags == EECQHT_STATUS)
1518                 LoadString (glob_hinst, IDS_ENCRYPT_STATUSBAR,
1519                             pszText, nCharCnt);
1520             if (lFlags == EECQHT_TOOLTIP)
1521                 LoadString (glob_hinst, IDS_ENCRYPT_TOOLTIP,
1522                             pszText, nCharCnt);
1523             return S_OK;
1524         }
1525         if (nCommandID == m_nCmdSign) {
1526             if (lFlags == EECQHT_STATUS)
1527                 LoadString (glob_hinst, IDS_SIGN_STATUSBAR, pszText, nCharCnt);
1528             if (lFlags == EECQHT_TOOLTIP)
1529                 LoadString (glob_hinst, IDS_SIGN_TOOLTIP, pszText, nCharCnt);
1530             return S_OK;
1531         }
1532     }
1533     if (m_lContext == EECONTEXT_VIEWER) {
1534         if (nCommandID == m_nCmdEncrypt) {
1535             if (lFlags == EECQHT_STATUS)
1536                 LoadString (glob_hinst, IDS_KEY_MANAGER_STATUSBAR,
1537                             pszText, nCharCnt);
1538             if (lFlags == EECQHT_TOOLTIP)
1539                 LoadString (glob_hinst, IDS_KEY_MANAGER_TOOLTIP,
1540                             pszText, nCharCnt);
1541             return S_OK;
1542         }       
1543     }
1544     return S_FALSE;
1545 }
1546
1547
1548 /* Called by Exchange to get toolbar button infos.  Returns S_OK when
1549    it is a button of this plugin and the requested info was delivered;
1550    otherwise S_FALSE. */
1551 STDMETHODIMP 
1552 CGPGExchExtCommands::QueryButtonInfo (
1553         ULONG lToolbarID,       // The toolbar identifier.
1554         UINT nToolbarButtonID,  // The toolbar button index.
1555         LPTBBUTTON pTBB,        // A pointer to toolbar button structure
1556                                 //  (see TBBUTTON structure).
1557         LPTSTR lpszDescription, // A pointer to string describing button.
1558         UINT nCharCnt,          // The maximum size of lpsz buffer.
1559         ULONG lFlags)           // EXCHEXT_UNICODE may be specified
1560 {
1561         if (m_lContext == EECONTEXT_READNOTEMESSAGE)
1562         {
1563                 if (nToolbarButtonID == m_nToolbarButtonID1)
1564                 {
1565                         pTBB->iBitmap = m_nToolbarBitmap1;             
1566                         pTBB->idCommand = m_nCmdEncrypt;
1567                         pTBB->fsState = TBSTATE_ENABLED;
1568                         pTBB->fsStyle = TBSTYLE_BUTTON;
1569                         pTBB->dwData = 0;
1570                         pTBB->iString = -1;
1571                         LoadString(glob_hinst, IDS_DECRYPT_TOOLTIP,
1572                                    lpszDescription, nCharCnt);
1573                         return S_OK;
1574                 }
1575         }
1576         if (m_lContext == EECONTEXT_SENDNOTEMESSAGE)
1577         {
1578                 if (nToolbarButtonID == m_nToolbarButtonID1)
1579                 {
1580                         pTBB->iBitmap = m_nToolbarBitmap1;             
1581                         pTBB->idCommand = m_nCmdEncrypt;
1582                         pTBB->fsState = TBSTATE_ENABLED;
1583                         if (m_pExchExt->m_gpgEncrypt)
1584                                 pTBB->fsState |= TBSTATE_CHECKED;
1585                         pTBB->fsStyle = TBSTYLE_BUTTON | TBSTYLE_CHECK;
1586                         pTBB->dwData = 0;
1587                         pTBB->iString = -1;
1588                         LoadString(glob_hinst, IDS_ENCRYPT_TOOLTIP,
1589                                    lpszDescription, nCharCnt);
1590                         return S_OK;
1591                 }
1592                 if (nToolbarButtonID == m_nToolbarButtonID2)
1593                 {
1594                         pTBB->iBitmap = m_nToolbarBitmap2;             
1595                         pTBB->idCommand = m_nCmdSign;
1596                         pTBB->fsState = TBSTATE_ENABLED;
1597                         if (m_pExchExt->m_gpgSign)
1598                                 pTBB->fsState |= TBSTATE_CHECKED;
1599                         pTBB->fsStyle = TBSTYLE_BUTTON | TBSTYLE_CHECK;
1600                         pTBB->dwData = 0;
1601                         pTBB->iString = -1;
1602                         LoadString(glob_hinst, IDS_SIGN_TOOLTIP,
1603                                    lpszDescription, nCharCnt);
1604                         return S_OK;
1605                 }
1606         }
1607         if (m_lContext == EECONTEXT_VIEWER)
1608         {
1609                 if (nToolbarButtonID == m_nToolbarButtonID1)
1610                 {
1611                         pTBB->iBitmap = m_nToolbarBitmap1;             
1612                         pTBB->idCommand = m_nCmdEncrypt;
1613                         pTBB->fsState = TBSTATE_ENABLED;
1614                         pTBB->fsStyle = TBSTYLE_BUTTON;
1615                         pTBB->dwData = 0;
1616                         pTBB->iString = -1;
1617                         LoadString(glob_hinst, IDS_KEY_MANAGER_TOOLTIP,
1618                                    lpszDescription, nCharCnt);
1619                         return S_OK;
1620                 }
1621         }
1622
1623         return S_FALSE;
1624 }
1625
1626
1627
1628 STDMETHODIMP 
1629 CGPGExchExtCommands::ResetToolbar (ULONG lToolbarID, ULONG lFlags)
1630 {       
1631     return S_OK;
1632 }
1633
1634
1635 CGPGExchExtPropertySheets::CGPGExchExtPropertySheets (
1636                                     CGPGExchExt* pParentInterface)
1637
1638     m_pExchExt = pParentInterface;
1639     m_lRef = 0; 
1640 }
1641
1642
1643 STDMETHODIMP 
1644 CGPGExchExtPropertySheets::QueryInterface(REFIID riid, LPVOID FAR * ppvObj)
1645 {   
1646     *ppvObj = NULL;
1647     if (riid == IID_IExchExtPropertySheets) {
1648         *ppvObj = (LPVOID)this;
1649         AddRef();
1650         return S_OK;
1651     }
1652     if (riid == IID_IUnknown) {
1653         *ppvObj = (LPVOID)m_pExchExt;
1654         m_pExchExt->AddRef();
1655         return S_OK;
1656     }
1657
1658     return E_NOINTERFACE;
1659 }
1660
1661
1662 /* Called by Echange to get the maximum number of property pages which
1663    are to be added. LFLAGS is a bitmask indicating what type of
1664    property sheet is being displayed. Return value: The maximum number
1665    of custom pages for the property sheet.  */
1666 STDMETHODIMP_ (ULONG) 
1667 CGPGExchExtPropertySheets::GetMaxPageCount(ULONG lFlags)
1668 {
1669     if (lFlags == EEPS_TOOLSOPTIONS)
1670         return 1;       
1671     return 0;
1672 }
1673
1674
1675 /* Called by Exchange to request information about the property page.
1676    Return value: S_OK to signal Echange to use the pPSP
1677    information. */
1678 STDMETHODIMP 
1679 CGPGExchExtPropertySheets::GetPages(
1680         LPEXCHEXTCALLBACK pEECB, // A pointer to Exchange callback interface.
1681         ULONG lFlags,            // A  bitmask indicating what type of
1682                                  //  property sheet is being displayed.
1683         LPPROPSHEETPAGE pPSP,    // The output parm pointing to pointer
1684                                  //  to list of property sheets.
1685         ULONG FAR * plPSP)       // The output parm pointing to buffer 
1686                                  //  containing the number of property 
1687                                  //  sheets actually used.
1688 {
1689     int resid = 0;
1690
1691     switch (GetUserDefaultLangID ()) {
1692     case 0x0407:    resid = IDD_GPG_OPTIONS_DE;break;
1693     default:        resid = IDD_GPG_OPTIONS; break;
1694     }
1695
1696     pPSP[0].dwSize = sizeof (PROPSHEETPAGE);
1697     pPSP[0].dwFlags = PSP_DEFAULT | PSP_HASHELP;
1698     pPSP[0].hInstance = glob_hinst;
1699     pPSP[0].pszTemplate = MAKEINTRESOURCE (resid);
1700     pPSP[0].hIcon = NULL;     
1701     pPSP[0].pszTitle = NULL;  
1702     pPSP[0].pfnDlgProc = (DLGPROC) GPGOptionsDlgProc;
1703     pPSP[0].lParam = 0;     
1704     pPSP[0].pfnCallback = NULL;
1705     pPSP[0].pcRefParent = NULL; 
1706
1707     *plPSP = 1;
1708
1709     return S_OK;
1710 }
1711
1712
1713 STDMETHODIMP_ (VOID) 
1714 CGPGExchExtPropertySheets::FreePages (LPPROPSHEETPAGE pPSP,
1715                                       ULONG lFlags, ULONG lPSP)
1716 {
1717 }
1718
1719
1720
1721