Thursday, August 5, 2010

Replacement of h:head in order to fix resources rendering

I came up against need to reimplement JSF 2 head component by reason of an issue. I have implemented my custom head tag and achieved more flexibility. I'm going to begin with a history before I will show the code. I'm working with PrimeFaces component library. PrimeFaces comes with predefined CSS settings. Developers often want to overwrite these predefined styles. I do that quite simple - include PrimeFaces resources inside of the head tag and include my own resources close to the body tag. Including of PrimeFaces resources (by p:resource) is actually not necessary for regular requests with full page refresh. But it's necessary if you have partial updates via ajax. An output looks like
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
<link rel="stylesheet" type="text/css" href="/icManagement/primefaces_resource/2.0.2/yui/tabview/assets/skins/sam/tabview.css" />
<link rel="stylesheet" type="text/css" href="/icManagement/primefaces_resource/2.0.2/yui/accordion/assets/skins/sam/accordionview.css" />
<link rel="stylesheet" type="text/css" href="/icManagement/primefaces_resource/2.0.2/yui/menu/assets/skins/sam/menu.css" />
<script type="text/javascript" src="/icManagement/primefaces_resource/2.0.2/yui/menu/menu-min.js"></script>
<script type="text/javascript" src="/icManagement/primefaces_resource/2.0.2/yui/json/json-min.js"></script>
...
<link type="text/css" rel="stylesheet" href="/icManagement/javax.faces.resource/sky/css/ip-jsftoolkit.css.jsf?ln=themes" />
<link type="text/css" rel="stylesheet" href="/icManagement/javax.faces.resource/sky/css/incursa-management.css.jsf?ln=themes" />
<script type="text/javascript" src="/icManagement/javax.faces.resource/ip-jsftoolkit.js.jsf?ln=js"></script>
<script type="text/javascript" src="/icManagement/javax.faces.resource/incursa-management.js.jsf?ln=js"></script>
Custom CSS, JavaScript files are places after PrimeFaces' one. Well. Mojarra 2.0.2 had an issue, that CSS selectors placed in h:head cannot override styles in implicitly added resources. This issue was fixed in Mojarra 2.0.3. But this bugfixing is not enough because it fixed one issue and caused another one. Why? Often we need <meta> tags to prevent page caching or make sites search engine friendly. Using the <link> tag you can add a "canonical" url to your page and a favicon. The order of these additional tags can be very important. Internet Explorer introduced e.g. a special <meta> tag
<meta http-equiv="X-UA-Compatible" content="..." />
Content of the X-UA-Compatible <meta> tag helps to control document compatibility. We can specify the rendering engine. For example, inserting this:
<meta http-equiv="X-UA-Compatible" content="IE=8" />
into the head of a document would force IE8 renders the page using the new standards mode. I often heard "IE8 breaks my site". Add this <meta> tag or HTTP header which can fix the site:
<meta http-equiv="X-UA-Compatible" content="IE=7" />
It fixes your site, at least until you can sort out the IE8 issues. I use on my pages some great features which are supported by IE8 only. For instance, IE6 / IE7 don't support border color for select elements. You cannot set border color for h:selectOneMenu or h:selectManyLisbox. Unfortunately, IE8 was rendering my pages in IE7 mode. DOCTYPE was ok, but maybe Apache settings were fault or something else. I decided to put content="IE=8" on all my XHTML pages. But it didn't have any effect. The reason: X-UA-Compatible must be the first child of the head! Internet Explorer doesn't accept this <meta> tag if it's placed after <link> or <script> tags. Many people also encountered the same problem and trey tried making the <meta> tag X-UA-Compatible as the first child of the head to solve this problem. But how could I place this <meta> tag quite at the beginning? The standard h:head tag of the last Mojarra implementation doesn't have this possibility. It always renders at first resources added by JSF 2 facility (don't matter what is placed within head). My output was similar this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
<link rel="stylesheet" type="text/css" href="/icManagement/primefaces_resource/2.0.2/yui/tabview/assets/skins/sam/tabview.css" />
<link rel="stylesheet" type="text/css" href="/icManagement/primefaces_resource/2.0.2/yui/accordion/assets/skins/sam/accordionview.css" />
<link rel="stylesheet" type="text/css" href="/icManagement/primefaces_resource/2.0.2/yui/menu/assets/skins/sam/menu.css" />
<script type="text/javascript" src="/icManagement/primefaces_resource/2.0.2/yui/menu/menu-min.js"></script>
<script type="text/javascript" src="/icManagement/primefaces_resource/2.0.2/yui/json/json-min.js"></script>
...
<meta http-equiv="X-UA-Compatible" content="IE=8"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta http-equiv="pragma" content="no-cache"/>
<meta http-equiv="cache-control" content="no-cache"/>
<meta http-equiv="expires" content="0"/>
You see, the standrad h:head is not flexible enough. Oracle ADF has a special <af:document> tag where you can use the facet "metaContainer" of the <af:document> to output the tags in the right place. Eureka! We need facets in the head component for right order controling of rendered resources. Let's write a custom head component:
public class Head extends UIOutput
{
 public static final String COMPONENT_TYPE = "ip.client.jsftoolkit.components.Head";
 public static final String COMPONENT_FAMILY = "ip.client.jsftoolkit.components";
 private static final String DEFAULT_RENDERER = "ip.client.jsftoolkit.components.HeadRenderer";

