Add framework for mime decrypt / verify
authorAndre Heinecke <aheinecke@intevation.de>
Wed, 16 Sep 2015 08:38:47 +0000 (10:38 +0200)
committerAndre Heinecke <aheinecke@intevation.de>
Wed, 16 Sep 2015 08:53:27 +0000 (10:53 +0200)
Add event handling for Application and MailItem
events and use the message incoming handler in
the before read event.

* src/comhelp.h: Removed again. Macros moved to mymapi.h where
  they are better placed.
* src/eventsink.h: (EVENT_SINK_DEFAULT_DTOR_CODE): Release /
  unadvise properly.
  (USE_INVOKE_ARGS): New. Used to avoid unused parameter
  warnings.
  (debug_oom, debug_oom_extra): Moved to util.h.
* src/eventsinks.h: New. Header to declare event sink install /
  detach functions.
* src/eventsinks.cpp: New. Event sinks for MailItem and Application.
* src/oomhelp.h: Define GUID's necessary for the Event handlers.
  (get_object_by_id): New. oomhelp API style wrapper around
  QueryInterface.
* src/oomhelp.cpp (get_object_by_id): New.
* src/util.h (log_oom, log_oom_extra): New. Logging helper.
* src/mymapi.h: Add Macros from comhelp.
  (IMAPISecureMessage): Declare COM interface.
* src/message.h: Add headers that were indirectly included
  in other usages.
* src/gpgoladdin.h (GpgolAddin): Add Application Event sink member.
* src/gpgoladdin.cpp (OnStartupComplete): Install Application Event
  sink.

--

This establishes the framework for proper MIME handling
currently mails are decrypted / verified but the message
content is not yet shown in Outlook.

The ApplicationEvent sink handles ItemLoad events (which should
happen once a Message is loaded into Outlook) and uses the event
to connect a MailItemEvent handler to the MailItem. The
MailItemEvent handler then handles the BeforeRead event and

12 files changed:
src/Makefile.am
src/comhelp.h [deleted file]
src/eventsink.h
src/eventsinks.cpp [new file with mode: 0644]
src/eventsinks.h [new file with mode: 0644]
src/gpgoladdin.cpp
src/gpgoladdin.h
src/message.h
src/mymapi.h
src/oomhelp.cpp
src/oomhelp.h
src/util.h

index 59ba6cf..b7c4046 100644 (file)
@@ -88,7 +88,8 @@ gpgol_SOURCES = \
        gpgoladdin.cpp gpgoladdin.h \
        ribbon-callbacks.cpp ribbon-callbacks.h \
        parsetlv.c parsetlv.h \
-       filetype.c filetype.h
+       filetype.c filetype.h \
+       eventsinks.h eventsinks.cpp
 
 
 #treeview_SOURCES = treeview.c
diff --git a/src/comhelp.h b/src/comhelp.h
deleted file mode 100644 (file)
index 073c85b..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/* comhelp.h - Helper macros to define / declare COM interfaces.
- *    Copyright (C) 2015 Intevation GmbH
- *
- * This file is part of GpgOL.
- *
- * GpgOL is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * GpgOL is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-#ifndef COMHELP_H
-#define COMHELP_H
-
-/*** IUnknown methods ***/
-#define DECLARE_IUNKNOWN_METHODS                        \
-  STDMETHOD(QueryInterface)(THIS_ REFIID, PVOID*) PURE; \
-  STDMETHOD_(ULONG,AddRef)(THIS) PURE;                  \
-  STDMETHOD_(ULONG,Release)(THIS) PURE
-
-/*** IDispatch methods ***/
-#define DECLARE_IDISPATCH_METHODS                                             \
-  STDMETHOD(GetTypeInfoCount)(THIS_ UINT*) PURE;                              \
-  STDMETHOD(GetTypeInfo)(THIS_ UINT, LCID, LPTYPEINFO*) PURE;                 \
-  STDMETHOD(GetIDsOfNames)(THIS_ REFIID, LPOLESTR*, UINT, LCID, DISPID*) PURE;\
-  STDMETHOD(Invoke)(THIS_ DISPID, REFIID, LCID, WORD,                         \
-                    DISPPARAMS*, VARIANT*, EXCEPINFO*, UINT*) PURE
-
-#endif // COMHELP_H
index f94793b..4886d65 100644 (file)
@@ -1,5 +1,6 @@
 /* eventsink.h - Macros to implement an OLE event sink
- *     Copyright (C) 2009 g10 Code GmbH
+ *     Copyright (C) 2009 g10 Code GmbH
+ *     Copyright (C) 2015 Intevation GmbH
  *
  * This file is part of GpgOL.
  *
    for our purpose but far from optimal; e.g. we should not simply
    provide stubs but do real sub-classing by modifying the vtables.  */
 
