Mike Kaganski <mikekaganski@gmail.com>
authorDavid Bienvenu <bienvenu@nventure.com>
Tue, 17 May 2011 12:27:14 -0700
changeset 7784 90c3929c5b5d13d2e677fff21e64cf0831f55e79
parent 7783 ad5f8b0ed1c3aa1114deac05081ed4fec12c199c
child 7785 e5dbe854dad7e759631216a9a1300aa493459bbf
push idunknown
push userunknown
push dateunknown
Mike Kaganski <mikekaganski@gmail.com>
mailnews/import/outlook/src/Makefile.in
mailnews/import/outlook/src/MapiApi.cpp
mailnews/import/outlook/src/MapiApi.h
mailnews/import/outlook/src/MapiMessage.cpp
mailnews/import/outlook/src/MapiMessage.h
mailnews/import/outlook/src/MapiMimeTypes.cpp
mailnews/import/outlook/src/MapiMimeTypes.h
mailnews/import/outlook/src/nsOutlookCompose.cpp
mailnews/import/outlook/src/nsOutlookCompose.h
mailnews/import/outlook/src/nsOutlookEditor.cpp
mailnews/import/outlook/src/nsOutlookEditor.h
mailnews/import/outlook/src/nsOutlookMail.cpp
mailnews/import/outlook/src/nsOutlookMail.h
mailnews/import/outlook/src/rtfDecoder.cpp
mailnews/import/outlook/src/rtfDecoder.h
mailnews/import/outlook/src/rtfMailDecoder.cpp
mailnews/import/outlook/src/rtfMailDecoder.h
--- a/mailnews/import/outlook/src/Makefile.in
+++ b/mailnews/import/outlook/src/Makefile.in
@@ -54,14 +54,17 @@ CPPSRCS		= \
 		nsOutlookStringBundle.cpp	\
 		nsOutlookImport.cpp		\
 		nsOutlookSettings.cpp		\
 		MapiApi.cpp			\
 		nsOutlookMail.cpp		\
 		MapiMessage.cpp			\
 		MapiMimeTypes.cpp		\
 		nsOutlookCompose.cpp		\
+		rtfDecoder.cpp		\
+		rtfMailDecoder.cpp		\
+		nsOutlookEditor.cpp		\
 		$(NULL)
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
--- a/mailnews/import/outlook/src/MapiApi.cpp
+++ b/mailnews/import/outlook/src/MapiApi.cpp
@@ -33,16 +33,19 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "MapiDbgLog.h"
 #include "MapiApi.h"
 
+#include <sstream>
+#include "rtfMailDecoder.h"
+
 #include "prprf.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 
 int      CMapiApi::m_clients = 0;
 BOOL    CMapiApi::m_initialized = PR_FALSE;
 nsVoidArray  *CMapiApi::m_pStores = NULL;
 LPMAPISESSION CMapiApi::m_lpSession = NULL;
@@ -63,106 +66,220 @@ HINSTANCE  CMapiApi::m_hMapi32 = NULL;
 
 LPMAPIUNINITIALIZE    gpMapiUninitialize = NULL;
 LPMAPIINITIALIZE    gpMapiInitialize = NULL;
 LPMAPIALLOCATEBUFFER  gpMapiAllocateBuffer = NULL;
 LPMAPIFREEBUFFER    gpMapiFreeBuffer = NULL;
 LPMAPILOGONEX      gpMapiLogonEx = NULL;
 LPOPENSTREAMONFILE    gpMapiOpenStreamOnFile = NULL;
 
-BOOL CMapiApi::LoadMapiEntryPoints( void)
+typedef HRESULT (STDMETHODCALLTYPE WRAPCOMPRESSEDRTFSTREAM) (
+  LPSTREAM lpCompressedRTFStream, ULONG ulFlags, LPSTREAM FAR *lpUncompressedRTFStream);
+typedef WRAPCOMPRESSEDRTFSTREAM *LPWRAPCOMPRESSEDRTFSTREAM;
+LPWRAPCOMPRESSEDRTFSTREAM gpWrapCompressedRTFStream = NULL;
+
+// WrapCompressedRTFStreamEx related stuff - see http://support.microsoft.com/kb/839560
+typedef struct {
+  ULONG       size;
+  ULONG       ulFlags;
+  ULONG       ulInCodePage;
+  ULONG       ulOutCodePage;
+} RTF_WCSINFO;
+typedef struct {
+  ULONG       size;
+  ULONG       ulStreamFlags;
+} RTF_WCSRETINFO;
+
+typedef HRESULT (STDMETHODCALLTYPE WRAPCOMPRESSEDRTFSTREAMEX) (
+  LPSTREAM lpCompressedRTFStream, CONST RTF_WCSINFO * pWCSInfo,
+  LPSTREAM * lppUncompressedRTFStream, RTF_WCSRETINFO * pRetInfo);
+typedef WRAPCOMPRESSEDRTFSTREAMEX *LPWRAPCOMPRESSEDRTFSTREAMEX;
+LPWRAPCOMPRESSEDRTFSTREAMEX gpWrapCompressedRTFStreamEx = NULL;
+
+BOOL CMapiApi::LoadMapiEntryPoints(void)
 {
-  if (!(gpMapiUninitialize = (LPMAPIUNINITIALIZE) GetProcAddress( m_hMapi32, "MAPIUninitialize")))
-    return( FALSE);
-  if (!(gpMapiInitialize = (LPMAPIINITIALIZE) GetProcAddress( m_hMapi32, "MAPIInitialize")))
-    return( FALSE);
-  if (!(gpMapiAllocateBuffer = (LPMAPIALLOCATEBUFFER) GetProcAddress( m_hMapi32, "MAPIAllocateBuffer")))
-    return( FALSE);
-  if (!(gpMapiFreeBuffer = (LPMAPIFREEBUFFER) GetProcAddress( m_hMapi32, "MAPIFreeBuffer")))
-    return( FALSE);
-  if (!(gpMapiLogonEx = (LPMAPILOGONEX) GetProcAddress( m_hMapi32, "MAPILogonEx")))
-    return( FALSE);
-  if (!(gpMapiOpenStreamOnFile = (LPOPENSTREAMONFILE) GetProcAddress( m_hMapi32, "OpenStreamOnFile")))
-    return( FALSE);
+  if (!(gpMapiUninitialize = (LPMAPIUNINITIALIZE) GetProcAddress(
+      m_hMapi32, "MAPIUninitialize")))
+    return FALSE;
+  if (!(gpMapiInitialize = (LPMAPIINITIALIZE) GetProcAddress(
+      m_hMapi32, "MAPIInitialize")))
+    return FALSE;
+  if (!(gpMapiAllocateBuffer = (LPMAPIALLOCATEBUFFER) GetProcAddress(
+      m_hMapi32, "MAPIAllocateBuffer")))
+    return FALSE;
+  if (!(gpMapiFreeBuffer = (LPMAPIFREEBUFFER) GetProcAddress(
+      m_hMapi32, "MAPIFreeBuffer")))
+    return FALSE;
+  if (!(gpMapiLogonEx = (LPMAPILOGONEX) GetProcAddress(m_hMapi32,
+                                                       "MAPILogonEx")))
+    return FALSE;
+  if (!(gpMapiOpenStreamOnFile = (LPOPENSTREAMONFILE) GetProcAddress(
+      m_hMapi32, "OpenStreamOnFile")))
+    return FALSE;
 
-  return( TRUE);
+  // Available from the Outlook 2002 post-SP3 hotfix (http://support.microsoft.com/kb/883924/)
+  // Exported by msmapi32.dll; so it's unavailable to us using mapi32.dll
+  gpWrapCompressedRTFStreamEx = (LPWRAPCOMPRESSEDRTFSTREAMEX) GetProcAddress(
+    m_hMapi32, "WrapCompressedRTFStreamEx");
+  // Available always
+  gpWrapCompressedRTFStream = (LPWRAPCOMPRESSEDRTFSTREAM) GetProcAddress(
+    m_hMapi32, "WrapCompressedRTFStream");
+
+  return TRUE;
 }
 
-void CMapiApi::MAPIUninitialize( void)
+// Gets the PR_RTF_COMPRESSED tag property
+// Codepage is used only if the WrapCompressedRTFStreamEx is available
+BOOL CMapiApi::GetRTFPropertyDecodedAsUTF16( LPMAPIPROP pProp, nsString& val,
+                                            unsigned long& nativeBodyType,
+                                            unsigned long codepage)
+{
+  if (!m_hMapi32 || !(gpWrapCompressedRTFStreamEx || gpWrapCompressedRTFStream))
+    return FALSE; // Fallback to the default processing
+
+  LPSTREAM icstream = 0; // for the compressed stream
+  LPSTREAM iunstream = 0; // for the uncompressed stream
+  HRESULT hr = pProp->OpenProperty(PR_RTF_COMPRESSED,
+                                   &IID_IStream, STGM_READ | STGM_DIRECT,
+                                   0, (LPUNKNOWN *)&icstream);
+  if (HR_FAILED(hr))
+    return FALSE;
+
+  if (gpWrapCompressedRTFStreamEx) { // Impossible - we use mapi32.dll!
+    RTF_WCSINFO     wcsinfo = {0};
+    RTF_WCSRETINFO  retinfo = {0};
+
+    retinfo.size = sizeof(RTF_WCSRETINFO);
+
+    wcsinfo.size = sizeof(RTF_WCSINFO);
+    wcsinfo.ulFlags = MAPI_NATIVE_BODY;
+    wcsinfo.ulInCodePage = codepage;
+    wcsinfo.ulOutCodePage = CP_UTF8;
+
+    if(HR_SUCCEEDED(hr = gpWrapCompressedRTFStreamEx(icstream, &wcsinfo,
+                                                     &iunstream, &retinfo)))
+      nativeBodyType = retinfo.ulStreamFlags;
+  }
+  else { // mapi32.dll
+    gpWrapCompressedRTFStream(icstream,0,&iunstream);
+  }
+  icstream->Release();
+
+  if(iunstream) { // Succeeded
+    std::string streamData;
+    // Stream.Stat doesn't work for this stream!
+    bool done = false;
+    while (!done) {
+      // I think 10K is a good guess to minimize the number of reads while keeping memory usage low
+      const int bufsize = 10240; 
+      char buf[bufsize];
+      ULONG read;
+      hr = iunstream->Read(buf, bufsize, &read);
+      done = (read < bufsize) || (hr != S_OK);
+      if (read)
+        streamData.append(buf, read);
+    }
+    iunstream->Release();
+    // if rtf -> convert to plain text.
+    if (!gpWrapCompressedRTFStreamEx ||
+        (nativeBodyType==MAPI_NATIVE_BODY_TYPE_RTF)) {
+      std::stringstream s(streamData);
+      CRTFMailDecoder decoder;
+      DecodeRTF(s, decoder);
+      if (decoder.mode() == CRTFMailDecoder::mHTML)
+        nativeBodyType = MAPI_NATIVE_BODY_TYPE_HTML;
+      else if (decoder.mode() == CRTFMailDecoder::mText)
+        nativeBodyType = MAPI_NATIVE_BODY_TYPE_PLAINTEXT;
+      else
+        nativeBodyType = MAPI_NATIVE_BODY_TYPE_RTF;
+      val.Assign(decoder.text(), decoder.textSize());
+    }
+    else { // WrapCompressedRTFStreamEx available and original type is not rtf
+      CopyUTF8toUTF16(streamData.c_str(), val);
+    }
+    return TRUE;
+  }
+  return FALSE;
+}
+
+void CMapiApi::MAPIUninitialize(void)
 {
   if (m_hMapi32 && gpMapiUninitialize)
     (*gpMapiUninitialize)();
 }
 
-HRESULT CMapiApi::MAPIInitialize( LPVOID lpInit)
+HRESULT CMapiApi::MAPIInitialize(LPVOID lpInit)
 {
-  if (m_hMapi32 && gpMapiInitialize)
-    return( (*gpMapiInitialize)( lpInit));
-  return( MAPI_E_NOT_INITIALIZED);
+  return (m_hMapi32 && gpMapiInitialize) ? (*gpMapiInitialize)( lpInit) :
+          MAPI_E_NOT_INITIALIZED;
 }
 
-SCODE CMapiApi::MAPIAllocateBuffer( ULONG cbSize, LPVOID FAR * lppBuffer)
+SCODE CMapiApi::MAPIAllocateBuffer(ULONG cbSize, LPVOID FAR * lppBuffer)
 {
-  if (m_hMapi32 && gpMapiAllocateBuffer)
-    return( (*gpMapiAllocateBuffer)( cbSize, lppBuffer));
-  return( MAPI_E_NOT_INITIALIZED);
+  return (m_hMapi32 && gpMapiAllocateBuffer) ?
+          (*gpMapiAllocateBuffer)(cbSize, lppBuffer) : MAPI_E_NOT_INITIALIZED;
+}
+
+ULONG CMapiApi::MAPIFreeBuffer(LPVOID lpBuff)
+{
+  return (m_hMapi32 && gpMapiFreeBuffer) ? (*gpMapiFreeBuffer)(lpBuff) :
+          MAPI_E_NOT_INITIALIZED;
 }
 
-ULONG CMapiApi::MAPIFreeBuffer( LPVOID lpBuff)
+HRESULT CMapiApi::MAPILogonEx(ULONG ulUIParam, LPTSTR lpszProfileName,
+                              LPTSTR lpszPassword, FLAGS flFlags,
+                              LPMAPISESSION FAR * lppSession)
 {
-  if (m_hMapi32 && gpMapiFreeBuffer)
-    return( (*gpMapiFreeBuffer)( lpBuff));
-  return( MAPI_E_NOT_INITIALIZED);
+  return (m_hMapi32 && gpMapiLogonEx) ?
+    (*gpMapiLogonEx)(ulUIParam, lpszProfileName, lpszPassword, flFlags, lppSession) :
+     MAPI_E_NOT_INITIALIZED;
 }
 
-HRESULT CMapiApi::MAPILogonEx( ULONG ulUIParam, LPTSTR lpszProfileName, LPTSTR lpszPassword, FLAGS flFlags, LPMAPISESSION FAR * lppSession)
+HRESULT CMapiApi::OpenStreamOnFile(LPALLOCATEBUFFER lpAllocateBuffer,
+                                   LPFREEBUFFER lpFreeBuffer, ULONG ulFlags,
+                                   LPTSTR lpszFileName, LPTSTR lpszPrefix,
+                                   LPSTREAM FAR * lppStream)
 {
-  if (m_hMapi32 && gpMapiLogonEx)
-    return( (*gpMapiLogonEx)( ulUIParam, lpszProfileName, lpszPassword, flFlags, lppSession));
-  return( MAPI_E_NOT_INITIALIZED);
-}
-
-HRESULT CMapiApi::OpenStreamOnFile( LPALLOCATEBUFFER lpAllocateBuffer, LPFREEBUFFER lpFreeBuffer, ULONG ulFlags, LPTSTR lpszFileName, LPTSTR lpszPrefix, LPSTREAM FAR * lppStream)
-{
-  if (m_hMapi32 && gpMapiOpenStreamOnFile)
-    return( (*gpMapiOpenStreamOnFile)( lpAllocateBuffer, lpFreeBuffer, ulFlags, lpszFileName, lpszPrefix, lppStream));
-  return( MAPI_E_NOT_INITIALIZED);
+  return (m_hMapi32 && gpMapiOpenStreamOnFile) ?
+    (*gpMapiOpenStreamOnFile)(lpAllocateBuffer, lpFreeBuffer, ulFlags,
+                              lpszFileName, lpszPrefix, lppStream) :
+    MAPI_E_NOT_INITIALIZED;
 }
 
 void CMapiApi::FreeProws( LPSRowSet prows)
 {
   ULONG    irow;
   if (!prows)
     return;
   for (irow = 0; irow < prows->cRows; ++irow)
-    MAPIFreeBuffer( prows->aRow[irow].lpProps);
-  MAPIFreeBuffer( prows);
+    MAPIFreeBuffer(prows->aRow[irow].lpProps);
+  MAPIFreeBuffer(prows);
 }
 
 BOOL CMapiApi::LoadMapi( void)
 {
   if (m_hMapi32)
-    return( TRUE);
+    return TRUE;
 
   HINSTANCE  hInst = ::LoadLibrary( "MAPI32.DLL");
   if (!hInst)
-    return( FALSE);
+    return FALSE;
   FARPROC pProc = GetProcAddress( hInst, "MAPIGetNetscapeVersion");
   if (pProc) {
     ::FreeLibrary( hInst);
     hInst = ::LoadLibrary( "MAPI32BAK.DLL");
     if (!hInst)
-      return( FALSE);
+      return FALSE;
   }
 
   m_hMapi32 = hInst;
-  return( LoadMapiEntryPoints());
+  return LoadMapiEntryPoints();
 }
 