 /**
  * Properties that are tracked by state saving.
  */
 protected enum PropertyKeys
 {
  title, shortcutIcon;

  String toString;

  PropertyKeys(String toString)
  {
   this.toString = toString;
  }

  PropertyKeys()
  {
  }

  public String toString()
  {
   return ((this.toString != null) ? this.toString : super.toString());
  }
 }

 public Head()
 {
  setRendererType(DEFAULT_RENDERER);
 }

 public String getFamily()
 {
  return COMPONENT_FAMILY;
 }

 public String getTitle()
 {
  return (String) getStateHelper().eval(PropertyKeys.title, null);
 }

 public void setTitle(String title)
 {
  getStateHelper().put(PropertyKeys.title, title);
 }

 public String getShortcutIcon()
 {
  return (String) getStateHelper().eval(PropertyKeys.shortcutIcon, null);
 }

 public void setShortcutIcon(String shortcutIcon)
 {
  getStateHelper().put(PropertyKeys.shortcutIcon, shortcutIcon);
 }
}
I took "title" and "shortcutIcon" attributes to the head component in order to achieve more convenience by using. Now the renderer:
import java.io.IOException;
import java.util.ListIterator;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.Renderer;

public class HeadRenderer extends Renderer
{
 public void encodeBegin(FacesContext facesContext, UIComponent component) throws IOException
 {
  ResponseWriter writer = facesContext.getResponseWriter();
  writer.startElement("head", component);

  UIComponent first = component.getFacet("first");
  if (first != null) {
   first.encodeAll(facesContext);
  }

  UIViewRoot viewRoot = facesContext.getViewRoot();
  ListIterator<UIComponent> iter = (viewRoot.getComponentResources(facesContext, "head")).listIterator();
  while (iter.hasNext()) {
   UIComponent resource = (UIComponent) iter.next();
   resource.encodeAll(facesContext);
  }
 }

 public void encodeChildren(FacesContext facesContext, UIComponent component) throws IOException
 {
 }

 public void encodeEnd(FacesContext facesContext, UIComponent component) throws IOException
 {
  ResponseWriter writer = facesContext.getResponseWriter();
  Head head = (Head) component;

  UIComponent last = component.getFacet("last");
  if (last != null) {
   last.encodeAll(facesContext);
  }

  if (head.getTitle() != null) {
   writer.startElement("title", null);
   writer.write(head.getTitle());
   writer.endElement("title");
  }

  if (head.getShortcutIcon() != null) {
   writer.startElement("link", null);
   writer.writeAttribute("rel", "shortcut icon", null);
   writer.writeAttribute("href", head.getShortcutIcon(), null);
   writer.endElement("link");
  }

  writer.endElement("head");
 }
}
I introduced two facets called "first" and "last". The resource rendering order with this approach:
  1. Resources placed into "first" facet.
  2. Resources added by JSF 2 facility.
  3. Resources placed into "last" facet.
  4. Page title and shortcut icon.
