Started with Revert code.
authorWerner Koch <wk@gnupg.org>
Wed, 30 Jul 2008 17:53:36 +0000 (17:53 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 30 Jul 2008 17:53:36 +0000 (17:53 +0000)
Improved version check.

12 files changed:
doc/gpgol.texi
src/ChangeLog
src/Makefile.am
src/engine-assuan.c
src/ext-commands.cpp
src/ext-commands.h
src/gpgol.def
src/mapihelp.cpp
src/mapihelp.h
src/ol-ext-callback.cpp
src/olflange.cpp
src/olflange.h

index f9f890c..79b1c8c 100644 (file)
@@ -167,7 +167,7 @@ GpgOL uses this internally for creating messages.
 
 @item GpgOL Old Msg Class
 This is a STRING8 property which saves the original PR_MESSAGE_CLASS
-before GpgOL chnages it.
+before GpgOL changes it.
 
 @item GpgOL Attach Type
 This is a property of type LONG and used to further describe the
@@ -280,8 +280,7 @@ given in this entry.  It is assumed that the UI server is stored in the
 @code{Install Directory} (as described above).  This Registry entry
 gives the actual command name relative to this directory.  If the key
 does not exist, is is first searched below @code{HKLM} and then it
-defaults to @code{bin/kleopatra.exe} (FIXME: The final name will be just
-@code{kleopatra.exe}).
+defaults to @code{kleopatra.exe}.
 
 @item HKCU\Software\GNU\GpgOL:enableDebug
 Setting this key to the string @code{1} enables a few extra features in
@@ -307,7 +306,7 @@ Tell what the MIME parser is doing
 Print data lines while parsing MIME.
 @end table
 You may use the regular C-syntax for entering the value.  As an
-alternative you may use the names ofthe flags, separated by space or
+alternative you may use the names of the flags, separated by space or
 comma.
 
 
@@ -486,7 +485,7 @@ exists, delete the properties @code{PR_BODY} and @code{PR_BODY_HTML}.
 @end itemize
 
 Instead of deleting it should be sufficient to make sure
-that such PR_BODYs are not update and don't make it to the disk or a
+that such PR_BODYs are not updated and don't make it to the disk or a
 strage server. 
 
 Implementing such a feature would really help with end-to-end encryption
index 3772361..369cb19 100644 (file)
@@ -1,3 +1,17 @@
+2008-07-30  Werner Koch  <wk@g10code.com>
+
+       * olflange.cpp (Install): Improve version check.
+
+       * revert.cpp, revert.h: New. 
+       * mapihelp.cpp (mapi_attachment_to_body): New.
+       (mapi_get_old_message_class): New.
+       (mapi_change_message_class): Do not release newvalue when saving
+       the old class.
+       * olflange.cpp (parse_version_number, parse_version_string) 
+       (compare_versions, gpgol_check_version): New.
+       * ext-commands.cpp (DoCommand): Support a "revert message class"
+       debug command.
+
 2008-06-27  Werner Koch  <wk@g10code.com>
 
        * mapihelp.cpp (get_gpgololdmsgclass_tag): New.
index 9ee0e60..1844b5a 100644 (file)
@@ -35,6 +35,7 @@ gpgol_SOURCES = \
        myexchext.h                 \
        display.cpp display.h       \
        message.cpp message.h       \
+       revert.cpp revert.h         \
        mimeparser.c mimeparser.h   \
        mimemaker.c mimemaker.h     \
        msgcache.c msgcache.h       \
index 8b1a0a6..9799176 100644 (file)
@@ -349,9 +349,8 @@ get_uiserver_name (void)
       xfree (uiserver);
       if (extra_arglen && access (name, F_OK))
         {
-          /* Kleopatra iis not nstalled: Try GPA instead but if it is
-             also not available still return the Kleopatra
-             filename.  */
+          /* Kleopatra is not installed: Try GPA instead but if it is
+             also not available return the Kleopatra filename.  */
           const char gpaserver[] = "gpa.exe";
           char *name2;
           
@@ -526,7 +525,7 @@ connect_uiserver (assuan_context_t *r_ctx, pid_t *r_pid, ULONG *r_cmdid,
 }
 
 
-/* end the optiona session information. */
+/* Send the optional session information. */
 static void
 send_session_info (assuan_context_t ctx, engine_filter_t filter)
 {
index 0ec349f..5368848 100644 (file)
@@ -39,7 +39,7 @@
 #include "message.h"
 #include "engine.h"
 #include "ext-commands.h"
-
+#include "revert.h"
 
 #define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
                                      SRCNAME, __func__, __LINE__); \
@@ -110,6 +110,7 @@ GpgolExtCommands::GpgolExtCommands (GpgolExt* pParentInterface)
   m_nCmdDebug0 = 0;
   m_nCmdDebug1 = 0;
   m_nCmdDebug2 = 0;
+  m_nCmdDebug3 = 0;
   m_toolbar_info = NULL; 
   m_hWnd = NULL; 
 
@@ -532,6 +533,8 @@ GpgolExtCommands::InstallCommands (
                 &m_nCmdDebug0,
         opt.enable_debug? "GpgOL Debug-1 (open_inspector)":"", &m_nCmdDebug1,
         opt.enable_debug? "GpgOL Debug-2 (change msg class)":"", &m_nCmdDebug2,
+        opt.enable_debug? "GpgOL Debug-3 (revert message class)":"",
+                &m_nCmdDebug3,
         NULL);
 
       add_toolbar (pTBEArray, nTBECnt, 
@@ -792,6 +795,22 @@ GpgolExtCommands::DoCommand (LPEXCHEXTCALLBACK eecb, UINT nCommandID)
       ul_release (message, __func__, __LINE__);
       ul_release (mdb, __func__, __LINE__);
     }
+  else if (opt.enable_debug && nCommandID == m_nCmdDebug3
+           && m_lContext == EECONTEXT_READNOTEMESSAGE)
+    {
+      log_debug ("%s:%s: command Debug3 (revert_message_class) called\n", 
+                 SRCNAME, __func__);
+      hr = eecb->GetObject (&mdb, (LPMAPIPROP *)&message);
+      if (SUCCEEDED (hr))
+        {
+          int rc = gpgol_message_revert (message, 1, 
+                                         KEEP_OPEN_READWRITE|FORCE_SAVE);
+          log_debug ("%s:%s: gpgol_message_revert returns %d\n", 
+                     SRCNAME, __func__, rc);
+       }
+      ul_release (message, __func__, __LINE__);
+      ul_release (mdb, __func__, __LINE__);
+    }
   else
     {
       if (debug_commands)
index 145243d..65ec9bc 100644 (file)
@@ -50,6 +50,7 @@ private:
   UINT  m_nCmdDebug0;
   UINT  m_nCmdDebug1;
   UINT  m_nCmdDebug2;
+  UINT  m_nCmdDebug3;
 
   /* A list of all active toolbar items.  */
   toolbar_info_t m_toolbar_info;
index a480327..4d319ed 100644 (file)
@@ -6,3 +6,8 @@ EXPORTS
     ExchEntryPoint = ExchEntryPoint@0            @1
     DllRegisterServer = DllRegisterServer@0      @2    PRIVATE
     DllUnregisterServer = DllUnregisterServer@0  @3    PRIVATE
+
+    gpgol_check_version = gpgol_check_version@4     @11
+    gpgol_message_revert = gpgol_message_revert@12  @12
+
+
index 6bbeaa5..a828a99 100644 (file)
@@ -951,7 +951,7 @@ mapi_change_message_class (LPMESSAGE message, int sync_override)
   else
     {
       /* Save old message class if not yet done.  (The second
-         consition is just a failsafe check). */
+         condition is just a failsafe check). */
       if (!get_gpgololdmsgclass_tag (message, &tag)
           && PROP_TYPE (propval->ulPropTag) == PT_STRING8)
         {
@@ -968,7 +968,6 @@ mapi_change_message_class (LPMESSAGE message, int sync_override)
               prop.ulPropTag = tag;
               prop.Value.lpszA = propval->Value.lpszA; 
               hr = message->SetProps (1, &prop, NULL);
-              xfree (newvalue);
               if (hr)
                 {
                   log_error ("%s:%s: can't save old message class: hr=%#lx\n",
@@ -982,7 +981,7 @@ mapi_change_message_class (LPMESSAGE message, int sync_override)
       
       /* Change message class.  */
       log_debug ("%s:%s: setting message class to `%s'\n",
-                     SRCNAME, __func__, newvalue);
+                 SRCNAME, __func__, newvalue);
       prop.ulPropTag = PR_MESSAGE_CLASS_A;
       prop.Value.lpszA = newvalue; 
       hr = message->SetProps (1, &prop, NULL);
@@ -1009,7 +1008,7 @@ mapi_change_message_class (LPMESSAGE message, int sync_override)
 
 
 /* Return the message class.  This function will never return NULL so
-   it is only useful for debugging.  Caller needs to release the
+   it is mostly useful for debugging.  Caller needs to release the
    returned string.  */
 char *
 mapi_get_message_class (LPMESSAGE message)
@@ -1040,6 +1039,40 @@ mapi_get_message_class (LPMESSAGE message)
   return retstr;
 }
 
+/* Return the old message class.  This function returns NULL if no old
+   message class has been saved.  Caller needs to release the returned
+   string.  */
+char *
+mapi_get_old_message_class (LPMESSAGE message)
+{
+  HRESULT hr;
+  ULONG tag;
+  LPSPropValue propval = NULL;
+  char *retstr;
+
+  if (!message)
+    return NULL;
+  
+  if (get_gpgololdmsgclass_tag (message, &tag))
+    return NULL;
+
+  hr = HrGetOneProp ((LPMAPIPROP)message, tag, &propval);
+  if (FAILED (hr))
+    {
+      log_error ("%s:%s: HrGetOneProp() failed: hr=%#lx\n",
+                 SRCNAME, __func__, hr);
+      return NULL;
+    }
+
+  if ( PROP_TYPE (propval->ulPropTag) == PT_STRING8 )
+    retstr = xstrdup (propval->Value.lpszA);
+  else
+    retstr = NULL;
+    
+  MAPIFreeBuffer (propval);
+  return retstr;
+}
+
 
 
 /* Return the sender of the message.  According to the specs this is
@@ -1615,8 +1648,8 @@ mapi_release_attach_table (mapi_attach_item_t *table)
 
 
 /* Return an attachment as a new IStream object.  Returns NULL on
-   failure.  If R_ATATCH is not NULL the actual attachment will not be
-   released by stored at that address; the caller needs to release it
+   failure.  If R_ATTACH is not NULL the actual attachment will not be
+   released but stored at that address; the caller needs to release it
    in this case.  */
 LPSTREAM
 mapi_get_attach_as_stream (LPMESSAGE message, mapi_attach_item_t *item,
@@ -1807,7 +1840,7 @@ mapi_get_attach (LPMESSAGE message, int unprotect,
 }
 
 
-/* Mark this attachment as the orginal MOSS message.  We set a custom
+/* Mark this attachment as the original MOSS message.  We set a custom
    property as well as the hidden flag.  */
 int 
 mapi_mark_moss_attach (LPMESSAGE message, mapi_attach_item_t *item)
@@ -1828,13 +1861,6 @@ mapi_mark_moss_attach (LPMESSAGE message, mapi_attach_item_t *item)
       return -1;
     }
 
-  if (FAILED (hr)) 
-    {
-      log_error ("%s:%s: can't map %s property: hr=%#lx\n",
-                 SRCNAME, __func__, "GpgOL Attach Type", hr); 
-      goto leave;
-    }
-    
   if (get_gpgolattachtype_tag (message, &prop.ulPropTag) )
     goto leave;
   prop.Value.l = ATTACHTYPE_MOSS;
@@ -1977,7 +2003,7 @@ mapi_test_sig_status (LPMESSAGE msg)
     return 0; /* No.  */  
 
   /* We return False if we have an unknown signature status (?) or the
-     message has been setn by us and not yet checked (@).  */
+     message has been sent by us and not yet checked (@).  */
   if (PROP_TYPE (propval->ulPropTag) == PT_STRING8)
     yes = !(propval->Value.lpszA && (!strcmp (propval->Value.lpszA, "?")
                                      || !strcmp (propval->Value.lpszA, "@")));
@@ -2109,7 +2135,7 @@ mapi_get_gpgol_charset (LPMESSAGE obj)
 }
 
 
-/* Set the GpgOl charset t an asstachment. 
+/* Set the GpgOl charset property to an attachment. 
    Note that this function does not call SaveChanges.  */
 int 
 mapi_set_gpgol_charset (LPMESSAGE obj, const char *charset)
@@ -2535,3 +2561,89 @@ mapi_get_gpgol_body_attachment (LPMESSAGE message,
   return 0;
 }
 
+
+/* Copy the attachment ITEM of the message MESSAGE verbatim to the
+   PR_BODY property.  Returns 0 on success.  This function does not
+   call SaveChanges. */
+int
+mapi_attachment_to_body (LPMESSAGE message, mapi_attach_item_t *item)
+{
+  int result = -1;
+  HRESULT hr; 
+  LPATTACH att = NULL;
+  LPSTREAM instream = NULL;
+  LPSTREAM outstream = NULL;
+  LPUNKNOWN punk;
+
+  if (!message || !item || item->end_of_table || item->mapipos == -1)
+    return -1; /* Error.  */
+
+  hr = message->OpenAttach (item->mapipos, NULL, MAPI_BEST_ACCESS, &att);
+  if (FAILED (hr))
+    {
+      log_error ("%s:%s: can't open attachment at %d: hr=%#lx",
+                 SRCNAME, __func__, item->mapipos, hr);
+      goto leave;
+    }
+  if (item->method != ATTACH_BY_VALUE)
+    {
+      log_error ("%s:%s: attachment: method not supported", SRCNAME, __func__);
+      goto leave;
+    }
+
+  hr = att->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream, 
+                          0, 0, (LPUNKNOWN*) &instream);
+  if (FAILED (hr))
+    {
+      log_error ("%s:%s: can't open data stream of attachment: hr=%#lx",
+                 SRCNAME, __func__, hr);
+      goto leave;
+    }
+
+
+  punk = (LPUNKNOWN)outstream;
+  hr = message->OpenProperty (PR_BODY_A, &IID_IStream, 0,
+                              MAPI_CREATE|MAPI_MODIFY, &punk);
+  if (FAILED (hr))
+    {
+      log_error ("%s:%s: can't open body stream for update: hr=%#lx",
+                 SRCNAME, __func__, hr);
+      goto leave;
+    }
+  outstream = (LPSTREAM)punk;
+
+  {
+    ULARGE_INTEGER cb;
+    cb.QuadPart = 0xffffffffffffffffll;
+    hr = instream->CopyTo (outstream, cb, NULL, NULL);
+  }
+  if (hr)
+    {
+      log_error ("%s:%s: can't copy streams: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      goto leave;
+    }
+  hr = outstream->Commit (0);
+  if (hr)
+    {
+      log_error ("%s:%s: commiting output stream failed: hr=%#lx",
+                 SRCNAME, __func__, hr);
+      goto leave;
+    }
+  result = 0;
+  
+ leave:
+  if (outstream)
+    {
+      if (result)
+        outstream->Revert ();
+      outstream->Release ();
+    }
+  if (instream)
+    instream->Release ();
+  if (att)
+    att->Release ();
+  return result;
+}
+
+
index b1f9cab..43cdfcb 100644 (file)
@@ -112,6 +112,7 @@ int mapi_set_header (LPMESSAGE msg, const char *name, const char *val);
 
 int mapi_change_message_class (LPMESSAGE message, int sync_override);
 char *mapi_get_message_class (LPMESSAGE message);
+char *mapi_get_old_message_class (LPMESSAGE message);
 char *mapi_get_sender (LPMESSAGE message);
 msgtype_t mapi_get_message_type (LPMESSAGE message);
 int mapi_to_mime (LPMESSAGE message, const char *filename);
@@ -158,6 +159,8 @@ int   mapi_get_gpgol_body_attachment (LPMESSAGE message,
                                       int *r_ishtml, int *r_protected);
 
 
+int   mapi_attachment_to_body (LPMESSAGE message, mapi_attach_item_t *item);
+
 #ifdef __cplusplus
 }
 #endif
index 5d03c39..e893b52 100644 (file)
@@ -75,7 +75,7 @@ find_outlook_property (LPEXCHEXTCALLBACK lpeecb,
   wchar_t *wname;
   const char *s;
 
-  log_debug ("%s:%s: looking for `%s'\n", SRCNAME, __func__, name);
+  // log_debug ("%s:%s: looking for `%s'\n", SRCNAME, __func__, name);
 
   pCb = NULL;
   pObj = NULL;
@@ -136,8 +136,8 @@ find_outlook_property (LPEXCHEXTCALLBACK lpeecb,
   if (r_dispid)
     *r_dispid = dispid;
 
-  log_debug ("%s:%s:    got IDispatch=%p dispid=%u\n",
-            SRCNAME, __func__, pDisp, (unsigned int)dispid);
+  //log_debug ("%s:%s:    got IDispatch=%p dispid=%u\n",
+  //        SRCNAME, __func__, pDisp, (unsigned int)dispid);
   return pDisp;
 }
 
@@ -277,8 +277,8 @@ get_outlook_property (void *pEECB, const char *key)
   else if (aVariant.bstrVal)
     {
       result = wchar_to_utf8 (aVariant.bstrVal);
-      log_debug ("%s:%s: `%s' is `%s'",
-                 SRCNAME, __func__, key, result);
+      //log_debug ("%s:%s: `%s' is `%s'",
+      //           SRCNAME, __func__, key, result);
       /* From MSDN (Invoke): It is up to the caller to free the return value.*/
       SysFreeString (aVariant.bstrVal);
     }
index 236b266..b53aeb7 100644 (file)
@@ -48,6 +48,7 @@
 #include "property-sheets.h"
 #include "attached-file-events.h"
 #include "item-events.h"
+#include "ol-ext-callback.h"
 
 /* The GUID for this plugin.  */
 #define CLSIDSTR_GPGOL   "{42d30988-1a3a-11da-c687-000d6080e735}"
@@ -283,6 +284,83 @@ DllUnregisterServer (void)
 }
 
 
+static const char*
+parse_version_number (const char *s, int *number)
+{
+  int val = 0;
+
+  if (*s == '0' && digitp (s+1))
+    return NULL;  /* Leading zeros are not allowed.  */
+  for (; digitp (s); s++)
+    {
+      val *= 10;
+      val += *s - '0';
+    }
+  *number = val;
+  return val < 0 ? NULL : s;
+}
+
+static const char *
+parse_version_string (const char *s, int *major, int *minor, int *micro)
+{
+  s = parse_version_number (s, major);
+  if (!s || *s != '.')
+    return NULL;
+  s++;
+  s = parse_version_number (s, minor);
+  if (!s || *s != '.')
+    return NULL;
+  s++;
+  s = parse_version_number (s, micro);
+  if (!s)
+    return NULL;
+  return s;  /* Patchlevel.  */
+}
+
+static const char *
+compare_versions (const char *my_version, const char *req_version)
+{
+  int my_major, my_minor, my_micro;
+  int rq_major, rq_minor, rq_micro;
+  const char *my_plvl, *rq_plvl;
+
+  if (!req_version)
+    return my_version;
+  if (!my_version)
+    return NULL;
+
+  my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro);
+  if (!my_plvl)
+    return NULL;       /* Very strange: our own version is bogus.  */
+  rq_plvl = parse_version_string(req_version,
+                                &rq_major, &rq_minor, &rq_micro);
+  if (!rq_plvl)
+    return NULL;       /* Requested version string is invalid.  */
+
+  if (my_major > rq_major
+       || (my_major == rq_major && my_minor > rq_minor)
+      || (my_major == rq_major && my_minor == rq_minor 
+         && my_micro > rq_micro)
+      || (my_major == rq_major && my_minor == rq_minor
+         && my_micro == rq_micro
+         && strcmp( my_plvl, rq_plvl ) >= 0))
+    {
+      return my_version;
+    }
+  return NULL;
+}
+
+/* Check that the the version of GpgOL is at minimum the requested one
+ * and return GpgOL's version string; return NULL if that condition is
+ * not met.  If a NULL is passed to this function, no check is done
+ * and the version string is simply returned.  */
+EXTERN_C const char * __stdcall
+gpgol_check_version (const char *req_version)
+{
+  return compare_versions (PACKAGE_VERSION, req_version);
+}
+
+
 
 \f
 /* The entry point which Exchange/Outlook calls.  This is called for
@@ -334,6 +412,10 @@ GpgolExt::GpgolExt (void)
   if (!g_initdll)
     {
       read_options ();
+      log_debug ("%s:%s: this is GpgOL %s\n", 
+                 SRCNAME, __func__, PACKAGE_VERSION);
+      log_debug ("%s:%s:   using GPGME %s\n", 
+                 SRCNAME, __func__, gpgme_check_version (NULL));
       engine_init ();
       g_initdll = TRUE;
       log_debug ("%s:%s: first time initialization done\n",
@@ -448,27 +530,30 @@ STDMETHODIMP
 GpgolExt::Install(LPEXCHEXTCALLBACK pEECB, ULONG lContext, ULONG lFlags)
 {
   static int version_shown;
-  
+  static char *olversion;
   ULONG lBuildVersion;
   ULONG lActualVersion;
   ULONG lVirtualVersion;
 
+
   /* Save the context in an instance variable. */
   m_lContext = lContext;
 
   log_debug ("%s:%s: context=%s flags=0x%lx\n", SRCNAME, __func__,
              ext_context_name (lContext), lFlags);
   
-  /* Check version. */
-  log_debug ("%s:%s: this is %s\n", SRCNAME, __func__, PACKAGE_STRING);
+  /* Check version.  This install method is called by Outlook even
+     before the OOM interface is available, thus we need to keep on
+     checking for the olversion until we get one.  Only then we can
+     display the complete version and do a final test to see whether
+     this is a supported version. */
+  if (!olversion)
+    olversion = get_outlook_property (pEECB, "Application.Version");
   pEECB->GetVersion (&lBuildVersion, EECBGV_GETBUILDVERSION);
   pEECB->GetVersion (&lActualVersion, EECBGV_GETACTUALVERSION);
   pEECB->GetVersion (&lVirtualVersion, EECBGV_GETVIRTUALVERSION);
   if (!version_shown)
     {
-      version_shown = 1;
-      log_debug ("%s:%s: using gpgme %s\n", 
-                 SRCNAME, __func__, gpgme_check_version (NULL));
       log_debug ("%s:%s: detected Outlook build version 0x%lx (%lu.%lu)\n",
                  SRCNAME, __func__, lBuildVersion,
                  (lBuildVersion & EECBGV_BUILDVERSION_MAJOR_MASK) >> 16,
@@ -485,6 +570,12 @@ GpgolExt::Install(LPEXCHEXTCALLBACK pEECB, ULONG lContext, ULONG lFlags)
                  (unsigned int)((lVirtualVersion >> 16) & 0xff),
              (unsigned int)((lVirtualVersion >> 8) & 0xff),
                  (unsigned int)(lVirtualVersion & 0xff));
+      if (olversion)
+        {
+          log_debug ("%s:%s:                    OOM version %s\n",
+                     SRCNAME, __func__, olversion);
+          version_shown = 1;
+        }
     }
   
   if (EECBGV_BUILDVERSION_MAJOR
@@ -495,8 +586,14 @@ GpgolExt::Install(LPEXCHEXTCALLBACK pEECB, ULONG lContext, ULONG lFlags)
       return S_FALSE;
     }
   
-  if ((lBuildVersion & EECBGV_BUILDVERSION_MAJOR_MASK) < 13
-      ||(lBuildVersion & EECBGV_BUILDVERSION_MINOR_MASK) < 1573)
+  /* The version numbers as returned by GetVersion are the same for
+     OL2003 as well as for recent OL2002.  My guess is that this
+     version comes from the Exchange Client Extension API and that has
+     been updated in all version of OL.  Thus we also need to check
+     the version number as taken from the Outlook Object Model.  */
+  if ( (lBuildVersion & EECBGV_BUILDVERSION_MAJOR_MASK) < 13
+       || (lBuildVersion & EECBGV_BUILDVERSION_MINOR_MASK) < 1573
+       || (olversion && atoi (olversion) < 11) )
     {
       static int shown;
       HWND hwnd;
index 1cd922b..ddffe25 100644 (file)
@@ -72,4 +72,7 @@ public:
 
 const char *ext_context_name (unsigned long no);
 
+EXTERN_C const char * __stdcall gpgol_check_version (const char *req_version);
+
+
 #endif /*OLFLANGE_H*/