+/* See eventsinks.cpp for example usage */
+
 #ifndef EVENTSINK_H
 #define EVENTSINK_H
 
-#define debug_oom        (opt.enable_debug & DBG_OOM)
-#define debug_oom_extra  (opt.enable_debug & DBG_OOM_EXTRA)
-
 
 #define BEGIN_EVENT_SINK(subcls,parentcls)                               \
 class subcls : public parentcls                                          \
@@ -84,14 +84,19 @@ STDMETHODIMP subcls::Invoke (DISPID dispid, REFIID riid, LCID lcid,      \
                 EXCEPINFO *exepinfo, UINT *argerr)                       \
 /* End of macro EVENT_SINK_INVOKE.  */
 
+#define USE_INVOKE_ARGS                                                  \
+  (void)riid; (void)lcid; (void) flags; (void)parms; (void)result;       \
+  (void)exepinfo; (void)argerr;
+/* End of macro USE_INVOKE_ARGS. */
+
 #define EVENT_SINK_DEFAULT_DTOR_CODE(subcls)                             \
 {                                                                        \
   if (debug_oom)                                                         \
     log_debug ("%s:" #subcls ":%s: tdor", SRCNAME, __func__);            \
   if (m_pCP)                                                             \
-    log_error ("%s:%s: Unadvise missing", SRCNAME, __func__);            \
+    m_pCP->Unadvise(m_cookie);                                           \
   if (m_object)                                                          \
-    log_error ("%s:%s: Object not released", SRCNAME,__func__);          \
+    m_object->Release();                                                 \
 }                                                                        \
 /* End of macro EVENT_SINK_DTOR_DEFAULT_CODE.  */
 
diff --git a/src/eventsinks.cpp b/src/eventsinks.cpp
new file mode 100644 (file)
index 0000000..dffe246
--- /dev/null
@@ -0,0 +1,207 @@
+/* eventsinks.cpp - Event handling classes.
+ *    Copyright (C) 2015 Intevation GmbH
+ *
+ * This file is part of GpgOL.
+ *
+ * GpgOL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GpgOL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* The event handler classes defined in this file follow the
+   general pattern that they implment the IDispatch interface
+   through the eventsink macros and handle event invocations
+   in their invoke methods.
+*/
+#include "eventsink.h"
+#include "eventsinks.h"
+#include "ocidl.h"
+#include "common.h"
+#include "oomhelp.h"
+#include "mymapi.h"
+#include "message.h"
+
+/* Application Events */
+BEGIN_EVENT_SINK(ApplicationEvents, IDispatch)
+EVENT_SINK_DEFAULT_CTOR(ApplicationEvents)
+EVENT_SINK_DEFAULT_DTOR(ApplicationEvents)
+typedef enum
+  {
+    AdvancedSearchComplete = 0xFA6A,
+    AdvancedSearchStopped = 0xFA6B,
+    AttachmentContextMenuDisplay = 0xFB3E,
+    BeforeFolderSharingDialog = 0xFC01,
+    ContextMenuClose = 0xFBA6,
+    FolderContextMenuDisplay = 0xFB42,
+    ItemContextMenuDisplay = 0xFB41,
+    ItemLoad = 0xFBA7,
+    ItemSend = 0xF002,
+    MAPILogonComplete = 0xFA90,
+    NewMail = 0xF003,
+    NewMailEx = 0xFAB5,
+    OptionsPagesAdd = 0xF005,
+    Quit = 0xF007,
+    Reminder = 0xF004,
+    ShortcutContextMenuDisplay = 0xFB44,
+    Startup = 0xF006,
+    StoreContextMenuDisplay = 0xFB43,
+    ViewContextMenuDisplay = 0xFB40
+  } ApplicationEvent;
+
+EVENT_SINK_INVOKE(ApplicationEvents)
+{
+  USE_INVOKE_ARGS
+  switch(dispid)
+    {
+      case ItemLoad:
+        {
+          LPDISPATCH mailItem;
+          LPDISPATCH mailEventSink;
+          if (parms->cArgs == 0)
+            {
+              log_error ("%s:%s: Itemload without arguments.",
+                         SRCNAME, __func__);
+              break;
+            }
+          mailItem = get_object_by_id (parms->rgvarg[0].pdispVal,
+                                       IID_MailItem);
+          if (!mailItem)
+            {
+              log_error ("%s:%s: ItemLoad event without mailitem.",
+                         SRCNAME, __func__);
+              break;
+            }
+          mailEventSink = install_MailItemEvents_sink (mailItem);
+          /* TODO figure out what we need to do with the event sink.
+             Does it need to be Released at some point? What happens
+             on unload? */
+          if (!mailEventSink)
+            {
+              log_error ("%s:%s: Failed to install MailItemEvents sink.",
+                         SRCNAME, __func__);
+            }
+          mailItem->Release ();
+          break;
+        }
+      default:
+        log_oom_extra ("%s:%s: Unhandled Event: %lx \n",
+                       SRCNAME, __func__, dispid);
+    }
+  /* We always return S_OK even on error so that outlook
+     continues to handle the event and is not disturbed
+     by our errors. There shouldn't be errors in here
+     anyway if everything works as documented. */
+  return S_OK;
+}
+END_EVENT_SINK(ApplicationEvents, IID_ApplicationEvents)
+
+/* Mail Item Events */
+BEGIN_EVENT_SINK(MailItemEvents, IDispatch)
+EVENT_SINK_DEFAULT_CTOR(MailItemEvents)
+EVENT_SINK_DEFAULT_DTOR(MailItemEvents)
+typedef enum
+  {
+    /* TODO add dispids. */
+    AfterWrite,
+    AttachmentAdd,
+    AttachmentRead,
+    AttachmentRemove,
+    BeforeAttachmentAdd,
+    BeforeAttachmentPreview,
+    BeforeAttachmentRead,
+    BeforeAttachmentSave,
+    BeforeAttachmentWriteToTempFile,
+    BeforeAutoSave,
+    BeforeCheckNames,
+    BeforeDelete,
+    BeforeRead = 0xFC8C,
+    Close,
+    CustomAction,
+    CustomPropertyChange,
+    Forward,
+    Open,
+    PropertyChange,
+    Read,
+    ReadComplete = 0xFC8F,
+    Reply,
+    ReplyAll,
+    Send,
+    Unload,
+    Write
+  } MailEvent;
+
+EVENT_SINK_INVOKE(MailItemEvents)
+{
+  USE_INVOKE_ARGS
+  switch(dispid)
+    {
+      case BeforeRead:
+        {
+          LPUNKNOWN mailItem = NULL;
+          HRESULT hr;
+          LPDISPATCH secureItem = NULL;
+          LPMESSAGE message = NULL;
+          LPMAPISECUREMESSAGE secureMessage = NULL;
+
+          mailItem = get_oom_iunknown (m_object, "MapiObject");
+          if (!mailItem)
+            {
+              log_error ("%s:%s: Failed to obtain MailItem.",
+                         SRCNAME, __func__);
+            }
+
+          secureItem = get_object_by_id ((LPDISPATCH)mailItem,
+                                         IID_IMAPISecureMessage);
+          if (!secureItem)
+            {
+              log_error ("%s:%s: Failed to obtain SecureItem.",
+                         SRCNAME, __func__);
+              mailItem->Release();
+              break;
+            }
+
+          secureMessage = (LPMAPISECUREMESSAGE) secureItem;
+
+          /* The call to GetBaseMessage is pretty much a jump
+             in the dark. So it would not be surprising to get
+             crashes here in the future. */
+          log_oom_extra("%s:%s: About to call GetBaseMessage.",
+                        SRCNAME, __func__);
+          hr = secureMessage->GetBaseMessage (&message);
+          if ( hr == S_OK)
+            {
+              int ret;
+              log_oom_extra ("%s:%s: GetBaseMessage OK.",
+                             SRCNAME, __func__);
+              ret = message_incoming_handler(message, NULL, false);
+              log_debug ("%s:%s: incoming handler status: %i",
+                         SRCNAME, __func__, ret);
+            }
+          else
+            {
+              log_error_w32 (hr, "Failed to GetBaseMessage.");
+            }
+          secureMessage->Release ();
+          mailItem->Release ();
+          break;
+        }
+      case ReadComplete:
+        {
+          break;
+        }
+      default:
+        log_oom_extra ("%s:%s: Unhandled Event: %lx \n",
+                       SRCNAME, __func__, dispid);
+    }
+  return S_OK;
+}
+END_EVENT_SINK(MailItemEvents, IID_MailItemEvents)
diff --git a/src/eventsinks.h b/src/eventsinks.h
new file mode 100644 (file)
index 0000000..0bcfc03
--- /dev/null
@@ -0,0 +1,28 @@
+/* eventsinks.h - Declaraion of eventsink installation functions.
+ *    Copyright (C) 2015 Intevation GmbH
+ *
+ * This file is part of GpgOL.
+ *
+ * GpgOL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GpgOL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef EVENTSINKS_H
+#define EVENTSINKS_H
+
+#include <windows.h>
+
+LPDISPATCH install_ApplicationEvents_sink (LPDISPATCH obj);
+void detach_ApplicationEvents_sink (LPDISPATCH obj);
+LPDISPATCH install_MailItemEvents_sink (LPDISPATCH obj);
+void detach_MailItemEvents_sink (LPDISPATCH obj);
+#endif // EVENTSINKS_H
index 9c15fe5..7323674 100644 (file)
@@ -45,6 +45,7 @@
 
 #include "gpgol-ids.h"
 #include "ribbon-callbacks.h"
+#include "eventsinks.h"
 
 #define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
                                      SRCNAME, __func__, __LINE__); \
@@ -148,7 +149,7 @@ STDMETHODIMP GpgolAddinFactory::CreateInstance (LPUNKNOWN punk, REFIID riid,
    The ref count is set by the factory after creation.
 */
 GpgolAddin::GpgolAddin (void) : m_lRef(0), m_application(0),
-  m_addin(0), m_disabled(false)
+  m_addin(0), m_applicationEventSink(0), m_disabled(false)
 {
   read_options ();
   /* RibbonExtender is it's own object to avoid the pitfalls of
@@ -163,6 +164,7 @@ GpgolAddin::~GpgolAddin (void)
              SRCNAME, __func__);
 
   delete m_ribbonExtender;
+  delete m_applicationEventSink;
 
   if (!m_disabled)
     {
@@ -276,12 +278,7 @@ GpgolAddin::OnStartupComplete (SAFEARRAY** custom)
 
   if (m_application)
     {
-      /*
-         An install_sinks here works this but we
-         don't implement all the old extension feature
-         in the addin yet.
-         install_sinks ((LPEXCHEXTCALLBACK)m_application);
-      */
+      m_applicationEventSink = install_ApplicationEvents_sink(m_application);
       return S_OK;
     }
   /* Should not happen as OnConnection should be called before */
index 754765d..7b0c29a 100644 (file)
 
 #include <windows.h>
 
-#include "comhelp.h"
+#include "mymapi.h"
 
 class GpgolAddinRibbonExt;
-class GpgolExt;
+class ApplicationEventListener;
 
 /* Enums for the IDTExtensibility2 interface*/
 typedef enum
@@ -205,6 +205,7 @@ private:
 
   LPDISPATCH m_application;
   LPDISPATCH m_addin;
+  LPDISPATCH m_applicationEventSink;
   bool m_disabled;
 
 };