The new component and its tag have be registered as usual in faces-config and taglib XMLs.
<renderer>
 <component-family>ip.client.jsftoolkit.components</component-family>
 <renderer-type>ip.client.jsftoolkit.components.HeadRenderer</renderer-type>
 <renderer-class>ip.client.jsftoolkit.components.head.HeadRenderer</renderer-class>
</renderer>
<component>
 <component-type>ip.client.jsftoolkit.components.Head</component-type>
 <component-class>ip.client.jsftoolkit.components.head.Head</component-class>
</component>

<tag>
 <tag-name>head</tag-name>
 <component>
  <component-type>ip.client.jsftoolkit.components.Head</component-type>
  <renderer-type>ip.client.jsftoolkit.components.HeadRenderer</renderer-type>
 </component>
</tag>
Using on page:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
   xmlns:f="http://java.sun.com/jsf/core"
   xmlns:h="http://java.sun.com/jsf/html"
   xmlns:ui="http://java.sun.com/jsf/facelets"
   xmlns:jtcomp="http://ip.client/ip-jsftoolkit/components">
<f:view contentType="text/html" locale="#{userSettings.locale}">
<ui:insert name="metadata"/>
<jtcomp:head title="#{messageParameters['frameworkTitle']}" shortcutIcon="#{request.contextPath}/favicon.ico">
  <f:facet name="first">
    <meta http-equiv="X-UA-Compatible" content="IE=8"/>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta http-equiv="pragma" content="no-cache"/>
    <meta http-equiv="cache-control" content="no-cache"/>
    <meta http-equiv="expires" content="0"/>
  </f:facet>
  <f:facet name="last">
    <style type="text/css">
      html, body {
        width: 100%;
        height: 100%;
        margin: 0px;
      }
    </style>
    <script type="text/javascript">...</script>
  </f:facet>
  <ui:insert name="resources-pf"/>
</jtcomp:head>
<h:body>
...
 <ui:insert name="resources-app"/>
</h:body>
</f:view>
</html>
Generated output in the right defined order:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
  <meta http-equiv="X-UA-Compatible" content="IE=8" />
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <meta http-equiv="pragma" content="no-cache" />
  <meta http-equiv="cache-control" content="no-cache" />
  <meta http-equiv="expires" content="0" />
  
  <link rel="stylesheet" type="text/css" href="/icManagement/primefaces_resource/2.0.2/yui/tabview/assets/skins/sam/tabview.css" />
  <link rel="stylesheet" type="text/css" href="/icManagement/primefaces_resource/2.0.2/yui/accordion/assets/skins/sam/accordionview.css" />
  <link rel="stylesheet" type="text/css" href="/icManagement/primefaces_resource/2.0.2/yui/menu/assets/skins/sam/menu.css" />
  <script type="text/javascript" src="/icManagement/primefaces_resource/2.0.2/yui/menu/menu-min.js"></script>
  <script type="text/javascript" src="/icManagement/primefaces_resource/2.0.2/yui/json/json-min.js"></script>
  ...
  <link type="text/css" rel="stylesheet" href="/icManagement/javax.faces.resource/sky/css/ip-jsftoolkit.css.jsf?ln=themes" />
  <link type="text/css" rel="stylesheet" href="/icManagement/javax.faces.resource/sky/css/incursa-management.css.jsf?ln=themes" />
  <script type="text/javascript" src="/icManagement/javax.faces.resource/ip-jsftoolkit.js.jsf?ln=js"></script>
  <script type="text/javascript" src="/icManagement/javax.faces.resource/incursa-management.js.jsf?ln=js"></script>
  <style type="text/css">
   html, body {
    width: 100%;
    height: 100%;
    margin: 0px;
   }
  </style>
  <script type="text/javascript">...</script>
  <title>Management Client</title>
  <link rel="shortcut icon" href="/icManagement/favicon.ico" />
  </head>
