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