-void CMapiApi::UnloadMapi( void)
+void CMapiApi::UnloadMapi(void)
 {
   if (m_hMapi32)
     ::FreeLibrary( m_hMapi32);
   m_hMapi32 = NULL;
 }
 
 CMapiApi::CMapiApi()
 {
@@ -1103,69 +1220,85 @@ LPSPropValue CMapiApi::GetMapiProperty( 
 BOOL CMapiApi::IsLargeProperty( LPSPropValue pVal)
 {
   if ((PROP_TYPE( pVal->ulPropTag) == PT_ERROR) && (pVal->Value.l == E_OUTOFMEMORY)) {
     return( TRUE);
   }
   return( FALSE);
 }
 
-BOOL CMapiApi::GetLargeStringProperty( LPMAPIPROP pProp, ULONG tag, nsCString& val)
+// The output buffer (result) must be freed with operator delete[]
+BOOL CMapiApi::GetLargeProperty( LPMAPIPROP pProp, ULONG tag, void** result)
 {
   LPSTREAM  lpStream;
   HRESULT    hr = pProp->OpenProperty( tag, &IID_IStream, 0, 0, (LPUNKNOWN *)&lpStream);
   if (HR_FAILED( hr))
     return( FALSE);
   STATSTG    st;
   BOOL bResult = TRUE;
   hr = lpStream->Stat( &st, STATFLAG_NONAME);
   if (HR_FAILED( hr))
     bResult = FALSE;
   else {
     if (!st.cbSize.QuadPart)
       st.cbSize.QuadPart = 1;
-    char *pVal = new char[ (int) st.cbSize.QuadPart + 1];
-    // val.SetCapacity( (int) st.cbSize.QuadPart);
+    char *pVal = new char[ (int) st.cbSize.QuadPart + 2];
     if (pVal) {
       ULONG  sz;
-      hr = lpStream->Read( pVal, (ULONG) st.cbSize.QuadPart, &sz);
-      if (HR_FAILED( hr)) {
+      hr = lpStream->Read(pVal, (ULONG) st.cbSize.QuadPart, &sz);
+      if (HR_FAILED(hr)) {
         bResult = FALSE;
-        *pVal = 0;
-        sz = 0;
+        delete[] pVal;
       }
-      else
-        pVal[(int) st.cbSize.QuadPart] = 0;
-      val = pVal;
-      delete [] pVal;
+      else {
+         // Just in case it's a UTF16 string
+        pVal[(int) st.cbSize.QuadPart] = pVal[(int) st.cbSize.QuadPart+1] = 0;
+        *result = pVal;
+      }
     }
     else
       bResult = FALSE;
   }
 
   lpStream->Release();
 
   return( bResult);
 }
 
-BOOL CMapiApi::GetLargeStringProperty( LPMAPIPROP pProp, ULONG tag, nsString& val)
+BOOL CMapiApi::GetLargeStringProperty( LPMAPIPROP pProp, ULONG tag, nsCString& val)
 {
-  nsCString  result;
-  if (GetLargeStringProperty( pProp, tag, result)) {
-    CStrToUnicode( result.get(), val);
-    return( TRUE);
-  }
+  void* result;
+  if (!GetLargeProperty(pProp, tag, &result))
+    return FALSE;
+  if (PROP_TYPE(tag) == PT_UNICODE) // unicode string
+    LossyCopyUTF16toASCII(static_cast<wchar_t*>(result), val);
+  else // either PT_STRING8 or some other binary - use as is
+    val.Assign(static_cast<char*>(result));
+  delete[] result;
+  return TRUE;
+}
 
-  return( FALSE);
+BOOL CMapiApi::GetLargeStringProperty(LPMAPIPROP pProp, ULONG tag, nsString& val)
+{
+  void* result;
+  if (!GetLargeProperty(pProp, tag, &result))
+    return FALSE;
+  if (PROP_TYPE(tag) == PT_UNICODE) // We already get the unicode string
+    val.Assign(static_cast<wchar_t*>(result));
+  else // either PT_STRING8 or some other binary
+    CStrToUnicode(static_cast<char*>(result), val);
+  delete[] result;
+  return TRUE;
 }
 // If the value is a string, get it...
-BOOL CMapiApi::GetEntryIdFromProp( LPSPropValue pVal, ULONG& cbEntryId, LPENTRYID& lpEntryId, BOOL delVal)
+BOOL CMapiApi::GetEntryIdFromProp(LPSPropValue pVal, ULONG& cbEntryId,
+                                  LPENTRYID& lpEntryId, BOOL delVal)
 {
   if (!pVal)
-    return( FALSE);
+    return FALSE;
 
   BOOL bResult = TRUE;
     switch( PROP_TYPE( pVal->ulPropTag)) {
     case PT_BINARY:
       cbEntryId = pVal->Value.bin.cb;
       MAPIAllocateBuffer( cbEntryId, (LPVOID *) &lpEntryId);
       memcpy( lpEntryId, pVal->Value.bin.lpb, cbEntryId);
     break;
@@ -1174,17 +1307,17 @@ BOOL CMapiApi::GetEntryIdFromProp( LPSPr
       MAPI_TRACE0( "EntryId not in BINARY prop value\n");
       bResult = FALSE;
         break;
     }
 
   if (pVal && delVal)
     MAPIFreeBuffer( pVal);
 
-  return( bResult);
+  return bResult;
 }
 
 BOOL CMapiApi::GetStringFromProp( LPSPropValue pVal, nsCString& val, BOOL delVal)
 {
   BOOL bResult = TRUE;
   if ( pVal && (PROP_TYPE( pVal->ulPropTag) == PT_STRING8))
     val = pVal->Value.lpszA;
   else if ( pVal && (PROP_TYPE( pVal->ulPropTag) == PT_UNICODE))
--- a/mailnews/import/outlook/src/MapiApi.h
+++ b/mailnews/import/outlook/src/MapiApi.h
@@ -49,16 +49,41 @@
 #include <mapidefs.h>
 #include <mapicode.h>
 #include <mapitags.h>
 #include <mapiutil.h>
 // wabutil.h expects mapiutil to define _MAPIUTIL_H but it actually
 // defines _MAPIUTIL_H_
 #define _MAPIUTIL_H
 
+#ifndef PR_INTERNET_CPID
+#define PR_INTERNET_CPID (PROP_TAG(PT_LONG,0x3FDE))
+#endif
+#ifndef MAPI_NATIVE_BODY
+#define MAPI_NATIVE_BODY (0x00010000)
+#endif
+#ifndef MAPI_NATIVE_BODY_TYPE_RTF
+#define MAPI_NATIVE_BODY_TYPE_RTF (0x00000001)
+#endif
+#ifndef MAPI_NATIVE_BODY_TYPE_HTML
+#define MAPI_NATIVE_BODY_TYPE_HTML (0x00000002)
+#endif
+#ifndef MAPI_NATIVE_BODY_TYPE_PLAINTEXT
+#define MAPI_NATIVE_BODY_TYPE_PLAINTEXT (0x00000004)
+#endif
+#ifndef PR_BODY_HTML_A
+#define PR_BODY_HTML_A (PROP_TAG(PT_STRING8,0x1013))
+#endif
+#ifndef PR_BODY_HTML_W
+#define PR_BODY_HTML_W (PROP_TAG(PT_UNICODE,0x1013))
+#endif
+#ifndef PR_BODY_HTML
+#define PR_BODY_HTML (PROP_TAG(PT_TSTRING,0x1013))
+#endif
+
 class CMapiFolderList;
 class CMsgStore;
 class CMapiFolder;
 
 class CMapiContentIter {
 public:
   virtual BOOL HandleContentItem( ULONG oType, ULONG cb, LPENTRYID pEntry) = 0;
 };
@@ -100,31 +125,37 @@ public:
 
   // Fill in the folders list with the hierarchy from the given
   // message store.
   BOOL  GetStoreFolders( ULONG cbEid, LPENTRYID lpEid, CMapiFolderList& folders, int startDepth);
   BOOL  GetStoreAddressFolders( ULONG cbEid, LPENTRYID lpEid, CMapiFolderList& folders);
   BOOL  OpenStore( ULONG cbEid, LPENTRYID lpEid, LPMDB *ppMdb);
 
   // Iteration
-  BOOL  IterateStores( CMapiFolderList& list);
-  BOOL  IterateContents( CMapiContentIter *pIter, LPMAPIFOLDER pFolder, ULONG flags = 0);
-  BOOL  IterateHierarchy( CMapiHierarchyIter *pIter, LPMAPIFOLDER pFolder, ULONG flags = 0);
+  BOOL  IterateStores(CMapiFolderList& list);
+  BOOL  IterateContents(CMapiContentIter *pIter, LPMAPIFOLDER pFolder, ULONG flags = 0);
+  BOOL  IterateHierarchy(CMapiHierarchyIter *pIter, LPMAPIFOLDER pFolder, ULONG flags = 0);
 
   // Properties
-  static LPSPropValue  GetMapiProperty( LPMAPIPROP pProp, ULONG tag);
-  static BOOL      GetEntryIdFromProp( LPSPropValue pVal, ULONG& cbEntryId, LPENTRYID& lpEntryId, BOOL delVal = TRUE);
-  static BOOL      GetStringFromProp( LPSPropValue pVal, nsCString& val, BOOL delVal = TRUE);
-  static BOOL      GetStringFromProp( LPSPropValue pVal, nsString& val, BOOL delVal = TRUE);
-  static LONG      GetLongFromProp( LPSPropValue pVal, BOOL delVal = TRUE);
-  static BOOL      GetLargeStringProperty( LPMAPIPROP pProp, ULONG tag, nsCString& val);
-  static BOOL      GetLargeStringProperty( LPMAPIPROP pProp, ULONG tag, nsString& val);
-  static BOOL      IsLargeProperty( LPSPropValue pVal);
+  static LPSPropValue  GetMapiProperty(LPMAPIPROP pProp, ULONG tag);
+  // If delVal is true, functions will call CMapiApi::MAPIFreeBuffer on pVal.
+  static BOOL      GetEntryIdFromProp(LPSPropValue pVal, ULONG& cbEntryId,
+                                      LPENTRYID& lpEntryId, BOOL delVal = TRUE);
+  static BOOL      GetStringFromProp(LPSPropValue pVal, nsCString& val, BOOL delVal = TRUE);
+  static BOOL      GetStringFromProp(LPSPropValue pVal, nsString& val, BOOL delVal = TRUE);
+  static LONG      GetLongFromProp(LPSPropValue pVal, BOOL delVal = TRUE);
+  static BOOL      GetLargeStringProperty(LPMAPIPROP pProp, ULONG tag, nsCString& val);
+  static BOOL      GetLargeStringProperty(LPMAPIPROP pProp, ULONG tag, nsString& val);
+  static BOOL      IsLargeProperty(LPSPropValue pVal);
   static ULONG    GetEmailPropertyTag(LPMAPIPROP lpProp, LONG nameID);
 
+  static BOOL GetRTFPropertyDecodedAsUTF16(LPMAPIPROP pProp, nsString& val,
+                                           unsigned long& nativeBodyType,
+                                           unsigned long codepage = 0);
+
   // Debugging & reporting stuff
   static void      ListProperties( LPMAPIPROP lpProp, BOOL getValues = TRUE);
   static void      ListPropertyValue( LPSPropValue pVal, nsCString& s);
 
 protected:
   BOOL      HandleHierarchyItem( ULONG oType, ULONG cb, LPENTRYID pEntry);
   BOOL      HandleContentsItem( ULONG oType, ULONG cb, LPENTRYID pEntry);
   void      GetStoreInfo( CMapiFolder *pFolder, long *pSzContents);
@@ -148,20 +179,20 @@ private:
   static int        m_clients;
   static BOOL        m_initialized;
   static nsVoidArray *  m_pStores;
   static LPMAPISESSION  m_lpSession;
   static LPMDB      m_lpMdb;
   static HRESULT      m_lastError;
   static PRUnichar *    m_pUniBuff;
   static int        m_uniBuffLen;
+
+  static BOOL      GetLargeProperty(LPMAPIPROP pProp, ULONG tag, void** result);
 };
 
-
-
 class CMapiFolder {
 public:
   CMapiFolder();
   CMapiFolder( const CMapiFolder *pCopyFrom);
   CMapiFolder( const PRUnichar *pDisplayName, ULONG cbEid, LPENTRYID lpEid, int depth, LONG oType = MAPI_FOLDER);
   ~CMapiFolder();
 
   void  SetDoImport( BOOL doIt) { m_doImport = doIt;}
--- a/mailnews/import/outlook/src/MapiMessage.cpp
+++ b/mailnews/import/outlook/src/MapiMessage.cpp
@@ -30,761 +30,1452 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#ifndef INITGUID
+#define INITGUID
+#endif
+
+#ifndef USES_IID_IMessage
+#define USES_IID_IMessage
+#endif
+
 #include "nscore.h"
 #include <time.h>
 #include "nsString.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsMsgUtils.h"
+#include "nsMimeTypes.h"
 
 #include "MapiDbgLog.h"
 #include "MapiApi.h"
+
+#include "MapiMimeTypes.h"
+
+#include <algorithm>
+#include "nsMsgI18N.h"
+#include "nsICharsetConverterManager.h"
+
+#include "nsNetUtil.h"
 #include "MapiMessage.h"
 
-#include "MapiMimeTypes.h"
+#include "nsOutlookMail.h"
 
 // needed for the call the OpenStreamOnFile
 extern LPMAPIALLOCATEBUFFER gpMapiAllocateBuffer;
 extern LPMAPIFREEBUFFER gpMapiFreeBuffer;
 
 // Sample From line: From - 1 Jan 1965 00:00:00
 
 typedef const char * PC_S8;
 
 static const char * kWhitespace = "\b\t\r\n ";
-static const char * sFromLine = "From - ";
-static const char * sFromDate = "Mon Jan 1 00:00:00 1965";
-static const char * sDaysOfWeek[7] = {
-  "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+static const wchar_t * sFromLine = L"From - ";
+static const wchar_t * sFromDate = L"Mon Jan 1 00:00:00 1965";
+static const wchar_t * sDaysOfWeek[7] = {
+  L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat"
 };
 
-static const char *sMonths[12] = {
-  "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+static const wchar_t *sMonths[12] = {
+  L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec"
 };
 
+const nsCString CMapiMessage::m_whitespace(kWhitespace);
 
 CMapiMessage::CMapiMessage( LPMESSAGE lpMsg)
+  : m_lpMsg(lpMsg), m_pIOService(0), m_dldStateHeadersOnly(false), m_msgFlags(0)
 {
-  m_lpMsg = lpMsg;
-  m_pAttachTable = NULL;
-  m_bMimeEncoding = FALSE;
-  m_bMimeVersion = FALSE;
-  m_ownsAttachFile = FALSE;
-  m_whitespace = kWhitespace;
+  nsresult rv;
+  NS_WITH_PROXIED_SERVICE(nsIIOService, service, NS_IOSERVICE_CONTRACTID,
+                          NS_PROXY_TO_MAIN_THREAD, &rv);
+  if (NS_FAILED(rv))
+    return;
+  NS_IF_ADDREF(m_pIOService = service);
+
+  FetchHeaders();
+  if (ValidState()) {
+    BuildFromLine();
+    FetchFlags();
+    GetDownloadState();
+    if (FullMessageDownloaded()) {
+      FetchBody();
+      ProcessAttachments();
+    }
+  }
 }
 
 CMapiMessage::~CMapiMessage()
 {
-  if (m_pAttachTable)
-    m_pAttachTable->Release();
+  ClearAttachments();
   if (m_lpMsg)
     m_lpMsg->Release();
-
-  ClearTempAttachFile();
+  NS_IF_RELEASE(m_pIOService);
 }
 
 
-void CMapiMessage::FormatDateTime( SYSTEMTIME & tm, nsCString& s, BOOL includeTZ)
+void CMapiMessage::FormatDateTime( SYSTEMTIME & tm, nsString& s, bool includeTZ)
 {
   long offset = _timezone;
   s += sDaysOfWeek[tm.wDayOfWeek];
-  s += ", ";
-  s.AppendInt( (PRInt32) tm.wDay);
-  s += " ";
+  s += L", ";
+  s.AppendInt((PRInt32) tm.wDay);
+  s += L" ";
   s += sMonths[tm.wMonth - 1];
-  s += " ";
-  s.AppendInt( (PRInt32) tm.wYear);
-  s += " ";
+  s += L" ";
+  s.AppendInt((PRInt32) tm.wYear);
+  s += L" ";
   int val = tm.wHour;
   if (val < 10)
-    s += "0";
-  s.AppendInt( (PRInt32) val);
-  s += ":";
+    s += L"0";
+  s.AppendInt((PRInt32) val);
+  s += L":";
   val = tm.wMinute;
   if (val < 10)
-    s += "0";
-  s.AppendInt( (PRInt32) val);
-  s += ":";
+    s += L"0";
+  s.AppendInt((PRInt32) val);
+  s += L":";
   val = tm.wSecond;
   if (val < 10)
-    s += "0";
-  s.AppendInt( (PRInt32) val);
+    s += L"0";
+  s.AppendInt((PRInt32) val);
   if (includeTZ) {
-    s += " ";
+    s += L" ";
     if (offset < 0) {
       offset *= -1;
-      s += "+";
+      s += L"+";
     }
     else
-      s += "-";
+      s += L"-";
     offset /= 60;
     val = (int) (offset / 60);
     if (val < 10)
-      s += "0";
-    s.AppendInt( (PRInt32) val);
+      s += L"0";
+    s.AppendInt((PRInt32) val);
     val = (int) (offset % 60);
     if (val < 10)
-      s += "0";
-    s.AppendInt( (PRInt32) val);
+      s += L"0";
+    s.AppendInt((PRInt32) val);
   }
 }
 
+bool CMapiMessage::EnsureHeader(CMapiMessageHeaders::SpecialHeader special,
+                                ULONG mapiTag)
+{
+  if (m_headers.Value(special))
+    return true;
+
+  nsString value;
+  LPSPropValue pVal = CMapiApi::GetMapiProperty(m_lpMsg, mapiTag);
+  if (CMapiApi::GetStringFromProp(pVal, value) && !value.IsEmpty()) {
+    m_headers.SetValue(special, value.get());
+    return true;
+  }
+
+  return false;
+}
+
+bool CMapiMessage::EnsureDate()
+{
+  if (m_headers.Value(CMapiMessageHeaders::hdrDate))
+    return true;
+
+  LPSPropValue pVal = CMapiApi::GetMapiProperty(m_lpMsg, PR_MESSAGE_DELIVERY_TIME);
+  if (!pVal)
+    pVal = CMapiApi::GetMapiProperty(m_lpMsg, PR_CREATION_TIME);
+  if (pVal) {
+    SYSTEMTIME st;
+    // the following call returns UTC
+    ::FileTimeToSystemTime(&(pVal->Value.ft), &st);
+    CMapiApi::MAPIFreeBuffer(pVal);
+    // FormatDateTime would append the local time zone, so don't use it.
+    // Instead, we just append +0000 for GMT/UTC here.
+    nsString str;
+    FormatDateTime( st, str, false);
+    str += L" +0000";
+    m_headers.SetValue(CMapiMessageHeaders::hdrDate, str.get());
+    return true;
+  }
+
+  return false;
+}
+
+void CMapiMessage::BuildFromLine( void)
+{
+  nsString fromLine(sFromLine);
+  LPSPropValue pVal = CMapiApi::GetMapiProperty( m_lpMsg, PR_CREATION_TIME);
+  if (pVal) {
+    SYSTEMTIME st;
+    ::FileTimeToSystemTime( &(pVal->Value.ft), &st);
+    CMapiApi::MAPIFreeBuffer( pVal);
+    FormatDateTime( st, fromLine, FALSE);
+  }
+  else
+    fromLine += sFromDate;
+
+  fromLine += L"\x0D\x0A";
+  CopyUTF16toUTF8(fromLine, m_fromLine);
+}
+
+#ifndef dispidHeaderItem
+#define dispidHeaderItem 0x8578
+#endif
+DEFINE_OLEGUID(PSETID_Common, MAKELONG(0x2000+(8),0x0006),0,0);
+
+void CMapiMessage::GetDownloadState()
+{
+  // See http://support.microsoft.com/kb/912239
+  HRESULT         hRes = S_OK;
+  ULONG           ulVal = 0;
+  LPSPropValue    lpPropVal = NULL;
+  LPSPropTagArray lpNamedPropTag = NULL;
+  MAPINAMEID      NamedID = {0};
+  LPMAPINAMEID    lpNamedID = NULL;
+
+  NamedID.lpguid = (LPGUID) &PSETID_Common;
+  NamedID.ulKind = MNID_ID;
+  NamedID.Kind.lID = dispidHeaderItem;
+  lpNamedID = &NamedID;
+
+  hRes = m_lpMsg->GetIDsFromNames(1, &lpNamedID, NULL, &lpNamedPropTag);
+
+  if (lpNamedPropTag && 1 == lpNamedPropTag->cValues)
+  {
+    lpNamedPropTag->aulPropTag[0] = CHANGE_PROP_TYPE(lpNamedPropTag->aulPropTag[0], PT_LONG);
+
+    //Get the value of the property.
+    hRes = m_lpMsg->GetProps(lpNamedPropTag, 0, &ulVal, &lpPropVal);
+    if (lpPropVal && 1 == ulVal && PT_LONG == PROP_TYPE(lpPropVal->ulPropTag) &&
+        lpPropVal->Value.ul)
+      m_dldStateHeadersOnly = true;
+  }
+
+  CMapiApi::MAPIFreeBuffer(lpPropVal);
+  CMapiApi::MAPIFreeBuffer(lpNamedPropTag);
+}
 
 // Headers - fetch will get PR_TRANSPORT_MESSAGE_HEADERS
 // or if they do not exist will build a header from
 //  PR_DISPLAY_TO, _CC, _BCC
 //  PR_SUBJECT
 //  PR_MESSAGE_RECIPIENTS
 // and PR_CREATION_TIME if needed?
-void CMapiMessage::BuildHeaders( void)
+bool CMapiMessage::FetchHeaders( void)
 {
-  // Try to the to line.
-  m_headers.Truncate();
-  AddHeader( m_headers, PR_DISPLAY_TO, "To: ");
-  AddHeader( m_headers, PR_DISPLAY_CC, "CC: ");
-  AddHeader( m_headers, PR_DISPLAY_BCC, "BCC: ");
-  AddDate( m_headers);
-  AddSubject( m_headers);
-  AddFrom( m_headers);
-}
+  // Get the Unicode string right away -> no need to double-convert,
+  // no possible conversion problems.
+  ULONG tag = PR_TRANSPORT_MESSAGE_HEADERS_W;
+  LPSPropValue pVal = CMapiApi::GetMapiProperty(m_lpMsg, tag);
+  if (!pVal)
+    pVal = CMapiApi::GetMapiProperty(m_lpMsg, tag = PR_TRANSPORT_MESSAGE_HEADERS_A);
+  if (pVal) {
+    if (CMapiApi::IsLargeProperty(pVal)) {
+      nsString headers;
+      CMapiApi::GetLargeStringProperty(m_lpMsg, tag, headers);
+      m_headers.Assign(headers.get());
+    }
+    else if ((PROP_TYPE(pVal->ulPropTag) == PT_UNICODE) &&
+             (pVal->Value.lpszW) && (*(pVal->Value.lpszW)))
+      m_headers.Assign(pVal->Value.lpszW);
+    else if ((PROP_TYPE(pVal->ulPropTag) == PT_STRING8) &&
+             (pVal->Value.lpszA) && (*(pVal->Value.lpszA)))
+      m_headers.Assign(NS_ConvertASCIItoUTF16(pVal->Value.lpszA).get());
 
-BOOL CMapiMessage::AddHeader( nsCString& str, ULONG tag, const char *pPrefix)
-{
-  nsCString value;
-  LPSPropValue pVal = CMapiApi::GetMapiProperty( m_lpMsg, tag);
-  if (CMapiApi::GetStringFromProp( pVal, value) && !value.IsEmpty()) {
-    str.Trim( kWhitespace, PR_FALSE, PR_TRUE);
-    if (!str.IsEmpty())
-      str += "\x0D\x0A";
-    str += pPrefix;
-    str += value;
-    return( TRUE);
+    CMapiApi::MAPIFreeBuffer(pVal);
   }
 
-  return( FALSE);
-}
-
-void CMapiMessage::AddSubject( nsCString& str)
-{
-  AddHeader( str, PR_SUBJECT, "Subject: ");
-}
-
-void CMapiMessage::AddFrom( nsCString& str)
-{
-  if (!AddHeader( str, PR_SENDER_NAME, "From: "))
-    AddHeader( str, PR_SENDER_EMAIL_ADDRESS, "From: ");
-}
-
-void CMapiMessage::AddDate( nsCString& str)
-{
-  LPSPropValue pVal = CMapiApi::GetMapiProperty( m_lpMsg, PR_MESSAGE_DELIVERY_TIME);
-  if (!pVal)
-    pVal = CMapiApi::GetMapiProperty( m_lpMsg, PR_CREATION_TIME);
-  if (pVal) {
-    SYSTEMTIME st;
-    // the following call returns UTC
-    ::FileTimeToSystemTime( &(pVal->Value.ft), &st);
-    CMapiApi::MAPIFreeBuffer( pVal);
-    str.Trim( kWhitespace, PR_FALSE, PR_TRUE);
-    if (!str.IsEmpty())
-      str += "\x0D\x0A";
-    str += "Date: ";
-    // FormatDateTime would append the local time zone, so don't use it.
-    // Instead, we just append +0000 for GMT/UTC here.
-    FormatDateTime( st, str, FALSE);
-    str += " +0000";
-  }
-}
-
+  EnsureDate();
+  if (!EnsureHeader(CMapiMessageHeaders::hdrFrom, PR_SENDER_NAME_W))
+    EnsureHeader(CMapiMessageHeaders::hdrFrom, PR_SENDER_EMAIL_ADDRESS_W);
+  EnsureHeader(CMapiMessageHeaders::hdrSubject, PR_SUBJECT_W);
+  EnsureHeader(CMapiMessageHeaders::hdrTo, PR_DISPLAY_TO_W);
+  EnsureHeader(CMapiMessageHeaders::hdrCc, PR_DISPLAY_CC_W);
+  EnsureHeader(CMapiMessageHeaders::hdrBcc, PR_DISPLAY_BCC_W);
 
-void CMapiMessage::BuildFromLine( void)
-{
-  m_fromLine = sFromLine;
-  LPSPropValue pVal = CMapiApi::GetMapiProperty( m_lpMsg, PR_CREATION_TIME);
-  if (pVal) {
-    SYSTEMTIME st;
-    ::FileTimeToSystemTime( &(pVal->Value.ft), &st);
-    CMapiApi::MAPIFreeBuffer( pVal);
-    FormatDateTime( st, m_fromLine, FALSE);
-  }
-  else
-    m_fromLine += sFromDate;
-  m_fromLine += "\x0D\x0A";
-}
-
-BOOL CMapiMessage::FetchHeaders( void)
-{
-  LPSPropValue pVal = CMapiApi::GetMapiProperty( m_lpMsg, PR_TRANSPORT_MESSAGE_HEADERS);
-  if (pVal && CMapiApi::IsLargeProperty( pVal)) {
-    m_headers.Truncate();
-    CMapiApi::GetLargeStringProperty( m_lpMsg, PR_TRANSPORT_MESSAGE_HEADERS, m_headers);
-  }
-  else if (pVal && (PROP_TYPE( pVal->ulPropTag) == PT_TSTRING) && (pVal->Value.LPSZ) && (*(pVal->Value.LPSZ))) {
-    m_headers = pVal->Value.LPSZ;
-  }
-  else {
-    // Need to build the headers from the other stuff
-    m_headers.Truncate();
-    BuildHeaders();
-  }
-
-  if (pVal)
-    CMapiApi::MAPIFreeBuffer( pVal);
-
-  m_fromLine.Truncate();
-  if (NeedsFromLine()) {
-    BuildFromLine();
-  }
-
-  if (!m_fromLine.IsEmpty()) {
-    MAPI_DUMP_STRING(m_fromLine.get());
-  }
-  MAPI_DUMP_STRING(m_headers.get());
-  MAPI_TRACE0("\r\n");
-
-  ProcessHeaders();
-
-  if (!m_headers.IsEmpty()) {
-    if (!m_bHasSubject)
-      AddSubject( m_headers);
-    if (!m_bHasFrom)
-      AddFrom( m_headers);
-    if (!m_bHasDate)
-      AddDate( m_headers);
-    m_headers.Trim( kWhitespace, PR_FALSE, PR_TRUE);
-    m_headers += "\x0D\x0A";
-  }
+  ProcessContentType();
 
   return( !m_headers.IsEmpty());
 }
 
-// TRUE if a From line needs to precede the headers, FALSE
-// if the headers already include a from line
-BOOL CMapiMessage::NeedsFromLine( void)
-{
-  nsCString l;
-  m_headers.Left( l, 5);
-  if (l.Equals("From "))
-    return( FALSE);
-  else
-    return( TRUE);
-}
-
-BOOL CMapiMessage::IsMultipart( void)
+bool CMapiMessage::IsMultipart( void) const
 {
   nsCString left;
   m_mimeContentType.Left( left, 10);
   if (left.Equals(NS_LITERAL_CSTRING("multipart/"), nsCaseInsensitiveCStringComparator()))
-    return( TRUE);
-  return( FALSE);
-}
-
-void CMapiMessage::GenerateBoundary( void)
-{
-  m_mimeBoundary = "===============_NSImport_Boundary_";
-  PRUint32 t = ::GetTickCount();
-  nsCString hex;
-  hex.AppendInt( (PRInt32) t, 16);
-  m_mimeBoundary += hex;
-  m_mimeBoundary += "====";
-}
-
-BOOL CMapiMessage::GetAttachFileLoc( nsIFile * pLoc)
-{
-  if (m_attachPath.IsEmpty())
-    return( FALSE);
-  nsCOMPtr <nsILocalFile> locFile = do_QueryInterface(pLoc);
-  locFile->InitWithNativePath(m_attachPath);
-  m_ownsAttachFile = FALSE;
-  return( TRUE);
+    return true;
+  return false;
 }
 
 // Mime-Version: 1.0
 // Content-Type: text/plain; charset="US-ASCII"
 // Content-Type: multipart/mixed; boundary="=====================_874475278==_"
 
-void CMapiMessage::ProcessHeaderLine( nsCString& line)
+void CMapiMessage::ProcessContentType()
 {
-  PRUint32 len, start;
-  nsCString tStr;
-  nsCString left13;
-  nsCString left26;
-  nsCString left8;
-  nsCString left5;
+  m_mimeContentType.Truncate();
+  m_mimeBoundary.Truncate();
+  m_mimeCharset.Truncate();
 
-  line.Left( left13, 13);
-  line.Left( left26, 26);
-  line.Left( left8, 8);
-  line.Left( left5, 5);
+  const wchar_t* contentType = m_headers.Value(CMapiMessageHeaders::hdrContentType);
+  if (!contentType)
+    return;
+
+  const wchar_t *begin = contentType, *end;
+  nsString tStr;
 
-  if (left13.Equals(NS_LITERAL_CSTRING("Mime-Version:"), nsCaseInsensitiveCStringComparator()))
-    m_bMimeVersion = TRUE;
-  else if (left13.Equals(NS_LITERAL_CSTRING("Content-Type:"), nsCaseInsensitiveCStringComparator())) {
-    // Note: this isn't a complete parser, the content type
-    // we extract could have rfc822 comments in it
-    len = 13;
-    while ((len < line.Length()) && IsSpace( line.CharAt( len)))
-      len++;
-    start = len;
-    while ((len < line.Length()) && (line.CharAt( len) != ';'))
-      len++;
-    line.Mid( m_mimeContentType, start, len - start);
-    len++;
-    // look for "boundary="
-    BOOL haveB;
-    BOOL haveC;
-    while (len < line.Length()) {
-      haveB = FALSE;
-      haveC = FALSE;
-      while ((len < line.Length()) && IsSpace( line.CharAt( len)))
-        len++;
-      start = len;
-      while ((len < line.Length()) && (line.CharAt( len) != '='))
-        len++;
-      if (len - start) {
-        line.Mid( tStr, start, len - start);
-        if (tStr.Equals(NS_LITERAL_CSTRING("boundary"), nsCaseInsensitiveCStringComparator()))
-          haveB = TRUE;
-        else if (tStr.Equals(NS_LITERAL_CSTRING("charset"), nsCaseInsensitiveCStringComparator()))
-          haveC = TRUE;
+  // Note: this isn't a complete parser, the content type
+  // we extract could have rfc822 comments in it
+  while (*begin && IsSpace(*begin))
+    begin++;
+  if (!(*begin))
+    return;
+  end = begin;
+  while (*end && (*end != L';'))
+    end++;
+  tStr.Assign(begin, end-begin);
+  CopyUTF16toUTF8(tStr, m_mimeContentType);
+  if (!(*end))
+    return;
+  // look for "boundary="
+  begin = end + 1;
+  bool haveB;
+  bool haveC;
+  while (*begin) {
+    haveB = false;
+    haveC = false;
+    while (*begin && IsSpace(*begin))
+      begin++;
+    if (!(*begin))
+      return;
+    end = begin;
+    while (*end && (*end != L'='))
+      end++;
+    if (end - begin) {
+      tStr.Assign(begin, end-begin);
+      if (tStr.Equals(NS_LITERAL_STRING("boundary"),
+                      nsCaseInsensitiveStringComparator()))
+        haveB = true;
+      else if (tStr.Equals(NS_LITERAL_STRING("charset"),
+                           nsCaseInsensitiveStringComparator()))
+        haveC = true;
+    }
+    if (!(*end))
+      return;
+    begin = end+1;
+    while (*begin && IsSpace(*begin))
+      begin++;
+    if (*begin == L'"') {
+      begin++;
+      bool slash = false;
+      tStr.Truncate();
+      while (*begin) {
+        if (slash) {
+          slash = false;
+          tStr.Append(*begin);
+        }
+        else if (*begin == L'"')
+          break;
+        else if (*begin != L'\\')
+          tStr.Append(*begin);
+        else
+          slash = true;
+        begin++;
       }
-      len++;
-      while ((len < line.Length()) && IsSpace( line.CharAt( len)))
-        len++;
-      if ((len < line.Length()) && (line.CharAt( len) == '"')) {
-        len++;
-        BOOL slash = FALSE;
-        tStr.Truncate();
-        while (len < line.Length()) {
-          if (slash) {
-            slash = FALSE;
-            tStr.Append(line.CharAt( len));
-          }
-          else if (line.CharAt( len) == '"')
-            break;
-          else if (line.CharAt( len) != '\\')
-            tStr.Append(line.CharAt( len));
-          else
-            slash = TRUE;
-          len++;
-        }
-        len++;
-        if (haveB) {
-          m_mimeBoundary = tStr;
-          haveB = FALSE;
-        }
-        if (haveC) {
-          m_mimeCharset = tStr;
-          haveC = FALSE;
-        }
-      }
-      tStr.Truncate();
-      while ((len < line.Length()) && (line.CharAt( len) != ';')) {
-        tStr.Append(line.CharAt( len));
-        len++;
-      }
-      len++;
       if (haveB) {
-        tStr.Trim( kWhitespace);
-        m_mimeBoundary = tStr;
+        CopyUTF16toUTF8(tStr, m_mimeBoundary);
+        haveB = false;
       }
       if (haveC) {
-        tStr.Trim( kWhitespace);
-        m_mimeCharset = tStr;
+        CopyUTF16toUTF8(tStr, m_mimeCharset);
+        haveC = false;
       }
-
+      if (!(*begin))
+        return;
+      begin++;
+    }
+    tStr.Truncate();
+    while (*begin && (*begin != L';')) {
+      tStr.Append(*(begin++));
     }
-  }
-  else if (left26.Equals(NS_LITERAL_CSTRING("Content-Transfer-Encoding:"), nsCaseInsensitiveCStringComparator())) {
-    m_bMimeEncoding = TRUE;
-  }
-  else if (left8.Equals(NS_LITERAL_CSTRING("Subject:"), nsCaseInsensitiveCStringComparator()))
-    m_bHasSubject = TRUE;
-  else if (left5.Equals(NS_LITERAL_CSTRING("From:"), nsCaseInsensitiveCStringComparator()))
-    m_bHasFrom = TRUE;
-  else if (left5.Equals(NS_LITERAL_CSTRING("Date:"), nsCaseInsensitiveCStringComparator()))
-    m_bHasDate = TRUE;
-}
-
-void CMapiMessage::ProcessHeaders( void)
-{
-  m_bHasSubject = FALSE;
-  m_bHasFrom = FALSE;
-  m_bHasDate = FALSE;
-
-  PC_S8 pChar = (PC_S8) m_headers.get();
-  int start = 0;
-  int len = 0;
-  int hdrLen = strlen(pChar);
-  nsCString line;
-  nsCString mid;
-  while (*pChar) {
-    if ((*pChar == 0x0D) && (*(pChar + 1) == 0x0A)) {
-      if ((*(pChar + 2) != ' ') && (*(pChar + 2) != 9)) {
-        m_headers.Mid( mid, start, len);
-        line += mid;
-        ProcessHeaderLine( line);
-        line.Truncate();
-        pChar++; // subsequent increment will move pChar to the next line
-        start += len;
-        start += 2;
-        len = -1;
-      }
+    if (haveB) {
+      tStr.Trim(kWhitespace);
+      CopyUTF16toUTF8(tStr, m_mimeBoundary);
     }
-    pChar++;
-    len++;
-  }
-
-  // See if we still have data to be processed.
-  if (start < hdrLen)
-  {
-    line.Assign(m_headers.get()+start);
-    ProcessHeaderLine(line);
-  }
-
-  if (!m_mimeContentType.IsEmpty() || !m_mimeBoundary.IsEmpty() || !m_mimeCharset.IsEmpty()) {
-    MAPI_TRACE1("\tDecoded mime content type: %s\r\n", m_mimeContentType.get());
-    MAPI_TRACE1("\tDecoded mime boundary: %s\r\n", m_mimeBoundary.get());
-    MAPI_TRACE1("\tDecoded mime charset: %s\r\n", m_mimeCharset.get());
+    if (haveC) {
+      tStr.Trim(kWhitespace);
+      CopyUTF16toUTF8(tStr, m_mimeCharset);
+    }
+    if (*begin)
+      begin++;
   }
 }
 
-BOOL CMapiMessage::FetchBody( void)
+const char* CpToCharset(unsigned int cp)
 {
-  m_bodyIsHtml = FALSE;
-  m_body.Truncate();
-  // Is it html?
-  LPSPropValue pVal = CMapiApi::GetMapiProperty( m_lpMsg, 0x1013001e);
-  if (pVal && CMapiApi::IsLargeProperty( pVal))
-    CMapiApi::GetLargeStringProperty( m_lpMsg, 0x1013001e, m_body);
-  else if (pVal && (PROP_TYPE( pVal->ulPropTag) == PT_TSTRING) && (pVal->Value.LPSZ) && (*(pVal->Value.LPSZ)))
-    m_body = pVal->Value.LPSZ;
+  struct CODEPAGE_TO_CHARSET {
+    unsigned long cp;
+    const char* charset;
+  };
 
-  // Kind-hearted Outlook will give us html even for a plain text message.
-  // But it will include a comment saying it did the conversion.
-  // We'll use this as a hack to really use the plain text part.
-  //
-  // Sadly there are cases where this string is returned despite the fact
-  // that the message is indeed HTML.
-  //
-  // To detect the "true" plain text messages, we look for our string
-  // immediately following the <BODY> tag.
-  if (!m_body.IsEmpty() &&
-      m_body.Find("<BODY>\r\n<!-- Converted from text/plain format -->") ==
-      kNotFound)
-    m_bodyIsHtml = TRUE;
-  else
-  {
-    pVal = CMapiApi::GetMapiProperty( m_lpMsg, PR_BODY);
-    if (pVal)
+  // This table is based on http://msdn.microsoft.com/en-us/library/dd317756(v=VS.85).aspx#1;
+  // Please extend as appropriate. The codepage values are sorted ascending.
+  static const CODEPAGE_TO_CHARSET cptocharset[] =
     {
-      if (pVal && CMapiApi::IsLargeProperty( pVal)) {
-        CMapiApi::GetLargeStringProperty( m_lpMsg, PR_BODY, m_body);
-      }
-      else {
-        if (pVal && (PROP_TYPE( pVal->ulPropTag) == PT_TSTRING) && (pVal->Value.LPSZ) && (*(pVal->Value.LPSZ))) {
-          m_body = pVal->Value.LPSZ;
-        }
+      {37, "IBM037"}, // IBM EBCDIC US-Canada
+      {437, "IBM437"}, //OEM United States
+      {500, "IBM500"}, //IBM EBCDIC International
+      {708, "ASMO-708"}, //Arabic (ASMO 708)
+      //709  Arabic (ASMO-449+, BCON V4)
+      //710  Arabic - Transparent Arabic
+      {720, "DOS-720"}, //Arabic (Transparent ASMO); Arabic (DOS)
+      {737, "ibm737"}, // OEM Greek (formerly 437G); Greek (DOS)
+      {775, "ibm775"}, // OEM Baltic; Baltic (DOS)
+      {850, "ibm850"}, // OEM Multilingual Latin 1; Western European (DOS)
+      {852, "ibm852"}, // OEM Latin 2; Central European (DOS)
+      {855, "IBM855"}, // OEM Cyrillic (primarily Russian)
+      {857, "ibm857"}, // OEM Turkish; Turkish (DOS)
+      {858, "IBM00858"}, // OEM Multilingual Latin 1 + Euro symbol
+      {860, "IBM860"}, // OEM Portuguese; Portuguese (DOS)
+      {861, "ibm861"}, // OEM Icelandic; Icelandic (DOS)
+      {862, "DOS-862"}, // OEM Hebrew; Hebrew (DOS)
+      {863, "IBM863"}, // OEM French Canadian; French Canadian (DOS)
+      {864, "IBM864"}, // OEM Arabic; Arabic (864)
+      {865, "IBM865"}, // OEM Nordic; Nordic (DOS)
+      {866, "cp866"}, // OEM Russian; Cyrillic (DOS)
+      {869, "ibm869"}, // OEM Modern Greek; Greek, Modern (DOS)
+      {870, "IBM870"}, // IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2
+      {874, "windows-874"}, // ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows)
+      {875, "cp875"}, // IBM EBCDIC Greek Modern
+      {932, "shift_jis"}, // ANSI/OEM Japanese; Japanese (Shift-JIS)
+      {936, "gb2312"}, // ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312)
+      {949, "ks_c_5601-1987"}, // ANSI/OEM Korean (Unified Hangul Code)
+      {950, "big5"}, // ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5)
+      {1026, "IBM1026"}, // IBM EBCDIC Turkish (Latin 5)
+      {1047, "IBM01047"}, // IBM EBCDIC Latin 1/Open System
+      {1140, "IBM01140"}, // IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro)
+      {1141, "IBM01141"}, // IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro)
+      {1142, "IBM01142"}, // IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro)
+      {1143, "IBM01143"}, // IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro)
+      {1144, "IBM01144"}, // IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro)
+      {1145, "IBM01145"}, // IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro)
+      {1146, "IBM01146"}, // IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro)
+      {1147, "IBM01147"}, // IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro)
+      {1148, "IBM01148"}, // IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro)
+      {1149, "IBM01149"}, // IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro)
+      {1200, "utf-16"}, // Unicode UTF-16, little endian byte order (BMP of ISO 10646); available only to managed applications
+      {1201, "unicodeFFFE"}, // Unicode UTF-16, big endian byte order; available only to managed applications
+      {1250, "windows-1250"}, // ANSI Central European; Central European (Windows)
+      {1251, "windows-1251"}, // ANSI Cyrillic; Cyrillic (Windows)
+      {1252, "windows-1252"}, // ANSI Latin 1; Western European (Windows)
+      {1253, "windows-1253"}, // ANSI Greek; Greek (Windows)
+      {1254, "windows-1254"}, // ANSI Turkish; Turkish (Windows)
+      {1255, "windows-1255"}, // ANSI Hebrew; Hebrew (Windows)
+      {1256, "windows-1256"}, // ANSI Arabic; Arabic (Windows)
+      {1257, "windows-1257"}, // ANSI Baltic; Baltic (Windows)
+      {1258, "windows-1258"}, // ANSI/OEM Vietnamese; Vietnamese (Windows)
+      {1361, "Johab"}, // Korean (Johab)
+      {10000, "macintosh"}, // MAC Roman; Western European (Mac)
+      {10001, "x-mac-japanese"}, // Japanese (Mac)
+      {10002, "x-mac-chinesetrad"}, // MAC Traditional Chinese (Big5); Chinese Traditional (Mac)
+      {10003, "x-mac-korean"}, // Korean (Mac)
+      {10004, "x-mac-arabic"}, // Arabic (Mac)
+      {10005, "x-mac-hebrew"}, // Hebrew (Mac)
+      {10006, "x-mac-greek"}, // Greek (Mac)
+      {10007, "x-mac-cyrillic"}, // Cyrillic (Mac)
+      {10008, "x-mac-chinesesimp"}, // MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac)
+      {10010, "x-mac-romanian"}, // Romanian (Mac)
+      {10017, "x-mac-ukrainian"}, // Ukrainian (Mac)
+      {10021, "x-mac-thai"}, // Thai (Mac)
+      {10029, "x-mac-ce"}, // MAC Latin 2; Central European (Mac)
+      {10079, "x-mac-icelandic"}, // Icelandic (Mac)
+      {10081, "x-mac-turkish"}, // Turkish (Mac)
+      {10082, "x-mac-croatian"}, // Croatian (Mac)
+      // Unicode UTF-32, little endian byte order; available only to managed applications 
+      // impossible in 8-bit mail
+      {12000, "utf-32"},
+       // Unicode UTF-32, big endian byte order; available only to managed applications
+       // impossible in 8-bit mail
+      {12001, "utf-32BE"},
+      {20000, "x-Chinese_CNS"}, // CNS Taiwan; Chinese Traditional (CNS)
+      {20001, "x-cp20001"}, // TCA Taiwan
+      {20002, "x_Chinese-Eten"}, // Eten Taiwan; Chinese Traditional (Eten)
+      {20003, "x-cp20003"}, // IBM5550 Taiwan
+      {20004, "x-cp20004"}, // TeleText Taiwan
+      {20005, "x-cp20005"}, // Wang Taiwan
+      {20105, "x-IA5"}, // IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5)
+      {20106, "x-IA5-German"}, // IA5 German (7-bit)
+      {20107, "x-IA5-Swedish"}, // IA5 Swedish (7-bit)
+      {20108, "x-IA5-Norwegian"}, // IA5 Norwegian (7-bit)
+      {20127, "us-ascii"}, // US-ASCII (7-bit)
+      {20261, "x-cp20261"}, // T.61
+      {20269, "x-cp20269"}, // ISO 6937 Non-Spacing Accent
+      {20273, "IBM273"}, // IBM EBCDIC Germany
+      {20277, "IBM277"}, // IBM EBCDIC Denmark-Norway
+      {20278, "IBM278"}, // IBM EBCDIC Finland-Sweden
+      {20280, "IBM280"}, // IBM EBCDIC Italy
+      {20284, "IBM284"}, // IBM EBCDIC Latin America-Spain
+      {20285, "IBM285"}, // IBM EBCDIC United Kingdom
+      {20290, "IBM290"}, // IBM EBCDIC Japanese Katakana Extended
+      {20297, "IBM297"}, // IBM EBCDIC France
+      {20420, "IBM420"}, // IBM EBCDIC Arabic
+      {20423, "IBM423"}, // IBM EBCDIC Greek
+      {20424, "IBM424"}, // IBM EBCDIC Hebrew
+      {20833, "x-EBCDIC-KoreanExtended"}, // IBM EBCDIC Korean Extended
+      {20838, "IBM-Thai"}, // IBM EBCDIC Thai
+      {20866, "koi8-r"}, // Russian (KOI8-R); Cyrillic (KOI8-R)
+      {20871, "IBM871"}, // IBM EBCDIC Icelandic
+      {20880, "IBM880"}, // IBM EBCDIC Cyrillic Russian
+      {20905, "IBM905"}, // IBM EBCDIC Turkish
+      {20924, "IBM00924"}, // IBM EBCDIC Latin 1/Open System (1047 + Euro symbol)
+      {20932, "EUC-JP"}, // Japanese (JIS 0208-1990 and 0121-1990)
+      {20936, "x-cp20936"}, // Simplified Chinese (GB2312); Chinese Simplified (GB2312-80)
+      {20949, "x-cp20949"}, // Korean Wansung
+      {21025, "cp1025"}, // IBM EBCDIC Cyrillic Serbian-Bulgarian
+      //21027  (deprecated)
+      {21866, "koi8-u"}, // Ukrainian (KOI8-U); Cyrillic (KOI8-U)
+      {28591, "iso-8859-1"}, // ISO 8859-1 Latin 1; Western European (ISO)
+      {28592, "iso-8859-2"}, // ISO 8859-2 Central European; Central European (ISO)
+      {28593, "iso-8859-3"}, // ISO 8859-3 Latin 3
+      {28594, "iso-8859-4"}, // ISO 8859-4 Baltic
+      {28595, "iso-8859-5"}, // ISO 8859-5 Cyrillic
+      {28596, "iso-8859-6"}, // ISO 8859-6 Arabic
+      {28597, "iso-8859-7"}, // ISO 8859-7 Greek
+      {28598, "iso-8859-8"}, // ISO 8859-8 Hebrew; Hebrew (ISO-Visual)
+      {28599, "iso-8859-9"}, // ISO 8859-9 Turkish
+      {28603, "iso-8859-13"}, // ISO 8859-13 Estonian
+      {28605, "iso-8859-15"}, // ISO 8859-15 Latin 9
+      {29001, "x-Europa"}, // Europa 3
+      {38598, "iso-8859-8-i"}, // ISO 8859-8 Hebrew; Hebrew (ISO-Logical)
+      {50220, "iso-2022-jp"}, // ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS)
+      {50221, "csISO2022JP"}, // ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana)
+      {50222, "iso-2022-jp"}, // ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI)
+      {50225, "iso-2022-kr"}, // ISO 2022 Korean
+      {50227, "x-cp50227"}, // ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022)
+      //50229  ISO 2022 Traditional Chinese
+      //50930  EBCDIC Japanese (Katakana) Extended
+      //50931  EBCDIC US-Canada and Japanese
+      //50933  EBCDIC Korean Extended and Korean
+      //50935  EBCDIC Simplified Chinese Extended and Simplified Chinese
+      //50936  EBCDIC Simplified Chinese
+      //50937  EBCDIC US-Canada and Traditional Chinese
+      //50939  EBCDIC Japanese (Latin) Extended and Japanese
+      {51932, "euc-jp"}, // EUC Japanese
+      {51936, "EUC-CN"}, // EUC Simplified Chinese; Chinese Simplified (EUC)
+      {51949, "euc-kr"}, // EUC Korean
+      //51950  EUC Traditional Chinese
+      {52936, "hz-gb-2312"}, // HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ)
+      {54936, "GB18030"}, // Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030)
+      {57002, "x-iscii-de"}, // ISCII Devanagari
+      {57003, "x-iscii-be"}, // ISCII Bengali
+      {57004, "x-iscii-ta"}, // ISCII Tamil
+      {57005, "x-iscii-te"}, // ISCII Telugu
+      {57006, "x-iscii-as"}, // ISCII Assamese
+      {57007, "x-iscii-or"}, // ISCII Oriya
+      {57008, "x-iscii-ka"}, // ISCII Kannada
+      {57009, "x-iscii-ma"}, // ISCII Malayalam
+      {57010, "x-iscii-gu"}, // ISCII Gujarati
+      {57011, "x-iscii-pa"}, // ISCII Punjabi
+      {65000, "utf-7"}, // Unicode (UTF-7)
+      {65001, "utf-8"}, // Unicode (UTF-8)
+    };
+
+  // Binary search
+  int begin = 0, end = sizeof(cptocharset)/sizeof(cptocharset[0])-1;
+  while (begin <= end) {
+    int mid = (begin+end)/2;
+    unsigned int mid_cp = cptocharset[mid].cp;
+    if (cp == mid_cp)
+      return cptocharset[mid].charset;
+    if (cp < mid_cp)
+      end = mid - 1;
+    else // cp > cptocharset[mid].cp
+      begin = mid + 1;
+  }
+  return 0; // not found
+}
+
+// We don't use nsMsgI18Ncheck_data_in_charset_range because it returns true
+// even if there's no such charset:
+// 1. result initialized by PR_TRUE and returned if, eg, GetUnicodeEncoderRaw fail
+// 2. it uses GetUnicodeEncoderRaw(), not GetUnicodeEncoder() (to normalize the
+//    charset string) (see nsMsgI18N.cpp)
+// This function returns true only if the unicode (utf-16) text can be
+// losslessly represented in specified charset
+bool CMapiMessage::CheckBodyInCharsetRange(const char* charset)
+{
+  if (m_body.IsEmpty())
+    return true;
+  if (!_stricmp(charset, "utf-8"))
+    return true;
+  if (!_stricmp(charset, "utf-7"))
+    return true;
+
+  nsresult rv;
+  static nsCOMPtr<nsICharsetConverterManager> ccm =
+    do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, false);
+  nsCOMPtr<nsIUnicodeEncoder> encoder;
+
+  // get an unicode converter
+  rv = ccm->GetUnicodeEncoder(charset, getter_AddRefs(encoder));
+  NS_ENSURE_SUCCESS(rv, false);
+  rv = encoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Signal, nsnull, 0);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  const wchar_t *txt = m_body.get();
+  PRInt32 txtLen = m_body.Length();
+  const wchar_t *currentSrcPtr = txt;
+  int srcLength;
+  int dstLength;
+  char localbuf[512];
+  int consumedLen = 0;
+
+  // convert
+  while (consumedLen < txtLen) {
+    srcLength = txtLen - consumedLen;  
+    dstLength = sizeof(localbuf)/sizeof(localbuf[0]);
+    rv = encoder->Convert(currentSrcPtr, &srcLength, localbuf, &dstLength);
+    if (rv == NS_ERROR_UENC_NOMAPPING)
+      return false;
+    if (NS_FAILED(rv) || dstLength == 0)
+      break;
+
+    currentSrcPtr += srcLength;
+    consumedLen = currentSrcPtr - txt; // src length used so far
+  }
+  return true;
+}
+
+bool CaseInsensitiveComp (wchar_t elem1, wchar_t elem2 )
+{
+  return _wcsnicmp(&elem1, &elem2, 1) == 0;
+}
+
+void ExtractMetaCharset( const wchar_t* body, int bodySz, /*out*/nsCString& charset)
+{
+  charset.Truncate();
+  const wchar_t* body_end = body+bodySz;
+  const wchar_t str_eohd[] = L"/head";
+  const wchar_t *str_eohd_end = str_eohd+sizeof(str_eohd)/sizeof(str_eohd[0])-1;
+  const wchar_t* eohd_pos = std::search(body, body_end, str_eohd, str_eohd_end,
+                                        CaseInsensitiveComp);
+  if (eohd_pos == body_end) // No header!
+    return;
+  const wchar_t str_chset[] = L"charset=";
+  const wchar_t *str_chset_end =
+    str_chset + sizeof(str_chset)/sizeof(str_chset[0])-1;
+  const wchar_t* chset_pos = std::search(body, eohd_pos, str_chset,
+                                         str_chset_end, CaseInsensitiveComp);
+  if (chset_pos == eohd_pos) // No charset!
+    return;
+  chset_pos += 8;
+
+  // remove everything from the string after the next ; or " or space,
+  // whichever comes first.
+  // The inital sting looks something like
+  // <META content="text/html; charset=utf-8" http-equiv=Content-Type>
+  // <META content="text/html; charset=utf-8;" http-equiv=Content-Type>
+  // <META content="text/html; charset=utf-8 ;" http-equiv=Content-Type>
+  // <META content="text/html; charset=utf-8 " http-equiv=Content-Type>
+  const wchar_t term[] = L";\" ", *term_end= term+sizeof(term)/sizeof(term[0])-1;
+  const wchar_t* chset_end = std::find_first_of(chset_pos, eohd_pos, term,
+                                                term_end);
+  if (chset_end != eohd_pos)
+    LossyCopyUTF16toASCII(Substring(chset_pos, chset_end), charset);
+}
+
+bool CMapiMessage::FetchBody( void)
+{
+  m_bodyIsHtml = false;
+  m_body.Truncate();
+
+  // Get the Outlook codepage info; if unsuccessful then it defaults to 0 (CP_ACP) -> system default
+  // Maybe we can use this info later?
+  unsigned int codepage=0;
+  LPSPropValue pVal = CMapiApi::GetMapiProperty( m_lpMsg, PR_INTERNET_CPID);
+  if (pVal) {
+    if (PROP_TYPE( pVal->ulPropTag) == PT_LONG)
+      codepage = pVal->Value.l;
+    CMapiApi::MAPIFreeBuffer( pVal);
+  }
+
+  unsigned long nativeBodyType = 0;
+  if (CMapiApi::GetRTFPropertyDecodedAsUTF16(m_lpMsg, m_body, nativeBodyType,
+                                             codepage)) {
+    m_bodyIsHtml = nativeBodyType == MAPI_NATIVE_BODY_TYPE_HTML;
+  }
+  else { // Cannot get RTF version
+    // Is it html?
+    pVal = CMapiApi::GetMapiProperty(m_lpMsg, PR_BODY_HTML_W);
+    if (pVal) {
+      if (CMapiApi::IsLargeProperty(pVal))
+        CMapiApi::GetLargeStringProperty(m_lpMsg, PR_BODY_HTML_W, m_body);
+      else if ((PROP_TYPE(pVal->ulPropTag) == PT_UNICODE) &&
+               (pVal->Value.lpszW) && (*(pVal->Value.lpszW)))
+        m_body.Assign(pVal->Value.lpszW);
+      CMapiApi::MAPIFreeBuffer( pVal);
+    }
+
+    // Kind-hearted Outlook will give us html even for a plain text message.
+    // But it will include a comment saying it did the conversion.
+    // We'll use this as a hack to really use the plain text part.
+    //
+    // Sadly there are cases where this string is returned despite the fact
+    // that the message is indeed HTML.
+    //
+    // To detect the "true" plain text messages, we look for our string
+    // immediately following the <BODY> tag.
+    if (!m_body.IsEmpty() &&
+        m_body.Find(L"<BODY>\r\n<!-- Converted from text/plain format -->") ==
+        kNotFound) {
+      m_bodyIsHtml = true;
+    }
+    else {
+      pVal = CMapiApi::GetMapiProperty(m_lpMsg, PR_BODY_W);
+      if (pVal) {
+        if (CMapiApi::IsLargeProperty(pVal))
+          CMapiApi::GetLargeStringProperty(m_lpMsg, PR_BODY_W, m_body);
+        else if ((PROP_TYPE(pVal->ulPropTag) == PT_UNICODE) &&
+                 (pVal->Value.lpszW) && (*(pVal->Value.lpszW)))
+          m_body.Assign(pVal->Value.lpszW);
+        CMapiApi::MAPIFreeBuffer(pVal);
       }
     }
   }
 
-  if (pVal)
-    CMapiApi::MAPIFreeBuffer( pVal);
+  // OK, now let's restore the original encoding!
+  // 1. We may have a header defining the charset (we already called the FetchHeaders(), and there ProcessHeaders();
+  //    in this case, the m_mimeCharset is set. See nsOutlookMail::ImportMailbox())
+  // 2. We may have the codepage walue provided by Outlook ("codepage" at the very beginning of this function)
+  // 3. We may have an HTML charset header.
+
+  bool bFoundCharset = false;
+
+  if (!m_mimeCharset.IsEmpty()) // The top-level header data
+    bFoundCharset = CheckBodyInCharsetRange(m_mimeCharset.get());
+  // No valid charset in the message header - try the HTML header.
+  // arguably may be useless
+  if (!bFoundCharset && m_bodyIsHtml) {
+    ExtractMetaCharset(m_body.get(), m_body.Length(), m_mimeCharset);
+    if (!m_mimeCharset.IsEmpty())
+      bFoundCharset = CheckBodyInCharsetRange(m_mimeCharset.get());
+  }
+  // Get from Outlook (seems like it keeps the MIME part header encoding info)
+  if (!bFoundCharset && codepage) {
+    const char* charset = CpToCharset(codepage);
+    if (charset) {
+      bFoundCharset = CheckBodyInCharsetRange(charset);
+      if (bFoundCharset)
+        m_mimeCharset.Assign(charset);
+    }
+  }
+  if (!bFoundCharset) { // Use system default
+    const char* charset = nsMsgI18NFileSystemCharset();
+    if (charset) {
+      bFoundCharset = CheckBodyInCharsetRange(charset);
+      if (bFoundCharset)
+        m_mimeCharset.Assign(charset);
+    }
+  }
+  if (!bFoundCharset) // Everything else failed, let's use the lossless utf-8...
+    m_mimeCharset.Assign("utf-8");
 
   MAPI_DUMP_STRING(m_body.get());
   MAPI_TRACE0("\r\n");
 
-  return( TRUE);
+  return true;
+}
+
+void CMapiMessage::GetBody(nsCString& dest) const
+{
+  nsMsgI18NConvertFromUnicode(m_mimeCharset.get(), m_body, dest);
+}
+
+void CMapiMessage::FetchFlags(void)
+{
+  LPSPropValue pVal = CMapiApi::GetMapiProperty(m_lpMsg, PR_MESSAGE_FLAGS);
+  if (pVal)
+    m_msgFlags = CMapiApi::GetLongFromProp(pVal);
+  pVal = CMapiApi::GetMapiProperty(m_lpMsg, PR_LAST_VERB_EXECUTED);
+  if (pVal)
+    m_msgLastVerb = CMapiApi::GetLongFromProp(pVal);
 }
 
 enum {
   ieidPR_ATTACH_NUM = 0,
   ieidAttachMax
 };
 
 static const SizedSPropTagArray(ieidAttachMax, ptaEid)=
 {
   ieidAttachMax,
   {
     PR_ATTACH_NUM
   }
 };
 
-int CMapiMessage::CountAttachments( void)
+bool CMapiMessage::IterateAttachTable(LPMAPITABLE lpTable)
 {
-  m_attachNums.Clear();
-
-  LPSPropValue pVal = CMapiApi::GetMapiProperty( m_lpMsg, PR_HASATTACH);
-  BOOL has = TRUE;
-
-  if (pVal && (PROP_TYPE( pVal->ulPropTag) == PT_BOOLEAN)) {
-    has = (pVal->Value.b != 0);
-  }
-  if (pVal)
-    CMapiApi::MAPIFreeBuffer( pVal);
-
-  if (has) {
-    // Get the attachment table?
-    HRESULT hr;
-    LPMAPITABLE pTable = NULL;
-
-    hr = m_lpMsg->GetAttachmentTable( 0, &pTable);
-    if (FAILED( hr))
-      return( 0);
-    m_pAttachTable = pTable;
-    IterateAttachTable();
-  }
-
-  return m_attachNums.Length();
-}
-
-
-BOOL CMapiMessage::IterateAttachTable( void)
-{
-  LPMAPITABLE lpTable = m_pAttachTable;
   ULONG rowCount;
   HRESULT hr = lpTable->GetRowCount( 0, &rowCount);
   if (!rowCount) {
-    return( TRUE);
+    return true;
   }
 
   hr = lpTable->SetColumns( (LPSPropTagArray)&ptaEid, 0);
   if (FAILED(hr)) {
     MAPI_TRACE2( "SetColumns for attachment table failed: 0x%lx, %d\r\n", (long)hr, (int)hr);
-    return( FALSE);
+    return false;
   }
 
   hr = lpTable->SeekRow( BOOKMARK_BEGINNING, 0, NULL);
   if (FAILED(hr)) {
     MAPI_TRACE2( "SeekRow for attachment table failed: 0x%lx, %d\r\n", (long)hr, (int)hr);
-    return( FALSE);
+    return false;
   }
 
   int cNumRows = 0;
   LPSRowSet lpRow;
-  BOOL bResult = TRUE;
+  bool bResult = true;
   do {
 
     lpRow = NULL;
     hr = lpTable->QueryRows( 1, 0, &lpRow);
 
     if(HR_FAILED(hr)) {
       MAPI_TRACE2( "QueryRows for attachment table failed: 0x%lx, %d\n", (long)hr, (int)hr);
-      bResult = FALSE;
+      bResult = false;
       break;
     }
 
     if (lpRow) {
       cNumRows = lpRow->cRows;
 
       if (cNumRows) {
         DWORD aNum = lpRow->aRow[0].lpProps[ieidPR_ATTACH_NUM].Value.ul;
-        m_attachNums.AppendElement(aNum);
+        AddAttachment(aNum);
         MAPI_TRACE1( "\t\t****Attachment found - #%d\r\n", (int)aNum);
       }
       CMapiApi::FreeProws( lpRow);
     }
 
   } while ( SUCCEEDED(hr) && cNumRows && lpRow);
 
   return( bResult);
 }
 
-void CMapiMessage::ClearTempAttachFile( void)
+bool CMapiMessage::GetTmpFile(/*out*/ nsILocalFile **aResult)
 {
-  if (m_ownsAttachFile && !m_attachPath.IsEmpty()) {
-    nsCOMPtr <nsILocalFile> locFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
-    if (locFile && (NS_SUCCEEDED(locFile->InitWithNativePath(m_attachPath))))
-      locFile->Remove(PR_FALSE);
-  }
-  m_ownsAttachFile = FALSE;
-  m_attachPath.Truncate();
-}
-
-BOOL CMapiMessage::CopyBinAttachToFile( LPATTACH lpAttach)
-{
-  LPSTREAM lpStreamFile;
-
-  m_ownsAttachFile = FALSE;
-  m_attachPath.Truncate();
-
   nsCOMPtr<nsIFile> tmpFile;
   nsresult rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR,
     "mapiattach.tmp",
     getter_AddRefs(tmpFile));
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_FAILED(rv))
+    return false;
 
   rv = tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_FAILED(rv))
+    return false;
+
+  return NS_SUCCEEDED(CallQueryInterface(tmpFile, aResult));
+}
+
+bool CMapiMessage::CopyMsgAttachToFile(LPATTACH lpAttach, /*out*/ nsILocalFile **tmp_file)
+{
+  bool bResult = true;
+  LPMESSAGE  lpMsg;
+  HRESULT hr = lpAttach->OpenProperty(PR_ATTACH_DATA_OBJ, &IID_IMessage, 0, 0,
+                                      reinterpret_cast<LPUNKNOWN *>(&lpMsg));
+  NS_ENSURE_SUCCESS(hr, false);
+
+  if (!GetTmpFile(tmp_file))
+    return false;
+
+  nsCOMPtr<nsIOutputStream> destOutputStream;
+  nsresult rv = MsgNewBufferedFileOutputStream(getter_AddRefs(destOutputStream), *tmp_file, -1, 0600);
+  if (NS_SUCCEEDED(rv))
+    rv = nsOutlookMail::ImportMessage(lpMsg, destOutputStream, nsIMsgSend::nsMsgSaveAsDraft);
+
+  if (NS_FAILED(rv)) {
+    (*tmp_file)->Remove(PR_FALSE);
+    (*tmp_file)->Release();
+    tmp_file = 0;
+  }
+
+  return NS_SUCCEEDED(rv);
+}
+
+bool CMapiMessage::CopyBinAttachToFile(LPATTACH lpAttach,
+                                       nsILocalFile **tmp_file)
+{
+  nsCOMPtr<nsIFile> _tmp_file;
+  nsresult rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR,
+    "mapiattach.tmp",
+    getter_AddRefs(_tmp_file));
+  NS_ENSURE_SUCCESS(rv, false);
+
+  rv = _tmp_file->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600);
+  NS_ENSURE_SUCCESS(rv, false);
 
   nsCString tmpPath;
-  tmpFile->GetNativePath(tmpPath);
+  _tmp_file->GetNativePath(tmpPath);
+  LPSTREAM lpStreamFile;
   HRESULT hr = CMapiApi::OpenStreamOnFile( gpMapiAllocateBuffer, gpMapiFreeBuffer, STGM_READWRITE | STGM_CREATE,
-    (char *) tmpPath.get(), NULL, &lpStreamFile);
+    const_cast<char*>(tmpPath.get()), NULL, &lpStreamFile);
   if (HR_FAILED(hr)) {
     MAPI_TRACE1("~~ERROR~~ OpenStreamOnFile failed - temp path: %s\r\n",
                 tmpPath.get());
-    return( FALSE);
+    return false;
   }
-  MAPI_TRACE1("\t\t** Attachment extracted to temp file: %s\r\n",
-              m_attachPath.get());
 
-  BOOL bResult = TRUE;
+  bool bResult = true;
   LPSTREAM lpAttachStream;
   hr = lpAttach->OpenProperty( PR_ATTACH_DATA_BIN, &IID_IStream, 0, 0, (LPUNKNOWN *)&lpAttachStream);
 
   if (HR_FAILED( hr)) {
     MAPI_TRACE0( "~~ERROR~~ OpenProperty failed for PR_ATTACH_DATA_BIN.\r\n");
     lpAttachStream = NULL;
-    bResult = FALSE;
+    bResult = false;
   }
   else {
     STATSTG st;
     hr = lpAttachStream->Stat( &st, STATFLAG_NONAME);
     if (HR_FAILED( hr)) {
       MAPI_TRACE0( "~~ERROR~~ Stat failed for attachment stream\r\n");
-      bResult = FALSE;
+      bResult = false;
     }
     else {
       hr = lpAttachStream->CopyTo( lpStreamFile, st.cbSize, NULL, NULL);
       if (HR_FAILED( hr)) {
         MAPI_TRACE0( "~~ERROR~~ Attach Stream CopyTo temp file failed.\r\n");
-        bResult = FALSE;
+        bResult = false;
       }
     }
   }
 
-  m_attachPath = tmpPath;
   if (lpAttachStream)
     lpAttachStream->Release();
   lpStreamFile->Release();
   if (!bResult)
-    tmpFile->Remove(PR_FALSE);
+    _tmp_file->Remove(PR_FALSE);
   else
-    m_ownsAttachFile = TRUE;
+    CallQueryInterface(_tmp_file, tmp_file);
+
+  return bResult;
+}
+
+bool CMapiMessage::GetURL(nsIFile *aFile, nsIURI **url)
+{
+  if (!m_pIOService)
+    return false;
+
+  nsresult rv = m_pIOService->NewFileURI(aFile, url);
+  return NS_SUCCEEDED(rv);
+}
+
+bool CMapiMessage::AddAttachment(DWORD aNum)
+{
+  LPATTACH lpAttach = NULL;
+  HRESULT hr = m_lpMsg->OpenAttach(aNum, NULL, 0, &lpAttach);
+  if (HR_FAILED(hr)) {
+    MAPI_TRACE2("\t\t****Attachment error, unable to open attachment: %d, 0x%lx\r\n", idx, hr);
+    return false;
+  }
+
+  bool bResult = false;
+  attach_data *data = new attach_data;
+  ULONG aMethod;
+  if (data) {
+    bResult = true;
+
+    // 1. Get the file that contains the attachment data
+    LPSPropValue pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_METHOD);
+    if (pVal) {
+      aMethod = CMapiApi::GetLongFromProp( pVal);
+      switch (aMethod) {
+      case ATTACH_BY_VALUE:
+        MAPI_TRACE1( "\t\t** Attachment #%d by value.\r\n", aNum);
+        bResult = CopyBinAttachToFile(lpAttach, getter_AddRefs(data->tmp_file));
+        data->delete_file = true;
+        break;
+      case ATTACH_BY_REFERENCE:
+      case ATTACH_BY_REF_RESOLVE:
+      case ATTACH_BY_REF_ONLY:
+        pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_PATHNAME_W);
+        if (pVal) {
+          nsCString path;
+          CMapiApi::GetStringFromProp(pVal, path);
+          nsresult rv;
+          data->tmp_file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+          if (NS_FAILED(rv) || !data->tmp_file) {
+            MAPI_TRACE0("*** Error creating file spec for attachment\n");
+            bResult = false;
+          }
+          else data->tmp_file->InitWithNativePath(path);
+        }
+        MAPI_TRACE2("\t\t** Attachment #%d by ref: %s\r\n",
+          aNum, m_attachPath.get());
+        break;
+      case ATTACH_EMBEDDED_MSG:
+        MAPI_TRACE1("\t\t** Attachment #%d by Embedded Message??\r\n", aNum);
+        // Convert the embedded IMessage from PR_ATTACH_DATA_OBJ to rfc822 attachment
+        // (see http://msdn.microsoft.com/en-us/library/cc842329.aspx)
+        // This is a recursive call.
+        bResult = CopyMsgAttachToFile(lpAttach, getter_AddRefs(data->tmp_file));
+        data->delete_file = true;
+        break;
+      case ATTACH_OLE:
+        MAPI_TRACE1("\t\t** Attachment #%d by OLE - yuck!!!\r\n", aNum);
+        break;
+      default:
+        MAPI_TRACE2("\t\t** Attachment #%d unknown attachment method - 0x%lx\r\n", aNum, aMethod);
+        bResult = false;
+      }
+    }
+    else
+      bResult = false;
+
+    if (bResult)
+      bResult = data->tmp_file;
+
+    if (bResult) {
+      PRBool isFile = PR_FALSE;
+      PRBool exists = PR_FALSE;
+      data->tmp_file->Exists(&exists);
+      data->tmp_file->IsFile(&isFile);
+
+      if (!exists || !isFile) {
+        bResult = false;
+        MAPI_TRACE0("Attachment file does not exist\n");
+      }
+    }
+
+    if (bResult)
+      bResult = GetURL(data->tmp_file, getter_AddRefs(data->orig_url));
+
+    if (bResult) {
+      // Now we have the file; proceed to the other properties
 
+      data->encoding = NS_strdup(ENCODING_BINARY);
+
+      nsString fname, fext;
+      pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_LONG_FILENAME_W);
+      if (!pVal)
+        pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_FILENAME_W);
+      CMapiApi::GetStringFromProp(pVal, fname);
+      pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_EXTENSION_W);
+      CMapiApi::GetStringFromProp(pVal, fext);
+      MAPI_TRACE2("\t\t\t--- File name: %s, extension: %s\r\n",
+        fname.get(), fext.get());
+
+      if (fext.IsEmpty()) {
+        int idx = fname.RFindChar(L'.');
+        if (idx != -1)
+          fname.Right(fext, fname.Length() - idx);
+      }
+      else if (fname.RFindChar(L'.') == -1) {
+        fname += L".";
+        fname += fext;
+      }
+      if (fname.IsEmpty()) {
+        // If no description use "Attachment i" format.
+        fname = L"Attachment ";
+        fname.AppendInt(static_cast<PRUint32>(aNum));
+      }
+      data->real_name = ToNewUTF8String(fname);
+
+      nsCString tmp;
+       // We have converted it to the rfc822 document
+      if (aMethod == ATTACH_EMBEDDED_MSG) {
+        data->type = NS_strdup(MESSAGE_RFC822);
+      } else {
+        pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_MIME_TAG_A);
+        CMapiApi::GetStringFromProp(pVal, tmp);
+        MAPI_TRACE1("\t\t\t--- Mime type: %s\r\n", tmp.get());
+        if (tmp.IsEmpty()) {
+          PRUint8 *pType = NULL;
+          if (!fext.IsEmpty()) {
+            pType = CMimeTypes::GetMimeType(fext);
+          }
+          if (pType)
+            data->type = NS_strdup((PC_S8)pType);
+          else
+            data->type = NS_strdup(APPLICATION_OCTET_STREAM);
+        }
+        else
+          data->type = ToNewCString(tmp);
+      }
+
+      pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_CONTENT_ID_A);
+      CMapiApi::GetStringFromProp(pVal, tmp);
+      if (!tmp.IsEmpty())
+        data->cid = ToNewCString(tmp);
+    }
+    if (bResult) {
+      // Now we need to decide if this attachment is embedded or not.
+      // At first, I tried to simply check for the presence of the Content-Id.
+      // But it turned out that this method is unreliable, since there exist cases
+      // when an attachment has a Content-Id while isn't embedded (even in a message
+      // with a plain-text body!). So next I tried to look for <img> tags that contain
+      // the found Content-Id. But this is unreliable, too, because there exist cases
+      // where other places of HTML reference the embedded messages (e.g. it may be
+      // a background of a table cell, or some CSS; further, it is possible that the
+      // reference to an embedded object is not in the main body, but in another
+      // embedded object - like body references a CSS attachment that in turn references
+      // a picture as a background of its element). From the other hand, it's unreliable
+      // to relax the search criteria to any occurence of the Content-Id string in the body -
+      // partly because the string may be simply in a text or other non-referencing part,
+      // partly because of the abovementioned possibility that the reference is outside
+      // the body at all.
+      // There exist the PR_ATTACH_FLAGS property of the attachment. The MS documentation
+      // tells about two possible flags in it: ATT_INVISIBLE_IN_HTML and ATT_INVISIBLE_IN_RTF.
+      // There is at least one more undocumented flag: ATT_MHTML_REF. Some sources in Internet
+      // suggest simply check for the latter flag to distinguish between the embedded
+      // and ordinary attachments. But my observations indicate that even if the flags
+      // don't include ATT_MHTML_REF, the attachment is still may be embedded.
+      // However, my observations always show that the message is embedded if the flags
+      // is not 0.
+      // So now I will simply test for the non-zero flags to decide whether the attachment
+      // is embedded or not. Possible advantage is reliability (I hope).
+      // Another advantage is that it's much faster than search the body for Content-Id.
+
+      DWORD flags = 0;
+
+      pVal = CMapiApi::GetMapiProperty(lpAttach, PR_ATTACH_FLAGS);
+      if (pVal)
+        flags = CMapiApi::GetLongFromProp(pVal);
+      if (m_bodyIsHtml && data->cid && (flags != 0)) // this is the embedded attachment
+        m_embattachments.push_back(data);
+      else // this is ordinary attachment
+        m_stdattachments.push_back(data);
+    }
+    else {
+      delete data;
+    }
+  }
+
+  lpAttach->Release();
   return( bResult);
 }
 