...
Cool? :-)

Wednesday, August 4, 2010

Metadata extraction from MS Outlook messages

In the previous post I showed how to extract metadata from MS Office documents. In this post I will show how to extract metadata from MS Outlook messages. There are some solutions like Apache POI-HSMF oder msgparser. But all these solutions are not universal enough, API are restricted and algorithm are not reliable because they try to find / match some subsequences without deep knowledge about MSG file structure. My task was to extract the properties "Subject", "From", "To", "Cc", "Sent", "Received". Well. Let's start with MsgExtractor. The method to be called after instantiation is parseMetaData. The idea is similar to the one for MS Office documents.
public class MsgExtractor
{
 public static final String PROPERTY_SUBJECT = "Subject";
 public static final String PROPERTY_FROM = "From";
 public static final String PROPERTY_TO = "To";
 public static final String PROPERTY_CC = "Cc";
 public static final String PROPERTY_SENT = "Sent";
 public static final String PROPERTY_RECEIVED = "Received";

 private static final Map TEMPLATE_PROPERTY_2_GET_METHODS;

 static {
  TEMPLATE_PROPERTY_2_GET_METHODS = new HashMap();
  TEMPLATE_PROPERTY_2_GET_METHODS.put(PROPERTY_SUBJECT, "getSubject");
  TEMPLATE_PROPERTY_2_GET_METHODS.put(PROPERTY_FROM, "getDisplayFrom");
  TEMPLATE_PROPERTY_2_GET_METHODS.put(PROPERTY_TO, "getDisplayTo");
  TEMPLATE_PROPERTY_2_GET_METHODS.put(PROPERTY_CC, "getDisplayCc");
  TEMPLATE_PROPERTY_2_GET_METHODS.put(PROPERTY_SENT, "getClientSubmitTime");
  TEMPLATE_PROPERTY_2_GET_METHODS.put(PROPERTY_RECEIVED, "getMessageDeliveryTime");
 }
 
 final String[] properties;

 public MsgExtractor(final String[] properties)
 {
  this.properties = (properties == null ? new String[] {} : properties);
 }

 public Map parseMetaData(final byte[] data) throws MetaDataExtractionException
 {
  // check at first whether byte array data is null and throw an exception if it's null
  ...
  
  if (properties.length == 0) {
   return Collections.EMPTY_MAP;
  }

  Map metaData = new HashMap();
  OutlookMessage outlookMessage = new OutlookMessage(new ByteArrayInputStream(data));

  try {
   for (int i = 0; i < properties.length; i++) {
    Object propertyValue = null;
    String strMethod = (String) TEMPLATE_PROPERTY_2_GET_METHODS.get(properties[i]);
    if (strMethod != null) {
     Method method = OutlookMessage.class.getMethod(strMethod, (Class[]) null);
     propertyValue = method.invoke(outlookMessage, (Object[]) (Object[]) null);
    } else {
     LOG.warn("No Get-Method to the MAPI-Property was found for the defined template property '"
              + properties[i] + "'");
    }

    // buffer property values
    metaData.put(properties[i], propertyValue);
   }
  } catch (final Exception e) {
   // failed to extract meta data ==> error handling
   ...
  }

  return metaData;
 }
}
The main class is OutlookMessage which hides a magic work. It looks as follows
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Iterator;

import org.apache.commons.io.EndianUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
import org.apache.poi.poifs.filesystem.Entry;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;

