a283ea3e217fb4d6583fb8945258fd1860b380d9
[gpgol.git] / src / ext-commands.cpp
1 /* ext-commands.cpp - Subclass impl of IExchExtCommands
2  *      Copyright (C) 2004, 2005, 2007 g10 Code GmbH
3  * 
4  * This file is part of GpgOL.
5  * 
6  * GpgOL is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  * 
11  * GpgOL is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  * 
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <windows.h>
25
26 #include "mymapi.h"
27 #include "mymapitags.h"
28 #include "myexchext.h"
29 #include "common.h"
30 #include "display.h"
31 #include "msgcache.h"
32 #include "mapihelp.h"
33
34 #include "dialogs.h"       /* For IDB_foo. */
35 #include "olflange-def.h"
36 #include "olflange.h"
37 #include "ol-ext-callback.h"
38 #include "message.h"
39 #include "ext-commands.h"
40
41
42 #define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
43                                      SRCNAME, __func__, __LINE__); \
44                         } while (0)
45
46 static void toolbar_add_menu (LPEXCHEXTCALLBACK pEECB, 
47                               UINT FAR *pnCommandIDBase, ...)
48 #if __GNUC__ >= 4 
49                                __attribute__ ((sentinel))
50 #endif
51   ;
52
53
54
55
56 /* Wrapper around UlRelease with error checking. */
57 /* FIXME: Duplicated code.  */
58 static void 
59 ul_release (LPVOID punk)
60 {
61   ULONG res;
62   
63   if (!punk)
64     return;
65   res = UlRelease (punk);
66 //   log_debug ("%s UlRelease(%p) had %lu references\n", __func__, punk, res);
67 }
68
69
70
71
72
73 /* Constructor */
74 GpgolExtCommands::GpgolExtCommands (GpgolExt* pParentInterface)
75
76   m_pExchExt = pParentInterface; 
77   m_lRef = 0; 
78   m_lContext = 0; 
79   m_nCmdSelectSmime = 0;
80   m_nCmdEncrypt = 0;  
81   m_nCmdDecrypt = 0;  
82   m_nCmdSign = 0; 
83   m_nCmdShowInfo = 0;  
84   m_nCmdCheckSig = 0;
85   m_nCmdKeyManager = 0;
86   m_nCmdDebug1 = 0;
87   m_nCmdDebug2 = 0;
88   m_nToolbarButtonID1 = 0; 
89   m_nToolbarButtonID2 = 0; 
90   m_nToolbarButtonID3 = 0; 
91   m_nToolbarBitmap1 = 0;
92   m_nToolbarBitmap2 = 0; 
93   m_nToolbarBitmap3 = 0; 
94   m_hWnd = NULL; 
95 }
96
97
98
99 STDMETHODIMP 
100 GpgolExtCommands::QueryInterface (REFIID riid, LPVOID FAR * ppvObj)
101 {
102     *ppvObj = NULL;
103     if ((riid == IID_IExchExtCommands) || (riid == IID_IUnknown)) {
104         *ppvObj = (LPVOID)this;
105         AddRef ();
106         return S_OK;
107     }
108     return E_NOINTERFACE;
109 }
110
111
112 /* Return the toolbar's window from the button entry array.  On
113    success the handle of the window is return as well as the
114    corresponding index at R_IDX.  On error NULL is returned and
115    the value at R_IDX is not changed. */
116 static HWND
117 toolbar_from_tbe (LPTBENTRY pTBEArray, UINT nTBECnt, int *r_idx)
118 {
119   int nTBIndex;
120
121   for (nTBIndex = nTBECnt-1; nTBIndex > -1; --nTBIndex)
122     {   
123       if (EETBID_STANDARD == pTBEArray[nTBIndex].tbid)
124         {
125           *r_idx = nTBIndex;
126           return pTBEArray[nTBIndex].hwnd;
127         }       
128     }
129   return NULL;
130 }
131
132 /* Add a new menu.  The variable entries are made up of pairs of
133    strings and UINT *.  A NULL is used to terminate this list. An empty
134    string is translated to a separator menu item. */
135 static void
136 toolbar_add_menu (LPEXCHEXTCALLBACK pEECB, 
137                   UINT FAR *pnCommandIDBase, ...)
138 {
139   va_list arg_ptr;
140   HMENU menu;
141   const char *string;
142   UINT *cmdptr;
143   
144   va_start (arg_ptr, pnCommandIDBase);
145   pEECB->GetMenuPos (EECMDID_ToolsCustomizeToolbar, &menu, NULL, NULL, 0);
146   while ( (string = va_arg (arg_ptr, const char *)) )
147     {
148       cmdptr = va_arg (arg_ptr, UINT*);
149
150       if (!*string)
151         ; /* Ignore this entry.  */
152       else if (*string == '@' && !string[1])
153         AppendMenu (menu, MF_SEPARATOR, 0, NULL);
154       else
155         {
156           AppendMenu (menu, MF_STRING, *pnCommandIDBase, string);
157           if (cmdptr)
158             *cmdptr = *pnCommandIDBase;
159           (*pnCommandIDBase)++;
160         }
161     }
162   va_end (arg_ptr);
163 }
164
165
166
167 /* Called by Exchange to install commands and toolbar buttons.  Returns
168    S_FALSE to signal Exchange to continue calling extensions. */
169 STDMETHODIMP 
170 GpgolExtCommands::InstallCommands (
171         LPEXCHEXTCALLBACK pEECB, // The Exchange Callback Interface.
172         HWND hWnd,               // The window handle to the main window
173                                  // of context.
174         HMENU hMenu,             // The menu handle to main menu of context.
175         UINT FAR *pnCommandIDBase,  // The base command id.
176         LPTBENTRY pTBEArray,     // The array of toolbar button entries.
177         UINT nTBECnt,            // The count of button entries in array.
178         ULONG lFlags)            // reserved
179 {
180   HRESULT hr;
181   m_hWnd = hWnd;
182   LPDISPATCH pDisp;
183   DISPID dispid;
184   DISPID dispid_put = DISPID_PROPERTYPUT;
185   DISPPARAMS dispparams;
186   VARIANT aVariant;
187   int force_encrypt = 0;
188   int tb_idx;
189   HWND hwnd_toolbar;
190   TBADDBITMAP tbab;
191
192   
193   log_debug ("%s:%s: context=%s flags=0x%lx\n", SRCNAME, __func__, 
194              ext_context_name (m_lContext), lFlags);
195
196
197   /* Outlook 2003 sometimes displays the plaintext and sometimes the
198      original undecrypted text when doing a reply.  This seems to
199      depend on the size of the message; my guess it that only short
200      messages are locally saved in the process and larger ones are
201      fetched again from the backend - or the other way around.
202      Anyway, we can't rely on that and thus me make sure to update the
203      Body object right here with our own copy of the plaintext.  To
204      match the text we use the ConversationIndex property.
205
206      Unfortunately there seems to be no way of resetting the saved
207      property after updating the body, thus even without entering a
208      single byte the user will be asked when cancelling a reply
209      whether he really wants to do that.
210
211      Note, that we can't optimize the code here by first reading the
212      body because this would pop up the securiy window, telling the
213      user that someone is trying to read this data.
214   */
215   if (m_lContext == EECONTEXT_SENDNOTEMESSAGE)
216     {
217       LPMDB mdb = NULL;
218       LPMESSAGE message = NULL;
219       
220       /*  Note that for read and send the object returned by the
221           outlook extension callback is of class 43 (MailItem) so we
222           only need to ask for Body then. */
223       hr = pEECB->GetObject (&mdb, (LPMAPIPROP *)&message);
224       if (FAILED(hr))
225         log_debug ("%s:%s: getObject failed: hr=%#lx\n", SRCNAME,__func__,hr);
226       else if (!opt.compat.no_msgcache)
227         {
228           const char *body;
229           char *key = NULL;
230           size_t keylen = 0;
231           void *refhandle = NULL;
232      
233           pDisp = find_outlook_property (pEECB, "ConversationIndex", &dispid);
234           if (pDisp)
235             {
236               DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
237
238               aVariant.bstrVal = NULL;
239               hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
240                                   DISPATCH_PROPERTYGET, &dispparamsNoArgs,
241                                   &aVariant, NULL, NULL);
242               if (hr != S_OK)
243                 log_debug ("%s:%s: retrieving ConversationIndex failed: %#lx",
244                            SRCNAME, __func__, hr);
245               else if (aVariant.vt != VT_BSTR)
246                 log_debug ("%s:%s: ConversationIndex is not a string (%d)",
247                            SRCNAME, __func__, aVariant.vt);
248               else if (aVariant.bstrVal)
249                 {
250                   char *p;
251
252                   key = wchar_to_utf8 (aVariant.bstrVal);
253                   log_debug ("%s:%s: ConversationIndex is `%s'",
254                            SRCNAME, __func__, key);
255                   /* The key is a hex string.  Convert it to binary. */
256                   for (keylen=0,p=key; hexdigitp(p) && hexdigitp(p+1); p += 2)
257                     ((unsigned char*)key)[keylen++] = xtoi_2 (p);
258                   
259                   SysFreeString (aVariant.bstrVal);
260                 }
261
262               pDisp->Release();
263               pDisp = NULL;
264             }
265           
266           if (key && keylen
267               && (body = msgcache_get (key, keylen, &refhandle)) 
268               && (pDisp = find_outlook_property (pEECB, "Body", &dispid)))
269             {
270 #if 1
271               dispparams.cNamedArgs = 1;
272               dispparams.rgdispidNamedArgs = &dispid_put;
273               dispparams.cArgs = 1;
274               dispparams.rgvarg = &aVariant;
275               dispparams.rgvarg[0].vt = VT_LPWSTR;
276               dispparams.rgvarg[0].bstrVal = utf8_to_wchar (body);
277               hr = pDisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
278                                  DISPATCH_PROPERTYPUT, &dispparams,
279                                  NULL, NULL, NULL);
280               xfree (dispparams.rgvarg[0].bstrVal);
281               log_debug ("%s:%s: PROPERTYPUT(body) result -> %#lx\n",
282                          SRCNAME, __func__, hr);
283 #else
284               log_window_hierarchy (hWnd, "%s:%s:%d: Windows hierarchy:",
285                                     SRCNAME, __func__, __LINE__);
286 #endif
287               pDisp->Release();
288               pDisp = NULL;
289               
290               /* Because we found the plaintext in the cache we can assume
291                  that the orginal message has been encrypted and thus we
292                  now set a flag to make sure that by default the reply
293                  gets encrypted too. */
294               force_encrypt = 1;
295             }
296           msgcache_unref (refhandle);
297           xfree (key);
298         }
299       
300       ul_release (message);
301       ul_release (mdb);
302     }
303
304
305   if (m_lContext == EECONTEXT_READNOTEMESSAGE)
306     {
307       int need_dvm = 0;
308
309       switch (m_pExchExt->getMsgtype (pEECB))
310         {
311         case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
312         case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
313         case MSGTYPE_GPGOL_PGP_MESSAGE:
314           need_dvm = 1;
315           break;
316         default:
317           break;
318         }
319
320       /* We always enable the verify button as it might be useful on
321          an already decrypted message. */
322       toolbar_add_menu 
323         (pEECB, pnCommandIDBase,
324          "@", NULL,
325          need_dvm? _("&Decrypt and verify message"):"", &m_nCmdDecrypt,
326          _("&Verify signature"), &m_nCmdCheckSig,
327          _("&Display crypto information"), &m_nCmdShowInfo,
328          "@", NULL,
329          "Debug-1 (open_inspector)", &m_nCmdDebug1,
330          "Debug-2 (n/a)", &m_nCmdDebug2,
331          NULL);
332       
333       hwnd_toolbar = toolbar_from_tbe (pTBEArray, nTBECnt, &tb_idx);
334       if (hwnd_toolbar)
335         {
336           m_nToolbarButtonID1 = pTBEArray[tb_idx].itbbBase;
337           pTBEArray[tb_idx].itbbBase++;
338
339           tbab.hInst = glob_hinst;
340           tbab.nID = IDB_DECRYPT;
341           m_nToolbarBitmap1 = SendMessage(hwnd_toolbar, TB_ADDBITMAP,
342                                           1, (LPARAM)&tbab);
343           m_nToolbarButtonID2 = pTBEArray[tb_idx].itbbBase;
344           pTBEArray[tb_idx].itbbBase++;
345         }
346     }
347
348   if (m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
349     {
350       toolbar_add_menu 
351         (pEECB, pnCommandIDBase,
352          "@", NULL,
353          opt.enable_smime? _("use S/MIME protocol"):"", &m_nCmdSelectSmime,
354          _("&encrypt message with GnuPG"), &m_nCmdEncrypt,
355          _("&sign message with GnuPG"), &m_nCmdSign,
356          NULL );
357       
358
359       hwnd_toolbar = toolbar_from_tbe (pTBEArray, nTBECnt, &tb_idx);
360       if (hwnd_toolbar) 
361         {
362           m_nToolbarButtonID1 = pTBEArray[tb_idx].itbbBase;
363           pTBEArray[tb_idx].itbbBase++;
364
365           tbab.hInst = glob_hinst;
366           tbab.nID = IDB_ENCRYPT;
367           m_nToolbarBitmap1 = SendMessage (hwnd_toolbar, TB_ADDBITMAP,
368                                            1, (LPARAM)&tbab);
369
370           m_nToolbarButtonID2 = pTBEArray[tb_idx].itbbBase;
371           pTBEArray[tb_idx].itbbBase++;
372
373           tbab.nID = IDB_SIGN;
374           m_nToolbarBitmap2 = SendMessage (hwnd_toolbar, TB_ADDBITMAP,
375                                            1, (LPARAM)&tbab);
376
377           m_nToolbarButtonID3 = pTBEArray[tb_idx].itbbBase;
378           pTBEArray[tb_idx].itbbBase++;
379
380           tbab.nID = IDB_SELECT_SMIME;
381           m_nToolbarBitmap3 = SendMessage (hwnd_toolbar, TB_ADDBITMAP,
382                                            1, (LPARAM)&tbab);
383         }
384
385       m_pExchExt->m_gpgSelectSmime = opt.enable_smime && opt.smime_default;
386       m_pExchExt->m_gpgEncrypt = opt.encrypt_default;
387       m_pExchExt->m_gpgSign    = opt.sign_default;
388       if (force_encrypt)
389         m_pExchExt->m_gpgEncrypt = true;
390     }
391
392   if (m_lContext == EECONTEXT_VIEWER) 
393     {
394       toolbar_add_menu 
395         (pEECB, pnCommandIDBase, 
396          "@", NULL,
397          _("GnuPG Certificate &Manager"), &m_nCmdKeyManager,
398          NULL);
399
400       hwnd_toolbar = toolbar_from_tbe (pTBEArray, nTBECnt, &tb_idx);
401       if (hwnd_toolbar)
402         {
403           m_nToolbarButtonID1 = pTBEArray[tb_idx].itbbBase;
404           pTBEArray[tb_idx].itbbBase++;
405
406           tbab.hInst = glob_hinst;
407           tbab.nID = IDB_KEY_MANAGER;
408           m_nToolbarBitmap1 = SendMessage(hwnd_toolbar, TB_ADDBITMAP,
409                                           1, (LPARAM)&tbab);
410         }       
411     }
412   return S_FALSE;
413 }
414
415
416 /* Called by Exchange when a user selects a command.  Return value:
417    S_OK if command is handled, otherwise S_FALSE. */
418 STDMETHODIMP 
419 GpgolExtCommands::DoCommand (
420                   LPEXCHEXTCALLBACK pEECB, // The Exchange Callback Interface.
421                   UINT nCommandID)         // The command id.
422 {
423   HRESULT hr;
424   HWND hWnd = NULL;
425   LPMESSAGE message = NULL;
426   LPMDB mdb = NULL;
427       
428   if (FAILED (pEECB->GetWindow (&hWnd)))
429     hWnd = NULL;
430
431   log_debug ("%s:%s: commandID=%u (%#x) context=%s hwnd=%p\n",
432              SRCNAME, __func__, nCommandID, nCommandID, 
433              ext_context_name (m_lContext), hWnd);
434
435   if (nCommandID == SC_CLOSE && m_lContext == EECONTEXT_READNOTEMESSAGE)
436     {
437       /* This is the system close command. Replace it with our own to
438          avoid the "save changes" query, apparently induced by OL
439          internal syncronisation of our SetWindowText message with its
440          own OOM (in this case Body). */
441       LPDISPATCH pDisp;
442       DISPID dispid;
443       DISPPARAMS dispparams;
444       VARIANT aVariant;
445       
446       pDisp = find_outlook_property (pEECB, "Close", &dispid);
447       if (pDisp)
448         {
449           /* Note that there is a report on the Net from 2005 by Amit
450              Joshi where he claims that in Outlook XP olDiscard does
451              not work but is treated like olSave.  */ 
452           dispparams.rgvarg = &aVariant;
453           dispparams.rgvarg[0].vt = VT_INT;
454           dispparams.rgvarg[0].intVal = 1; /* olDiscard */
455           dispparams.cArgs = 1;
456           dispparams.cNamedArgs = 0;
457           hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
458                               DISPATCH_METHOD, &dispparams,
459                               NULL, NULL, NULL);
460           pDisp->Release();
461           pDisp = NULL;
462           if (hr == S_OK)
463             {
464               log_debug ("%s:%s: invoking Close succeeded", SRCNAME,__func__);
465               return S_OK; /* We handled the close command. */
466             }
467
468           log_debug ("%s:%s: invoking Close failed: %#lx",
469                      SRCNAME, __func__, hr);
470         }
471
472       /* Closing on our own failed - pass it on. */
473       return S_FALSE; 
474     }
475   else if (nCommandID == 154)
476     {
477       log_debug ("%s:%s: command Reply called\n", SRCNAME, __func__);
478       /* What we might want to do is to call Reply, then GetInspector
479          and then Activate - this allows us to get full control over
480          the quoted message and avoids the ugly msgcache. */
481       return S_FALSE; /* Pass it on.  */
482     }
483   else if (nCommandID == 155)
484     {
485       log_debug ("%s:%s: command ReplyAll called\n", SRCNAME, __func__);
486       return S_FALSE; /* Pass it on.  */
487     }
488   else if (nCommandID == 156)
489     {
490       log_debug ("%s:%s: command Forward called\n", SRCNAME, __func__);
491       return S_FALSE; /* Pass it on.  */
492     }
493   else if (nCommandID == m_nCmdDecrypt
494            && m_lContext == EECONTEXT_READNOTEMESSAGE)
495     {
496       hr = pEECB->GetObject (&mdb, (LPMAPIPROP *)&message);
497       if (SUCCEEDED (hr))
498         {
499           message_decrypt (message, m_pExchExt->getMsgtype (pEECB), 1);
500         }
501       ul_release (message);
502       ul_release (mdb);
503     }
504   else if (nCommandID == m_nCmdCheckSig
505            && m_lContext == EECONTEXT_READNOTEMESSAGE)
506     {
507       hr = pEECB->GetObject (&mdb, (LPMAPIPROP *)&message);
508       if (SUCCEEDED (hr))
509         {
510           message_verify (message, m_pExchExt->getMsgtype (pEECB), 1);
511         }
512       else
513         log_debug_w32 (hr, "%s:%s: CmdCheckSig failed", SRCNAME, __func__);
514       ul_release (message);
515       ul_release (mdb);
516     }
517   else if (nCommandID == m_nCmdShowInfo
518            && m_lContext == EECONTEXT_READNOTEMESSAGE)
519     {
520       hr = pEECB->GetObject (&mdb, (LPMAPIPROP *)&message);
521       if (SUCCEEDED (hr))
522         {
523           message_show_info (message, hWnd);
524         }
525       ul_release (message);
526       ul_release (mdb);
527     }
528   else if (nCommandID == m_nCmdSelectSmime
529            && m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
530     {
531       if (opt.enable_smime)
532         m_pExchExt->m_gpgSelectSmime = !m_pExchExt->m_gpgSelectSmime;
533     }
534   else if (nCommandID == m_nCmdEncrypt
535            && m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
536     {
537       m_pExchExt->m_gpgEncrypt = !m_pExchExt->m_gpgEncrypt;
538     }
539   else if (nCommandID == m_nCmdSign
540            && m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
541     {
542       m_pExchExt->m_gpgSign = !m_pExchExt->m_gpgSign;
543     }
544   else if (nCommandID == m_nCmdKeyManager
545            && m_lContext == EECONTEXT_VIEWER)
546     {
547       if (start_key_manager ())
548         MessageBox (NULL, _("Could not start certificate manager"),
549                     "GpgOL", MB_ICONERROR|MB_OK);
550     }
551   else if (nCommandID == m_nCmdDebug1
552            && m_lContext == EECONTEXT_READNOTEMESSAGE)
553     {
554       hr = pEECB->GetObject (&mdb, (LPMAPIPROP *)&message);
555       if (SUCCEEDED (hr))
556         {
557           open_inspector (pEECB, message);
558         }
559       ul_release (message);
560       ul_release (mdb);
561
562     }
563   else
564     return S_FALSE; /* Pass on unknown command. */
565
566   return S_OK; 
567 }
568
569
570 /* Called by Exchange when it receives a WM_INITMENU message, allowing
571    the extension object to enable, disable, or update its menu
572    commands before the user sees them. This method is called
573    frequently and should be written in a very efficient manner. */
574 STDMETHODIMP_(VOID) 
575 GpgolExtCommands::InitMenu(LPEXCHEXTCALLBACK pEECB) 
576 {
577 #if 0
578   log_debug ("%s:%s: context=%s\n",
579              SRCNAME, __func__, ext_context_name (m_lContext));
580 #endif
581 }
582
583
584 /* Called by Exchange when the user requests help for a menu item.
585    PEECP is the pointer to Exchange Callback Interface.  NCOMMANDID is
586    the command id.  Return value: S_OK when it is a menu item of this
587    plugin and the help was shown; otherwise S_FALSE.  */
588 STDMETHODIMP 
589 GpgolExtCommands::Help (LPEXCHEXTCALLBACK pEECB, UINT nCommandID)
590 {
591   if (nCommandID == m_nCmdDecrypt && 
592       m_lContext == EECONTEXT_READNOTEMESSAGE) 
593     {
594       MessageBox (m_hWnd,
595                   _("Select this option to decrypt and verify the message."),
596                   "GpgOL", MB_OK);
597     }
598   else if (nCommandID == m_nCmdShowInfo
599            && m_lContext == EECONTEXT_READNOTEMESSAGE) 
600     {
601       MessageBox (m_hWnd,
602                   _("Select this option to show information"
603                     " on the crypto status"),
604                   "GpgOL", MB_OK);
605     }
606   else if (nCommandID == m_nCmdCheckSig
607            && m_lContext == EECONTEXT_READNOTEMESSAGE) 
608     {
609       MessageBox (m_hWnd,
610                   _("Check the signature now and display the result"),
611                   "GpgOL", MB_OK);
612     }
613   else if (nCommandID == m_nCmdSelectSmime
614            && m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
615     {
616       MessageBox (m_hWnd,
617                   _("Select this option to select the S/MIME protocol."),
618                   "GpgOL", MB_OK);      
619     } 
620   else if (nCommandID == m_nCmdEncrypt 
621            && m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
622     {
623       MessageBox (m_hWnd,
624                   _("Select this option to encrypt the message."),
625                   "GpgOL", MB_OK);      
626     } 
627   else if (nCommandID == m_nCmdSign
628            && m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
629     {
630       MessageBox (m_hWnd,
631                   _("Select this option to sign the message."),
632                   "GpgOL", MB_OK);      
633     }
634   else if (nCommandID == m_nCmdKeyManager
635            && m_lContext == EECONTEXT_VIEWER) 
636     {
637       MessageBox (m_hWnd,
638                   _("Select this option to open the certificate manager"),
639                   "GpgOL", MB_OK);
640     }
641   else
642     return S_FALSE;
643
644   return S_OK;
645 }
646
647
648 /* Called by Exchange to get the status bar text or the tooltip of a
649    menu item.  NCOMMANDID is the command id corresponding to the menu
650    item activated.  LFLAGS identifies either EECQHT_STATUS or
651    EECQHT_TOOLTIP.  PSZTEXT is a pointer to buffer to received the
652    text to be displayed.  NCHARCNT is the available space in PSZTEXT.
653
654    Returns S_OK when it is a menu item of this plugin and the text was
655    set; otherwise S_FALSE.  */
656 STDMETHODIMP 
657 GpgolExtCommands::QueryHelpText(UINT nCommandID, ULONG lFlags,
658                                 LPTSTR pszText,  UINT nCharCnt)    
659 {
660         
661   if (nCommandID == m_nCmdDecrypt
662       && m_lContext == EECONTEXT_READNOTEMESSAGE) 
663     {
664       if (lFlags == EECQHT_STATUS)
665         lstrcpyn (pszText, ".", nCharCnt);
666       if (lFlags == EECQHT_TOOLTIP)
667         lstrcpyn (pszText, _("Decrypt message and verify signature"), 
668                   nCharCnt);
669     }
670   else if (nCommandID == m_nCmdShowInfo
671            && m_lContext == EECONTEXT_READNOTEMESSAGE) 
672     {
673       if (lFlags == EECQHT_STATUS)
674         lstrcpyn (pszText, ".", nCharCnt);
675       if (lFlags == EECQHT_TOOLTIP)
676         lstrcpyn (pszText, _("Show S/MIME status info"),
677                   nCharCnt);
678     }
679   else if (nCommandID == m_nCmdCheckSig
680            && m_lContext == EECONTEXT_READNOTEMESSAGE) 
681     {
682       if (lFlags == EECQHT_STATUS)
683         lstrcpyn (pszText, ".", nCharCnt);
684       if (lFlags == EECQHT_TOOLTIP)
685         lstrcpyn (pszText,
686                   _("Check the signature now and display the result"),
687                   nCharCnt);
688     }
689   else if (nCommandID == m_nCmdSelectSmime
690            && m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
691     {
692       if (lFlags == EECQHT_STATUS)
693         lstrcpyn (pszText, ".", nCharCnt);
694       if (lFlags == EECQHT_TOOLTIP)
695         lstrcpyn (pszText,
696                   _("Use S/MIME for sign/encrypt"),
697                   nCharCnt);
698     }
699   else if (nCommandID == m_nCmdEncrypt
700            && m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
701     {
702       if (lFlags == EECQHT_STATUS)
703         lstrcpyn (pszText, ".", nCharCnt);
704       if (lFlags == EECQHT_TOOLTIP)
705         lstrcpyn (pszText,
706                   _("Encrypt message with GPG"),
707                   nCharCnt);
708     }
709   else if (nCommandID == m_nCmdSign
710            && m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
711     {
712       if (lFlags == EECQHT_STATUS)
713         lstrcpyn (pszText, ".", nCharCnt);
714       if (lFlags == EECQHT_TOOLTIP)
715         lstrcpyn (pszText,
716                   _("Sign message with GPG"),
717                   nCharCnt);
718     }
719   else if (nCommandID == m_nCmdKeyManager
720            && m_lContext == EECONTEXT_VIEWER) 
721     {
722       if (lFlags == EECQHT_STATUS)
723         lstrcpyn (pszText, ".", nCharCnt);
724       if (lFlags == EECQHT_TOOLTIP)
725         lstrcpyn (pszText,
726                   _("Open the GpgOL certificate manager"),
727                   nCharCnt);
728     }
729   else 
730     return S_FALSE;
731
732   return S_OK;
733 }
734
735
736 /* Called by Exchange to get toolbar button infos.  TOOLBARID is the
737    toolbar identifier.  BUTTONID is the toolbar button index.  PTBB is
738    a pointer to toolbar button structure DESCRIPTION is a pointer to
739    buffer receiving the text for the button.  DESCRIPTION_SIZE is the
740    maximum size of DESCRIPTION.  FLAGS are flags which might have the
741    EXCHEXT_UNICODE bit set.
742
743    Returns S_OK when it is a button of this plugin and the requested
744    info was delivered; otherwise S_FALSE.  */
745 STDMETHODIMP 
746 GpgolExtCommands::QueryButtonInfo (ULONG toolbarid, UINT buttonid, 
747                                    LPTBBUTTON pTBB, 
748                                    LPTSTR description, UINT description_size,
749                                    ULONG flags)          
750 {
751   if (buttonid == m_nToolbarButtonID1
752       && m_lContext == EECONTEXT_READNOTEMESSAGE)
753     {
754       pTBB->iBitmap = m_nToolbarBitmap1;             
755       pTBB->idCommand = m_nCmdEncrypt;
756       pTBB->fsState = TBSTATE_ENABLED;
757       pTBB->fsStyle = TBSTYLE_BUTTON;
758       pTBB->dwData = 0;
759       pTBB->iString = -1;
760       lstrcpyn (description,
761                 _("Decrypt message and verify signature"),
762                 description_size);
763     }
764   else if (buttonid == m_nToolbarButtonID1
765            && m_lContext == EECONTEXT_SENDNOTEMESSAGE)
766     {
767       pTBB->iBitmap = m_nToolbarBitmap1;             
768       pTBB->idCommand = m_nCmdEncrypt;
769       pTBB->fsState = TBSTATE_ENABLED;
770       if (m_pExchExt->m_gpgEncrypt)
771         pTBB->fsState |= TBSTATE_CHECKED;
772       pTBB->fsStyle = TBSTYLE_BUTTON | TBSTYLE_CHECK;
773       pTBB->dwData = 0;
774       pTBB->iString = -1;
775       lstrcpyn (description, _("Encrypt message with GPG"),
776                 description_size);
777     }
778   else if (buttonid == m_nToolbarButtonID2
779            && m_lContext == EECONTEXT_SENDNOTEMESSAGE)
780     {
781       pTBB->iBitmap = m_nToolbarBitmap2;             
782       pTBB->idCommand = m_nCmdSign;
783       pTBB->fsState = TBSTATE_ENABLED;
784       if (m_pExchExt->m_gpgSign)
785         pTBB->fsState |= TBSTATE_CHECKED;
786       pTBB->fsStyle = TBSTYLE_BUTTON | TBSTYLE_CHECK;
787       pTBB->dwData = 0;
788       pTBB->iString = -1;
789       lstrcpyn (description, _("Sign message with GPG"),
790                 description_size);
791     }
792   else if (buttonid == m_nToolbarButtonID3
793            && m_lContext == EECONTEXT_SENDNOTEMESSAGE)
794     {
795       pTBB->iBitmap = m_nToolbarBitmap3;             
796       pTBB->idCommand = m_nCmdSelectSmime;
797       pTBB->fsState = TBSTATE_ENABLED;
798       if (m_pExchExt->m_gpgSelectSmime)
799         pTBB->fsState |= TBSTATE_CHECKED;
800       pTBB->fsStyle = TBSTYLE_BUTTON | TBSTYLE_CHECK;
801       pTBB->dwData = 0;
802       pTBB->iString = -1;
803       lstrcpyn (description, _("Use the S/MIME protocol"),
804                 description_size);
805     }
806   else if (buttonid == m_nToolbarButtonID1
807            && m_lContext == EECONTEXT_VIEWER)
808     {
809       pTBB->iBitmap = m_nToolbarBitmap1;             
810       pTBB->idCommand = m_nCmdEncrypt;
811       pTBB->fsState = TBSTATE_ENABLED;
812       pTBB->fsStyle = TBSTYLE_BUTTON;
813       pTBB->dwData = 0;
814       pTBB->iString = -1;
815       lstrcpyn (description, _("Open the certificate manager"),
816                 description_size);
817     }
818   else
819     return S_FALSE;
820
821   return S_OK;
822 }
823
824
825
826 STDMETHODIMP 
827 GpgolExtCommands::ResetToolbar (ULONG lToolbarID, ULONG lFlags)
828 {       
829   return S_OK;
830 }
831
832