index 4b46139..7331e99 100644 (file)
@@ -20,6 +20,8 @@
 #ifndef MESSAGE_H
 #define MESSAGE_H
 
+#include "myexchext.h"
+#include "mapihelp.h"
 
 int message_incoming_handler (LPMESSAGE message, HWND hwnd, bool force);
 bool message_display_handler (LPMESSAGE message, LPDISPATCH inspector, 
index f3ca441..4be28c3 100644 (file)
@@ -2,6 +2,7 @@
  * Copyright (C) 1998 Justin Bradford
  * Copyright (C) 2000 Fran├žois Gouget
  * Copyright (C) 2005, 2007 g10 Code GmbH
+ * Copyright (C) 2015 Intevation GmbH
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -38,8 +39,6 @@
 #ifndef MAPI_H
 #define MAPI_H
 
-#include "comhelp.h"
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -872,6 +871,22 @@ typedef struct IMAPIFormContainer *LPMAPIFORMCONTAINER;
                              LPMDB mdb, ULONG FAR *r_flags,                   \
                              ULONG FAR *n_entryid, LPBYTE FAR *entryid) PURE
 
+/* In difference to the MY_ macros the DECLARE_ macros are not undefined
+   in this header. */
+
+/*** IUnknown methods ***/
+#define DECLARE_IUNKNOWN_METHODS                                              \
+  STDMETHOD(QueryInterface)(THIS_ REFIID, PVOID*) PURE;                       \
+  STDMETHOD_(ULONG,AddRef)(THIS) PURE;                                        \
+  STDMETHOD_(ULONG,Release)(THIS) PURE
+
+/*** IDispatch methods ***/
+#define DECLARE_IDISPATCH_METHODS                                             \
+  STDMETHOD(GetTypeInfoCount)(THIS_ UINT*) PURE;                              \
+  STDMETHOD(GetTypeInfo)(THIS_ UINT, LCID, LPTYPEINFO*) PURE;                 \
+  STDMETHOD(GetIDsOfNames)(THIS_ REFIID, LPOLESTR*, UINT, LCID, DISPID*) PURE;\
+  STDMETHOD(Invoke)(THIS_ DISPID, REFIID, LCID, WORD,                         \
+                    DISPPARAMS*, VARIANT*, EXCEPINFO*, UINT*) PURE
 
 
 \f