**
 * Reads an Outlook MSG File in and provides hooks into its data structure. Some hints to the structure were found under
 *
 * <ul>
 *   <li>http://www.fileformat.info/format/outlookmsg/</li>
 *   <li>http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/</li>
 *   <li>http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.ole/2006-08/msg00123.html
 *   </li>
 * </ul>
 *
 */
public class OutlookMessage
{
 private static final Log LOG = LogFactory.getLog(OutlookMessage.class);

 /** id for the mapi property "subject" */
 private static final int PROP_SUBJECT = 0x0037;

 /** id for the mapi property "display from" */
 private static final int PROP_DISPLAY_FROM = 0x0C1A;

 /** id for the mapi property "display to" */
 private static final int PROP_DISPLAY_TO = 0x0E04;

 /** id for the mapi property "display cc" */
 private static final int PROP_DISPLAY_CC = 0x0E03;

 /** reversed endian format of the property id "client submit time" */
 private static final byte[] PROP_CLIENT_SUBMIT_TIME = {0x40, 0x00, 0x39, 0x00};

 /** reversed endian format of the property id "message delivery time" */
 private static final byte[] PROP_MESSAGE_DELIVERY_TIME = {0x40, 0x00, 0x06, 0x0E};

 private String subject;

 private String displayFrom;

 private String displayTo;

 private String displayCc;

 private Date clientSubmitTime;

 private Date messageDeliveryTime;

 public OutlookMessage(String filename) throws IOException
 {
  this(new FileInputStream(new File(filename)));
 }

 public OutlookMessage(InputStream in)
 {
  try {
   POIFSFileSystem fs = new POIFSFileSystem(in);
   initMapiProperties(fs);
  } catch (IOException ioe) {
   LOG.warn("Some properties could be not parsed from given MSG message " + ioe);
  } finally {
   if (in != null) {
    try {
     in.close();
    } catch (IOException e) {
     ;
    }
   }
  }
 }

 private void initMapiProperties(POIFSFileSystem fs) throws IOException
 {
  DirectoryEntry root = fs.getRoot();

  for (Iterator iter = root.getEntries(); iter.hasNext();) {
   Entry entry = (Entry) iter.next();
   if (!(entry instanceof DocumentEntry)) {
    continue;
   }

   String entryName = entry.getName();
   if (entryName == null) {
    continue;
   }

   // parse MAPI properties
   if (entryName.startsWith("__substg1.0_")) {
    byte[] substgBytes = getBytes((DocumentEntry) entry);

    int id = Integer.parseInt(entryName.substring(12, 16), 16);
    int type = Integer.parseInt(entryName.substring(16, 20), 16);

    if (id == PROP_SUBJECT) {
     // subject
     this.subject = getString(substgBytes, isUnicodeString(type));
    } else if (id == PROP_DISPLAY_FROM) {
     // display from
     this.displayFrom = getString(substgBytes, isUnicodeString(type));
    } else if (id == PROP_DISPLAY_TO) {
     // display to
     this.displayTo = getString(substgBytes, isUnicodeString(type));
    } else if (id == PROP_DISPLAY_CC) {
     // display cc
     this.displayCc = getString(substgBytes, isUnicodeString(type));
    }
   } else if (entryName.startsWith("__properties_version1.0")) {
    byte[] propBytes = getBytes((DocumentEntry) entry);
    int offset = 0;
    int bytesLength = propBytes.length;

    while (offset + 16 <= bytesLength) {
     byte[] propId = read4(propBytes, offset);

     if (compare(propId, PROP_CLIENT_SUBMIT_TIME)) {
      // read value
      this.clientSubmitTime = getDate(propBytes, offset);
     } else if (compare(propId, PROP_MESSAGE_DELIVERY_TIME)) {
      // read value
      this.messageDeliveryTime = getDate(propBytes, offset);
     }

     offset = offset + 16;
    }
   }
  }
 }