-BOOL CMapiMessage::GetAttachmentInfo( int idx)
+void CMapiMessage::ClearAttachment(attach_data* data)
 {
-  ClearTempAttachFile();
+  if (data->delete_file && data->tmp_file)
+    data->tmp_file->Remove(PR_FALSE);
+
+  if (data->type)
+    NS_Free(data->type);
+  if (data->encoding)
+    NS_Free(data->encoding);
+  if (data->real_name)
+    NS_Free(data->real_name);
+  if (data->cid)
+    NS_Free(data->cid);
+
+  delete data;
+}
 
-  BOOL bResult = TRUE;
-  if ((idx < 0) || (idx >= (int)m_attachNums.Length())) {
-    return( FALSE);
+void CMapiMessage::ClearAttachments()
+{
+  std::for_each(m_stdattachments.begin(), m_stdattachments.end(), ClearAttachment);
+  m_stdattachments.clear();
+  std::for_each(m_embattachments.begin(), m_embattachments.end(), ClearAttachment);
+  m_embattachments.clear();
+}
+
+// This method must be called AFTER the retrieval of the body,
+// since the decision if an attachment is embedded or not is made
+// based on the body type and contents
+void CMapiMessage::ProcessAttachments()
+{
+  LPSPropValue pVal = CMapiApi::GetMapiProperty(m_lpMsg, PR_HASATTACH);
+  bool hasAttach = true;
+
+  if (pVal) {
+    if (PROP_TYPE( pVal->ulPropTag) == PT_BOOLEAN)
+      hasAttach = (pVal->Value.b != 0);
+    CMapiApi::MAPIFreeBuffer( pVal);
   }
 
-  DWORD aNum = m_attachNums[idx];
-  LPATTACH lpAttach = NULL;
-  HRESULT hr = m_lpMsg->OpenAttach( aNum, NULL, 0, &lpAttach);
-  if (HR_FAILED( hr)) {
-    MAPI_TRACE2( "\t\t****Attachment error, unable to open attachment: %d, 0x%lx\r\n", idx, hr);
-    return( FALSE);
+  if (!hasAttach)
+    return;
+
+  // Get the attachment table?
+  LPMAPITABLE pTable = NULL;
+  HRESULT hr = m_lpMsg->GetAttachmentTable( 0, &pTable);
+  if (FAILED( hr) || !pTable)
+    return;
+  IterateAttachTable(pTable);
+  pTable->Release();
+}
+
+nsMsgAttachedFile* CMapiMessage::GetAttachments()
+{
+  nsMsgAttachedFile* result = new nsMsgAttachedFile[m_stdattachments.size()+1];
+  if (!result)
+    return 0;
+  memset(result, 0, sizeof(nsMsgAttachedFile)*m_stdattachments.size()+1);
+
+  nsMsgAttachedFile* pos=result;
+  for (std::vector<attach_data*>::const_iterator it = m_stdattachments.begin();
+       it != m_stdattachments.end(); pos++, it++) {
+    pos->orig_url = (*it)->orig_url;
+    pos->tmp_file = (*it)->tmp_file;
+    pos->encoding = (*it)->encoding;
+    pos->real_name = (*it)->real_name;
+    pos->type = (*it)->type;
   }
 
-  LPSPropValue pVal;
-  pVal = CMapiApi::GetMapiProperty( lpAttach, PR_ATTACH_MIME_TAG);
-  if (pVal)
-    CMapiApi::GetStringFromProp( pVal, m_attachMimeType);
-  else
-    m_attachMimeType.Truncate();
+  return result;
+}
+
+bool CMapiMessage::GetEmbeddedAttachmentInfo(unsigned int i, nsIURI **uri,
+                                             const char **cid,
+                                             const char **name) const
+{
+  if ((i < 0) || ( i >= m_embattachments.size()))
+    return false;
+  attach_data* data = m_embattachments[i];
+  if (!data)
+    return false;
+  *uri = data->orig_url;
+  *cid = data->cid;
+  *name = data->real_name;
+  return true;
+}
+
+//////////////////////////////////////////////////////
+
+// begin and end MUST point to the same string
+wchar_t* dup(const wchar_t* begin, const wchar_t* end)
+{
+  if (begin >= end)
+    return 0;
+  wchar_t* str = new wchar_t[end-begin+1];
+  memcpy(str, begin, (end-begin)*sizeof(begin[0]));
+  str[end - begin] = 0;
+  return str;
+}
+
+// See RFC822
+inline bool IsPrintableASCII(wchar_t c) { return (c > 32) && (c < 127); }
+inline bool IsWSP(wchar_t c) { return (c == 32) || (c == 9); }
+
+CMapiMessageHeaders::CHeaderField::CHeaderField(const wchar_t* begin, int len)
+  : m_fname(0), m_fbody(0)
+{
+  const wchar_t *end = begin+len, *fname_end = begin;
+  while ((fname_end < end) && IsPrintableASCII(*fname_end) && (*fname_end != L':'))
+    ++fname_end;
+  if ((fname_end == end) || (*fname_end != L':'))
+    return; // Not a valid header!
+  m_fname = dup(begin, fname_end+1); // including colon
+  m_fbody = dup(fname_end+1, end);
+}
+
+CMapiMessageHeaders::CHeaderField::CHeaderField(const wchar_t* name, const wchar_t* body)
+  : m_fname(dup(name, name+wcslen(name))), m_fbody(dup(body, body+wcslen(body)))
+{
+}
+
+CMapiMessageHeaders::CHeaderField::~CHeaderField()
+{
+  delete[] m_fname;
+  delete[] m_fbody;
+}
+
+void CMapiMessageHeaders::CHeaderField::set_fbody(const wchar_t* txt)
+{
+  if (m_fbody == txt)
+    return; // to avoid assigning to self
+  wchar_t* oldbody = m_fbody;
+  m_fbody = dup(txt, txt+wcslen(txt));
+  delete[] oldbody;
+}
+
+void CMapiMessageHeaders::CHeaderField::UnfoldFoldedSpaces(const wchar_t* body,
+                                                           nsString& dest)
+{
+  dest.Truncate();
+  if (!body)
+    return;
+  const wchar_t* pos = body;
+  while (*pos) {
+    if ((*pos == '\x0D') && (*(pos+1) == '\x0A') && *(pos+2) && IsWSP(*(pos+2)))
+      pos += 2; // Skip CRLF if it is followed by SPACE or TAB
+    else
+      dest.Append(*(pos++));
+  }
+}
+
+////////////////////////////////////////
+
+const wchar_t* CMapiMessageHeaders::Specials[hdrMax] = {
+  L"Date:",
+  L"From:",
+  L"Sender:",
+  L"Reply-To:",
+  L"To:",
+  L"Cc:",
+  L"Bcc:",
+  L"Message-ID:",
+  L"Subject:",
+  L"Mime-Version:",
+  L"Content-Type:",
+  L"Content-Transfer-Encoding:"
+};
+
+CMapiMessageHeaders::~CMapiMessageHeaders()
+{
+  ClearHeaderFields();
+}
 
-  pVal = CMapiApi::GetMapiProperty( lpAttach, PR_ATTACH_METHOD);
-  if (pVal) {
-    LONG aMethod = CMapiApi::GetLongFromProp( pVal);
-    if ((aMethod == ATTACH_BY_REF_ONLY) || (aMethod == ATTACH_BY_REFERENCE) || (aMethod == ATTACH_BY_REF_RESOLVE)) {
-      m_attachPath.Truncate();
-      pVal = CMapiApi::GetMapiProperty( lpAttach, PR_ATTACH_PATHNAME);
-      if (pVal)
-        CMapiApi::GetStringFromProp( pVal, m_attachPath);
-      MAPI_TRACE2("\t\t** Attachment #%d by ref: %s\r\n",
-                  idx, m_attachPath.get());
-      m_ownsAttachFile = FALSE;
+void Delete(void* p) { delete p; }
+
+void CMapiMessageHeaders::ClearHeaderFields()
+{
+  std::for_each(m_headerFields.begin(), m_headerFields.end(), Delete);
+  m_headerFields.clear();
+}
+
+void CMapiMessageHeaders::Assign(const wchar_t* headers)
+{
+  for (int i=0; i<hdrMax; i++)
+    m_SpecialHeaders[i] = 0;
+  ClearHeaderFields();
+  if (!headers)
+    return;
+  const wchar_t *start=headers, *end=headers;
+  while (*end) {
+    if ((*end == L'\x0D') && (*(end+1) == L'\x0A')) { // CRLF
+      if (!IsWSP(*(end+2))) { // Not SPACE nor TAB (avoid FSP) -> next header or EOF
+        Add(new CHeaderField(start, end-start));
+        start = ++end + 1;
+      }
+    }
+    ++end;
+  }
+
+  if (start < end) { // Last header left
+    Add(new CHeaderField(start, end-start));
+  }
+}
+
+void CMapiMessageHeaders::Add(CHeaderField* f)
+{
+  if (!f)
+    return;
+  if (!f->Valid()) {
+    delete f;
+    return;
+  }
+
+  SpecialHeader idx = CheckSpecialHeader(f->fname());
+  if (idx != hdrNone) {
+    // Now check if the special header was already inserted;
+    // if so, remove previous and add this new
+    CHeaderField* PrevSpecial = m_SpecialHeaders[idx];
+    if (PrevSpecial) {
+      std::vector<CHeaderField*>::iterator iter = std::find(m_headerFields.begin(), m_headerFields.end(), PrevSpecial);
+      if (iter != m_headerFields.end())
+        m_headerFields.erase(iter);
+      delete PrevSpecial;
     }
-    else if (aMethod == ATTACH_BY_VALUE) {
-      MAPI_TRACE1( "\t\t** Attachment #%d by value.\r\n", idx);
-      bResult = CopyBinAttachToFile( lpAttach);
-    }
-    else if (aMethod == ATTACH_OLE) {
-      MAPI_TRACE1( "\t\t** Attachment #%d by OLE - yuck!!!\r\n", idx);
-    }
-    else if (aMethod == ATTACH_EMBEDDED_MSG) {
-      MAPI_TRACE1( "\t\t** Attachment #%d by Embedded Message??\r\n", idx);
-    }
-    else {
-      MAPI_TRACE2( "\t\t** Attachment #%d unknown attachment method - 0x%lx\r\n", idx, aMethod);
-      bResult = FALSE;
-    }
+    m_SpecialHeaders[idx] = f;
+  }
+  m_headerFields.push_back(f);
+}
+
+CMapiMessageHeaders::SpecialHeader CMapiMessageHeaders::CheckSpecialHeader(const wchar_t* fname) const
+{
+  for (int i = hdrFirst; i < hdrMax; i++)
+    if (!wcsicmp(fname, Specials[i]))
+      return static_cast<SpecialHeader>(i);
+
+  return hdrNone;
+}
+
+const CMapiMessageHeaders::CHeaderField* CMapiMessageHeaders::CFind(const wchar_t* name) const
+{
+  SpecialHeader special = CheckSpecialHeader(name);
+  if ((special > hdrNone) && (special < hdrMax))
+    return m_SpecialHeaders[special]; // No need to search further, because it MUST be here
+
+  std::vector<CHeaderField*>::const_iterator iter = std::find_if(m_headerFields.begin(), m_headerFields.end(), fname_equals(name));
+  if (iter == m_headerFields.end())
+    return 0;
+  return *iter;
+}
+
+const wchar_t* CMapiMessageHeaders::Value(SpecialHeader special) const
+{
+  if ((special <= hdrNone) || (special >= hdrMax))
+    return 0;
+  return (m_SpecialHeaders[special]) ? m_SpecialHeaders[special]->fbody() : 0;
+}
+
+const wchar_t* CMapiMessageHeaders::Value(const wchar_t* name) const
+{
+  const CHeaderField* result = CFind(name);
+  return result ? result->fbody() : 0;
+}
+
+void CMapiMessageHeaders::UnfoldValue(const wchar_t* name, nsString& dest) const
+{
+  CHeaderField::UnfoldFoldedSpaces(Value(name), dest);
+}
+
+void CMapiMessageHeaders::UnfoldValue(SpecialHeader special, nsString& dest) const
+{
+  CHeaderField::UnfoldFoldedSpaces(Value(special), dest);
+}
+
+int CMapiMessageHeaders::SetValue(const wchar_t* name, const wchar_t* value, bool replace)
+{
+  CHeaderField* result = Find(name);
+  if (result) {
+    result->set_fbody(value);
   }
   else
-    bResult = FALSE;
-
-  nsCString fName, fExt;
-  pVal = CMapiApi::GetMapiProperty( lpAttach, PR_ATTACH_LONG_FILENAME);
-  if (pVal)
-    CMapiApi::GetStringFromProp( pVal, fName);
-  pVal = CMapiApi::GetMapiProperty( lpAttach, PR_ATTACH_EXTENSION);
-  if (pVal)
-    CMapiApi::GetStringFromProp( pVal, fExt);
-  pVal = CMapiApi::GetMapiProperty( lpAttach, PR_ATTACH_SIZE);
-  long sz = 0;
-  if (pVal)
-    sz = CMapiApi::GetLongFromProp( pVal);
+    Add(new CHeaderField(name, value));
+  return 0; // No sensible result is returned; maybe do something senseful later
+}
 
-  /*
-    // I have no idea how this tag is used, how to interpret it's value, etc.
-    // Fortunately, the Microsoft documentation is ABSOLUTELY NO HELP AT ALL.  In fact,
-    // if one goes by the docs and sample code, this tag is completely 100% useless.  I'm
-    // sure it has some important meaning which will one day be obvious, but for now,
-    // it is ignored.
-        pVal = CMapiApi::GetMapiProperty( lpAttach, PR_ATTACH_TAG);
-       if (pVal) {
-        ::MAPIFreeBuffer( pVal);
-      }
-  */
+int CMapiMessageHeaders::SetValue(SpecialHeader special, const wchar_t* value)
+{
+  CHeaderField* result = m_SpecialHeaders[special];
+  if (result)
+    result->set_fbody(value);
+  else
+    Add(new CHeaderField(Specials[special], value));
+  return 0;
+}
 
-  MAPI_TRACE1("\t\t\t--- Mime type: %s\r\n", m_attachMimeType.get());
-  MAPI_TRACE2("\t\t\t--- File name: %s, extension: %s\r\n",
-              fName.get(), fExt.get());
-  MAPI_TRACE1("\t\t\t--- Size: %ld\r\n", sz);
+void CMapiMessageHeaders::write_to_stream::operator () (const CHeaderField* f)
+{
+  if (!f || NS_FAILED(m_rv))
+    return;
 
-  if (fExt.IsEmpty()) {
-    int idx = fName.RFindChar( '.');
-    if (idx != -1)
-      fName.Right( fExt, fName.Length() - idx);
-  }
-
-  if ((fName.RFindChar( '.') == -1) && !fExt.IsEmpty()) {
-    fName += ".";
-    fName += fExt;
-  }
-
-  m_attachFileName = fName;
+  nsCString str;
+  PRUint32 written;
+  LossyCopyUTF16toASCII(f->fname(), str);
+  m_rv = m_pDst->Write( str.get(), str.Length(), &written);
+  NS_ENSURE_SUCCESS(m_rv,);
+  LossyCopyUTF16toASCII(f->fbody(), str);
+  m_rv = m_pDst->Write(str.get(), str.Length(), &written);
+  NS_ENSURE_SUCCESS(m_rv,);
+  m_rv = m_pDst->Write( "\x0D\x0A", 2, &written);
+}
 
-  if (m_attachMimeType.IsEmpty()) {
-    PRUint8 *pType = NULL;
-    if (!fExt.IsEmpty()) {
-      pType = CMimeTypes::GetMimeType( fExt);
-    }
-    if (pType)
-      m_attachMimeType = (PC_S8)pType;
-    else
-      m_attachMimeType = "application/octet-stream";
+nsresult CMapiMessageHeaders::ToStream(nsIOutputStream *pDst) const
+{
+  nsresult rv = std::for_each(m_headerFields.begin(), m_headerFields.end(),
+                              write_to_stream(pDst));
+  if (NS_SUCCEEDED(rv)) {
+    PRUint32 written;
+    rv = pDst->Write( "\x0D\x0A", 2, &written); // Separator line
   }
-
-  pVal = CMapiApi::GetMapiProperty( lpAttach, PR_ATTACH_TRANSPORT_NAME);
-  if (pVal) {
-    CMapiApi::GetStringFromProp( pVal, fName);
-    MAPI_TRACE1("\t\t\t--- Transport name: %s\r\n", fName.get());
-  }
-
-  lpAttach->Release();
-
-  return( bResult);
+  return rv;
 }
--- a/mailnews/import/outlook/src/MapiMessage.h
+++ b/mailnews/import/outlook/src/MapiMessage.h
@@ -36,103 +36,269 @@
  * ***** END LICENSE BLOCK ***** */
 #ifndef MapiMessage_h___
 #define MapiMessage_h___
 
 #include "nsTArray.h"
 #include "nsString.h"
 #include "nsILocalFile.h"
 #include "MapiApi.h"
+#include "nsIMsgSend.h"
 
+#include "nsProxiedService.h"
+
+#include <vector>
+
+#ifndef PR_LAST_VERB_EXECUTED
+#define PR_LAST_VERB_EXECUTED PROP_TAG( PT_LONG, 0x1081)
+#endif
+
+#define EXCHIVERB_REPLYTOSENDER (102)
+#define EXCHIVERB_REPLYTOALL    (103)
+#define EXCHIVERB_FORWARD       (104)
+
+#ifndef PR_ATTACH_CONTENT_ID
+#define PR_ATTACH_CONTENT_ID PROP_TAG( PT_TSTRING,	0x3712)
+#endif
+#ifndef PR_ATTACH_CONTENT_ID_W
+#define PR_ATTACH_CONTENT_ID_W PROP_TAG( PT_UNICODE,	0x3712)
+#endif
+#ifndef PR_ATTACH_CONTENT_ID_A
+#define PR_ATTACH_CONTENT_ID_A PROP_TAG( PT_STRING8,	0x3712)
+#endif
+
+#ifndef PR_ATTACH_FLAGS
+#define PR_ATTACH_FLAGS PROP_TAG(PT_LONG,	0x3714)
+#endif
+
+#ifndef ATT_INVISIBLE_IN_HTML
+#define ATT_INVISIBLE_IN_HTML (0x1)
+#endif
+#ifndef ATT_INVISIBLE_IN_RTF
+#define ATT_INVISIBLE_IN_RTF  (0x2)
+#endif
+#ifndef ATT_MHTML_REF
+#define ATT_MHTML_REF         (0x4)
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+
+class CMapiMessageHeaders {
+public:
+  // Special headers that MUST appear at most once (see RFC822)
+  enum SpecialHeader { hdrNone=-1, hdrFirst = 0, // utility values
+                       hdrDate=hdrFirst,
+                       hdrFrom,
+                       hdrSender,
+                       hdrReplyTo,
+                       hdrTo,
+                       hdrCc,
+                       hdrBcc,
+                       hdrMessageID,
+                       hdrSubject,
+                       hdrMimeVersion,
+                       hdrContentType,
+                       hdrContentTransferEncoding,
+                       hdrMax // utility value
+                     };
+
+  CMapiMessageHeaders(const wchar_t* headers = 0) { Assign(headers); }
+  CMapiMessageHeaders(const char* headers)
+  {
+    nsString uniHeaders;
+    CopyASCIItoUTF16(headers, uniHeaders);
+    Assign(uniHeaders.get());
+  }
+  ~CMapiMessageHeaders();
+  void Assign(const wchar_t* headers);
+
+  inline bool IsEmpty() const { return m_headerFields.empty(); }
+  // if no such header exists then 0 is returned, else the first value returned
+  const wchar_t* Value(const wchar_t* name) const;
+  // if no such header exists then 0 is returned
+  const wchar_t* Value(SpecialHeader special) const;
+
+  void UnfoldValue(const wchar_t* name, nsString& dest) const;
+  void UnfoldValue(SpecialHeader special, nsString& dest) const;
+
+  // TODO: if replace is set, then all headers with this name will be removed
+  //  and one with this value will be added, otherwise a new header is added
+  // (Unnecessary for now)
+  int SetValue(const wchar_t* name, const wchar_t* value, bool replace = true);
+  int SetValue(SpecialHeader special, const wchar_t* value);
+
+  nsresult ToStream(nsIOutputStream *pDst) const;
+private:
+  class CHeaderField {
+  public:
+    CHeaderField(const wchar_t* begin, int len);
+    CHeaderField(const wchar_t* name, const wchar_t* body);
+    ~CHeaderField();
+    inline bool Valid() const { return m_fname; }
+    inline const wchar_t* fname() const { return m_fname; }
+    inline const wchar_t* fbody() const { return m_fbody; }
+    void set_fbody(const wchar_t* txt);
+
+    static void UnfoldFoldedSpaces(const wchar_t* body, nsString& dest);
+  private:
+    wchar_t* m_fname;
+    wchar_t* m_fbody;
+  }; //class HeaderField
+
+  class write_to_stream {
+  public:
+    write_to_stream(nsIOutputStream *pDst) : m_pDst(pDst), m_rv(NS_OK) {}
+    void operator () (const CHeaderField* f);
+    inline operator nsresult() const { return m_rv; }
+  private:
+    nsIOutputStream *m_pDst;
+    nsresult m_rv;
+  };
+
+  // Search helper
+  class fname_equals {
+  public:
+    fname_equals(const wchar_t* search) : m_search(search) {}
+    inline bool operator () (const CHeaderField* f) const { return wcsicmp(f->fname(), m_search) == 0; }
+  private:
+    const wchar_t* m_search;
+  }; // class fname_equals
+
+  // The common array of special headers' names
+  static const wchar_t* Specials[hdrMax];
+  
+  std::vector<CHeaderField*> m_headerFields;
+  CHeaderField* m_SpecialHeaders[hdrMax]; // Pointers into the m_headerFields
+
+  void ClearHeaderFields();
+  void Add(CHeaderField* f);
+  SpecialHeader CheckSpecialHeader(const wchar_t* fname) const;
+  const CHeaderField* CFind(const wchar_t* name) const;
+  inline CHeaderField* Find(const wchar_t* name) { return const_cast<CHeaderField*>(CFind(name)); }
+
+}; // class CMapiMessageHeaders
+
+//////////////////////////////////////////////////////
 
 class CMapiMessage {
 public:
   CMapiMessage( LPMESSAGE  lpMsg);
   ~CMapiMessage();
 
+  // Attachments
+  // Ordinary (not embedded) attachments; result MUST be disposed of with DisposeAttachments()
+  nsMsgAttachedFile* GetAttachments();
+  static void DisposeAttachments(nsMsgAttachedFile* att) { delete[] att; }
+  // Embedded attachments
+  size_t EmbeddedAttachmentsCount() const { return m_embattachments.size(); }
+  bool GetEmbeddedAttachmentInfo(unsigned int i, nsIURI **uri, const char **cid,
+                                 const char **name) const;
+  // We don't check MSGFLAG_HASATTACH, since it returns true even if there are
+  // only embedded attachmentsin the message. TB only counts the ordinary
+  // attachments when shows the message status, so here we check only for the
+  // ordinary attachments.
+  inline bool HasAttach() const { return !m_stdattachments.empty(); }
+
+  // Retrieve info for message
+  inline bool BodyIsHtml( void) const { return( m_bodyIsHtml);}
+  const char *GetFromLine(int& len) const {
+    if (m_fromLine.IsEmpty())
+      return NULL; 
+    else {
+      len = m_fromLine.Length();
+      return( m_fromLine.get());}
+  }
+  inline CMapiMessageHeaders *GetHeaders() { return &m_headers; }
+  inline const wchar_t *GetBody( void) const { return( m_body.get()); }
+  inline size_t GetBodyLen( void) const { return( m_body.Length()); }
+  void GetBody(nsCString& dest) const;
+  inline const char *GetBodyCharset( void) const { return( m_mimeCharset.get());}
+  inline bool IsRead() const { return m_msgFlags & MSGFLAG_READ; }
+  inline bool IsReplied() const {
+    return (m_msgLastVerb == EXCHIVERB_REPLYTOSENDER) ||
+           (m_msgLastVerb == EXCHIVERB_REPLYTOALL); }
+  inline bool IsForvarded() const {
+    return m_msgLastVerb == EXCHIVERB_FORWARD; }
+
+  bool    IsMultipart( void) const;
+  bool    HasContentHeader( void) const {
+    return( !m_mimeContentType.IsEmpty());}
+  bool    HasMimeVersion( void) const {
+    return m_headers.Value(CMapiMessageHeaders::hdrMimeVersion); }
+  const char *GetMimeContent( void) const { return( m_mimeContentType.get());}
+  PRInt32     GetMimeContentLen( void) const { return( m_mimeContentType.Length());}
+  const char *GetMimeBoundary( void) const { return( m_mimeBoundary.get());}
+
+   // The only required part of a message is its header
+  inline bool ValidState() const { return !m_headers.IsEmpty(); }
+  inline bool FullMessageDownloaded() const { return !m_dldStateHeadersOnly; }
+
+private:
+  struct attach_data {
+    nsCOMPtr<nsIURI> orig_url;
+    nsCOMPtr<nsILocalFile> tmp_file;
+    char *type;
+    char *encoding;
+    char *real_name;
+    char *cid;
+    bool delete_file;
+    attach_data() : type(0), encoding(0), real_name(0), cid(0), delete_file(false) {}
+  };
+
+  static const nsCString    m_whitespace;
+
+  LPMESSAGE    m_lpMsg;
+
+  bool         m_dldStateHeadersOnly; // if the message has not been downloaded yet
+  CMapiMessageHeaders     m_headers;
+  nsCString    m_fromLine; // utf-8
+  nsCString    m_mimeContentType; // utf-8
+  nsCString    m_mimeBoundary; // utf-8
+  nsCString    m_mimeCharset; // utf-8
+
+  std::vector<attach_data*> m_stdattachments;
+  std::vector<attach_data*> m_embattachments; // Embedded
+
+  nsString     m_body; // to be converted from UTF-16 using m_mimeCharset
+  bool         m_bodyIsHtml;
+
+  PRUint32 m_msgFlags;
+  PRUint32 m_msgLastVerb;
+  
+  nsIIOService *      m_pIOService;
+
+  void    GetDownloadState();
+
   // Headers - fetch will get PR_TRANSPORT_MESSAGE_HEADERS
   // or if they do not exist will build a header from
   //  PR_DISPLAY_TO, _CC, _BCC
   //  PR_SUBJECT
   //  PR_MESSAGE_RECIPIENTS
   // and PR_CREATION_TIME if needed?
-  BOOL    FetchHeaders( void);
-
-  // Do the headers need a From separator line.
-  // TRUE if a From line needs to precede the headers, FALSE
-  // if the headers already include a from line
-  BOOL    NeedsFromLine( void);
-
-  // Fetch the
-  BOOL    FetchBody( void);
-
-  // Attachments
-  int      CountAttachments( void);
-  BOOL    GetAttachmentInfo( int idx);
+  bool    FetchHeaders( void);
+  bool    FetchBody( void);
+  void    FetchFlags( void);
 
-  // Retrieve info for message
-  BOOL    BodyIsHtml( void) { return( m_bodyIsHtml);}
-  const char *GetFromLine( int& len) { if (m_fromLine.IsEmpty()) return( NULL); else { len = m_fromLine.Length(); return( m_fromLine.get());}}
-  const char *GetHeaders( int& len) { if (m_headers.IsEmpty()) return( NULL); else { len = m_headers.Length(); return( m_headers.get());}}
-  const char *GetBody( int& len) { if (m_body.IsEmpty()) return( NULL); else { len = m_body.Length(); return( m_body.get());}}
-  const char *GetBody( void) { return( m_body.get());}
-  const char *GetHeaders( void) { return( m_headers.get());}
-  PRInt32    GetBodyLen( void) { return( m_body.Length());}
-  PRInt32    GetHeaderLen( void) { return( m_headers.Length());}
-
-
-  BOOL    IsMultipart( void);
-  BOOL    HasContentHeader( void) { return( !m_mimeContentType.IsEmpty());}
-  BOOL    HasMimeVersion( void) { return( m_bMimeVersion);}
-  const char *GetMimeContent( void) { return( m_mimeContentType.get());}
-  PRInt32     GetMimeContentLen( void) { return( m_mimeContentType.Length());}
-  const char *GetMimeBoundary( void) { return( m_mimeBoundary.get());}
-  void    GenerateBoundary( void);
-
-  BOOL    GetAttachFileLoc( nsIFile *pLoc);
-
-  const char *GetMimeType( void) { return( m_attachMimeType.get());}
-  const char *GetFileName( void) { return( m_attachFileName.get());}
+  static bool GetTmpFile(/*out*/ nsILocalFile **aResult);
+  static bool CopyMsgAttachToFile(LPATTACH lpAttach, /*out*/ nsILocalFile **tmp_file);
+  static bool CopyBinAttachToFile( LPATTACH lpAttach, nsILocalFile **tmp_file);
 
-protected:
-  BOOL    IterateAttachTable( void);
-  void    ClearTempAttachFile( void);
-  BOOL    CopyBinAttachToFile( LPATTACH lpAttach);
-
-  void    ProcessHeaderLine( nsCString& line);
-  void    ProcessHeaders( void);
-  void    FormatDateTime( SYSTEMTIME & tm, nsCString& s, BOOL includeTZ = TRUE);
-  void    BuildHeaders( void);
-  void    BuildFromLine( void);
-  void    AddSubject( nsCString& str);
-  void    AddFrom( nsCString& str);
-  BOOL    AddHeader( nsCString& str, ULONG tag, const char *pPrefix);
-  void    AddDate( nsCString& str);
-
-  BOOL    IsSpace( char c) { return( m_whitespace.FindChar( c) != -1);}
+  static void ClearAttachment(attach_data* data);
+  void    ClearAttachments();
+  bool    AddAttachment(DWORD aNum);
+  bool    IterateAttachTable(LPMAPITABLE tbl);
+  bool    GetURL(nsIFile *aFile, nsIURI **url);
+  void    ProcessAttachments();
 
-private:
-  LPMESSAGE    m_lpMsg;
-  LPMAPITABLE    m_pAttachTable;
-  nsCString    m_headers;
-  nsCString    m_fromLine;
-  nsCString    m_body;
-  nsCString    m_mimeContentType;
-  nsCString    m_mimeBoundary;
-  nsCString    m_mimeCharset;
-  BOOL      m_bMimeVersion;
-  BOOL      m_bMimeEncoding;
-  nsTArray<DWORD>  m_attachNums;
-  nsCString    m_attachMimeType;
-  nsCString    m_attachPath;
-  nsCString    m_attachFileName;
-  BOOL      m_ownsAttachFile;
-  BOOL      m_bodyIsHtml;
-  BOOL      m_bHasSubject;
-  BOOL      m_bHasFrom;
-  BOOL      m_bHasDate;
-  nsCString    m_whitespace;
+  bool    EnsureHeader(CMapiMessageHeaders::SpecialHeader special, ULONG mapiTag);
+  bool    EnsureDate();
+
+  void    ProcessContentType();
+  bool    CheckBodyInCharsetRange(const char* charset);
+  void    FormatDateTime( SYSTEMTIME & tm, nsString& s, bool includeTZ = true);
+  void    BuildFromLine( void);
+
+  inline static bool IsSpace( char c) { return( m_whitespace.FindChar( c) != -1);}
+  inline static bool IsSpace( wchar_t c) { 
+    return ((c & 0xFF) == c) && IsSpace(static_cast<char>(c)); } // Avoid false detections
 };
 
-
-
-
 #endif /* MapiMessage_h__ */
--- a/mailnews/import/outlook/src/MapiMimeTypes.cpp
+++ b/mailnews/import/outlook/src/MapiMimeTypes.cpp
@@ -82,17 +82,24 @@ BOOL CMimeTypes::GetMimeTypeFromReg( con
   if (GetKey( HKEY_CLASSES_ROOT, ext.get(), &extensionKey)) {
     result = GetValueBytes( extensionKey, "Content Type", ppBytes);
     RegCloseKey( extensionKey);
   }
 
   return( result);
 }
 
-PRUint8 * CMimeTypes::GetMimeType( nsCString& theExt)
+PRUint8 * CMimeTypes::GetMimeType(const nsString& theExt)
+{
+  nsCString ext;
+  LossyCopyUTF16toASCII(theExt, ext);
+  return GetMimeType(ext);
+}
+
+PRUint8 * CMimeTypes::GetMimeType(const nsCString& theExt)
 {
   nsCString  ext = theExt;
   if (ext.Length()) {
     if (ext.First() != '.') {
       ext = ".";
       ext += theExt;
     }
   }
--- a/mailnews/import/outlook/src/MapiMimeTypes.h
+++ b/mailnews/import/outlook/src/MapiMimeTypes.h
@@ -40,17 +40,18 @@
 
 #include <windows.h>
 
 #define kMaxMimeTypeSize  256
 
 class CMimeTypes {
 public:
 
-static PRUint8 *  GetMimeType( nsCString& theExt);
+static PRUint8 *  GetMimeType(const nsCString& theExt);
+static PRUint8 *  GetMimeType(const nsString& theExt);
 
 protected:
   // Registry stuff
 static BOOL  GetKey( HKEY root, LPCTSTR pName, PHKEY pKey);
 static BOOL  GetValueBytes( HKEY rootKey, LPCTSTR pValName, LPBYTE *ppBytes);
 static void  ReleaseValueBytes( LPBYTE pBytes);
 static BOOL  GetMimeTypeFromReg( const nsCString& ext, LPBYTE *ppBytes);
 
--- a/mailnews/import/outlook/src/nsOutlookCompose.cpp
+++ b/mailnews/import/outlook/src/nsOutlookCompose.cpp
@@ -47,44 +47,49 @@
 #include "nsIServiceManager.h"
 #include "nsIIOService.h"
 #include "nsIURI.h"
 #include "nsIProxyObjectManager.h"
 #include "nsProxiedService.h"
 #include "nsMsgI18N.h"
 #include "nsNativeCharsetUtils.h"
 #include "nsIOutputStream.h"
-#include "nsNetUtil.h"
 
 #include "nsMsgBaseCID.h"
 #include "nsMsgCompCID.h"
 
 #include "nsIMsgCompose.h"
 #include "nsIMsgCompFields.h"
-#include "nsIMsgSend.h"
 #include "nsIMsgAccountManager.h"
 
 #include "nsNetCID.h"
 
 #include "nsOutlookCompose.h"
 
 #include "OutlookDebugLog.h"
 
 #include "nsMimeTypes.h"
 #include "nsMsgUtils.h"
 
+#include "nsOutlookEditor.h"
+#include "nsAutoPtr.h"
+
+#include "nsMsgMessageFlags.h"
+#include "nsMsgLocalFolderHdrs.h"
+
+#include <algorithm>
+
 static NS_DEFINE_CID( kMsgSendCID, NS_MSGSEND_CID);
 static NS_DEFINE_CID( kMsgCompFieldsCID, NS_MSGCOMPFIELDS_CID);
 
 // We need to do some calculations to set these numbers to something reasonable!
 // Unless of course, CreateAndSendMessage will NEVER EVER leave us in the lurch
 #define kHungCount 100000
 #define kHungAbortCount 1000
 
-
 #ifdef IMPORT_DEBUG
 static const char *p_test_headers =
 "Received: from netppl.fi (IDENT:monitor@get.freebsd.because.microsoftsucks.net [209.3.31.115])\n\
  by mail4.sirius.com (8.9.1/8.9.1) with SMTP id PAA27232;\n\
  Mon, 17 May 1999 15:27:43 -0700 (PDT)\n\
 Message-ID: <ikGD3jRTsKklU.Ggm2HmE2A1Jsqd0p@netppl.fi>\n\
 From: \"adsales@qualityservice.com\" <adsales@qualityservice.com>\n\
 Subject: Re: Your College Diploma (36822)\n\
@@ -98,19 +103,52 @@ Status: RO";
 static const char *p_test_body =
 "Hello world?\n\
 ";
 #else
 #define p_test_headers nsnull
 #define p_test_body nsnull
 #endif
 
-
 #define kWhitespace "\b\t\r\n "
 
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+// A replacement for SimpleBufferTonyRCopiedTwice round-robin buffer and ReadFileState classes
+class CCompositionFile {
+public:
+  // fifoBuffer is used for memory allocation optimization
+  // convertCRs controls if we want to convert standalone CRs to CRLFs
+  CCompositionFile(nsIFile* aFile, void* fifoBuffer, PRUint32 fifoBufferSize, bool convertCRs=false);
+
+  operator bool() const { return m_fileSize; }
+
+  // Reads up to and including the term sequence, or entire file if term isn't found
+  // termSize may be used to include NULLs in the terminator sequences.
+  // termSize value of -1 means "zero-terminated string" -> size is calculated with strlen
+  nsresult ToString(nsCString& dest, const char* term=0, int termSize=-1);
+  nsresult ToStream(nsIOutputStream *dest, const char* term=0, int termSize=-1);
+  char LastChar() { return m_lastChar; }
+private:
+  nsCOMPtr<nsIFile>  m_pFile;
+  nsCOMPtr<nsIInputStream> m_pInputStream;
+  PRInt64 m_fileSize;
+  PRInt64 m_fileReadPos;
+  char* m_fifoBuffer;
+  PRUint32 m_fifoBufferSize;
+  char* m_fifoBufferReadPos; // next character to read
+  char* m_fifoBufferWrittenPos; // if we have read less than buffer size then this will show it
+  bool m_convertCRs;
+  char m_lastChar;
+
+  nsresult EnsureHasDataInBuffer();
+  template <class _OutFn> nsresult ToDest(_OutFn dest, const char* term, int termSize);
+};
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
 
 // First off, a listener
 class OutlookSendListener : public nsIMsgSendListener
 {
 public:
   OutlookSendListener() {
     m_done = PR_FALSE;
     m_location = nsnull;
@@ -129,979 +167,759 @@ public:
 
   /* void OnStatus (in string aMsgID, in wstring aMsg); */
   NS_IMETHOD OnStatus(const char *aMsgID, const PRUnichar *aMsg) {return NS_OK;}
 
   /* void OnStopSending (in string aMsgID, in nsresult aStatus, in wstring aMsg, in nsIFile returnFile); */
   NS_IMETHOD OnStopSending(const char *aMsgID, nsresult aStatus, const PRUnichar *aMsg,
                nsIFile *returnFile) {
     m_done = PR_TRUE;
-    m_location = returnFile;
-    NS_IF_ADDREF( m_location);
+    NS_IF_ADDREF(m_location = returnFile);
     return NS_OK;
   }
 
    /* void OnSendNotPerformed */
    NS_IMETHOD OnSendNotPerformed(const char *aMsgID, nsresult aStatus) {return NS_OK;}
 
   /* void OnGetDraftFolderURI (); */
   NS_IMETHOD OnGetDraftFolderURI(const char *aFolderURI) {return NS_OK;}
 
   static nsresult CreateSendListener( nsIMsgSendListener **ppListener);
-  void Reset() { m_done = PR_FALSE; NS_IF_RELEASE( m_location); m_location = nsnull;}
+  void Reset() { m_done = PR_FALSE; NS_IF_RELEASE( m_location);}
 
 public:
   PRBool m_done;
   nsIFile * m_location;
 };
 
-
-NS_IMPL_THREADSAFE_ISUPPORTS1( OutlookSendListener, nsIMsgSendListener)
+NS_IMPL_THREADSAFE_ISUPPORTS1(OutlookSendListener, nsIMsgSendListener)
 
 nsresult OutlookSendListener::CreateSendListener( nsIMsgSendListener **ppListener)
 {
-    NS_PRECONDITION(ppListener != nsnull, "null ptr");
-    if (! ppListener)
-        return NS_ERROR_NULL_POINTER;
+  NS_PRECONDITION(ppListener != nsnull, "null ptr");
+  NS_ENSURE_ARG_POINTER(ppListener);
 
-    *ppListener = new OutlookSendListener();
-    if (! *ppListener)
-        return NS_ERROR_OUT_OF_MEMORY;
+  *ppListener = new OutlookSendListener();
+  if (! *ppListener)
+    return NS_ERROR_OUT_OF_MEMORY;
 
-    NS_ADDREF(*ppListener);
-    return NS_OK;
+  NS_ADDREF(*ppListener);
+  return NS_OK;
 }
 
-
 /////////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////////
 
-
+#define hackBeginA "begin"
+#define hackBeginW NS_L(hackBeginA)
+#define hackEndA "\015\012end"
+#define hackEndW NS_L(hackEndA)
+#define hackCRLFA "crlf"
+#define hackCRLFW NS_L(hackCRLFA)
+#define hackAmpersandA "amp"
+#define hackAmpersandW NS_L(hackAmpersandA)
 
 nsOutlookCompose::nsOutlookCompose()
 {
-  m_pIOService = nsnull;
-  m_pAttachments = nsnull;
   m_pListener = nsnull;
   m_pMsgSend = nsnull;
   m_pSendProxy = nsnull;
   m_pMsgFields = nsnull;
   m_pIdentity = nsnull;
-#ifdef IMPORT_DEBUG
-  m_Headers = p_test_headers;
-  m_Body = p_test_body;
-#endif
 
-  m_readHeaders.m_convertCRs = PR_TRUE;
+  m_optimizationBufferSize = 16*1024;
+  m_optimizationBuffer = new char[m_optimizationBufferSize];
 }
 
-
 nsOutlookCompose::~nsOutlookCompose()
 {
-  NS_IF_RELEASE( m_pSendProxy);
-  NS_IF_RELEASE( m_pIOService);
-  NS_IF_RELEASE( m_pMsgSend);
-  NS_IF_RELEASE( m_pListener);
-  NS_IF_RELEASE( m_pMsgFields);
+  NS_IF_RELEASE(m_pSendProxy);
+  NS_IF_RELEASE(m_pMsgSend);
+  NS_IF_RELEASE(m_pListener);
+  NS_IF_RELEASE(m_pMsgFields);
   if (m_pIdentity) {
     nsresult rv = m_pIdentity->ClearAllValues();
     NS_ASSERTION(NS_SUCCEEDED(rv),"failed to clear values");
-    if (NS_FAILED(rv)) return;
+    if (NS_FAILED(rv))
+      return;
 
     NS_RELEASE(m_pIdentity);
   }
+  delete[] m_optimizationBuffer;
+  ClearReplaceCids();
+}
+
+void nsOutlookCompose::ClearReplaceCids()
+{
+  std::for_each(m_replacedCids.begin(), m_replacedCids.end(), ClearReplaceCid);
+  m_replacedCids.clear();
 }
 
 nsresult nsOutlookCompose::CreateIdentity( void)
 {
   if (m_pIdentity)
-    return( NS_OK);
+    return NS_OK;
 
   nsresult rv;
-  NS_WITH_PROXIED_SERVICE(nsIMsgAccountManager, accMgr, NS_MSGACCOUNTMANAGER_CONTRACTID, NS_PROXY_TO_MAIN_THREAD, &rv);
-  if (NS_FAILED(rv)) return( rv);
-  rv = accMgr->CreateIdentity( &m_pIdentity);
+  NS_WITH_PROXIED_SERVICE(nsIMsgAccountManager, accMgr,
+                          NS_MSGACCOUNTMANAGER_CONTRACTID,
+                          NS_PROXY_TO_MAIN_THREAD, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = accMgr->CreateIdentity(&m_pIdentity);
   nsString name;
   name.AssignLiteral("Import Identity");
   if (m_pIdentity) {
     m_pIdentity->SetFullName(name);
     m_pIdentity->SetIdentityName(name);
     m_pIdentity->SetEmail(NS_LITERAL_CSTRING("import@import.service"));
   }
 
-  return( rv);
+  return rv;
 }
 
 nsresult nsOutlookCompose::CreateComponents( void)
 {
   nsresult rv = NS_OK;
 
-  if (!m_pIOService) {
-    IMPORT_LOG0( "Creating nsIOService\n");
-
-    NS_WITH_PROXIED_SERVICE(nsIIOService, service, NS_IOSERVICE_CONTRACTID, NS_PROXY_TO_MAIN_THREAD, &rv);
-    if (NS_FAILED(rv))
-      return( rv);
-    m_pIOService = service;
-    NS_IF_ADDREF( m_pIOService);
-  }
-
-  NS_IF_RELEASE( m_pMsgFields);
+  NS_IF_RELEASE(m_pMsgFields);
   if (!m_pMsgSend) {
     rv = CallCreateInstance( kMsgSendCID, &m_pMsgSend);
     if (NS_SUCCEEDED( rv) && m_pMsgSend) {
       rv = NS_GetProxyForObject( NS_PROXY_TO_MAIN_THREAD, NS_GET_IID(nsIMsgSend),
                   m_pMsgSend, NS_PROXY_SYNC, (void **)&m_pSendProxy);
       if (NS_FAILED( rv)) {
         m_pSendProxy = nsnull;
-        NS_RELEASE( m_pMsgSend);
+        NS_RELEASE(m_pMsgSend);
       }
     }
   }
   if (!m_pListener && NS_SUCCEEDED( rv)) {
     rv = OutlookSendListener::CreateSendListener( &m_pListener);
   }
 
   if (NS_SUCCEEDED(rv) && m_pMsgSend) {
       rv = CallCreateInstance( kMsgCompFieldsCID, &m_pMsgFields);
     if (NS_SUCCEEDED(rv) && m_pMsgFields) {
       // IMPORT_LOG0( "nsOutlookCompose - CreateComponents succeeded\n");
       m_pMsgFields->SetForcePlainText( PR_FALSE);
-      return( NS_OK);
-    }
-  }
-
-  return( NS_ERROR_FAILURE);
-}
-
-void nsOutlookCompose::GetNthHeader( const char *pData, PRInt32 dataLen, PRInt32 n, nsCString& header, nsCString& val, PRBool unwrap)
-{
-  header.Truncate();
-  val.Truncate();
-  if (!pData)
-    return;
-
-  PRInt32  index = 0;
-  PRInt32  len;
-  PRInt32  start = 0;
-  const char *pChar = pData;
-  const char *pStart;
-  if (n == 0) {
-    pStart = pChar;
-    len = 0;
-    while ((start < dataLen) && (*pChar != ':')) {
-      start++;
-      len++;
-      pChar++;
-    }
-    header.Append( pStart, len);
-    header.Trim( kWhitespace);
-    start++;
-    pChar++;
-  }
-  else {
-    while (start < dataLen) {
-      if ((*pChar != ' ') && (*pChar != 9)) {
-        if (n == index) {
-          pStart = pChar;
-          len = 0;
-          while ((start < dataLen) && (*pChar != ':')) {
-            start++;
-            len++;
-            pChar++;
-          }
-          header.Append( pStart, len);
-          header.Trim( kWhitespace);
-          start++;
-          pChar++;
-          break;
-        }
-        else
-          index++;
-      }
-
-      while ((start < dataLen) && (*pChar != 0x0D) && (*pChar != 0x0A)) {
-        start++;
-        pChar++;
-      }
-      while ((start < dataLen) && ((*pChar == 0x0D) || (*pChar == 0x0A))) {
-        start++;
-        pChar++;
-      }
+      return NS_OK;
     }
   }
 
-  if (start >= dataLen)
-    return;
-
-  PRInt32 lineEnd;
-  PRInt32 end = start;
-  while (end < dataLen) {
-    while ((end < dataLen) && (*pChar != 0x0D) && (*pChar != 0x0A)) {
-      end++;
-      pChar++;
-    }
-    if (end > start) {
-      val.Append( pData + start, end - start);
-    }
-
-    lineEnd = end;
-    pStart = pChar;
-    while ((end < dataLen) && ((*pChar == 0x0D) || (*pChar == 0x0A))) {
-      end++;
-      pChar++;
-    }
-
-    start = end;
-
-    while ((end < dataLen) && ((*pChar == ' ') || (*pChar == '\t'))) {
-      end++;
-      pChar++;
-    }
-
-    if (start == end)
-      break;
-
-    if (unwrap)
-      val.Append( ' ');
-    else {
-      val.Append( pStart, end - lineEnd);
-    }
-
-    start = end;
-  }
-
-  val.Trim( kWhitespace);
-}
-
-
-void nsOutlookCompose::GetHeaderValue( const char *pData, PRInt32 dataLen, const char *pHeader, nsCString& val, PRBool unwrap)
-{
-  val.Truncate();
-  if (!pData)
-    return;
-
-  PRInt32 start = 0;
-  PRInt32 len = strlen( pHeader);
-  const char *pChar = pData;
-  if (!PL_strncasecmp( pHeader, pData, len)) {
-    start = len;
-  }
-  else {
-    while (start < dataLen) {
-      while ((start < dataLen) && (*pChar != 0x0D) && (*pChar != 0x0A)) {
-        start++;
-        pChar++;
-      }
-      while ((start < dataLen) && ((*pChar == 0x0D) || (*pChar == 0x0A))) {
-        start++;
-        pChar++;
-      }
-      if ((start < dataLen) && !PL_strncasecmp( pChar, pHeader, len))
-        break;
-    }
-    if (start < dataLen)
-      start += len;
-  }
-
-  if (start >= dataLen)
-    return;
-
-  PRInt32 end = start;
-  PRInt32 lineEnd;
-  const char * pStart;
-
-  pChar = pData + start;
-
-  while (end < dataLen) {
-    while ((end < dataLen) && (*pChar != 0x0D) && (*pChar != 0x0A)) {
-      end++;
-      pChar++;
-    }
-    if (end > start) {
-      val.Append( pData + start, end - start);
-    }
-
-    lineEnd = end;
-    pStart = pChar;
-    while ((end < dataLen) && ((*pChar == 0x0D) || (*pChar == 0x0A))) {
-      end++;
-      pChar++;
-    }
-
-    start = end;
-
-    while ((end < dataLen) && ((*pChar == ' ') || (*pChar == '\t'))) {
-      end++;
-      pChar++;
-    }
-
-    if (start == end)
-      break;
-
-    if (unwrap)
-      val.Append( ' ');
-    else {
-      val.Append( pStart, end - lineEnd);
-    }
-
-    start = end;
-  }
-
-  val.Trim( kWhitespace);
+  return NS_ERROR_FAILURE;
 }
 
-
-void nsOutlookCompose::ExtractCharset( nsString& str)
-{
-  nsString tStr;
-  PRInt32 idx = str.Find( "charset=", PR_TRUE);
-  if (idx != -1) {
-    idx += 8;
-    str.Right( tStr, str.Length() - idx);
-    idx = tStr.FindChar( ';');
-    if (idx != -1)
-      tStr.Left( str, idx);
-    else
-      str = tStr;
-    str.Trim( kWhitespace);
-    if ((str.CharAt( 0) == '"') && (str.Length() > 2)) {
-      str.Mid( tStr, 1, str.Length() - 2);
-      str = tStr;
-      str.Trim( kWhitespace);
-    }
-  }
-  else
-    str.Truncate();
-}
-
-void nsOutlookCompose::ExtractMetaCharset( nsCString str, nsString& newstr)
-{
-  PRInt32 idx_endhead = str.Find( "/head", CaseInsensitiveCompare);
-  PRInt32 idx_charset = str.Find( "charset=", CaseInsensitiveCompare);
-  if (idx_charset == -1 || idx_endhead == -1 || idx_charset > idx_endhead) {
-    newstr.Truncate();
-    return;
-  }
-
-  idx_charset += 8;
-
-  // remove everything from the string after the next ; or " or space,
-  // whichever comes first.
-  // The inital sting looks something like
-  // <META content="text/html; charset=utf-8" http-equiv=Content-Type>
-  // <META content="text/html; charset=utf-8;" http-equiv=Content-Type>
-  // <META content="text/html; charset=utf-8 ;" http-equiv=Content-Type>
-  // <META content="text/html; charset=utf-8 " http-equiv=Content-Type>
-  PRInt32 idx  = str.FindCharInSet( ";\" ", idx_charset);
-
-  if (idx == kNotFound) {
-    // what??, no ; " or space, we give up
-    newstr.Truncate();
-  } else {
-    CopyASCIItoUTF16 (Substring(str, idx_charset, idx - idx_charset), newstr);
-  }
-}
-
-void nsOutlookCompose::ExtractType( nsString& str)
-{
-  nsString tStr;
-  PRInt32 idx = str.FindChar( ';');
-  if (idx != -1) {
-    str.Left( tStr, idx);
-    str = tStr;
-  }
-  str.Trim( kWhitespace);
-
-  if ((str.CharAt( 0) == '"') && (str.Length() > 2)) {
-    str.Mid( tStr, 1, str.Length() - 2);
-    str = tStr;
-    str.Trim( kWhitespace);
-  }
-
-  // if multipart then ignore it since no outlook message body is ever
-  // valid multipart!
-  if (str.Length() > 10) {
-    str.Left( tStr, 10);
-    if (tStr.LowerCaseEqualsLiteral("multipart/"))
-      str.Truncate();
-  }
-}
-
-void nsOutlookCompose::CleanUpAttach( nsMsgAttachedFile *a, PRInt32 count)
-{
-  for (PRInt32 i = 0; i < count; i++)
-  {
-    a[i].orig_url=nsnull;
-    if (a[i].type)
-      NS_Free( a[i].type);
-    if (a[i].description)
-      NS_Free( a[i].description);
-    if (a[i].encoding)
-      NS_Free( a[i].encoding);
-  }
-  delete [] a;
-}
-
-nsMsgAttachedFile * nsOutlookCompose::GetLocalAttachments( void)
-{
-  /*
-  nsIURI      *url = nsnull;
-  */
-
-  PRInt32 count = 0;
-  if (m_pAttachments)
-    count = m_pAttachments->Count();
-  if (!count)
-    return( nsnull);
-
-  nsMsgAttachedFile *a = (nsMsgAttachedFile *) new nsMsgAttachedFile[count + 1];
-  if (!a)
-    return( nsnull);
-  memset(a, 0, sizeof(nsMsgAttachedFile) * (count + 1));
-
-  nsresult rv;
-  nsCString urlStr;
-  OutlookAttachment * pAttach;
-
-  for (PRInt32 i = 0; i < count; i++) {
-    // nsMsgNewURL(&url, "file://C:/boxster.jpg");
-    // a[i].orig_url = url;
-
-    // NS_PRECONDITION( PR_FALSE, "Forced Break");
-
-    pAttach = (OutlookAttachment *) m_pAttachments->ElementAt( i);
-                // should we clone here?
-    a[i].tmp_file = pAttach->pAttachment;
-    nsCOMPtr <nsIURI> uri;
-    rv = NS_NewFileURI(getter_AddRefs(uri), pAttach->pAttachment);
-    NS_ENSURE_SUCCESS(rv, nsnull);
-    uri->GetSpec(urlStr);
-    if (urlStr.IsEmpty()) {
-      CleanUpAttach( a, count);
-      return( nsnull);
-    }
-    rv = m_pIOService->NewURI( urlStr, nsnull, nsnull, getter_AddRefs(a[i].orig_url));
-    if (NS_FAILED( rv)) {
-      CleanUpAttach( a, count);
-      return( nsnull);
-    }
-
-    a[i].type = strdup( pAttach->mimeType);
-    a[i].real_name = strdup( pAttach->description);
-    a[i].encoding = strdup( ENCODING_BINARY);
-  }
-
-  return( a);
-}
-
-// Test a message send????
-nsresult nsOutlookCompose::SendTheMessage(nsMsgDeliverMode mode, nsCString &useThisCType, nsIFile **pMsg)
+nsresult nsOutlookCompose::ComposeTheMessage(nsMsgDeliverMode mode, CMapiMessage &msg, nsIFile **pMsg)
 {
   nsresult rv = CreateComponents();
   NS_ENSURE_SUCCESS(rv, rv);
   rv = CreateIdentity();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // IMPORT_LOG0( "Outlook Compose created necessary components\n");
-  nsAutoString bodyType;
-  nsAutoString charSet;
-  nsAutoString headerVal;
-  nsCAutoString asciiHeaderVal;
+
+  CMapiMessageHeaders* headers = msg.GetHeaders();
 
-  GetHeaderValue( m_Headers.get(), m_Headers.Length(), "From:", headerVal);
-  if (!headerVal.IsEmpty())
-    m_pMsgFields->SetFrom( headerVal);
-  GetHeaderValue( m_Headers.get(), m_Headers.Length(), "To:", headerVal);
-  if (!headerVal.IsEmpty())
-    m_pMsgFields->SetTo( headerVal);
-  GetHeaderValue( m_Headers.get(), m_Headers.Length(), "Subject:", headerVal);
-  if (!headerVal.IsEmpty())
-    m_pMsgFields->SetSubject( headerVal);
-  GetHeaderValue( m_Headers.get(), m_Headers.Length(), "Content-type:", headerVal);
+  nsString val;
+  headers->UnfoldValue(CMapiMessageHeaders::hdrFrom, val);
+  m_pMsgFields->SetFrom(val);
+  headers->UnfoldValue(CMapiMessageHeaders::hdrTo, val);
+  m_pMsgFields->SetTo(val);
+  headers->UnfoldValue(CMapiMessageHeaders::hdrSubject, val);
+  m_pMsgFields->SetSubject(val);
+  m_pMsgFields->SetCharacterSet( msg.GetBodyCharset() );
+  headers->UnfoldValue(CMapiMessageHeaders::hdrCc, val);
+  m_pMsgFields->SetCc(val);
+  headers->UnfoldValue(CMapiMessageHeaders::hdrReplyTo, val);
+  m_pMsgFields->SetReplyTo(val);
 
-  // If callers pass in a content type then use it, else get it from the header.
-  if (!useThisCType.IsEmpty())
-    CopyASCIItoUTF16(useThisCType, bodyType);
-  else
-  {
-    bodyType = headerVal;
-    ExtractType( bodyType);
-  }
-  ExtractCharset( headerVal);
-  // Use platform charset as default if the msg doesn't specify one
-  // (ie, no 'charset' param in the Content-Type: header). As the last
-  // resort we'll use the mail default charset.
-  PRBool try_to_improve_charset = PR_FALSE;
-  if (headerVal.IsEmpty())
-  {
-    try_to_improve_charset = PR_TRUE;
-    CopyASCIItoUTF16(nsMsgI18NFileSystemCharset(), headerVal);
-    if (headerVal.IsEmpty())
-    { // last resort
-      NS_GetLocalizedUnicharPreferenceWithDefault(nsnull, "mailnews.view_default_charset",
-                                                  NS_LITERAL_STRING("ISO-8859-1"), headerVal);
+  nsCAutoString asciiHeaderVal;
+  LossyCopyUTF16toASCII(headers->Value(CMapiMessageHeaders::hdrMessageID),
+                        asciiHeaderVal); // Message-Id cannot fold
+  m_pMsgFields->SetMessageId(asciiHeaderVal.get());
+  // We only use those headers that may need to be processed by Thunderbird
+  // to create a good rfc822 document, or need to be encoded (like To and Cc).
+  // These will replace the originals on import. All the other headers
+  // will be copied to the destination unaltered in CopyComposedMessage().
+
+  nsMsgAttachedFile *pAttach = msg.GetAttachments();
+
+  nsString bodyW;
+  // Bug 593907
+  if (GenerateHackSequence(msg.GetBody(), msg.GetBodyLen()))
+    HackBody(msg.GetBody(), msg.GetBodyLen(), bodyW);
+  // End Bug 593907
+
+  // We only get the editor interface when there's embedded content.
+  // Otherwise pEditor remains NULL. That way we only import with the pseudo
+  // editor when it helps.
+  nsRefPtr<nsOutlookEditor> pOutlookEditor = new nsOutlookEditor(bodyW.IsEmpty() ? msg.GetBody() : bodyW.get());
+  nsCOMPtr<nsIEditor> pEditor;
+
+  if (msg.BodyIsHtml()) {
+    for (unsigned int i = 0; i<msg.EmbeddedAttachmentsCount(); i++) {
+      nsIURI *uri;
+      const char* cid;
+      const char* name;
+      if (msg.GetEmbeddedAttachmentInfo(i, &uri, &cid, &name))
+        pOutlookEditor->AddEmbeddedImage(uri, NS_ConvertASCIItoUTF16(cid).get(), NS_ConvertASCIItoUTF16(name).get());
     }
   }
-  charSet = headerVal;
 
-  char *pMimeType = nsnull;
-  if (!bodyType.IsEmpty())
-    pMimeType = ToNewCString(bodyType);
-  if (!pMimeType)
-    pMimeType = NS_strdup ("text/plain");
-
-  if (try_to_improve_charset && strcmp (pMimeType, "text/html") == 0) {
-    // get charset from HTML meta tag
-    ExtractMetaCharset (m_Body, headerVal);
-    if (headerVal.IsEmpty())
-      headerVal = charSet;
+  nsCString bodyA;
+  if (pOutlookEditor && pOutlookEditor->HasEmbeddedContent()) {
+    // There's embedded content that we need to import, so query for the editor interface
+    pEditor = do_QueryObject(pOutlookEditor);
   }
-
-  m_pMsgFields->SetCharacterSet( NS_LossyConvertUTF16toASCII(headerVal).get() );
-  GetHeaderValue( m_Headers.get(), m_Headers.Length(), "CC:", headerVal);
-  if (!headerVal.IsEmpty())
-    m_pMsgFields->SetCc( headerVal);
-  GetHeaderValue( m_Headers.get(), m_Headers.Length(), "Message-ID:", headerVal);
-  if (!headerVal.IsEmpty()) {
-    LossyCopyUTF16toASCII(headerVal, asciiHeaderVal);
-    m_pMsgFields->SetMessageId(asciiHeaderVal.get());
+  else {
+    if (bodyW.IsEmpty())
+      msg.GetBody(bodyA);
+    else
+      nsMsgI18NConvertFromUnicode(msg.GetBodyCharset(), bodyW, bodyA);
   }
-  GetHeaderValue( m_Headers.get(), m_Headers.Length(), "Reply-To:", headerVal);
-  if (!headerVal.IsEmpty())
-    m_pMsgFields->SetReplyTo( headerVal);
-
-  // what about all of the other headers?!?!?!?!?!?!
-
-  nsMsgAttachedFile *pAttach = GetLocalAttachments();
-
-  // Do body conversion here
-  nsString uniBody;
-  NS_CopyNativeToUnicode( m_Body, uniBody);
-
-  nsCString body;
-  rv = nsMsgI18NConvertFromUnicode( NS_LossyConvertUTF16toASCII(charSet).get(),
-                                    uniBody, body);
-  uniBody.Truncate();
 
   // IMPORT_LOG0( "Outlook compose calling CreateAndSendMessage\n");
   rv = m_pSendProxy->CreateAndSendMessage(
-                    nsnull,                       // no editor shell
+                    pEditor,                      // editor shell
                     m_pIdentity,                  // dummy identity
-                                                                                nsnull,                         // account key
+                    nsnull,                       // account key
                     m_pMsgFields,                 // message fields
                     PR_FALSE,                     // digest = NO
                     PR_TRUE,                      // dont_deliver = YES, make a file
                     mode,                         // mode
                     nsnull,                       // no message to replace
-                    pMimeType,                    // body type
-                    NS_FAILED(rv) ? m_Body.get() : body.get(), // body pointer
-                    NS_FAILED(rv) ? m_Body.Length() : body.Length(), // body length
+                    msg.BodyIsHtml() ? "text/html" : "text/plain",           // body type
+                    bodyA.get(),                  // body pointer
+                    bodyA.Length(),               // body length
                     nsnull,                       // remote attachment data
                     pAttach,                      // local attachments
                     nsnull,                       // related part
                     nsnull,                       // parent window
                     nsnull,                       // progress listener
                     m_pListener,                  // listener
                     nsnull,                       // password
                     EmptyCString(),               // originalMsgURI
                     nsnull);                      // message compose type
   // IMPORT_LOG0( "Returned from CreateAndSendMessage\n");
 
-  if (pAttach)
-    delete [] pAttach;
-
   OutlookSendListener *pListen = (OutlookSendListener *)m_pListener;
-  if (NS_FAILED( rv)) {
+  if (NS_FAILED(rv)) {
     IMPORT_LOG1( "*** Error, CreateAndSendMessage FAILED: 0x%lx\n", rv);
   }
   else {
     // wait for the listener to get done!
     PRInt32 abortCnt = 0;
     PRInt32 cnt = 0;
     PRInt32 sleepCnt = 1;
     while (!pListen->m_done && (abortCnt < kHungAbortCount)) {
-      PR_Sleep( sleepCnt);
+      PR_Sleep(sleepCnt);
       cnt++;
       if (cnt > kHungCount) {
         abortCnt++;
         sleepCnt *= 2;
         cnt = 0;
       }
     }
 
     if (abortCnt >= kHungAbortCount) {
       IMPORT_LOG0( "**** Create and send message hung\n");
-      IMPORT_LOG1( "Headers: %s\n", m_Headers.get());
-      IMPORT_LOG1( "Body: %s\n", m_Body.get());
+//      IMPORT_LOG1( "Headers: %s\n", m_Headers.get());
+//      IMPORT_LOG1( "Body: %s\n", m_Body.get());
       rv = NS_ERROR_FAILURE;
     }
-
   }
 
-  if (pMimeType)
-    NS_Free( pMimeType);
+  if (pAttach)
+    msg.DisposeAttachments(pAttach);
 
   if (pListen->m_location) {
     pListen->m_location->Clone(pMsg);
     rv = NS_OK;
   }
   else {
     rv = NS_ERROR_FAILURE;
     IMPORT_LOG0( "*** Error, Outlook compose unsuccessful\n");
   }
 
   pListen->Reset();
 
-  return( rv);
-}
-
-
-PRBool SimpleBufferTonyRCopiedTwice::SpecialMemCpy( PRInt32 offset, const char *pData, PRInt32 len, PRInt32 *pWritten)
-{
-  // Arg!!!!!  Mozilla can't handle plain CRs in any mail messages.  Particularly a
-  // problem with Eudora since it doesn't give a rats a**
-  *pWritten = len;
-  PRInt32  sz = offset + len;
-  if (offset) {
-    if ((m_pBuffer[offset - 1] == 0x0D) && (*pData != 0x0A)) {
-      sz++;
-      if (!Grow( sz)) return( PR_FALSE);
-      m_pBuffer[offset] = 0x0A;
-      offset++;
-      (*pWritten)++;
-    }
-  }
-  while (len > 0) {
-    if ((*pData == 0x0D) && (*(pData + 1) != 0x0A)) {
-      sz++;
-      if (!Grow( sz)) return( PR_FALSE);
-      m_pBuffer[offset] = 0x0D;
-      offset++;
-      m_pBuffer[offset] = 0x0A;
-      (*pWritten)++;
-    }
-    else {
-      m_pBuffer[offset] = *pData;
-    }
-    offset++;
-    pData++;
-    len--;
-  }
-
-  return PR_TRUE;
-}
-
-nsresult nsOutlookCompose::ReadHeaders( ReadFileState *pState, SimpleBufferTonyRCopiedTwice& copy, SimpleBufferTonyRCopiedTwice& header)
-{
-  // This should be the headers...
-  header.m_writeOffset = 0;
-
-  nsresult rv;
-  PRInt32 lineLen;
-  PRInt32 endLen = -1;
-  PRInt8 endBuffer = 0;
-
-  while ((endLen = IsEndHeaders( copy)) == -1) {
-    while ((lineLen = FindNextEndLine( copy)) == -1) {
-      copy.m_writeOffset = copy.m_bytesInBuf;
-      if (!header.Write( copy.m_pBuffer, copy.m_writeOffset)) {
-        IMPORT_LOG0( "*** ERROR, writing headers\n");
-        return( NS_ERROR_FAILURE);
-      }
-      if (NS_FAILED( rv = FillMailBuffer( pState, copy))) {
-        IMPORT_LOG0( "*** Error reading message headers\n");
-        return( rv);
-      }
-      if (!copy.m_bytesInBuf) {
-        IMPORT_LOG0( "*** Error, end of file while reading headers\n");
-        return( NS_ERROR_FAILURE);
-      }
-    }
-    copy.m_writeOffset += lineLen;
-    if ((copy.m_writeOffset + 4) >= copy.m_bytesInBuf) {
-      if (!header.Write( copy.m_pBuffer, copy.m_writeOffset)) {
-        IMPORT_LOG0( "*** ERROR, writing headers 2\n");
-        return( NS_ERROR_FAILURE);
-      }
-      if (NS_FAILED( rv = FillMailBuffer( pState, copy))) {
-        IMPORT_LOG0( "*** Error reading message headers 2\n");
-        return( rv);
-      }
+  if (NS_SUCCEEDED(rv) && pEditor) {
+    PRUint32 embedCount = pOutlookEditor->EmbeddedObjectsCount();
+    for (PRUint32 i=0; i<embedCount; i++) {
+      CidReplacePair *pair = new CidReplacePair;
+      if (NS_SUCCEEDED(pOutlookEditor->GetCids(i, pair->cidOrig, pair->cidNew)))
+        m_replacedCids.push_back(pair);
+      else delete pair;
     }
   }
 
-  if (!header.Write( copy.m_pBuffer, copy.m_writeOffset)) {
-    IMPORT_LOG0( "*** Error writing final headers\n");
-    return( NS_ERROR_FAILURE);
-  }
-  if (!header.Write( (const char *)&endBuffer, 1)) {
-    IMPORT_LOG0( "*** Error writing header trailing null\n");
-    return( NS_ERROR_FAILURE);
-  }
+  return rv;
+}
 
-  copy.m_writeOffset += endLen;
-
-  return( NS_OK);
+nsOutlookCompose::ReplaceCidInLine::ReplaceCidInLine(nsCString& line)
+  : m_line(line)
+{
+  // If the line begins with Content-ID: string, process it! Otherwise, no need to waste time
+  m_finishedReplacing = (line.Compare("Content-ID:", PR_TRUE, 11) != 0);
 }
 
-PRInt32 nsOutlookCompose::FindNextEndLine( SimpleBufferTonyRCopiedTwice& data)
+void nsOutlookCompose::ReplaceCidInLine::operator () (const CidReplacePair* pair)
 {
-  PRInt32 len = data.m_bytesInBuf - data.m_writeOffset;
-  if (!len)
-    return( -1);
-  PRInt32  count = 0;
-  const char *pData = data.m_pBuffer + data.m_writeOffset;
-  while (((*pData == 0x0D) || (*pData == 0x0A)) && (count < len)) {
-    pData++;
-    count++;
+  if (m_finishedReplacing)
+    return; // Only one cid per line possible!
+  PRInt32 pos = m_line.Find(pair->cidNew, PR_FALSE, 12);
+  if (pos != kNotFound) {
+    m_finishedReplacing = true; // Stop further search
+    m_line.Replace(pos, pair->cidNew.Length(), pair->cidOrig);
   }
-  while ((*pData != 0x0D) && (*pData != 0x0A) && (count < len)) {
-    pData++;
-    count++;
-  }
-
-  if (count < len)
-    return( count);
-
-  return( -1);
 }
 
-PRInt32 nsOutlookCompose::IsEndHeaders( SimpleBufferTonyRCopiedTwice& data)
+nsresult nsOutlookCompose::CopyComposedMessage(nsIFile *pSrc,
+                                               nsIOutputStream *pDst,
+                                               CMapiMessage& origMsg)
 {
-  PRInt32 len = data.m_bytesInBuf - data.m_writeOffset;
-  if (len < 2)
-    return( -1);
-  const char *pChar = data.m_pBuffer + data.m_writeOffset;
-  if ((*pChar == 0x0D) && (*(pChar + 1) == 0x0D))
-    return( 2);
-
-  if (len < 4)
-    return( -1);
-  if ((*pChar == 0x0D) && (*(pChar + 1) == 0x0A) &&
-    (*(pChar + 2) == 0x0D) && (*(pChar + 3) == 0x0A))
-    return( 4);
-
-  return( -1);
-}
-
-
-nsresult nsOutlookCompose::CopyComposedMessage( nsCString& fromLine, nsIFile *pSrc, nsIOutputStream *pDst, SimpleBufferTonyRCopiedTwice& copy)
-{
-  copy.m_bytesInBuf = 0;
-  copy.m_writeOffset = 0;
-  ReadFileState  state;
-  state.pFile = pSrc;
-  state.offset = 0;
-  state.size = 0;
-  pSrc->GetFileSize( &state.size);
-  if (!state.size) {
+  // I'm unsure if we really need the convertCRs feature here.
+  // The headers in the file are generated by TB, the body was generated by rtf reader that always used CRLF,
+  // and the attachments were processed by TB either... However, I let it stay as it was in the original code.
+  CCompositionFile f(pSrc, m_optimizationBuffer, m_optimizationBufferSize, true);
+  if (!f) {
     IMPORT_LOG0( "*** Error, unexpected zero file size for composed message\n");
-    return( NS_ERROR_FAILURE);
+    return NS_ERROR_FAILURE;
   }
 
-        nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(state.pInputStream), pSrc);
-  if (NS_FAILED( rv)) {
-    IMPORT_LOG0( "*** Error, unable to open composed message file\n");
-    return( NS_ERROR_FAILURE);
-  }
+  // The "From ..." separates the messages. Without it, TB cannot see the messages in the mailbox file.
+  // Thus, the lines that look like "From ..." in the message must be escaped (see EscapeFromSpaceLine())
+  int fromLineLen;
+  const char* fromLine = origMsg.GetFromLine(fromLineLen);
+  PRUint32 written;
+  nsresult rv = pDst->Write( fromLine, fromLineLen, &written);
 
-  PRUint32 written;
-  rv = pDst->Write( fromLine.get(), fromLine.Length(), &written);
+  // Bug 219269
+  // Write out the x-mozilla-status headers.
+  char statusLine[50];
+  PRUint32 msgFlags = 0;
+  if (origMsg.IsRead())
+    msgFlags |= nsMsgMessageFlags::Read;
+  if (!origMsg.FullMessageDownloaded())
+    msgFlags |= nsMsgMessageFlags::Partial;
+  if (origMsg.IsForvarded())
+    msgFlags |= nsMsgMessageFlags::Forwarded;
+  if (origMsg.IsReplied())
+    msgFlags |= nsMsgMessageFlags::Replied;
+  if (origMsg.HasAttach())
+    msgFlags |= nsMsgMessageFlags::Attachment;
+  _snprintf(statusLine, sizeof(statusLine), X_MOZILLA_STATUS_FORMAT MSG_LINEBREAK, msgFlags & 0xFFFF);
+  rv = pDst->Write(statusLine, strlen(statusLine), &written);
+  _snprintf(statusLine, sizeof(statusLine), X_MOZILLA_STATUS2_FORMAT MSG_LINEBREAK, msgFlags & 0xFFFF0000);
+  rv = pDst->Write(statusLine, strlen(statusLine), &written);
+  // End Bug 219269
 
   // well, isn't this a hoot!
   // Read the headers from the new message, get the ones we like
   // and write out only the headers we want from the new message,
   // along with all of the other headers from the "old" message!
-  if (NS_SUCCEEDED( rv))
-    rv = FillMailBuffer( &state, copy);
-  if (NS_SUCCEEDED( rv))
-    rv = ReadHeaders( &state, copy, m_readHeaders);
+
+  nsCString newHeadersStr;
+  rv = f.ToString(newHeadersStr, MSG_LINEBREAK MSG_LINEBREAK); // Read all the headers
+  NS_ENSURE_SUCCESS(rv, rv);
+  UpdateHeaders(*origMsg.GetHeaders(), newHeadersStr.get());
+  rv = origMsg.GetHeaders()->ToStream(pDst);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  if (NS_SUCCEEDED( rv)) {
-    rv = WriteHeaders( pDst, m_readHeaders);
+  // Bug 593907
+  if (!m_hackedPostfix.IsEmpty()) {
+    nsCString hackedPartEnd;
+    LossyCopyUTF16toASCII(m_hackedPostfix, hackedPartEnd);
+    hackedPartEnd.Insert(hackEndA, 0);
+    nsCString body;
+    rv = f.ToString(body, hackedPartEnd.get(), hackedPartEnd.Length());
+    UnhackBody(body);
+    EscapeFromSpaceLine(pDst, const_cast<char*>(body.get()), body.get()+body.Length());
+  }
+  // End Bug 593907
+
+  // I use the terminating sequence here to avoid a possible situation when a "From " line
+  // gets split over two sequential reads and thus will not be escaped.
+  // This is done by reading up to CRLF (one line every time), though it may be slower
+
+  // Here I revert the changes that were made when the multipart/related message
+  // was composed in nsMsgSend::ProcessMultipartRelated() - the Content-Ids of
+  // attachments were replaced with new ones.
+  nsCString line;
+  while (NS_SUCCEEDED(f.ToString(line, MSG_LINEBREAK))) {
+    if (!m_replacedCids.empty())
+      std::for_each(m_replacedCids.begin(), m_replacedCids.end(), ReplaceCidInLine(line));
+    EscapeFromSpaceLine(pDst, const_cast<char*>(line.get()), line.get()+line.Length());
   }
 
-  // We need to go ahead and write out the rest of the copy buffer
-  // so that the following will properly copy the rest of the body
-  char lastChar = 0;
-
-    rv = EscapeFromSpaceLine(pDst, copy.m_pBuffer + copy.m_writeOffset, copy.m_pBuffer+copy.m_bytesInBuf);
-    if (copy.m_bytesInBuf)
-      lastChar = copy.m_pBuffer[copy.m_bytesInBuf - 1];
-    if (NS_SUCCEEDED(rv))
-      copy.m_writeOffset = copy.m_bytesInBuf;
-
-  while ((state.offset < state.size) && NS_SUCCEEDED( rv)) {
-    rv = FillMailBuffer( &state, copy);
-    if (NS_SUCCEEDED( rv)) {
-      rv = EscapeFromSpaceLine(pDst, copy.m_pBuffer + copy.m_writeOffset, copy.m_pBuffer+copy.m_bytesInBuf);
-      lastChar = copy.m_pBuffer[copy.m_bytesInBuf - 1];
-      if (NS_SUCCEEDED( rv))
-        copy.m_writeOffset = copy.m_bytesInBuf;
-      else
-        IMPORT_LOG0( "*** Error writing to destination mailbox\n");
-    }
-  }
-
-
-  if ((lastChar != 0x0A) && NS_SUCCEEDED( rv)) {
-    rv = pDst->Write( "\x0D\x0A", 2, &written);
+  if (f.LastChar() != 0x0A) {
+    rv = pDst->Write( MSG_LINEBREAK, 2, &written);
     if (written != 2)
       rv = NS_ERROR_FAILURE;
   }
 
   return rv;
 }
 
-nsresult nsOutlookCompose::FillMailBuffer( ReadFileState *pState, SimpleBufferTonyRCopiedTwice& read)
+nsresult nsOutlookCompose::ProcessMessage(nsMsgDeliverMode mode,
+                                          CMapiMessage &msg,
+                                          nsIOutputStream *pDst)
 {
-  if (read.m_writeOffset >= read.m_bytesInBuf) {
-    read.m_writeOffset = 0;
-    read.m_bytesInBuf = 0;
-  }
-  else if (read.m_writeOffset) {
-    memcpy( read.m_pBuffer, read.m_pBuffer + read.m_writeOffset, read.m_bytesInBuf - read.m_writeOffset);
-    read.m_bytesInBuf -= read.m_writeOffset;
-    read.m_writeOffset = 0;
+  nsCOMPtr<nsIFile> compositionFile;
+  nsresult rv = ComposeTheMessage(mode, msg, getter_AddRefs(compositionFile));
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = CopyComposedMessage(compositionFile, pDst, msg);
+  compositionFile->Remove(PR_FALSE);
+  if (NS_FAILED( rv)) {
+    IMPORT_LOG0( "*** Error copying composed message to destination mailbox\n");
   }
-
-  PRInt32  count = read.m_size - read.m_bytesInBuf;
-  if (((PRUint32)count + pState->offset) > pState->size)
-    count = pState->size - pState->offset;
-  if (count) {
-    PRUint32 bytesRead = 0;
-    char * pBuffer = read.m_pBuffer + read.m_bytesInBuf;
-    nsresult rv = pState->pInputStream->Read( pBuffer, count, &bytesRead);
-    if (NS_FAILED( rv)) return( rv);
-    if (bytesRead != PRUint32(count)) return( NS_ERROR_FAILURE);
-    read.m_bytesInBuf += bytesRead;
-    pState->offset += bytesRead;
-  }
-
-  return( NS_OK);
+  return rv;
 }
 
-
-#define kMaxSpecialHeaders  3
-static const char *gSpecialHeaders[kMaxSpecialHeaders] = {
-  "Content-type",
-  "MIME-Version",
-  "Content-transfer-encoding"
-};
-// consider "X-Accept-Language"?
-
-#define kMaxReplaceHeaders  5
-static const char *gReplaceHeaders[kMaxReplaceHeaders] = {
-  "From",
-  "To",
-  "Subject",
-  "Reply-to",
-  "cc"
-};
-
-PRBool nsOutlookCompose::IsReplaceHeader( const char *pHeader)
+void nsOutlookCompose::UpdateHeader(CMapiMessageHeaders& oldHeaders,
+                                    const CMapiMessageHeaders& newHeaders,
+                                    CMapiMessageHeaders::SpecialHeader header,
+                                    bool addIfAbsent)
 {
-  for (int i = 0; i < kMaxReplaceHeaders; i++) {
-    if (!PL_strcasecmp( pHeader, gReplaceHeaders[i]))
-      return( PR_TRUE);
-  }
-
-  return( PR_FALSE);
+  if (!addIfAbsent && !oldHeaders.Value(header))
+    return;
+  const wchar_t* newVal = newHeaders.Value(header);
+  if (newVal) oldHeaders.SetValue(header, newVal);
+  // Bug 145150 - Turn "Content-Type: application/ms-tnef" into "Content-Type: text/plain"
+  //              so the body text can be displayed normally (instead of in an attachment).
+  if (header == CMapiMessageHeaders::hdrContentType)
+    if (!wcsicmp(oldHeaders.Value(header), L"application/ms-tnef"))
+      oldHeaders.SetValue(header, L"text/plain");
 }
 
-PRInt32 nsOutlookCompose::IsSpecialHeader( const char *pHeader)
-{
-  for (int i = 0; i < kMaxSpecialHeaders; i++) {
-    if (!PL_strcasecmp( pHeader, gSpecialHeaders[i]))
-      return( (PRInt32) i);
-  }
-
-  return( -1);
-}
-
-
-nsresult nsOutlookCompose::WriteHeaders( nsIOutputStream *pDst, SimpleBufferTonyRCopiedTwice& newHeaders)
+void nsOutlookCompose::UpdateHeaders(CMapiMessageHeaders& oldHeaders, const CMapiMessageHeaders& newHeaders)
 {
   // Well, ain't this a peach?
   // This is rather disgusting but there really isn't much to be done about it....
 
   // 1. For each "old" header, replace it with the new one if we want,
   // then right it out.
   // 2. Then if we haven't written the "important" new headers, write them out
   // 3. Terminate the headers with an extra eol.
 
-  PRInt32 n = 0;
-  nsCString header;
-  nsCString val;
-  nsCString replaceVal;
-  PRUint32 written;
-  nsresult rv = NS_OK; // it's ok if we don't have the first header on the predefined lists.
-  PRInt32 specialHeader;
-  PRBool specials[kMaxSpecialHeaders];
-  int i;
+  // Important headers:
+  //  "Content-type",
+  //  "MIME-Version",
+  //  "Content-transfer-encoding"
+  // consider "X-Accept-Language"?
+
+  UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrContentType);
+  UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrMimeVersion);
+  UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrContentTransferEncoding);
+
+  // Other replaced headers (only if they exist):
+  //  "From",
+  //  "To",
+  //  "Subject",
+  //  "Reply-to",
+  //  "Cc"
+
+  UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrFrom, false);
+  UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrTo, false);
+  UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrSubject, false);
+  UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrReplyTo, false);
+  UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrCc, false);
+}
+
+// Bug 593907
+// This is just a workaround of the deficiency of the nsMsgComposeAndSend::EnsureLineBreaks().
+// The import from Outlook will stay OK (I hope), but other messages may still suffer.
+// However, I cannot deny the possibility that the (possible) recode of the body
+// may interfere with this hack. A possible scenario is if a multi-byte character will either
+// contain 0x0D 0x0A sequence, or end with 0x0D, after which MAC-style standalone LF will go.
+// I hope that this possibility is insignificant (eg, utf-8 doesn't contain such sequences).
+// This hack will slow down the import, but as the import is one-time procedure, I hope that
+// the user will agree to wait a little longer to get better results.
+
+// The process of composing the message differs depending on whether the editor is present or not.
+// If the editor is absent, the "attachment1_body" parameter of CreateAndSendMessage() is taken as is,
+// while in the presence o the editor, the body that is taken from it is further processed in the
+// nsMsgComposeAndSend::GetBodyFromEditor(). Specifically, the TXTToHTML::ScanHTML() first calls
+// UnescapeStr() to properly handle a limited number of HTML character entities (namely &amp; &lt; &gt; &quot;)
+// and then calls ScanTXT() where escapes all ampersands and quotes again. As the UnescapeStr() works so
+// selectively (i.e. handling only a subset of valid entities), the so often seen "&nbsp;" becomes "&amp;nbsp;"
+// in the resulting body, which leads to text "&nbsp;" interspersed all over the imported mail. The same
+// applies to html &#XXXX; (where XXXX is unicode codepoint).
+// See also Bug 503690, where the same issue in Eudora import is reported.
+// By the way, the root of the Bug 359303 lies in the same place - the nsMsgComposeAndSend::GetBodyFromEditor()
+// changes the 0xA0 codes to 0x20 when it converts the body to plain text.
+// We scan the body here to find all the & and convert them to the safe character sequense to revert later.
+
+void nsOutlookCompose::HackBody(const wchar_t* orig, size_t origLen, nsString& hack)
+{
+  hack.SetCapacity(static_cast<size_t>(origLen*1.4));
+  hack.Assign(hackBeginW);
+  hack.Append(m_hackedPostfix);
 
-  for (i = 0; i < kMaxSpecialHeaders; i++)
-    specials[i] = PR_FALSE;
+  while (*orig) {
+    if (*orig == L'&') {
+      hack.Append(hackAmpersandW);
+      hack.Append(m_hackedPostfix);
+    } else if ((*orig == L'\x0D') && (*(orig+1) == L'\x0A')) {
+      hack.Append(hackCRLFW);
+      hack.Append(m_hackedPostfix);
+      ++orig;
+    } else
+      hack.Append(*orig);
+    ++orig;
+  }
+
+  hack.Append(hackEndW);
+  hack.Append(m_hackedPostfix);
+}
+
+void nsOutlookCompose::UnhackBody(nsCString& txt)
+{
+  nsCString hackedPostfixA;
+  LossyCopyUTF16toASCII(m_hackedPostfix, hackedPostfixA);
+
+  nsCString hackedString(hackBeginA);
+  hackedString.Append(hackedPostfixA);
+  PRInt32 begin = txt.Find(hackedString);
+  if (begin == kNotFound)
+    return;
+  txt.Cut(begin, hackedString.Length());
+
+  hackedString.Assign(hackEndA);
+  hackedString.Append(hackedPostfixA);
+  PRInt32 end = txt.Find(hackedString, PR_FALSE, begin);
+  if (end == kNotFound)
+    return; // ?
+  txt.Cut(end, hackedString.Length());
+
+  // 1. Remove all CRLFs from the selected range
+  PRInt32 i = begin;
+  while (i < end) {
+    PRInt32 r = txt.Find(MSG_LINEBREAK, PR_FALSE, i, end-i);
+    if (r == kNotFound)
+      break;
+
+    txt.Cut(r, 2);
+    end -= 2;
+    i = r;
+  }
+
+  // 2. Restore the original CRLFs
+  hackedString.Assign(hackCRLFA);
+  hackedString.Append(hackedPostfixA);
+  i = begin;
+  while (i < end) {
+    PRInt32 r = txt.Find(hackedString, PR_FALSE, i, end-i);
+    if (r == kNotFound)
+      break;
+
+    txt.Replace(r, hackedString.Length(), MSG_LINEBREAK, 2);
+    end -= hackedString.Length()-2;
+    i = r+2;
+  }
+
+  // 3. Restore the original ampersands
+  hackedString.Assign(hackAmpersandA);
+  hackedString.Append(hackedPostfixA);
+  i = begin;
+  while (i < end) {
+    PRInt32 r = txt.Find(hackedString, PR_FALSE, i, end-i);
+    if (r == kNotFound)
+      break;
+
+    txt.Replace(r, hackedString.Length(), '&');
+    end -= hackedString.Length()-1;
+    i = r+1;
+  }
+}
 
+bool nsOutlookCompose::GenerateHackSequence(const wchar_t* body, size_t origLen)
+{
+  nsDependentString nsBody(body, origLen);
+  const wchar_t* hack_base = L"hacked";
+  int i = 0;
   do {
-    GetNthHeader( m_Headers.get(), m_Headers.Length(), n, header, val, PR_FALSE);
-    // GetNthHeader( newHeaders.m_pBuffer, newHeaders.m_writeOffset, n, header.get(), val, PR_FALSE);
-    if (!header.IsEmpty()) {
-      if ((specialHeader = IsSpecialHeader( header.get())) != -1) {
-        header.Append( ':');
-        GetHeaderValue( newHeaders.m_pBuffer, newHeaders.m_writeOffset - 1, header.get(), val, PR_FALSE);
-        // Bug 145150 - Turn "Content-Type: application/ms-tnef" into "Content-Type: text/plain"
-        //              so the body text can be displayed normally (instead of in an attachment).
-        if (header.LowerCaseEqualsLiteral("content-type:") && val.LowerCaseEqualsLiteral("application/ms-tnef"))
-          val.Assign("text/plain");
-        header.SetLength( header.Length() - 1);
-        specials[specialHeader] = PR_TRUE;
+    if (++i == 0) { // Cycle complete :) - could not generate an unique string
+      m_hackedPostfix.Truncate();
+      return false;
+    }
+    m_hackedPostfix.Assign(hack_base);
+    m_hackedPostfix.AppendInt(i);
+  } while (nsBody.Find(m_hackedPostfix) != kNotFound);
+
+  return true;
+}
+// End Bug 593907
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+CCompositionFile::CCompositionFile(nsIFile* aFile, void* fifoBuffer,
+                                   PRUint32 fifoBufferSize, bool convertCRs)
+  : m_pFile(aFile), m_fileReadPos(0),
+    m_fifoBuffer(static_cast<char*>(fifoBuffer)),
+    m_fifoBufferSize(fifoBufferSize),
+    m_fifoBufferReadPos(static_cast<char*>(fifoBuffer)),
+    m_fifoBufferWrittenPos(static_cast<char*>(fifoBuffer)),
+    m_convertCRs(convertCRs),
+    m_lastChar(0)
+{
+  m_pFile->GetFileSize(&m_fileSize);
+  if (!m_fileSize) {
+    IMPORT_LOG0( "*** Error, unexpected zero file size for composed message\n");
+    return;
+  }
+
+  nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(m_pInputStream), m_pFile);
+  if (NS_FAILED(rv)) {
+    IMPORT_LOG0( "*** Error, unable to open composed message file\n");
+    return;
+  }
+}
+
+nsresult CCompositionFile::EnsureHasDataInBuffer()
+{
+  if (m_fifoBufferReadPos < m_fifoBufferWrittenPos)
+    return NS_OK;
+  // Populate the buffer with new data!
+  PRUint32 count = m_fifoBufferSize;
+  if ((m_fileReadPos + count) > m_fileSize)
+    count = m_fileSize - m_fileReadPos;
+  if (!count)
+    return NS_ERROR_FAILURE; // Isn't there a "No more data" error?
+
+  PRUint32 bytesRead = 0;
+  nsresult rv = m_pInputStream->Read( m_fifoBuffer, count, &bytesRead);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!bytesRead || (bytesRead > count))
+    return( NS_ERROR_FAILURE);
+  m_fifoBufferWrittenPos = m_fifoBuffer+bytesRead;
+  m_fifoBufferReadPos = m_fifoBuffer;
+  m_fileReadPos += bytesRead;
+
+  return NS_OK;
+}
+
+const char CR = '\x0D', LF = '\x0A';
+
+class CTermGuard {
+public:
+  CTermGuard(const char* term, int termSize)
+    : m_term(term),
+    m_termSize(term ? ((termSize!=-1) ? termSize : strlen(term)) : 0),
+    m_matchPos(0)
+  {}
+
+   // if the guard triggered
+  inline bool IsTriggered() const {
+    return m_termSize && (m_matchPos == m_termSize); }
+  // indicates if the guard has something to check
+  inline bool IsChecking() const { return m_termSize; } 
+
+  bool Check(char c) // returns true only if the whole sequence passed
+  {
+    if (!m_termSize) // no guard
+      return false;
+    if (m_matchPos >= m_termSize) // check past success!
+      m_matchPos = 0;
+    if (m_term[m_matchPos] == c) { // Sequence continues
+      return (++m_matchPos == m_termSize); // If equal then sequence complete!
+    }
+    // Sequence broken
+    m_matchPos = 0;
+    return false;
+  }
+private:
+  const char* m_term;
+  int m_termSize;
+  int m_matchPos;
+};
+
+template <class _OutFn>
+nsresult CCompositionFile::ToDest(_OutFn dest, const char* term, int termSize)
+{
+  CTermGuard guard(term, termSize);
+
+  // We already know the required string size, so reduce future reallocations
+  if (!guard.IsChecking() && !m_convertCRs) 
+    dest.SetCapacity(m_fileSize - m_fileReadPos);
+
+  bool wasCR = false;
+  char c = 0;
+  nsresult rv;
+  while (NS_SUCCEEDED(rv = EnsureHasDataInBuffer())) {
+    if (!guard.IsChecking() && !m_convertCRs) { // Use efficient algorithm
+      dest.Append(m_fifoBufferReadPos, m_fifoBufferWrittenPos-m_fifoBufferReadPos);
+    }
+    else { // Check character by character to convert CRs and find terminating sequence
+      while (m_fifoBufferReadPos < m_fifoBufferWrittenPos) {
+        c = *m_fifoBufferReadPos;
+        if (m_convertCRs && wasCR) {
+          wasCR = false;
+          if (c != LF) {
+            dest.Append(&LF, 1);
+            if (guard.Check(LF)) {
+              c = LF; // save last char
+              break;
+            }
+          }
+        }
+        dest.Append(&c, 1);
+        m_fifoBufferReadPos++;
+
+        if (guard.Check(c))
+          break;
+
+        if (m_convertCRs && (c == CR))
+          wasCR = true;
       }
-      else if (IsReplaceHeader( header.get())) {
-        replaceVal.Truncate();
-        header.Append( ':');
-        GetHeaderValue( newHeaders.m_pBuffer, newHeaders.m_writeOffset - 1, header.get(), replaceVal, PR_FALSE);
-        header.SetLength( header.Length() - 1);
-        if (!replaceVal.IsEmpty())
-          val = replaceVal;
-      }
-      if (!val.IsEmpty()) {
-        rv = pDst->Write( header.get(), header.Length(), &written);
-        if (NS_SUCCEEDED( rv))
-          rv = pDst->Write( ": ", 2, &written);
-        if (NS_SUCCEEDED( rv))
-          rv = pDst->Write( val.get(), val.Length(), &written);
-        if (NS_SUCCEEDED( rv))
-          rv = pDst->Write( "\x0D\x0A", 2, &written);
-
-      }
-    }
-    n++;
-  } while (NS_SUCCEEDED( rv) && !header.IsEmpty());
-
-  for (i = 0; (i < kMaxSpecialHeaders) && NS_SUCCEEDED( rv); i++) {
-    if (!specials[i]) {
-      header = gSpecialHeaders[i];
-      header.Append( ':');
-      GetHeaderValue( newHeaders.m_pBuffer, newHeaders.m_writeOffset - 1, header.get(), val, PR_FALSE);
-      header.SetLength( header.Length() - 1);
-      if (!val.IsEmpty()) {
-        rv = pDst->Write( header.get(), header.Length(), &written);
-        if (NS_SUCCEEDED( rv))
-          rv = pDst->Write( ": ", 2, &written);
-        if (NS_SUCCEEDED( rv))
-          rv = pDst->Write( val.get(), val.Length(), &written);
-        if (NS_SUCCEEDED( rv))
-          rv = pDst->Write( "\x0D\x0A", 2, &written);
-      }
+      if (guard.IsTriggered())
+        break;
     }
   }
 
+  // check for trailing CR (only if caller didn't specify the terminating sequence that ends with CR -
+  // in this case he knows what he does!)
+  if (m_convertCRs && !guard.IsTriggered() && (c == CR)) {
+    c = LF;
+    dest.Append(&c, 1);
+  }
 
-  if (NS_SUCCEEDED( rv))
-    rv = pDst->Write( "\x0D\x0A", 2, &written);
-  return( rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  m_lastChar = c;
+  return NS_OK;
 }
 
+class dest_nsCString {
+public:
+  dest_nsCString(nsCString& str) : m_str(str) { m_str.Truncate(); }
+  void SetCapacity(PRInt32 sz) { m_str.SetCapacity(sz); }
+  nsresult Append(const char* buf, PRUint32 count) {
+    m_str.Append(buf, count); return NS_OK; }
+private:
+  nsCString& m_str;
+};
 
+class dest_Stream {
+public:
+  dest_Stream(nsIOutputStream *dest) : m_stream(dest) {}
+  void SetCapacity(PRInt32) { /*do nothing*/ }
+  // const_cast here is due to the poor design of the EscapeFromSpaceLine()
+  // that requires a non-constant pointer while doesn't modify its data
+  nsresult Append(const char* buf, PRUint32 count) {
+    return EscapeFromSpaceLine(m_stream, const_cast<char*>(buf), buf+count); }
+private:
+  nsIOutputStream *m_stream;
+};
+
+nsresult CCompositionFile::ToString(nsCString& dest, const char* term,
+                                    int termSize)
+{
+  return ToDest(dest_nsCString(dest), term, termSize);
+}
+
+nsresult CCompositionFile::ToStream(nsIOutputStream *dest, const char* term,
+                                    int termSize)
+{
+  return ToDest(dest_Stream(dest), term, termSize);
+}
--- a/mailnews/import/outlook/src/nsOutlookCompose.h
+++ b/mailnews/import/outlook/src/nsOutlookCompose.h
@@ -49,137 +49,72 @@ class nsIMsgSend;
 class nsIMsgCompFields;
 class nsIMsgIdentity;
 class nsIMsgSendListener;
 class nsIIOService;
 
 #include "nsIMsgSend.h"
 #include "nsNetUtil.h"
 
-typedef class {
-public:
-  nsCOMPtr <nsILocalFile>  pAttachment;
-  char *      mimeType;
-  char *      description;
-} OutlookAttachment;
-
-typedef class {
-public:
-  PRUint32    offset;
-  PRInt64    size;
-  nsCOMPtr <nsIFile>  pFile;
-        nsCOMPtr <nsIInputStream> pInputStream;
-} ReadFileState;
-
-class SimpleBufferTonyRCopiedTwice {
-public:
-  SimpleBufferTonyRCopiedTwice() {m_pBuffer = nsnull; m_size = 0; m_growBy = 4096; m_writeOffset = 0;
-          m_bytesInBuf = 0; m_convertCRs = PR_FALSE;}
-  ~SimpleBufferTonyRCopiedTwice() { if (m_pBuffer) delete [] m_pBuffer;}
-
-  PRBool Allocate( PRInt32 sz) {
-    if (m_pBuffer) delete [] m_pBuffer;
-    m_pBuffer = new char[sz];
-    if (m_pBuffer) { m_size = sz; return( PR_TRUE); }
-    else { m_size = 0; return( PR_FALSE);}
-  }
+#include "MapiMessage.h"
 
-  PRBool Grow( PRInt32 newSize) { if (newSize > m_size) return( ReAllocate( newSize)); else return( PR_TRUE);}
-  PRBool ReAllocate( PRInt32 newSize) {
-    if (newSize <= m_size) return( PR_TRUE);
-    char *pOldBuffer = m_pBuffer;
-    PRInt32  oldSize = m_size;
-    m_pBuffer = nsnull;
-    while (m_size < newSize) m_size += m_growBy;
-    if (Allocate( m_size)) {
-      if (pOldBuffer) { memcpy( m_pBuffer, pOldBuffer, oldSize); delete [] pOldBuffer;}
-      return( PR_TRUE);
-    }
-    else { m_pBuffer = pOldBuffer; m_size = oldSize; return( PR_FALSE);}
-  }
+#include <list>
 
-  PRBool Write( PRInt32 offset, const char *pData, PRInt32 len, PRInt32 *pWritten) {
-    *pWritten = len;
-    if (!len) return( PR_TRUE);
-    if (!Grow( offset + len)) return( PR_FALSE);
-    if (m_convertCRs)
-      return( SpecialMemCpy( offset, pData, len, pWritten));
-    memcpy( m_pBuffer + offset, pData, len);
-    return( PR_TRUE);
-  }
-
-  PRBool Write( const char *pData, PRInt32 len) {
-    PRInt32 written;
-    if (Write( m_writeOffset, pData, len, &written)) { m_writeOffset += written; return( PR_TRUE);}
-    else return( PR_FALSE);
-  }
-
-  PRBool  SpecialMemCpy( PRInt32 offset, const char *pData, PRInt32 len, PRInt32 *pWritten);
-
-  PRBool  m_convertCRs;
-  char *  m_pBuffer;
-  PRInt32  m_bytesInBuf;  // used when reading into this buffer
-  PRInt32  m_size;      // allocated size of buffer
-  PRInt32  m_growBy;    // duh
-  PRInt32  m_writeOffset;  // used when writing into and reading from the buffer
-};
-
-
+///////////////////////////////////////////////////////////////////////////////////////////////
 
 class nsOutlookCompose {
 public:
   nsOutlookCompose();
   ~nsOutlookCompose();
 
-  nsresult  SendTheMessage(nsMsgDeliverMode mode, nsCString &useThisCType, nsIFile **pMsg);
-
-  void    SetBody( const char *pBody) { m_Body = pBody;}
-  void    SetHeaders( const char *pHeaders) { m_Headers = pHeaders;}
-  void    SetAttachments( nsVoidArray *pAttachments) { m_pAttachments = pAttachments;}
+  nsresult ProcessMessage(nsMsgDeliverMode mode, CMapiMessage &msg, nsIOutputStream *pDst);
+private:
+  struct CidReplacePair {
+    nsCString cidOrig;
+    nsCString cidNew;
+  };
 
-  nsresult  CopyComposedMessage( nsCString& fromLine, nsIFile *pSrc, nsIOutputStream *pDst, SimpleBufferTonyRCopiedTwice& copy);
-
-  static nsresult  FillMailBuffer( ReadFileState *pState, SimpleBufferTonyRCopiedTwice& read);
-
-private:
   nsresult  CreateComponents( void);
   nsresult  CreateIdentity( void);
 
-  void    GetNthHeader( const char *pData, PRInt32 dataLen, PRInt32 n, nsCString& header, nsCString& val, PRBool unwrap);
-  void    GetHeaderValue( const char *pData, PRInt32 dataLen, const char *pHeader, nsCString& val, PRBool unwrap = PR_TRUE);
-  void    GetHeaderValue( const char *pData, PRInt32 dataLen, const char *pHeader, nsString& val) {
-    val.Truncate();
-    nsCString  hVal;
-    GetHeaderValue( pData, dataLen, pHeader, hVal, PR_TRUE);
-    CopyUTF8toUTF16(hVal, val);
-  }
-  void    ExtractCharset( nsString& str);
-  void    ExtractMetaCharset( nsCString str, nsString& newstr);
-  void    ExtractType( nsString& str);
+  void      UpdateHeader(CMapiMessageHeaders& oldHeaders, const CMapiMessageHeaders& newHeaders, CMapiMessageHeaders::SpecialHeader header, bool addIfAbsent = true);
+  void      UpdateHeaders(CMapiMessageHeaders& oldHeaders, const CMapiMessageHeaders& newHeaders);
+
+  nsresult  ComposeTheMessage(nsMsgDeliverMode mode, CMapiMessage &msg, nsIFile **pMsg);
+  nsresult  CopyComposedMessage( nsIFile *pSrc, nsIOutputStream *pDst, CMapiMessage& origMsg);
+
+  // Bug 593907
+  void HackBody(const wchar_t* orig, size_t origLen, nsString& hack);
+  void UnhackBody(nsCString& body);
+  bool GenerateHackSequence(const wchar_t* body, size_t origLen);
+  // End Bug 593907
 
-  nsMsgAttachedFile * GetLocalAttachments( void);
-  void        CleanUpAttach( nsMsgAttachedFile *a, PRInt32 count);
+  static void ClearReplaceCid(CidReplacePair* pair) { delete pair; }
+  void ClearReplaceCids();
+private:
+  std::list<CidReplacePair*> m_replacedCids;
 
-  nsresult  ReadHeaders( ReadFileState *pState, SimpleBufferTonyRCopiedTwice& copy, SimpleBufferTonyRCopiedTwice& header);
-  PRInt32    FindNextEndLine( SimpleBufferTonyRCopiedTwice& data);
-  PRInt32    IsEndHeaders( SimpleBufferTonyRCopiedTwice& data);
-  PRInt32    IsSpecialHeader( const char *pHeader);
-  nsresult  WriteHeaders( nsIOutputStream *pDst, SimpleBufferTonyRCopiedTwice& newHeaders);
-  PRBool    IsReplaceHeader( const char *pHeader);
-  void      ConvertSystemStringToUnicode( const char *pSysStr, nsString& uniStr);
+  class ReplaceCidInLine {
+  public:
+    ReplaceCidInLine(nsCString& line);
+    void operator () (const CidReplacePair* pair);
+  private:
+    nsCString& m_line;
+    bool m_finishedReplacing;
+  };
 
 
-private:
-  nsVoidArray *      m_pAttachments;
   nsIMsgSendListener *  m_pListener;
   nsIMsgSend *      m_pMsgSend;
   nsIMsgSend *      m_pSendProxy;
   nsIMsgCompFields *    m_pMsgFields;
   nsIMsgIdentity *    m_pIdentity;
-  nsIIOService *      m_pIOService;
-  nsCString       m_Headers;
-  nsCString       m_Body;
-  SimpleBufferTonyRCopiedTwice      m_readHeaders;
+  char* m_optimizationBuffer;
+  unsigned int m_optimizationBufferSize;
   nsCOMPtr<nsIImportService>  m_pImportService;
+
+  // Bug 593907
+  nsString m_hackedPostfix;
+  // End Bug 593907
 };
 
 
 #endif /* nsOutlookCompose_h__ */
new file mode 100644
--- /dev/null
+++ b/mailnews/import/outlook/src/nsOutlookEditor.cpp
@@ -0,0 +1,1161 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: MPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Mozilla Public License Version
+* 1.1 (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+* http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is qualcomm.com code.
+*
+* The Initial Developer of the Original Code is
+* QUALCOMM, Inc.
+* Portions created by the Initial Developer are Copyright (C) 2007
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+*   Author: Geoffrey C. Wenger (gwenger@qualcomm.com)
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the MPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the MPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+
+
+#include "nsOutlookEditor.h"
+#include "nsISupportsArray.h"
+#include "nsIDOMHTMLImageElement.h"
+#include "nsComponentManagerUtils.h"
+#include "nsString.h"
+#include "nsNetUtil.h"
+
+NS_IMPL_ISUPPORTS2(nsOutlookEditor, nsIEditor, nsIEditorMailSupport)
+NS_IMPL_THREADSAFE_ISUPPORTS5(nsOutlookHTMLImageElement,
+                              nsOutlookHTMLImageElement,
+                              nsIDOMHTMLImageElement,
+                              nsIDOMHTMLElement,
+                              nsIDOMElement,
+                              nsIDOMNode)
+
+nsOutlookEditor::nsOutlookEditor(const wchar_t * body)
+  : m_body(body)
+{
+}
+
+nsOutlookEditor::~nsOutlookEditor()
+{
+}
+
+// readonly attribute nsISelection selection
+NS_IMETHODIMP nsOutlookEditor::GetSelection(nsISelection * *aSelection)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// [noscript] void init (in nsIDOMDocument doc, in nsIPresShellPtr shell, in nsIContentPtr aRoot, in nsISelectionController aSelCon, in unsigned long aFlags)
+NS_IMETHODIMP nsOutlookEditor::Init(nsIDOMDocument *doc, nsIContent * aRoot, nsISelectionController *aSelCon, PRUint32 aFlags)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void setAttributeOrEquivalent (in nsIDOMElement element, in AString sourceAttrName, in AString sourceAttrValue, in boolean aSuppressTransaction)
+NS_IMETHODIMP nsOutlookEditor::SetAttributeOrEquivalent(nsIDOMElement *element, const nsAString & sourceAttrName, const nsAString & sourceAttrValue, PRBool aSuppressTransaction)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void removeAttributeOrEquivalent (in nsIDOMElement element, in DOMString sourceAttrName, in boolean aSuppressTransaction)
+NS_IMETHODIMP nsOutlookEditor::RemoveAttributeOrEquivalent(nsIDOMElement *element, const nsAString & sourceAttrName, PRBool aSuppressTransaction)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void postCreate ()
+NS_IMETHODIMP nsOutlookEditor::PostCreate()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void preDestroy (in boolean aDestroyingFrames)
+NS_IMETHODIMP nsOutlookEditor::PreDestroy(PRBool aDestroyingFrames)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute unsigned long flags
+NS_IMETHODIMP nsOutlookEditor::GetFlags(PRUint32 *aFlags)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookEditor::SetFlags(PRUint32 aFlags)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute string contentsMIMEType
+NS_IMETHODIMP nsOutlookEditor::GetContentsMIMEType(char * *aContentsMIMEType)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookEditor::SetContentsMIMEType(const char * aContentsMIMEType)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute boolean isDocumentEditable
+NS_IMETHODIMP nsOutlookEditor::GetIsDocumentEditable(PRBool *aIsDocumentEditable)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute nsIDOMDocument document
+NS_IMETHODIMP nsOutlookEditor::GetDocument(nsIDOMDocument * *aDocument)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute nsIDOMElement rootElement
+NS_IMETHODIMP nsOutlookEditor::GetRootElement(nsIDOMElement * *aRootElement)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute nsISelectionController selectionController
+NS_IMETHODIMP nsOutlookEditor::GetSelectionController(nsISelectionController * *aSelectionController)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void deleteSelection (in short action)
+NS_IMETHODIMP nsOutlookEditor::DeleteSelection(PRInt16 action)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute boolean documentIsEmpty
+NS_IMETHODIMP nsOutlookEditor::GetDocumentIsEmpty(PRBool *aDocumentIsEmpty)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute boolean documentModified
+NS_IMETHODIMP nsOutlookEditor::GetDocumentModified(PRBool *aDocumentModified)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute ACString documentCharacterSet
+NS_IMETHODIMP nsOutlookEditor::GetDocumentCharacterSet(nsACString & aDocumentCharacterSet)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookEditor::SetDocumentCharacterSet(const nsACString & aDocumentCharacterSet)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void resetModificationCount ()
+NS_IMETHODIMP nsOutlookEditor::ResetModificationCount()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// long getModificationCount ()
+NS_IMETHODIMP nsOutlookEditor::GetModificationCount(PRInt32 *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void incrementModificationCount (in long aModCount)
+NS_IMETHODIMP nsOutlookEditor::IncrementModificationCount(PRInt32 aModCount)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+//  attribute nsITransactionManager transactionManager
+NS_IMETHODIMP nsOutlookEditor::GetTransactionManager(nsITransactionManager * *aTransactionManager)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookEditor::SetTransactionManager(nsITransactionManager *aTxnManager)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void doTransaction (in nsITransaction txn)
+NS_IMETHODIMP nsOutlookEditor::DoTransaction(nsITransaction *txn)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void enableUndo (in boolean enable)
+NS_IMETHODIMP nsOutlookEditor::EnableUndo(PRBool enable)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void undo (in unsigned long count)
+NS_IMETHODIMP nsOutlookEditor::Undo(PRUint32 count)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void canUndo (out boolean isEnabled, out boolean canUndo)
+NS_IMETHODIMP nsOutlookEditor::CanUndo(PRBool *isEnabled, PRBool *canUndo)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void redo (in unsigned long count)
+NS_IMETHODIMP nsOutlookEditor::Redo(PRUint32 count)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void canRedo (out boolean isEnabled, out boolean canRedo)
+NS_IMETHODIMP nsOutlookEditor::CanRedo(PRBool *isEnabled, PRBool *canRedo)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void beginTransaction ()
+NS_IMETHODIMP nsOutlookEditor::BeginTransaction()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void endTransaction ()
+NS_IMETHODIMP nsOutlookEditor::EndTransaction()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void beginPlaceHolderTransaction (in nsIAtom name)
+NS_IMETHODIMP nsOutlookEditor::BeginPlaceHolderTransaction(nsIAtom *name)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void endPlaceHolderTransaction ()
+NS_IMETHODIMP nsOutlookEditor::EndPlaceHolderTransaction()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// boolean shouldTxnSetSelection ()
+NS_IMETHODIMP nsOutlookEditor::ShouldTxnSetSelection(PRBool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void setShouldTxnSetSelection (in boolean should)
+NS_IMETHODIMP nsOutlookEditor::SetShouldTxnSetSelection(PRBool should)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute nsIInlineSpellChecker inlineSpellChecker
+NS_IMETHODIMP nsOutlookEditor::GetInlineSpellChecker(PRBool autoCreate, nsIInlineSpellChecker * *aInlineSpellChecker)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookEditor::SyncRealTimeSpell()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookEditor::SetSpellcheckUserOverride(PRBool enable)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+PRBool
+nsOutlookEditor::IsModifiableNode(nsIDOMNode *aNode)
+{
+  return PR_TRUE;
+}
+// void cut ()
+NS_IMETHODIMP nsOutlookEditor::Cut()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// boolean canCut ()
+NS_IMETHODIMP nsOutlookEditor::CanCut(PRBool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void copy ()
+NS_IMETHODIMP nsOutlookEditor::Copy()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// boolean canCopy ()
+NS_IMETHODIMP nsOutlookEditor::CanCopy(PRBool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void paste (in long aSelectionType)
+NS_IMETHODIMP nsOutlookEditor::Paste(PRInt32 aSelectionType)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// boolean canPaste (in long aSelectionType)
+NS_IMETHODIMP nsOutlookEditor::CanPaste(PRInt32 aSelectionType, PRBool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+class nsITransferable;
+
+// void pasteTransferable(in nsITransferable aTransferable)
+NS_IMETHODIMP nsOutlookEditor::PasteTransferable(nsITransferable *aTransferable)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// boolean canPasteTransferable([optional] in nsITransferable aTransferable)
+NS_IMETHODIMP nsOutlookEditor::CanPasteTransferable(nsITransferable *aTransferable, PRBool *aCanPaste)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void selectAll ()
+NS_IMETHODIMP nsOutlookEditor::SelectAll()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void beginningOfDocument ()
+NS_IMETHODIMP nsOutlookEditor::BeginningOfDocument()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void endOfDocument ()
+NS_IMETHODIMP nsOutlookEditor::EndOfDocument()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// boolean canDrag (in nsIDOMEvent aEvent)
+NS_IMETHODIMP nsOutlookEditor::CanDrag(nsIDOMEvent *aEvent, PRBool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void doDrag (in nsIDOMEvent aEvent)
+NS_IMETHODIMP nsOutlookEditor::DoDrag(nsIDOMEvent *aEvent)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void insertFromDrop (in nsIDOMEvent aEvent)
+NS_IMETHODIMP nsOutlookEditor::InsertFromDrop(nsIDOMEvent *aEvent)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void setAttribute (in nsIDOMElement aElement, in AString attributestr, in AString attvalue)
+NS_IMETHODIMP nsOutlookEditor::SetAttribute(nsIDOMElement *aElement, const nsAString & attributestr, const nsAString & attvalue)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* boolean getAttributeValue (in nsIDOMElement aElement, in AString attributestr, out AString resultValue); */
+NS_IMETHODIMP nsOutlookEditor::GetAttributeValue(nsIDOMElement *aElement, const nsAString & attributestr, nsAString & resultValue, PRBool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* void removeAttribute (in nsIDOMElement aElement, in AString aAttribute); */
+NS_IMETHODIMP nsOutlookEditor::RemoveAttribute(nsIDOMElement *aElement, const nsAString & aAttribute)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* void cloneAttribute (in AString aAttribute, in nsIDOMNode aDestNode, in nsIDOMNode aSourceNode); */
+NS_IMETHODIMP nsOutlookEditor::CloneAttribute(const nsAString & aAttribute, nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* void cloneAttributes (in nsIDOMNode destNode, in nsIDOMNode sourceNode); */
+NS_IMETHODIMP nsOutlookEditor::CloneAttributes(nsIDOMNode *destNode, nsIDOMNode *sourceNode)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* nsIDOMNode createNode (in AString tag, in nsIDOMNode parent, in long position); */
+NS_IMETHODIMP nsOutlookEditor::CreateNode(const nsAString & tag, nsIDOMNode *parent, PRInt32 position, nsIDOMNode **_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void insertNode (in nsIDOMNode node, in nsIDOMNode parent, in long aPosition)
+NS_IMETHODIMP nsOutlookEditor::InsertNode(nsIDOMNode *node, nsIDOMNode *parent, PRInt32 aPosition)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void splitNode (in nsIDOMNode existingRightNode, in long offset, out nsIDOMNode newLeftNode)
+NS_IMETHODIMP nsOutlookEditor::SplitNode(nsIDOMNode *existingRightNode, PRInt32 offset, nsIDOMNode **newLeftNode)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void joinNodes (in nsIDOMNode leftNode, in nsIDOMNode rightNode, in nsIDOMNode parent)
+NS_IMETHODIMP nsOutlookEditor::JoinNodes(nsIDOMNode *leftNode, nsIDOMNode *rightNode, nsIDOMNode *parent)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void deleteNode (in nsIDOMNode child)
+NS_IMETHODIMP nsOutlookEditor::DeleteNode(nsIDOMNode *child)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void markNodeDirty (in nsIDOMNode node)
+NS_IMETHODIMP nsOutlookEditor::MarkNodeDirty(nsIDOMNode *node)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void switchTextDirection ()
+NS_IMETHODIMP nsOutlookEditor::SwitchTextDirection()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// AString outputToString (in AString formatType, in unsigned long flags)
+NS_IMETHODIMP
+nsOutlookEditor::OutputToString(const nsAString & formatType,
+                                PRUint32 flags, nsAString & _retval)
+{
+  _retval.Assign(m_body);
+  return NS_OK;
+}
+
+// void outputToStream (in nsIOutputStream aStream, in AString formatType, in ACString charsetOverride, in unsigned long flags)
+
+NS_IMETHODIMP nsOutlookEditor::OutputToStream(nsIOutputStream *aStream,
+                                              const nsAString &formatType,
+                                              const nsACString &charsetOverride,
+                                              PRUint32 flags)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void addEditorObserver (in nsIEditorObserver observer)
+NS_IMETHODIMP nsOutlookEditor::AddEditorObserver(nsIEditorObserver *observer)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void removeEditorObserver (in nsIEditorObserver observer)
+NS_IMETHODIMP nsOutlookEditor::RemoveEditorObserver(nsIEditorObserver *observer)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void addEditActionListener (in nsIEditActionListener listener)
+NS_IMETHODIMP nsOutlookEditor::AddEditActionListener(nsIEditActionListener *listener)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void removeEditActionListener (in nsIEditActionListener listener)
+NS_IMETHODIMP nsOutlookEditor::RemoveEditActionListener(nsIEditActionListener *listener)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void addDocumentStateListener (in nsIDocumentStateListener listener)
+NS_IMETHODIMP
+nsOutlookEditor::AddDocumentStateListener(nsIDocumentStateListener *listener)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void removeDocumentStateListener (in nsIDocumentStateListener listener)
+NS_IMETHODIMP
+nsOutlookEditor::RemoveDocumentStateListener(nsIDocumentStateListener *listener)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void dumpContentTree ()
+NS_IMETHODIMP nsOutlookEditor::DumpContentTree()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void debugDumpContent ()
+NS_IMETHODIMP nsOutlookEditor::DebugDumpContent()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void debugUnitTests (out long outNumTests, out long outNumTestsFailed)
+NS_IMETHODIMP nsOutlookEditor::DebugUnitTests(PRInt32 *outNumTests,
+                                              PRInt32 *outNumTestsFailed)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookEditor::GetLastKeypressEventTrusted(PRBool *aResult)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void pasteAsQuotation (in long aSelectionType)
+NS_IMETHODIMP nsOutlookEditor::PasteAsQuotation(PRInt32 aSelectionType)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsIDOMNode insertAsQuotation (in AString aQuotedText)
+NS_IMETHODIMP nsOutlookEditor::InsertAsQuotation(const nsAString &aQuotedText,
+                                                 nsIDOMNode **_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void insertTextWithQuotations (in DOMString aStringToInsert)
+NS_IMETHODIMP
+nsOutlookEditor::InsertTextWithQuotations(const nsAString & aStringToInsert)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void pasteAsCitedQuotation (in AString aCitation, in long aSelectionType)
+NS_IMETHODIMP
+nsOutlookEditor::PasteAsCitedQuotation(const nsAString & aCitation,
+                                       PRInt32 aSelectionType)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsIDOMNode insertAsCitedQuotation (in AString aQuotedText, in AString aCitation, in boolean aInsertHTML)
+NS_IMETHODIMP
+nsOutlookEditor::InsertAsCitedQuotation(const nsAString &aQuotedText,
+                                        const nsAString & aCitation,
+                                        PRBool aInsertHTML,
+                                        nsIDOMNode **_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void rewrap (in boolean aRespectNewlines)
+NS_IMETHODIMP nsOutlookEditor::Rewrap(PRBool aRespectNewlines)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void stripCites ()
+NS_IMETHODIMP nsOutlookEditor::StripCites()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsISupportsArray getEmbeddedObjects ()
+NS_IMETHODIMP nsOutlookEditor::GetEmbeddedObjects(nsISupportsArray ** aNodeList)
+{
+  NS_ENSURE_ARG_POINTER(aNodeList);
+
+  NS_IF_ADDREF(*aNodeList = m_EmbeddedObjectList);
+  return (m_EmbeddedObjectList == nsnull) ? NS_ERROR_NULL_POINTER : NS_OK;
+}
+
+nsresult nsOutlookEditor::AddEmbeddedImage(nsIURI *uri, const wchar_t* cid,
+                                           const wchar_t *name)
+{
+  // Check to see if we were already called
+  if (!m_EmbeddedObjectList) {
+    // Create array in m_EmbeddedObjectList
+    nsresult rv = NS_NewISupportsArray(getter_AddRefs(m_EmbeddedObjectList) );
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // Create the embedded image node
+  nsCOMPtr<nsIDOMHTMLImageElement> imageNode =
+    new nsOutlookHTMLImageElement(this, uri, cid, name);
+
+  // Append the embedded image node to the list
+  m_EmbeddedObjectList->AppendElement(imageNode);
+
+  return NS_OK;
+}
+
+PRUint32 nsOutlookEditor::EmbeddedObjectsCount() const
+{
+  if (!m_EmbeddedObjectList)
+    return 0;
+  PRUint32 res;
+  nsresult rv = m_EmbeddedObjectList->Count(&res);
+  return (NS_FAILED(rv)) ? 0 : res;
+}
+
+nsresult nsOutlookEditor::GetCids(PRUint32 embedIndex, nsACString& origCid,
+                                  nsACString& newCid) const
+{
+  if (!m_EmbeddedObjectList)
+    return NS_ERROR_FAILURE;
+  nsCOMPtr<nsOutlookHTMLImageElement> node;
+  nsresult rv = m_EmbeddedObjectList->QueryElementAt(
+    embedIndex, NS_GET_IID(nsOutlookHTMLImageElement), getter_AddRefs(node));
+  if (node) {
+    if (!node->NewCid())
+      return NS_ERROR_FAILURE; // no need to replace anything!
+    LossyCopyUTF16toASCII(node->OrigCid(), origCid);
+    LossyCopyUTF16toASCII(node->NewCid(), newCid);
+  }
+  return rv;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+nsOutlookHTMLImageElement::nsOutlookHTMLImageElement
+  (nsOutlookEditor *pEditor, nsIURI *uri, const wchar_t *cid, const wchar_t *name)
+  : m_pEditor(pEditor), m_name(name), m_cid_orig(cid)
+{
+  // Get the URL for the embedded image
+  nsCString embeddedImageURL;
+  uri->GetSpec(embeddedImageURL);
+  CopyASCIItoUTF16(embeddedImageURL, m_src);
+
+  // The cid that is passed here is not prepended with "cid:", so if it
+  // becomes important that it is, we'd need to prepend it here.
+}
+
+nsOutlookHTMLImageElement::~nsOutlookHTMLImageElement()
+{
+}
+
+// readonly attribute DOMString nodeName
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetNodeName(nsAString & aNodeName)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute DOMString nodeValue
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetNodeValue(nsAString & aNodeValue)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetNodeValue(const nsAString & aNodeValue)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute unsigned short nodeType
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetNodeType(PRUint16 *aNodeType)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute nsIDOMNode parentNode
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetParentNode(nsIDOMNode * *aParentNode)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute nsIDOMNodeList childNodes
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetChildNodes(nsIDOMNodeList * *aChildNodes)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute nsIDOMNode firstChild
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetFirstChild(nsIDOMNode * *aFirstChild)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute nsIDOMNode lastChild
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetLastChild(nsIDOMNode * *aLastChild)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute nsIDOMNode previousSibling
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetPreviousSibling(nsIDOMNode * *aPreviousSibling)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute nsIDOMNode nextSibling
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetNextSibling(nsIDOMNode * *aNextSibling)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute nsIDOMNamedNodeMap attributes
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetAttributes(nsIDOMNamedNodeMap * *aAttributes)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute nsIDOMDocument ownerDocument
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetOwnerDocument(nsIDOMDocument * *aOwnerDocument)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsIDOMNode insertBefore (in nsIDOMNode newChild, in nsIDOMNode refChild)  raises (DOMException)
+NS_IMETHODIMP nsOutlookHTMLImageElement::InsertBefore(nsIDOMNode *newChild, nsIDOMNode *refChild, nsIDOMNode **_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsIDOMNode replaceChild (in nsIDOMNode newChild, in nsIDOMNode oldChild)  raises (DOMException)
+NS_IMETHODIMP nsOutlookHTMLImageElement::ReplaceChild(nsIDOMNode *newChild, nsIDOMNode *oldChild, nsIDOMNode **_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsIDOMNode removeChild (in nsIDOMNode oldChild)  raises (DOMException)
+NS_IMETHODIMP nsOutlookHTMLImageElement::RemoveChild(nsIDOMNode *oldChild, nsIDOMNode **_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsIDOMNode appendChild (in nsIDOMNode newChild)  raises (DOMException)
+NS_IMETHODIMP nsOutlookHTMLImageElement::AppendChild(nsIDOMNode *newChild, nsIDOMNode **_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// boolean hasChildNodes ()
+NS_IMETHODIMP nsOutlookHTMLImageElement::HasChildNodes(PRBool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsIDOMNode cloneNode (in boolean deep)
+NS_IMETHODIMP nsOutlookHTMLImageElement::CloneNode(PRBool deep, nsIDOMNode **_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void normalize ()
+NS_IMETHODIMP nsOutlookHTMLImageElement::Normalize()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// boolean isSupported (in DOMString feature, in DOMString version)
+NS_IMETHODIMP nsOutlookHTMLImageElement::IsSupported(const nsAString & feature, const nsAString & version, PRBool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute DOMString namespaceURI
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetNamespaceURI(nsAString & aNamespaceURI)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute DOMString prefix
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetPrefix(nsAString & aPrefix)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute DOMString localName
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetLocalName(nsAString & aLocalName)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// boolean hasAttributes ()
+NS_IMETHODIMP nsOutlookHTMLImageElement::HasAttributes(PRBool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute DOMString tagName
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetTagName(nsAString & aTagName)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// DOMString getAttribute (in DOMString name)
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetAttribute(const nsAString & name, nsAString & _retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void setAttribute (in DOMString name, in DOMString value)  raises (DOMException)
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetAttribute(const nsAString & name, const nsAString & value)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void removeAttribute (in DOMString name)  raises (DOMException)
+NS_IMETHODIMP nsOutlookHTMLImageElement::RemoveAttribute(const nsAString & name)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsIDOMAttr getAttributeNode (in DOMString name)
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetAttributeNode(const nsAString & name, nsIDOMAttr **_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsIDOMAttr setAttributeNode (in nsIDOMAttr newAttr)  raises (DOMException)
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetAttributeNode(nsIDOMAttr *newAttr, nsIDOMAttr **_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsIDOMAttr removeAttributeNode (in nsIDOMAttr oldAttr)  raises (DOMException)
+NS_IMETHODIMP nsOutlookHTMLImageElement::RemoveAttributeNode(nsIDOMAttr *oldAttr, nsIDOMAttr **_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsIDOMNodeList getElementsByTagName (in DOMString name)
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetElementsByTagName(const nsAString & name, nsIDOMNodeList **_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// DOMString getAttributeNS (in DOMString namespaceURI, in DOMString localName)
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetAttributeNS(const nsAString & namespaceURI, const nsAString & localName, nsAString & _retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void setAttributeNS (in DOMString namespaceURI, in DOMString qualifiedName, in DOMString value)  raises (DOMException)
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetAttributeNS(const nsAString & namespaceURI, const nsAString & qualifiedName, const nsAString & value)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// void removeAttributeNS (in DOMString namespaceURI, in DOMString localName)  raises (DOMException)
+NS_IMETHODIMP nsOutlookHTMLImageElement::RemoveAttributeNS(const nsAString & namespaceURI, const nsAString & localName)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsIDOMAttr getAttributeNodeNS (in DOMString namespaceURI, in DOMString localName)
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetAttributeNodeNS(const nsAString & namespaceURI, const nsAString & localName, nsIDOMAttr **_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsIDOMAttr setAttributeNodeNS (in nsIDOMAttr newAttr)  raises (DOMException)
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetAttributeNodeNS(nsIDOMAttr *newAttr, nsIDOMAttr **_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsIDOMNodeList getElementsByTagNameNS (in DOMString namespaceURI, in DOMString localName)
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetElementsByTagNameNS(const nsAString & namespaceURI, const nsAString & localName, nsIDOMNodeList **_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// boolean hasAttribute (in DOMString name)
+NS_IMETHODIMP nsOutlookHTMLImageElement::HasAttribute(const nsAString & name, PRBool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// boolean hasAttributeNS (in DOMString namespaceURI, in DOMString localName)
+NS_IMETHODIMP nsOutlookHTMLImageElement::HasAttributeNS(const nsAString & namespaceURI, const nsAString & localName, PRBool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute DOMString id
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetId(nsAString & aId)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetId(const nsAString & aId)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute DOMString title
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetTitle(nsAString & aTitle)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetTitle(const nsAString & aTitle)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute DOMString lang
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetLang(nsAString & aLang)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetLang(const nsAString & aLang)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute DOMString dir
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetDir(nsAString & aDir)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetDir(const nsAString & aDir)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute DOMString className
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetClassName(nsAString & aClassName)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetClassName(const nsAString & aClassName)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute DOMString name
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetName(nsAString & aName)
+{
+  aName.Assign(m_name);
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetName(const nsAString & aName)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute DOMString align
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetAlign(nsAString & aAlign)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetAlign(const nsAString & aAlign)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute DOMString alt
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetAlt(nsAString & aAlt)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetAlt(const nsAString & aAlt)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute DOMString lowsrc
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetLowsrc(nsAString &aResult)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetLowsrc(const nsAString &aResult)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute DOMString complete
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetComplete(PRBool *aResult)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute long naturalWidth
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetNaturalWidth(PRInt32 *aResult)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute long naturalHeight
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetNaturalHeight(PRInt32 *aResult)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// readonly attribute long x
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetX(PRInt32 *aResult)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+// readonly attribute long y
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetY(PRInt32 *aResult)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute DOMString border
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetBorder(nsAString & aBorder)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetBorder(const nsAString & aBorder)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute long height
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetHeight(PRInt32 *aHeight)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetHeight(PRInt32 aHeight)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute long hspace
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetHspace(PRInt32 *aHspace)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetHspace(PRInt32 aHspace)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute boolean isMap
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetIsMap(PRBool *aIsMap)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetIsMap(PRBool aIsMap)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute DOMString longDesc
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetLongDesc(nsAString & aLongDesc)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetLongDesc(const nsAString & aLongDesc)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+// attribute DOMString src
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetSrc(nsAString & aSrc)
+{
+  aSrc = m_src;
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetSrc(const nsAString & aSrc)
+{
+  // nsEudoraHTMLImageElement calls UpdateEmbeddedImageReference(m_cid, aSrc)
+  // on the editor here, but our editor doen't implement it.
+
+  // The nsMsgNend::ProcessMultipartRelated seems to call SetSrc twice.
+  // I'm not sure if I need to do it second time.
+  if (m_cid_new.IsEmpty()) 
+    m_cid_new.Assign(aSrc.Data()+4, aSrc.Length()-4); // strip the "cid:"
+
+  return NS_OK;
+}
+
+// attribute DOMString useMap
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetUseMap(nsAString & aUseMap)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetUseMap(const nsAString & aUseMap)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute long vspace
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetVspace(PRInt32 *aVspace)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetVspace(PRInt32 aVspace)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// attribute long width
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetWidth(PRInt32 *aWidth)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetWidth(PRInt32 aWidth)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::GetAccessKey(nsAString &aAccessKey)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::SetAccessKey(const nsAString &aAccessKey)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::Blur()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::Focus()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsOutlookHTMLImageElement::Click()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
new file mode 100644
--- /dev/null
+++ b/mailnews/import/outlook/src/nsOutlookEditor.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: MPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Mozilla Public License Version
+* 1.1 (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+* http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is qualcomm.com code.
+*
+* The Initial Developer of the Original Code is
+* QUALCOMM, Inc.
+* Portions created by the Initial Developer are Copyright (C) 2007
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+*   Author: Geoffrey C. Wenger (gwenger@qualcomm.com)
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the MPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the MPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+
+#include "nscore.h"
+#include "nsIEditor.h"
+#include "nsIEditorMailSupport.h"
+#include "nsIDOMHTMLImageElement.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsIFile.h"
+
+class nsOutlookEditor : public nsIEditor, public nsIEditorMailSupport
+{
+  public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIEDITOR
+    NS_DECL_NSIEDITORMAILSUPPORT
+
+    nsOutlookEditor(const wchar_t * body);
+
+    nsresult AddEmbeddedImage(nsIURI *uri, const wchar_t *cid, const wchar_t *name);
+
+    inline PRBool HasEmbeddedContent() const { return (m_EmbeddedObjectList != nsnull); }
+    PRUint32 EmbeddedObjectsCount() const;
+    nsresult GetCids(PRUint32 embedIndex, nsACString& origCid, nsACString& newCid) const;
+
+    ~nsOutlookEditor();
+
+  private:
+//    nsString                    m_body;
+    const wchar_t*              m_body;
+    nsCOMPtr<nsISupportsArray>  m_EmbeddedObjectList; // it's initialized when AddEmbeddedImage is called
+};
+
+#define NS_OUTLOOKHTMLIMAGEELEMENT_IID_STR "5fb3c060-20b5-11e0-b2ba-0002a5d5c51b"
+
+#define NS_OUTLOOKHTMLIMAGEELEMENT_IID \
+  {0x5fb3c060, 0x20b5, 0x11e0, \
+    { 0xb2, 0xba, 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b }}
+
+class nsOutlookHTMLImageElement : public nsIDOMHTMLImageElement
+{
+  public:
+    NS_DECLARE_STATIC_IID_ACCESSOR(NS_OUTLOOKHTMLIMAGEELEMENT_IID)
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIDOMNODE
+    NS_DECL_NSIDOMELEMENT
+    NS_DECL_NSIDOMHTMLELEMENT
+    NS_DECL_NSIDOMHTMLIMAGEELEMENT
+
+    nsOutlookHTMLImageElement(nsOutlookEditor *pEditor, nsIURI *uri, const wchar_t *cid, const wchar_t *name);
+    inline const wchar_t* OrigCid() const { return m_cid_orig.get(); }
+    inline const wchar_t* NewCid() const { return m_cid_new.IsEmpty() ? 0 : m_cid_new.get(); }
+
+  private:
+    ~nsOutlookHTMLImageElement();
+
+    nsCOMPtr<nsIEditor>         m_pEditor;
+    nsString                    m_src;
+    nsString                    m_cid_orig;
+    nsString                    m_cid_new;
+    nsString                    m_name;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsOutlookHTMLImageElement, NS_OUTLOOKHTMLIMAGEELEMENT_IID)
--- a/mailnews/import/outlook/src/nsOutlookMail.cpp
+++ b/mailnews/import/outlook/src/nsOutlookMail.cpp
@@ -46,17 +46,16 @@
 #include "nsIImportService.h"
 #include "nsIImportFieldMap.h"
 #include "nsIImportMailboxDescriptor.h"
 #include "nsIImportABDescriptor.h"
 #include "nsReadableUtils.h"
 #include "nsOutlookStringBundle.h"
 #include "nsABBaseCID.h"
 #include "nsIAbCard.h"
-#include "nsIAddrDatabase.h"
 #include "mdb.h"
 #include "OutlookDebugLog.h"
 #include "nsOutlookMail.h"
 #include "nsUnicharUtils.h"
 #include "nsMsgUtils.h"
 #include "nsIOutputStream.h"
 #include "nsMsgI18N.h"
 #include "nsNetUtil.h"
@@ -132,17 +131,17 @@ nsOutlookMail::nsOutlookMail()
   m_gotAddresses = PR_FALSE;
   m_gotFolders = PR_FALSE;
   m_haveMapi = CMapiApi::LoadMapi();
   m_lpMdb = NULL;
 }
 
 nsOutlookMail::~nsOutlookMail()
 {
-  EmptyAttachments();
+//  EmptyAttachments();
 }
 
 nsresult nsOutlookMail::GetMailFolders( nsISupportsArray **pArray)
 {
   if (!m_haveMapi) {
     IMPORT_LOG0( "GetMailFolders called before Mapi is initialized\n");
     return( NS_ERROR_FAILURE);
   }
@@ -344,45 +343,34 @@ void nsOutlookMail::OpenMessageStore( CM
       }
     }
     else {
       IMPORT_LOG0( "*** Error importing a folder without a valid message store\n");
     }
   }
 }
 
-void nsOutlookMail::SetDefaultContentType(CMapiMessage &msg, nsCString &cType)
-{
-  cType.Truncate();
-
-  // MAPI doesn't seem to return the entire body data (ie, multiple parts) for
-  // content type "multipart/alternative", instead it only returns the body data
-  // for a particular part. For this reason we'll need to set the content type
-  // here. Same thing when conten type is not being set at all.
-  if (msg.GetMimeContentLen())
-  {
-    // If content type is not multipart/alternative or mixed or related, return.
-    // for a multipart alternative with attachments, we get multipart mixed!
-    if (PL_strcasecmp(msg.GetMimeContent(), "multipart/alternative")
-      && PL_strcasecmp(msg.GetMimeContent(), "multipart/mixed")
-      && PL_strcasecmp(msg.GetMimeContent(), "multipart/related"))
-      return;
-
-    // For multipart/alternative, if no body or boundary,
-    // or boundary is found in body then return.
-    const char *body = msg.GetBody();
-    const char *boundary = msg.GetMimeBoundary();
-    if (!body || !boundary || strstr(body, boundary))
-      return;
-  }
-
-  // Now default the content type to text/html or text/plain
-  // depending whether or not the body data is html.
-  cType = msg.BodyIsHtml() ? "text/html" : "text/plain";
-}
+// Roles and responsibilities:
+// nsOutlookMail
+//   - Connect to Outlook
+//   - Enumerate the mailboxes
+//   - Iterate the mailboxes
+//   - For each mail, create one nsOutlookCompose object
+//   - For each mail, create one CMapiMessage object
+//
+// nsOutlookCompose
+//   - Establich a TB session
+//   - Connect to all required services
+//   - Perform the composition of the RC822 document from the data gathered by CMapiMessage
+//   - Save the composed message to the TB mailbox
+//   - Ensure the proper cleanup
+//
+// CMapiMessage
+//   - Encapsulate the MAPI message interface
+//   - Gather the information required to (re)compose the message
 
 nsresult nsOutlookMail::ImportMailbox( PRUint32 *pDoneSoFar, PRBool *pAbort, PRInt32 index, const PRUnichar *pName, nsIFile *pDest, PRInt32 *pMsgCount)
 {
   if ((index < 0) || (index >= m_folderList.GetSize())) {
     IMPORT_LOG0( "*** Bad mailbox identifier, unable to import\n");
     *pAbort = PR_TRUE;
     return( NS_ERROR_FAILURE);
   }
@@ -400,35 +388,26 @@ nsresult nsOutlookMail::ImportMailbox( P
     return( NS_ERROR_FAILURE);
   }
 
   if (pFolder->IsStore())
     return( NS_OK);
 
   nsresult  rv;
 
-  nsOutlookCompose    compose;
-  SimpleBufferTonyRCopiedTwice      copy;
-
-  copy.Allocate( kCopyBufferSize);
-
   // now what?
   CMapiFolderContents    contents( m_lpMdb, pFolder->GetCBEntryID(), pFolder->GetEntryID());
 
   BOOL    done = FALSE;
   ULONG    cbEid;
   LPENTRYID  lpEid;
   ULONG    oType;
   LPMESSAGE  lpMsg = nsnull;
-  int      attachCount;
   ULONG    totalCount;
   PRFloat64  doneCalc;
-  nsCString  fromLine;
-  int      fromLen;
-  PRBool    lostAttach = PR_FALSE;
 
   nsCOMPtr<nsIOutputStream> destOutputStream;
   rv = MsgNewBufferedFileOutputStream(getter_AddRefs(destOutputStream), pDest, -1, 0600);
   NS_ENSURE_SUCCESS(rv, rv);
 
   while (!done) {
     if (!contents.GetNext( &cbEid, &lpEid, &oType, &done)) {
       IMPORT_LOG1( "*** Error iterating mailbox: %S\n", pName);
@@ -446,386 +425,75 @@ nsresult nsOutlookMail::ImportMailbox( P
     }
 
     if (!done && (oType == MAPI_MESSAGE)) {
       if (!m_mapi.OpenMdbEntry( m_lpMdb, cbEid, lpEid, (LPUNKNOWN *) &lpMsg)) {
         IMPORT_LOG1( "*** Error opening messages in mailbox: %S\n", pName);
         return( NS_ERROR_FAILURE);
       }
 
-      CMapiMessage  msg( lpMsg);
-
-      BOOL bResult = msg.FetchHeaders();
-      if (bResult)
-        bResult = msg.FetchBody();
-      if (bResult)
-        fromLine = msg.GetFromLine( fromLen);
-
-      attachCount = msg.CountAttachments();
-      BuildAttachments( msg, attachCount);
-
-      if (!bResult) {
-        IMPORT_LOG1( "*** Error reading message from mailbox: %S\n", pName);
-        return( NS_ERROR_FAILURE);
-      }
-
-      // --------------------------------------------------------------
-      compose.SetBody( msg.GetBody());
-
-      // Need to convert all headers to unicode (for i18n).
-      // Init header here since 'composes' is used for all msgs.
-      compose.SetHeaders("");
-
-      nsAutoString newheader;
-      nsCAutoString tempCStr(msg.GetHeaders(), msg.GetHeaderLen());
-
-      rv = nsMsgI18NConvertToUnicode(nsMsgI18NFileSystemCharset(),
-        tempCStr, newheader);
-      NS_ASSERTION(NS_SUCCEEDED(rv), "failed to convert headers to utf8");
-      if (NS_SUCCEEDED(rv))
-        compose.SetHeaders(NS_ConvertUTF16toUTF8(newheader).get());
-
-      compose.SetAttachments( &m_attachments);
-
       // See if it's a drafts folder. Outlook doesn't allow drafts
       // folder to be configured so it's ok to hard code it here.
       nsAutoString folderName(pName);
       nsMsgDeliverMode mode = nsIMsgSend::nsMsgDeliverNow;
       mode = nsIMsgSend::nsMsgSaveAsDraft;
       if ( folderName.LowerCaseEqualsLiteral("drafts") )
         mode = nsIMsgSend::nsMsgSaveAsDraft;
 
-      /*
-      If I can't get no headers,
-      I can't get no satisfaction
-      */
-      if (msg.GetHeaderLen()) {
-        nsCAutoString cType;
-        SetDefaultContentType(msg, cType);
-        nsCOMPtr<nsIFile> compositionFile;
-        rv = compose.SendTheMessage(mode, cType, getter_AddRefs(compositionFile));
-        if (NS_SUCCEEDED( rv)) {
-          rv = compose.CopyComposedMessage( fromLine, compositionFile, destOutputStream, copy);
-          DeleteFile( compositionFile);
-          if (NS_FAILED( rv)) {
-            IMPORT_LOG0( "*** Error copying composed message to destination mailbox\n");
-            return( rv);
-          }
-          (*pMsgCount)++;
-        }
+      rv = ImportMessage(lpMsg, destOutputStream, mode);
+      if (NS_SUCCEEDED( rv)) // No errors & really imported
+        (*pMsgCount)++;
+      else {
+        IMPORT_LOG1( "*** Error reading message from mailbox: %S\n", pName);
       }
-      else
-        rv = NS_OK;
-
-      // The following code to write msg to folder when compose.SendTheMessage() fails is commented
-      // out for now because the code doesn't handle attachments and users will complain anyway so
-      // until we fix the code to handle all kinds of msgs correctly we should not even make users
-      // think that all msgs are imported ok. This will also help users to identify which msgs are
-      // not imported and help to debug the problem.
-#if 0
-      if (NS_FAILED( rv)) {
-
-        /* NS_PRECONDITION( FALSE, "Manual breakpoint"); */
-
-        IMPORT_LOG1( "Message #%d failed.\n", (int) (*pMsgCount));
-        DumpAttachments();
-
-        // --------------------------------------------------------------
-
-        // This is the OLD way of writing out the message which uses
-        // all kinds of crufty old crap for attachments.
-        // Since we now use Compose to send attachments,
-        // this is only fallback error stuff.
-
-        // Attachments get lost.
-
-        if (attachCount) {
-          lostAttach = PR_TRUE;
-          attachCount = 0;
-        }
-
-        BOOL needsTerminate = FALSE;
-        if (!WriteMessage( destOutputStream, &msg, attachCount, &needsTerminate)) {
-          IMPORT_LOG0( "*** Error writing message\n");
-          *pAbort = PR_TRUE;
-          return( NS_ERROR_FAILURE);
-        }
-
-        if (needsTerminate) {
-          if (!WriteMimeBoundary( destOutputStream, &msg, TRUE)) {
-            IMPORT_LOG0( "*** Error writing message mime boundary\n");
-            *pAbort = PR_TRUE;
-            return( NS_ERROR_FAILURE);
-          }
-        }
-      }
-#endif
-
-      // Just for YUCKS, let's try an extra endline
-      WriteData( destOutputStream, "\x0D\x0A", 2);
     }
   }
 
   return( NS_OK);
 }
 
-BOOL nsOutlookMail::WriteMessage( nsIOutputStream *pDest, CMapiMessage *pMsg, int& attachCount, BOOL *pTerminate)
+nsresult nsOutlookMail::ImportMessage( LPMESSAGE lpMsg, nsIOutputStream *pDest, nsMsgDeliverMode mode)
 {
-  BOOL    bResult = TRUE;
-  const char *pData;
-  int      len;
-  BOOL    checkStart = FALSE;
-
-  *pTerminate = FALSE;
-
-  pData = pMsg->GetFromLine( len);
-  if (pData) {
-    bResult = WriteData( pDest, pData, len);
-    checkStart = TRUE;
-  }
-
-  nsCOMPtr<nsIOutputStream> outStream = pDest;
-
-  pData = pMsg->GetHeaders( len);
-  if (pData && len) {
-    if (checkStart)
-      bResult = (EscapeFromSpaceLine(outStream, (char *)pData, pData+len) == NS_OK);
-    else
-      bResult = (EscapeFromSpaceLine(outStream, (char *)(pData+1), pData+len-1) == NS_OK);
-  }
-
-  // Do we need to add any mime headers???
-  //  Is the message multipart?
-  //    If so, then we are OK, but need to make sure we add mime
-  //    header info to the body of the message
-  //  If not AND we have attachments, then we need to add mime headers.
+  CMapiMessage  msg( lpMsg);
+  // If we wanted to skip messages that were downloaded in header only mode, we
+  // would return NS_ERROR_FAILURE if !msg.FullMessageDownloaded. However, we
+  // don't do this because it may cause seemingly wrong import results.
+  // A user will get less mails in his imported folder than were in the original folder,
+  // and this may make user feel like TB import is bad.
+  // In reality, the skipped messages are those that have not been downloaded yet, because
+  // they were downloaded in the "headers-only" mode. This is different from the case when
+  // the message is downloaded completely, but consists only of headers - in this case
+  // the message will be imported anyway.
 
-  BOOL needsMimeHeaders = pMsg->IsMultipart();
-  if (!needsMimeHeaders && attachCount) {
-    // if the message already has mime headers
-    // that aren't multipart then we are in trouble!
-    // in that case, ditch the attachments...  alternatively, we could
-    // massage the headers and replace the existing mime headers
-    // with our own but I think this case is likely not to occur.
-    if (pMsg->HasContentHeader())
-      attachCount = 0;
-    else {
-      if (bResult)
-        bResult = WriteMimeMsgHeader( pDest, pMsg);
-      needsMimeHeaders = TRUE;
-    }
-  }
-
-  if (bResult)
-    bResult = WriteStr( pDest, "\x0D\x0A");
+  if (!msg.ValidState())
+    return NS_ERROR_FAILURE;
 
-  if (needsMimeHeaders) {
-    if (bResult)
-      bResult = WriteStr( pDest, "This is a MIME formatted message.\x0D\x0A");
-    if (bResult)
-      bResult = WriteStr( pDest, "\x0D\x0A");
-    if (bResult)
-      bResult = WriteMimeBoundary( pDest, pMsg, FALSE);
-    if (pMsg->BodyIsHtml()) {
-      if (bResult)
-        bResult = WriteStr( pDest, "Content-type: text/html\x0D\x0A");
-    }
-    else {
-      if (bResult)
-        bResult = WriteStr( pDest, "Content-type: text/plain\x0D\x0A");
-    }
+  // I have to create a composer for each message, since it turns out that if we create
+  // one composer for several messages, the Send Proxy object that is shared between those messages
+  // isn't reset properly (at least in the current implementation), which leads to crash.
+  // If there's a proper way to reinitialize the Send Proxy object,
+  // then we could slightly optimize the send process.
+  nsOutlookCompose compose;
+  nsresult rv = compose.ProcessMessage(mode, msg, pDest);
 
-    if (bResult)
-      bResult = WriteStr( pDest, "\x0D\x0A");
-  }
+  // Just for YUCKS, let's try an extra endline
+  WriteData( pDest, "\x0D\x0A", 2);
 
-  pData = pMsg->GetBody( len);
-  if (pData && len) {
-    if (bResult)
-      bResult = (EscapeFromSpaceLine(outStream, (char *)pData, pData+len) == NS_OK);
-    if ((len < 2) || (pData[len - 1] != 0x0A) || (pData[len - 2] != 0x0D))
-      bResult = WriteStr( pDest, "\x0D\x0A");
-  }
-
-  *pTerminate = needsMimeHeaders;
-
-  return( bResult);
+  return rv;
 }
 
 BOOL nsOutlookMail::WriteData( nsIOutputStream *pDest, const char *pData, PRInt32 len)
 {
   PRUint32    written;
   nsresult rv = pDest->Write( pData, len, &written);
   if (NS_FAILED( rv) || (written != len))
     return( FALSE);
   return( TRUE);
 }
 
-BOOL nsOutlookMail::WriteStr( nsIOutputStream *pDest, const char *pStr)
-{
-  PRUint32 written;
-  PRInt32    len = strlen( pStr);
-
-  nsresult rv = pDest->Write( pStr, len, &written);
-  if (NS_FAILED( rv) || (written != len))
-    return( FALSE);
-  return( TRUE);
-}
-
-BOOL nsOutlookMail::WriteMimeMsgHeader( nsIOutputStream *pDest, CMapiMessage *pMsg)
-{
-  BOOL  bResult = TRUE;
-  if (!pMsg->HasMimeVersion())
-    bResult = WriteStr( pDest, "MIME-Version: 1.0\x0D\x0A");
-  pMsg->GenerateBoundary();
-  if (bResult)
-    bResult = WriteStr( pDest, "Content-type: multipart/mixed; boundary=\"");
-  if (bResult)
-    bResult = WriteStr( pDest, pMsg->GetMimeBoundary());
-  if (bResult)
-    bResult = WriteStr( pDest, "\"\x0D\x0A");
-
-  return( bResult);
-}
-
-BOOL nsOutlookMail::WriteMimeBoundary( nsIOutputStream *pDest, CMapiMessage *pMsg, BOOL terminate)
-{
-  BOOL  bResult = WriteStr( pDest, "--");
-  if (bResult)
-    bResult = WriteStr( pDest, pMsg->GetMimeBoundary());
-  if (terminate && bResult)
-    bResult = WriteStr( pDest, "--");
-  if (bResult)
-    bResult = WriteStr( pDest, "\x0D\x0A");
-
-  return( bResult);
-}
-
-nsresult nsOutlookMail::DeleteFile( nsIFile *pFile)
-{
-  return pFile->Remove(PR_FALSE);
-}
-
-void nsOutlookMail::EmptyAttachments( void)
-{
-  PRBool  exists;
-  PRBool  isFile;
-  PRInt32 max = m_attachments.Count();
-  OutlookAttachment *  pAttach;
-  for (PRInt32 i = 0; i < max; i++) {
-    pAttach = (OutlookAttachment *) m_attachments.ElementAt( i);
-    if (pAttach) {
-      if (pAttach->pAttachment) {
-        exists = PR_FALSE;
-        isFile = PR_FALSE;
-        pAttach->pAttachment->Exists( &exists);
-        if (exists)
-          pAttach->pAttachment->IsFile( &isFile);
-        if (exists && isFile)
-          DeleteFile( pAttach->pAttachment);
-        pAttach->pAttachment = nsnull;
-      }
-      NS_Free( pAttach->description);
-      NS_Free( pAttach->mimeType);
-      delete pAttach;
-    }
-  }
-
-  m_attachments.Clear();
-}
-
-void nsOutlookMail::BuildAttachments( CMapiMessage& msg, int count)
-{
-  EmptyAttachments();
-  if (count) {
-    nsresult rv;
-    nsCOMPtr <nsILocalFile>  pFile;
-    for (int i = 0; i < count; i++) {
-      if (!msg.GetAttachmentInfo( i)) {
-        IMPORT_LOG1( "*** Error getting attachment info for #%d\n", i);
-      }
-
-      pFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
-      if (NS_FAILED( rv) || !pFile) {
-        IMPORT_LOG0( "*** Error creating file spec for attachment\n");
-      }
-      else {
-        if (msg.GetAttachFileLoc( pFile)) {
-          PRBool isFile = PR_FALSE;
-          PRBool exists = PR_FALSE;
-          pFile->Exists( &exists);
-          pFile->IsFile( &isFile);
-
-          if (!exists || !isFile) {
-            IMPORT_LOG0( "Attachment file does not exist\n");
-          }
-          else {
-            // We have a file spec, now get the other info
-            OutlookAttachment *a = new OutlookAttachment;
-            a->mimeType = strdup( msg.GetMimeType());
-            // Init description here so that we cacn tell
-            // if defaul tattacchment is needed later.
-            a->description = nsnull;
-
-            const char *fileName = msg.GetFileName();
-            if (fileName && fileName[0]) {
-              // Convert description to unicode.
-              nsAutoString description;
-              rv = nsMsgI18NConvertToUnicode(nsMsgI18NFileSystemCharset(),
-                nsDependentCString(fileName), description);
-              NS_ASSERTION(NS_SUCCEEDED(rv), "failed to convert system string to unicode");
-              if (NS_SUCCEEDED(rv))
-                a->description = ToNewUTF8String(description);
-            }
-
-            // If no description use "Attachment i" format.
-            if (!a->description) {
-              nsCAutoString  str("Attachment ");
-              str.AppendInt( (PRInt32) i);
-              a->description = ToNewCString( str);
-            }
-
-            a->pAttachment = pFile;
-            m_attachments.AppendElement( a);
-          }
-        }
-      }
-    }
-  }
-}
-
-void nsOutlookMail::DumpAttachments( void)
-{
-#ifdef IMPORT_DEBUG
-  PRInt32    count = 0;
-  count = m_attachments.Count();
-  if (!count) {
-    IMPORT_LOG0( "*** No Attachments\n");
-    return;
-  }
-  IMPORT_LOG1( "#%d attachments\n", (int) count);
-
-  OutlookAttachment *  pAttach;
-
-  for (PRInt32 i = 0; i < count; i++) {
-    IMPORT_LOG1( "\tAttachment #%d ---------------\n", (int) i);
-    pAttach = (OutlookAttachment *) m_attachments.ElementAt( i);
-    if (pAttach->mimeType)
-      IMPORT_LOG1( "\t\tMime type: %s\n", pAttach->mimeType);
-    if (pAttach->description)
-      IMPORT_LOG1( "\t\tDescription: %s\n", pAttach->description);
-    if (pAttach->pAttachment) {
-      nsCString  path;
-      pAttach->pAttachment->GetNativePath( path);
-      IMPORT_LOG1( "\t\tFile: %s\n", path.get());
-    }
-  }
-#endif
-}
-
 nsresult nsOutlookMail::ImportAddresses( PRUint32 *pCount, PRUint32 *pTotal, const PRUnichar *pName, PRUint32 id, nsIAddrDatabase *pDb, nsString& errors)
 {
   if (id >= (PRUint32)(m_addressList.GetSize())) {
     IMPORT_LOG0( "*** Bad address identifier, unable to import\n");
     return( NS_ERROR_FAILURE);
   }
 
   PRUint32  dummyCount = 0;
--- a/mailnews/import/outlook/src/nsOutlookMail.h
+++ b/mailnews/import/outlook/src/nsOutlookMail.h
@@ -40,57 +40,46 @@
 
 #include "nsISupportsArray.h"
 #include "nsString.h"
 #include "nsVoidArray.h"
 #include "nsOutlookCompose.h"
 #include "nsIFile.h"
 #include "MapiApi.h"
 #include "MapiMessage.h"
+#include "nsIAddrDatabase.h"
 
 class nsIAddrDatabase;
 class nsIImportFieldMap;
 
 class nsOutlookMail {
 public:
   nsOutlookMail();
   ~nsOutlookMail();
   
   nsresult GetMailFolders( nsISupportsArray **pArray);
   nsresult GetAddressBooks( nsISupportsArray **pArray);
   nsresult ImportMailbox( PRUint32 *pDoneSoFar, PRBool *pAbort, PRInt32 index, const PRUnichar *pName, nsIFile *pDest, PRInt32 *pMsgCount);
+  static nsresult ImportMessage( LPMESSAGE lpMsg, nsIOutputStream *destOutputStream, nsMsgDeliverMode mode);
   nsresult ImportAddresses( PRUint32 *pCount, PRUint32 *pTotal, const PRUnichar *pName, PRUint32 id, nsIAddrDatabase *pDb, nsString& errors);
-  
-  
 private:
   void  OpenMessageStore( CMapiFolder *pNextFolder);
-  BOOL  WriteData( nsIOutputStream *pDest, const char *pData, PRInt32 len);
-  BOOL  WriteMessage( nsIOutputStream *pDest, CMapiMessage *pMsg, int& attachCount, BOOL *pTerminate);
-  BOOL  WriteStr( nsIOutputStream *pDest, const char *pStr);
-  BOOL  WriteMimeMsgHeader( nsIOutputStream *pDest, CMapiMessage *pMsg);
-  BOOL  WriteMimeBoundary( nsIOutputStream *pDest, CMapiMessage *pMsg, BOOL terminate);
-  
-  nsresult  DeleteFile( nsIFile *pSpec);
-  void      EmptyAttachments( void);
-  void      BuildAttachments( CMapiMessage& msg, int count);
-  void      DumpAttachments( void);
+  static BOOL  WriteData( nsIOutputStream *pDest, const char *pData, PRInt32 len);
   
   PRBool    IsAddressBookNameUnique( nsString& name, nsString& list);
   void      MakeAddressBookNameUnique( nsString& name, nsString& list);
   void      SanitizeValue( nsString& val);
   void      SplitString( nsString& val1, nsString& val2);
   PRBool    BuildCard( const PRUnichar *pName, nsIAddrDatabase *pDb, nsIMdbRow *newRow, LPMAPIPROP pUser, nsIImportFieldMap *pFieldMap);
   nsresult  CreateList( const PRUnichar * pName, nsIAddrDatabase *pDb, LPMAPIPROP pUserList, nsIImportFieldMap *pFieldMap);
-  void SetDefaultContentType(CMapiMessage &msg, nsCString &cType);
   
 private:
   PRBool            m_gotFolders;
   PRBool            m_gotAddresses;
   PRBool            m_haveMapi;
   CMapiApi          m_mapi;
   CMapiFolderList   m_folderList;
   CMapiFolderList   m_addressList;
   CMapiFolderList   m_storeList;
   LPMDB             m_lpMdb;
-  nsVoidArray       m_attachments;
 };
 
 #endif /* nsOutlookMail_h___ */
new file mode 100644
--- /dev/null
+++ b/mailnews/import/outlook/src/rtfDecoder.cpp
@@ -0,0 +1,551 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code
+ *
+ * The Initial Developer of the Original Code is
+ * Mike Kaganski <mikekaganski@gmail.com>.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <locale>
+#include <stack>
+#include <map>
+#include <sstream>
+#include "Windows.h"
+#include "rtfDecoder.h"
+
+#define SIZEOF(x) (sizeof(x)/sizeof((x)[0]))
+
+inline int HexToInt(char ch)
+{
+  switch (ch) {
+  case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
+    return ch-'0';
+  case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+    return ch-'A'+10;
+  case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+    return ch-'a'+10;
+  default:
+    return 0;
+  }
+}
+
+inline int CharsetToCP(int charset)
+{
+  // We don't know the Code page for the commented out charsets.
+  switch (charset) {
+  case 0: return 1252; // ANSI
+  case 1: return 0;   // Default
+//case 2: return 42; // Symbol
+  case 2: return 1252; // Symbol
+  case 77: return 10000; // Mac Roman
+  case 78: return 10001; // Mac Shift Jis
+  case 79: return 10003; // Mac Hangul
+  case 80: return 10008; // Mac GB2312
+  case 81: return 10002; // Mac Big5
+//case 82: Mac Johab (old)
+  case 83: return 10005; // Mac Hebrew
+  case 84: return 10004; // Mac Arabic
+  case 85: return 10006; // Mac Greek
+  case 86: return 10081; // Mac Turkish
+  case 87: return 10021; // Mac Thai
+  case 88: return 10029; // Mac East Europe
+  case 89: return 10007; // Mac Russian
+  case 128: return 932; // Shift JIS
+  case 129: return 949; // Hangul
+  case 130: return 1361; // Johab
+  case 134: return 936; // GB2312
+  case 136: return 950; // Big5
+  case 161: return 1253; // Greek
+  case 162: return 1254; // Turkish
+  case 163: return 1258; // Vietnamese
+  case 177: return 1255; // Hebrew
+  case 178: return 1256; // Arabic
+//case 179: Arabic Traditional (old)
+//case 180: Arabic user (old)
+//case 181: Hebrew user (old)
+  case 186: return 1257; // Baltic
+  case 204: return 1251; // Russian
+  case 222: return 874; // Thai
+  case 238: return 1250; // Eastern European
+  case 254: return 437; // PC 437
+  case 255: return 850; // OEM
+  default: return CP_ACP;
+  }
+}
+
+struct FontInfo {
+  enum Options {has_fcharset = 0x0001,
+                has_cpg      = 0x0002};
+  unsigned int options;
+  int fcharset;
+  unsigned int cpg;
+  FontInfo() : options(0), fcharset(0), cpg(0xFFFFFFFF) {}
+  unsigned int Codepage()
+  {
+    if (options & has_cpg)
+      return cpg;
+    else if (options & has_fcharset)
+      return CharsetToCP(fcharset);
+    else return 0xFFFFFFFF;
+  }
+};
+typedef std::map<int, FontInfo> Fonttbl;
+
+struct LocalState {
+  bool fonttbl;         // When fonts are being defined
+  int f;                // Index of the font being defined/used; defines the codepage if no \cpg
+  unsigned int uc;      // ucN keyword value; its default is 1
+  unsigned int codepage;// defined by \cpg
+};
+typedef std::stack<LocalState> StateStack;
+
+struct GlobalState {
+  enum Pcdata_state { pcdsno, pcdsin, pcdsfinished };
+  std::istream& stream;
+  Fonttbl fonttbl;
+  StateStack stack;
+  unsigned int codepage; // defined by \ansi, \mac, \pc, \pca, and \ansicpgN
+  int deff;
+  std::stringstream pcdata_a;
+  unsigned int pcdata_a_codepage;
+  Pcdata_state pcdata_a_state;
+
+  GlobalState(std::istream& s)
+    : stream(s), codepage(CP_ACP), deff(-1), pcdata_a_state(pcdsno)
+  {
+    LocalState st;
+    st.fonttbl = false;
+    st.f = -1;
+    st.uc = 1;
+    st.codepage = 0xFFFFFFFF;
+    stack.push(st);
+  }
+  unsigned int GetCurrentCP()
+  {
+    if (stack.top().codepage != 0xFFFFFFFF) // \cpg in use
+      return stack.top().codepage;
+    // \cpg not used; use font settings
+    int f = (stack.top().f != -1) ? stack.top().f : deff; 
+    if (f != -1) {
+      Fonttbl::iterator iter = fonttbl.find(f);
+      if (iter != fonttbl.end()) {
+        unsigned int cp = iter->second.Codepage();
+        if (cp != 0xFFFFFFFF)
+          return cp;
+      }
+    }
+    return codepage; // No overrides; use the top-level legacy setting
+  }
+};
+
+struct Keyword {
+  char name[33];
+  bool hasVal;
+  int val;
+};
+
+class Lexem {
+public:
+  enum Type {ltGroupBegin, ltGroupEnd, ltKeyword, ltPCDATA_A, ltPCDATA_W,
+             ltBDATA, ltEOF, ltError};
+  Lexem(Type t=ltError) : m_type(t) {}
+  Lexem(Lexem& from) // Move pointers when copying
+  {
+    switch (m_type = from.m_type) {
+    case ltKeyword:
+      m_keyword = from.m_keyword;
+      break;
+    case ltPCDATA_A:
+      m_pcdata_a = from.m_pcdata_a;
+      break;
+    case ltPCDATA_W:
+      m_pcdata_w = from.m_pcdata_w;
+      break;
+    case ltBDATA:
+      m_bdata = from.m_bdata;
+      from.m_type = ltError;
+      break;
+    }
+  }
+  ~Lexem() { Clear(); }
+  Lexem& operator = (Lexem& from)
+  {
+    if (&from != this) {
+      Clear();
+      switch (m_type = from.m_type) {
+      case ltKeyword:
+        m_keyword = from.m_keyword;
+        break;
+      case ltPCDATA_A:
+        m_pcdata_a = from.m_pcdata_a;
+        break;
+      case ltPCDATA_W:
+        m_pcdata_w = from.m_pcdata_w;
+        break;
+      case ltBDATA:
+        m_bdata = from.m_bdata;
+        from.m_type = ltError;
+        break;
+      }
+    }
+    return *this;
+  }
+  Type type() const { return m_type; }
+  void SetPCDATA_A(char chdata)
+  {
+    Clear();
+    m_pcdata_a = chdata;
+    m_type = ltPCDATA_A;
+  }
+  void SetPCDATA_W(wchar_t chdata)
+  {
+    Clear();
+    m_pcdata_w = chdata;
+    m_type = ltPCDATA_W;
+  }
+  void SetBDATA(const char* data, int sz)
+  {
+    char* tmp = new char[sz]; // to allow getting the data from itself
+    if (tmp) {
+      memcpy(tmp, data, sz);
+      Clear();
+      m_bdata.data = tmp;
+      m_bdata.sz = sz;
+      m_type = ltBDATA;
+    }
+    else m_type = ltError;
+  }
+  void SetKeyword(const Keyword& src)
+  {
+    Clear();
+    m_type = ltKeyword;
+    m_keyword = src;
+  }
+  void SetKeyword(const char* name, bool hasVal=false, int val=0)
+  {
+    char tmp[SIZEOF(m_keyword.name)];
+    strncpy(tmp, name, SIZEOF(m_keyword.name)-1); // to allow copy drom itself
+    tmp[SIZEOF(m_keyword.name)-1]=0;
+    Clear();
+    m_type = ltKeyword;
+    memcpy(m_keyword.name, tmp, SIZEOF(m_keyword.name));
+    m_keyword.hasVal=hasVal;
+    m_keyword.val=val;
+  }
+  const char* KeywordName() const {
+    return (m_type == ltKeyword) ? m_keyword.name : 0; }
+  const int* KeywordVal() const {
+    return ((m_type == ltKeyword) && m_keyword.hasVal) ? &m_keyword.val : 0; }
+  char pcdata_a() const { return (m_type == ltPCDATA_A) ? m_pcdata_a : 0; }
+  wchar_t pcdata_w() const { return (m_type == ltPCDATA_W) ? m_pcdata_w : 0; }
+  const char* bdata() const { return (m_type == ltBDATA) ? m_bdata.data : 0; }
+  int bdata_sz() const { return (m_type == ltBDATA) ? m_bdata.sz : 0; }
+  static Lexem eof;
+  static Lexem groupBegin;
+  static Lexem groupEnd;
+  static Lexem error;
+private:
+  struct BDATA {
+    size_t sz;
+    char* data;
+  };
+
+  Type m_type;
+  union {
+    Keyword m_keyword;
+    char m_pcdata_a;
+    wchar_t m_pcdata_w;
+    BDATA m_bdata;
+  };
+  // This function leaves the object in the broken state. Must be followed
+  // by a correct initialization.
+  void Clear() 
+  {
+    switch (m_type) {
+    case ltBDATA:
+      delete[] m_bdata.data;
+      break;
+    }
+//  m_type = ltError;
+  }
+};
+
+Lexem Lexem::eof(ltEOF);
+Lexem Lexem::groupBegin(ltGroupBegin);
+Lexem Lexem::groupEnd(ltGroupEnd);
+Lexem Lexem::error(ltError);
+
+// This function moves pos. When calling the function, pos must be next to the
+// backslash; pos must be in the same sequence and before end!
+Keyword GetKeyword(std::istream& stream)
+{
+  Keyword keyword = {"", false, 0};
+  char ch;
+  if (stream.get(ch).eof())
+    return keyword;
+  // Control word; maybe delimiter and value
+  if (std::isalpha(ch, std::locale::classic())) { 
+    int i = 0;
+    do {
+      // We take up to 32 characters into account, skipping over extra
+      // characters (allowing for some non-conformant implementation).
+      if (i < 32)
+        keyword.name[i++] = ch;
+    } while (!stream.get(ch).eof() && std::isalpha(ch, std::locale::classic()));
+    keyword.name[i] = 0; // NULL-terminating
+    if (!stream.eof() && (std::isdigit(ch, std::locale::classic()) || (ch == '-'))) { // Value begin
+      keyword.hasVal = true;
+      bool negative = (ch == '-');
+      if (negative) stream.get(ch);
+      i = 0;
+      while (!stream.eof() && std::isdigit(ch, std::locale::classic())) {
+        // We take into account only 10 digits, skip other. Older specs stated
+        // that we must be ready for an arbitrary number of digits.
+        if (i++ < 10) 
+          keyword.val = keyword.val*10 + (ch - '0');
+        stream.get(ch);
+      }
+      if (negative) keyword.val = -keyword.val;
+    }
+     // End of control word; the space is just a delimiter - skip it
+    if (!stream.eof() && !std::isspace(ch, std::locale::classic()))
+      stream.unget();
+  }
+  else { // Control symbol
+    keyword.name[0] = ch, keyword.name[1] = 0;
+  }
+  return keyword;
+}
+
+Lexem GetLexem(std::istream& stream)
+{
+  Lexem result;
+  // We always stay at the beginning of the next lexem or a crlf
+  // If it's a brace then it's group begin/end
+  // If it's a backslash -> Preprocess
+  // - if it's a \u or \' -> make UTF16 character
+  // - else it's a keyword -> Process (e.g., remember the codepage)
+  // - (if the keyword is \bin then the following is #BDATA)
+  // If it's some other character -> Preprocess
+  // - if it's 0x09 -> it's the keyword \tab
+  // - else it's a PCDATA
+  char ch;
+  while (!stream.get(ch).eof() && ((ch == '\n') || (ch == '\r'))); // Skip crlf
+  if (stream.eof())
+    result = Lexem::eof;
+  else {
+    switch (ch) {
+    case '{': // Group begin
+    case '}': // Group end
+      result = (ch == '{') ? Lexem::groupBegin : Lexem::groupEnd;
+      break;
+    case '\\': // Keyword
+      result.SetKeyword(GetKeyword(stream));
+      break;
+    case '\t': // tab
+      result.SetKeyword("tab");
+      break;
+    default: // PSDATA?
+      result.SetPCDATA_A(ch);
+      break;
+    }
+  }
+  return result;
+}
+
+void PreprocessLexem(/*inout*/Lexem& lexem, std::istream& stream, int uc)
+{
+  if (lexem.type() == Lexem::ltKeyword) {
+    if (lexem.KeywordName()[0] == 0) // Empty keyword - maybe eof?
+      lexem = Lexem::error;
+    else if (eq(lexem.KeywordName(), "u")) {
+       // Unicode character - get the UTF16 and skip the uc characters
+      if (const int* val = lexem.KeywordVal()) {
+        lexem.SetPCDATA_W(*val);
+        stream.ignore(uc);
+      }
+      else lexem = Lexem::error;
+    }
+    else if (eq(lexem.KeywordName(), "'")) {
+       // 8-bit character (\'hh) -> use current codepage
+      char ch, ch1;
+      if (!stream.get(ch).eof()) ch1 = HexToInt(ch);
+      if (!stream.get(ch).eof()) (ch1 <<= 4) += HexToInt(ch);
+      lexem.SetPCDATA_A(ch1);
+    }
+    else if (eq(lexem.KeywordName(), "\\") || eq(lexem.KeywordName(), "{") ||
+             eq(lexem.KeywordName(), "}")) // escaped characters
+      lexem.SetPCDATA_A(lexem.KeywordName()[0]);
+    else if (eq(lexem.KeywordName(), "bin")) {
+      if (const int* i = lexem.KeywordVal()) {
+        char* data = new char[*i];
+        if (data) {
+          stream.read(data, *i);
+          if (stream.fail())
+            lexem = Lexem::error;
+          else
+            lexem.SetBDATA(data, *i);
+          delete[] data;
+        }
+        else lexem = Lexem::error;
+      }
+      else lexem = Lexem::error;
+    }
+    else if (eq(lexem.KeywordName(), "\n") || eq(lexem.KeywordName(), "\r")) {
+      // escaped cr or lf
+      lexem.SetKeyword("par");
+    }
+  }
+}
+
+void UpdateState(const Lexem& lexem, /*inout*/GlobalState& globalState)
+{
+  switch (globalState.pcdata_a_state) {
+  case GlobalState::pcdsfinished: // Last time we finished the pcdata
+    globalState.pcdata_a_state = GlobalState::pcdsno;
+    break;
+  case GlobalState::pcdsin:
+     // to be reset later if still in the pcdata
+    globalState.pcdata_a_state = GlobalState::pcdsfinished;
+    break;
+  }
+
+  switch (lexem.type()) {
+  case Lexem::ltGroupBegin:
+    globalState.stack.push(globalState.stack.top());
+    break;
+  case Lexem::ltGroupEnd:
+    globalState.stack.pop();
+    break;
+  case Lexem::ltKeyword:
+    {
+      const int* val = lexem.KeywordVal();
+      if (eq(lexem.KeywordName(), "ansi")) globalState.codepage = CP_ACP;
+      else if (eq(lexem.KeywordName(), "mac")) globalState.codepage = CP_MACCP;
+      else if (eq(lexem.KeywordName(), "pc")) globalState.codepage = 437;
+      else if (eq(lexem.KeywordName(), "pca")) globalState.codepage = 850;
+      else if (eq(lexem.KeywordName(), "ansicpg") && val)
+        globalState.codepage = static_cast<unsigned int>(*val);
+      else if (eq(lexem.KeywordName(), "deff") && val)
+        globalState.deff = *val;
+      else if (eq(lexem.KeywordName(), "fonttbl")) globalState.stack.top().fonttbl = true;
+      else if (eq(lexem.KeywordName(), "f") && val) {
+        globalState.stack.top().f = *val;
+      }
+      else if (eq(lexem.KeywordName(), "fcharset") &&
+               globalState.stack.top().fonttbl &&
+               (globalState.stack.top().f != -1) && val) {
+        FontInfo& f = globalState.fonttbl[globalState.stack.top().f];
+        f.options |= FontInfo::has_fcharset;
+        f.fcharset = *val;
+      }
+      else if (eq(lexem.KeywordName(), "cpg") && val) {
+        if (globalState.stack.top().fonttbl && (globalState.stack.top().f != -1)) { // Defining a font
+          FontInfo& f = globalState.fonttbl[globalState.stack.top().f];
+          f.options |= FontInfo::has_cpg;
+          f.cpg = *val;
+        }
+        else { // Overriding the codepage for the block - may be in filenames
+          globalState.stack.top().codepage = *val;
+        }
+      }
+      else if (eq(lexem.KeywordName(), "plain"))
+        globalState.stack.top().f = -1;
+      else if (eq(lexem.KeywordName(), "uc") && val)
+        globalState.stack.top().uc = *val;
+    }
+    break;
+  case Lexem::ltPCDATA_A:
+    if (globalState.pcdata_a_state == GlobalState::pcdsno) // Beginning of the pcdata
+      globalState.pcdata_a_codepage = globalState.GetCurrentCP(); // to use later to convert to utf16
+    globalState.pcdata_a_state = GlobalState::pcdsin;
+    globalState.pcdata_a << lexem.pcdata_a();
+    break;
+  }
+}
+
+void DecodeRTF(std::istream& rtf, CRTFDecoder& decoder)
+{
+  // Check if this is the rtf
+  Lexem lexem = GetLexem(rtf);
+  if (lexem.type() != Lexem::ltGroupBegin)
+    return;
+  decoder.BeginGroup();
+  lexem = GetLexem(rtf);
+  if ((lexem.type() != Lexem::ltKeyword) || !eq(lexem.KeywordName(), "rtf") ||
+      !lexem.KeywordVal() || (*lexem.KeywordVal() != 1))
+    return;
+  decoder.Keyword(lexem.KeywordName(), lexem.KeywordVal());
+
+  GlobalState state(rtf);
+  // Level is the count of elements in the stack
+
+  while (!state.stream.eof() && (state.stack.size()>0)) { // Don't go past the global group
+    lexem = GetLexem(state.stream);
+    PreprocessLexem(lexem, state.stream, state.stack.top().uc);
+    UpdateState(lexem, state);
+
+    if (state.pcdata_a_state == GlobalState::pcdsfinished) {
+      std::string s = state.pcdata_a.str();
+      int sz = ::MultiByteToWideChar(state.pcdata_a_codepage, 0, s.c_str(), s.size(), 0, 0);
+      if (sz) {
+        wchar_t* data = new wchar_t[sz];
+        ::MultiByteToWideChar(state.pcdata_a_codepage, 0, s.c_str(), s.size(), data, sz);
+        decoder.PCDATA(data, sz);
+        delete[] data;
+      }
+      state.pcdata_a.str(""); // reset
+    }
+
+    switch (lexem.type()) {
+    case Lexem::ltGroupBegin:
+      decoder.BeginGroup();
+      break;
+    case Lexem::ltGroupEnd:
+      decoder.EndGroup();
+      break;
+    case Lexem::ltKeyword:
+      decoder.Keyword(lexem.KeywordName(), lexem.KeywordVal());
+      break;
+    case Lexem::ltPCDATA_W:
+      {
+        wchar_t ch = lexem.pcdata_w();
+        decoder.PCDATA(&ch, 1);
+      }
+      break;
+    case Lexem::ltBDATA:
+      decoder.BDATA(lexem.bdata(), lexem.bdata_sz());
+      break;
+    case Lexem::ltError:
+      break; // Just silently skip the erroneous data - basic error recovery
+    }
+  } // while
+} // DecodeRTF
new file mode 100644
--- /dev/null
+++ b/mailnews/import/outlook/src/rtfDecoder.h
@@ -0,0 +1,54 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code
+ *
+ * The Initial Developer of the Original Code is
+ * Mike Kaganski <mikekaganski@gmail.com>.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <istream>
+
+template <size_t len>
+inline bool eq(const char* str1, const char (&str2)[len])
+{
+  return ::strncmp(str1, str2, len) == 0;
+};
+
+class CRTFDecoder {
+public:
+  virtual void BeginGroup() = 0;
+  virtual void EndGroup() = 0;
+  virtual void Keyword(const char* name, const int* Val) = 0;
+  virtual void PCDATA(const wchar_t* data, size_t cch) = 0;
+  virtual void BDATA(const char* data, size_t sz) = 0;
+};
+
+void DecodeRTF(std::istream& rtf, CRTFDecoder& decoder);
new file mode 100644
--- /dev/null
+++ b/mailnews/import/outlook/src/rtfMailDecoder.cpp
@@ -0,0 +1,111 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code
+ *
+ * The Initial Developer of the Original Code is
+ * Mike Kaganski <mikekaganski@gmail.com>.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "rtfMailDecoder.h"
+
+void CRTFMailDecoder::BeginGroup()
+{
+  ClearState(sAsterisk);
+  SetState(sBeginGroup);
+  if (m_skipLevel)
+    ++m_skipLevel;
+}
+
+void CRTFMailDecoder::EndGroup()
+{
+  ClearState(sAsterisk|sBeginGroup);
+  if (m_skipLevel)
+    --m_skipLevel;
+}
+
+void CRTFMailDecoder::AddText(const wchar_t* txt, size_t cch)
+{
+  if (!IsHtmlRtf()) {
+    if (cch == static_cast<size_t>(-1))
+      m_text += txt;
+    else
+      m_text.append(txt, cch);
+  }
+}
+
+void CRTFMailDecoder::Keyword(const char* name, const int* Val)
+{
+  bool asterisk = IsAsterisk(); ClearState(sAsterisk); // for inside use only
+  bool beginGroup = IsBeginGroup(); ClearState(sBeginGroup); // for inside use only
+  if (!m_skipLevel) {
+    if (eq(name, "*") && beginGroup) SetState(sAsterisk);
+    else if (asterisk) {
+      if (eq(name, "htmltag") && (m_mode == mHTML)) { // \*\htmltag -> don't ignore; include the following text
+      }
+      else ++m_skipLevel;
+    }
+    else if (eq(name, "htmlrtf")) {
+      if (Val && (*Val==0))
+        ClearState(sHtmlRtf);
+      else
+        SetState(sHtmlRtf);
+    }
+    else if (eq(name, "par") || eq(name, "line")) {
+      AddText(L"\r\n");
+    }
+    else if (eq(name, "tab")) {
+      AddText(L"\t");
+    }
+    else if (eq(name, "rquote")) {
+      AddText(L"\x2019"); // Unicode right single quotation mark
+    }
+    else if (eq(name, "fromtext") && (m_mode==mNone)) { // avoid double "fromX"
+      m_mode = mText;
+    }
+    else if (eq(name, "fromhtml") && (m_mode==mNone)) { // avoid double "fromX"
+      m_mode = mHTML;
+    }
+    else if (eq(name, "fonttbl") || eq(name, "colortbl") || eq(name, "stylesheet") || eq(name, "pntext"))
+      ++m_skipLevel;
+  }
+}
+
+void CRTFMailDecoder::PCDATA(const wchar_t* data, size_t cch)
+{
+  ClearState(sAsterisk|sBeginGroup);
+  if (!m_skipLevel)
+    AddText(data, cch);
+}
+
+void CRTFMailDecoder::BDATA(const char* data, size_t sz)
+{
+  ClearState(sAsterisk|sBeginGroup);
+}
new file mode 100644
--- /dev/null
+++ b/mailnews/import/outlook/src/rtfMailDecoder.h
@@ -0,0 +1,72 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code
+ *
+ * The Initial Developer of the Original Code is
+ * Mike Kaganski <mikekaganski@gmail.com>.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <string>
+#include "rtfDecoder.h"
+
+class CRTFMailDecoder: public CRTFDecoder {
+public:
+  enum Mode {mNone, mText, mHTML};
+  CRTFMailDecoder() : m_mode(mNone), m_state(sNormal), m_skipLevel(0) {}
+  void BeginGroup();
+  void EndGroup();
+  void Keyword(const char* name, const int* Val);
+  void PCDATA(const wchar_t* data, size_t cch);
+  void BDATA(const char* data, size_t sz);
+  const wchar_t* text() { return m_text.c_str(); }
+  std::wstring::size_type textSize() { return m_text.size(); }
+  Mode mode() { return m_mode; }
+private:
+  enum State {sNormal = 0x0000,
+              sBeginGroup = 0x0001,
+              sAsterisk = 0x0002,
+              sHtmlRtf = 0x0004};
+
+  std::wstring m_text;
+  Mode m_mode;
+  unsigned int m_state; // bitmask of State
+// bool m_beginGroup; // true just after the {
+//bool m_asterisk; // true just after the {\*
+  int m_skipLevel; // if >0 then we ignore everything
+// bool m_htmlrtf;
+  inline void SetState(unsigned int s) { m_state |= s; }
+  inline void ClearState(unsigned int s) { m_state &= ~s; }
+  inline bool CheckState(State s) { return (m_state & s) != 0; }
+  inline bool IsAsterisk() { return CheckState(sAsterisk); }
+  inline bool IsBeginGroup() { return CheckState(sBeginGroup); }
+  inline bool IsHtmlRtf() { return CheckState(sHtmlRtf); }
+  void AddText(const wchar_t* txt, size_t cch=static_cast<size_t>(-1));
+};