@@ -1165,6 +1180,24 @@ HRESULT WINAPI OpenStreamOnFile(LPALLOCATEBUFFER,LPFREEBUFFER,
                                 ULONG,LPSTR,LPSTR,LPSTREAM*);
 #endif
 
+/* IMAPISecureMessage */
+struct IMAPISecureMessage;
+typedef struct IMAPISecureMessage *LPMAPISECUREMESSAGE;
+
+#undef INTERFACE
+#define INTERFACE IMAPISecureMessage
+
+DECLARE_INTERFACE_(IMAPISecureMessage, IUnknown)
+{
+  DECLARE_IUNKNOWN_METHODS;
+
+  STDMETHOD(Unknown1)(void) PURE;
+  STDMETHOD(Unknown2)(void) PURE;
+  STDMETHOD(Unknown3)(void) PURE;
+  STDMETHOD(Unknown4)(void) PURE;
+  STDMETHOD(Unknown5)(void) PURE;
+  STDMETHOD(GetBaseMessage)(LPMESSAGE FAR *) PURE;
+};
 
 STDAPI MAPIOpenLocalFormContainer (LPMAPIFORMCONTAINER FAR *ppfcnt);
 
index ccc389b..4df3eda 100644 (file)
@@ -1,5 +1,6 @@
 /* oomhelp.cpp - Helper functions for the Outlook Object Model
  *     Copyright (C) 2009 g10 Code GmbH
+ *     Copyright (C) 2015 Intevation GmbH
  * 
  * This file is part of GpgOL.
  * 
@@ -988,3 +989,16 @@ add_oom_attachment (LPDISPATCH disp, wchar_t* inFileW)
 
   return hr == S_OK ? 0 : -1;
 }
+
+LPDISPATCH
+get_object_by_id (LPDISPATCH pDisp, REFIID id)
+{
+  LPDISPATCH disp = NULL;
+
+  if (!pDisp)
+    return NULL;
+
+  if (pDisp->QueryInterface (id, (void **)&disp) != S_OK)
+    return NULL;
+  return disp;
+}
index 5c11797..3aca523 100644 (file)
@@ -1,6 +1,7 @@
 /* oomhelp.h - Defs for helper functions for the Outlook Object Model
- *     Copyright (C) 2009 g10 Code GmbH
- * 
+ *     Copyright (C) 2009 g10 Code GmbH
+ *     Copyright (C) 2015 Intevation GmbH
+ *
  * This file is part of GpgOL.
  * 
  * GpgOL is free software; you can redistribute it and/or
@@ -62,6 +63,14 @@ DEFINE_GUID(IID_IConnectionPointContainer,
 DEFINE_GUID(IID_IPictureDisp,
             0x7bf80981, 0xbf32, 0x101a,
             0x8b, 0xbb, 0x00, 0xaa, 0x00, 0x30, 0x0c, 0xab);
+DEFINE_GUID(IID_ApplicationEvents, 0x0006304E, 0x0000, 0x0000,
+            0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
+DEFINE_GUID(IID_MailItemEvents, 0x0006302B, 0x0000, 0x0000,
+            0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
+DEFINE_GUID(IID_MailItem, 0x00063034, 0x0000, 0x0000,
+            0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
+DEFINE_GUID(IID_IMAPISecureMessage, 0x253cc320, 0xeab6, 0x11d0,
+            0x82, 0x22, 0, 0x60, 0x97, 0x93, 0x87, 0xea);
 
 DEFINE_OLEGUID(IID_IUnknown,                  0x00000000, 0, 0);
 DEFINE_OLEGUID(IID_IDispatch,                 0x00020400, 0, 0);
@@ -135,6 +144,14 @@ add_oom_attachment (LPDISPATCH disp, wchar_t* inFile);
 char *
 get_pa_string (LPDISPATCH pDisp, const char *property);
 
+/* Queries the interface of the dispatcher for the id
+   id. Returns NULL on error. The returned Object
+   must be released.
+   Mainly useful to check if an object is what
+   it appears to be. */
+LPDISPATCH
+get_object_by_id (LPDISPATCH pDisp, REFIID id);
+
 #ifdef __cplusplus
 }
 #endif
index 09cde90..38924f8 100644 (file)
@@ -80,6 +80,8 @@ const void *get_128bit_session_key (void);
 const void *get_64bit_session_marker (void);
 void *create_initialization_vector (size_t nbytes);
 
+#define debug_oom        (opt.enable_debug & DBG_OOM)
+#define debug_oom_extra  (opt.enable_debug & DBG_OOM_EXTRA)
 void log_debug (const char *fmt, ...) __attribute__ ((format (printf,1,2)));
 void log_error (const char *fmt, ...) __attribute__ ((format (printf,1,2)));
 void log_vdebug (const char *fmt, va_list a);
@@ -92,6 +94,9 @@ void log_hexdump (const void *buf, size_t buflen, const char *fmt,
 void log_window_hierarchy (HWND window, const char *fmt, 
                            ...) __attribute__ ((format (printf,2,3)));
 
+#define log_oom if (opt.enable_debug & DBG_OOM) log_debug
+#define log_oom_extra if (opt.enable_debug & DBG_OOM_EXTRA) log_debug
+
 const char *log_srcname (const char *s);
 #define SRCNAME log_srcname (__FILE__)