 private byte[] getBytes(DocumentEntry docEntry) throws IOException
 {
  DocumentInputStream dis = new DocumentInputStream(docEntry);
  byte[] propBytes = new byte[dis.available()];

  try {
   byte[] bytes = new byte[4096];
   int readCount;
   int curPosition = 0;
   while ((readCount = dis.read(bytes)) > -1) {
    System.arraycopy(bytes, 0, propBytes, curPosition, readCount);
    curPosition = curPosition + readCount;
   }
  } finally {
   dis.close();
  }

  return propBytes;
 }

 private String getString(byte[] bytes, boolean isUnicode)
 {
  if (ArrayUtils.isEmpty(bytes)) {
   return null;
  }

  try {
   String str;
   if (isUnicode) {
    str = new String(bytes, 0, bytes.length, "UTF-16LE");
   } else {
    str = new String(bytes, 0, bytes.length, "ISO8859_1");
   }

   int len = str.length();
   while (len > 0 && str.charAt(len - 1) == '\0') {
    len--;
   }

   if (len != str.length()) {
    str = str.substring(0, len);
   }

   if (StringUtils.isBlank(str)) {
    str = null;
   }

   return str;
  } catch (UnsupportedEncodingException ignore) {
   ;
  }

  return null;
 }

 private boolean isUnicodeString(int type)
 {
  return (type == 0x001F ? true : false); // for not unicode string type = 0x001E
 }

 private byte[] read4(byte[] data, int offset)
 {
  byte[] readBytes = new byte[4];
  System.arraycopy(data, offset, readBytes, 0, 4);

  return readBytes;
 }

 private byte[] read8(byte[] data, int offset)
 {
  byte[] readBytes = new byte[8];
  System.arraycopy(data, offset, readBytes, 0, 8);

  return readBytes;
 }

 private boolean compare(byte[] b1, byte[] b2)
 {
  for (int i = 0; i < b1.length; ++i) {
   if (b1[i] != b2[i]) {
    return false;
   }
  }

  return true;
 }

 private Date getDate(byte[] propBytes, int offset)
 {
  // read value
  byte[] value = read8(propBytes, offset + 8);

  // convert to long (reverse Endian format)
  long time = EndianUtils.readSwappedLong(value, 0);

  // FILETIME 64-bit int number of 100ns periods since Jan 1, 1601 ==>
  // convert ns to ms and substruct milliseconds between 1/1/1601 and 1/1/1970
  time = (time / 10 / 1000) - 1000L * 60L * 60L * 24L * (365L * 369L + 89L);

  return new Date(time);
 }
 
 // getter

 public String getSubject() {return subject;}

 public String getDisplayFrom() {return displayFrom;}

 public String getDisplayTo() {return displayTo;}

 public String getDisplayCc() {return displayCc;}

 public Date getClientSubmitTime() {return clientSubmitTime;}

 public Date getMessageDeliveryTime() {return messageDeliveryTime;}
}
Parse of MAPI properties in the stream __substg1.0_ don't cause any problems. That are quite normally strings. But some properties are in a binary stream called __properties_version1.0. This is the most interesting stream.
Hint to the structure of __properties_version1.0: Each MAPI property has a well documented hex id, available on the MSDN site. For example, the property PR_CLIENT_SUBMIT_TIME has an identifier of 0x00390040. If you open the properties stream and work with 16 byte rows, divided into two 8 byte sections (just like a hex editor), you will see that the property identifier is in the first 8 byte section and the value is in the second. The trick is to reverse the Endian of the property ID. So the ID for the PR_CLIENT_SUBMIT_TIME property becomes 40 00 39 00. Locate this hex block and you will have the value (in Big Endian FILETIME format) in the next 8 byte section.

With a little code extension you can extract "creation time" (reversed endian format of the property id {0x40, 0x00, 0x07, 0x30}), "last modification time" (reversed endian format of the property id {0x40, 0x00, 0x08, 0x30}) or all other imaginable properties. You can use a Viewer for MS Outlook Messages to find out proper endian formats. I have tried this approach for MSG files produced with MS Outlook 2003 / 2007, for unicode / non unicode messages and any other combinations. It was working in all cases.