diff options
Diffstat (limited to 'mailnews/import/outlook/src/MapiApi.cpp')
-rw-r--r-- | mailnews/import/outlook/src/MapiApi.cpp | 1940 |
1 files changed, 1940 insertions, 0 deletions
diff --git a/mailnews/import/outlook/src/MapiApi.cpp b/mailnews/import/outlook/src/MapiApi.cpp new file mode 100644 index 000000000..d6a159754 --- /dev/null +++ b/mailnews/import/outlook/src/MapiApi.cpp @@ -0,0 +1,1940 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MapiDbgLog.h" +#include "MapiApi.h" + +#include <sstream> +#include "rtfMailDecoder.h" + +#include "prprf.h" +#include "nsMemory.h" +#include "nsMsgUtils.h" +#include "nsUnicharUtils.h" + +int CMapiApi::m_clients = 0; +BOOL CMapiApi::m_initialized = false; +nsTArray<CMsgStore*> *CMapiApi::m_pStores = NULL; +LPMAPISESSION CMapiApi::m_lpSession = NULL; +LPMDB CMapiApi::m_lpMdb = NULL; +HRESULT CMapiApi::m_lastError; +char16_t * CMapiApi::m_pUniBuff = NULL; +int CMapiApi::m_uniBuffLen = 0; +/* +Type: 1, name: Calendar, class: IPF.Appointment +Type: 1, name: Contacts, class: IPF.Contact +Type: 1, name: Journal, class: IPF.Journal +Type: 1, name: Notes, class: IPF.StickyNote +Type: 1, name: Tasks, class: IPF.Task +Type: 1, name: Drafts, class: IPF.Note +*/ + +HINSTANCE CMapiApi::m_hMapi32 = NULL; + +LPMAPIUNINITIALIZE gpMapiUninitialize = NULL; +LPMAPIINITIALIZE gpMapiInitialize = NULL; +LPMAPIALLOCATEBUFFER gpMapiAllocateBuffer = NULL; +LPMAPIFREEBUFFER gpMapiFreeBuffer = NULL; +LPMAPILOGONEX gpMapiLogonEx = NULL; +LPOPENSTREAMONFILE gpMapiOpenStreamOnFile = NULL; + +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; + + // 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; +} + +// 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(nsDependentCString(streamData.c_str()), val); + } + return TRUE; + } + return FALSE; +} + +void CMapiApi::MAPIUninitialize(void) +{ + if (m_hMapi32 && gpMapiUninitialize) + (*gpMapiUninitialize)(); +} + +HRESULT CMapiApi::MAPIInitialize(LPVOID lpInit) +{ + return (m_hMapi32 && gpMapiInitialize) ? (*gpMapiInitialize)(lpInit) : + MAPI_E_NOT_INITIALIZED; +} + +SCODE CMapiApi::MAPIAllocateBuffer(ULONG cbSize, LPVOID FAR * lppBuffer) +{ + 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; +} + +HRESULT CMapiApi::MAPILogonEx(ULONG ulUIParam, LPTSTR lpszProfileName, + LPTSTR lpszPassword, FLAGS flFlags, + LPMAPISESSION FAR * lppSession) +{ + return (m_hMapi32 && gpMapiLogonEx) ? + (*gpMapiLogonEx)(ulUIParam, lpszProfileName, lpszPassword, flFlags, lppSession) : + MAPI_E_NOT_INITIALIZED; +} + +HRESULT CMapiApi::OpenStreamOnFile(LPALLOCATEBUFFER lpAllocateBuffer, + LPFREEBUFFER lpFreeBuffer, ULONG ulFlags, + LPTSTR lpszFileName, LPTSTR lpszPrefix, + LPSTREAM FAR * lppStream) +{ + 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); +} + +BOOL CMapiApi::LoadMapi(void) +{ + if (m_hMapi32) + return TRUE; + + HINSTANCE hInst = ::LoadLibrary("MAPI32.DLL"); + if (!hInst) + return FALSE; + FARPROC pProc = GetProcAddress(hInst, "MAPIGetNetscapeVersion"); + if (pProc) { + ::FreeLibrary(hInst); + hInst = ::LoadLibrary("MAPI32BAK.DLL"); + if (!hInst) + return FALSE; + } + + m_hMapi32 = hInst; + return LoadMapiEntryPoints(); +} + +void CMapiApi::UnloadMapi(void) +{ + if (m_hMapi32) + ::FreeLibrary(m_hMapi32); + m_hMapi32 = NULL; +} + +CMapiApi::CMapiApi() +{ + m_clients++; + LoadMapi(); + if (!m_pStores) + m_pStores = new nsTArray<CMsgStore*>(); +} + +CMapiApi::~CMapiApi() +{ + m_clients--; + if (!m_clients) { + HRESULT hr; + + ClearMessageStores(); + delete m_pStores; + m_pStores = NULL; + + m_lpMdb = NULL; + + if (m_lpSession) { + hr = m_lpSession->Logoff(NULL, 0, 0); + if (FAILED(hr)) { + MAPI_TRACE2("Logoff failed: 0x%lx, %d\n", (long)hr, (int)hr); + } + m_lpSession->Release(); + m_lpSession = NULL; + } + + if (m_initialized) { + MAPIUninitialize(); + m_initialized = FALSE; + } + + UnloadMapi(); + + if (m_pUniBuff) + delete [] m_pUniBuff; + m_pUniBuff = NULL; + m_uniBuffLen = 0; + } +} + +void CMapiApi::CStrToUnicode(const char *pStr, nsString& result) +{ + result.Truncate(); + int wLen = MultiByteToWideChar(CP_ACP, 0, pStr, -1, wwc(m_pUniBuff), 0); + if (wLen >= m_uniBuffLen) { + if (m_pUniBuff) + delete [] m_pUniBuff; + m_pUniBuff = new char16_t[wLen + 64]; + m_uniBuffLen = wLen + 64; + } + if (wLen) { + MultiByteToWideChar(CP_ACP, 0, pStr, -1, wwc(m_pUniBuff), m_uniBuffLen); + result = m_pUniBuff; + } +} + +BOOL CMapiApi::Initialize(void) +{ + if (m_initialized) + return TRUE; + + HRESULT hr; + + hr = MAPIInitialize(NULL); + + if (FAILED(hr)) { + MAPI_TRACE2("MAPI Initialize failed: 0x%lx, %d\n", (long)hr, (int)hr); + return FALSE; + } + + m_initialized = TRUE; + MAPI_TRACE0("MAPI Initialized\n"); + + return TRUE; +} + +BOOL CMapiApi::LogOn(void) +{ + if (!m_initialized) { + MAPI_TRACE0("Tried to LogOn before initializing MAPI\n"); + return FALSE; + } + + if (m_lpSession) + return TRUE; + + HRESULT hr; + + hr = MAPILogonEx( 0, // might need to be passed in HWND + NULL, // profile name, 64 char max (LPTSTR) + NULL, // profile password, 64 char max (LPTSTR) + // MAPI_NEW_SESSION | MAPI_NO_MAIL | MAPI_LOGON_UI | MAPI_EXPLICIT_PROFILE, + // MAPI_NEW_SESSION | MAPI_NO_MAIL | MAPI_LOGON_UI, + // MAPI_NO_MAIL | MAPI_LOGON_UI, + MAPI_NO_MAIL | MAPI_USE_DEFAULT | MAPI_EXTENDED | MAPI_NEW_SESSION, + &m_lpSession); + + if (FAILED(hr)) { + m_lpSession = NULL; + MAPI_TRACE2("LogOn failed: 0x%lx, %d\n", (long)hr, (int)hr); + return FALSE; + } + + MAPI_TRACE0("MAPI Logged on\n"); + return TRUE; +} + +class CGetStoreFoldersIter : public CMapiHierarchyIter { +public: + CGetStoreFoldersIter(CMapiApi *pApi, CMapiFolderList& folders, int depth, BOOL isMail = TRUE); + + virtual BOOL HandleHierarchyItem(ULONG oType, ULONG cb, LPENTRYID pEntry); + +protected: + BOOL ExcludeFolderClass(const char16_t *pName); + + BOOL m_isMail; + CMapiApi * m_pApi; + CMapiFolderList * m_pList; + int m_depth; +}; + +CGetStoreFoldersIter::CGetStoreFoldersIter(CMapiApi *pApi, CMapiFolderList& folders, int depth, BOOL isMail) +{ + m_pApi = pApi; + m_pList = &folders; + m_depth = depth; + m_isMail = isMail; +} + +BOOL CGetStoreFoldersIter::ExcludeFolderClass(const char16_t *pName) +{ + BOOL bResult; + nsDependentString pNameStr(pName); + if (m_isMail) { + bResult = FALSE; + if (pNameStr.EqualsLiteral("IPF.Appointment")) + bResult = TRUE; + else if (pNameStr.EqualsLiteral("IPF.Contact")) + bResult = TRUE; + else if (pNameStr.EqualsLiteral("IPF.Journal")) + bResult = TRUE; + else if (pNameStr.EqualsLiteral("IPF.StickyNote")) + bResult = TRUE; + else if (pNameStr.EqualsLiteral("IPF.Task")) + bResult = TRUE; + // Skip IMAP folders + else if (pNameStr.EqualsLiteral("IPF.Imap")) + bResult = TRUE; + // else if (!stricmp(pName, "IPF.Note")) + // bResult = TRUE; + } + else { + bResult = TRUE; + if (pNameStr.EqualsLiteral("IPF.Contact")) + bResult = FALSE; + } + + return bResult; +} + +BOOL CGetStoreFoldersIter::HandleHierarchyItem(ULONG oType, ULONG cb, LPENTRYID pEntry) +{ + if (oType == MAPI_FOLDER) { + LPMAPIFOLDER pFolder; + if (m_pApi->OpenEntry(cb, pEntry, (LPUNKNOWN *) &pFolder)) { + LPSPropValue pVal; + nsString name; + + pVal = m_pApi->GetMapiProperty(pFolder, PR_CONTAINER_CLASS); + if (pVal) + m_pApi->GetStringFromProp(pVal, name); + else + name.Truncate(); + + if ((name.IsEmpty() && m_isMail) || (!ExcludeFolderClass(name.get()))) { + pVal = m_pApi->GetMapiProperty(pFolder, PR_DISPLAY_NAME); + m_pApi->GetStringFromProp(pVal, name); + CMapiFolder *pNewFolder = new CMapiFolder(name.get(), cb, pEntry, m_depth); + m_pList->AddItem(pNewFolder); + + pVal = m_pApi->GetMapiProperty(pFolder, PR_FOLDER_TYPE); + MAPI_TRACE2("Type: %d, name: %s\n", + m_pApi->GetLongFromProp(pVal), name.get()); + // m_pApi->ListProperties(pFolder); + + CGetStoreFoldersIter nextIter(m_pApi, *m_pList, m_depth + 1, m_isMail); + m_pApi->IterateHierarchy(&nextIter, pFolder); + } + pFolder->Release(); + } + else { + MAPI_TRACE0("GetStoreFolders - HandleHierarchyItem: Error opening folder entry.\n"); + return FALSE; + } + } + else + MAPI_TRACE1("GetStoreFolders - HandleHierarchyItem: Unhandled ObjectType: %ld\n", oType); + return TRUE; +} + +BOOL CMapiApi::GetStoreFolders(ULONG cbEid, LPENTRYID lpEid, CMapiFolderList& folders, int startDepth) +{ + // Fill in the array with the folders in the given store + if (!m_initialized || !m_lpSession) { + MAPI_TRACE0("MAPI not initialized for GetStoreFolders\n"); + return FALSE; + } + + m_lpMdb = NULL; + + CMsgStore * pStore = FindMessageStore(cbEid, lpEid); + BOOL bResult = FALSE; + LPSPropValue pVal; + + if (pStore && pStore->Open(m_lpSession, &m_lpMdb)) { + // Successful open, do the iteration of the store + pVal = GetMapiProperty(m_lpMdb, PR_IPM_SUBTREE_ENTRYID); + if (pVal) { + ULONG cbEntry; + LPENTRYID pEntry; + LPMAPIFOLDER lpSubTree = NULL; + + if (GetEntryIdFromProp(pVal, cbEntry, pEntry)) { + // Open up the folder! + bResult = OpenEntry(cbEntry, pEntry, (LPUNKNOWN *)&lpSubTree); + MAPIFreeBuffer(pEntry); + if (bResult && lpSubTree) { + // Iterate the subtree with the results going into the folder list + CGetStoreFoldersIter iterHandler(this, folders, startDepth); + bResult = IterateHierarchy(&iterHandler, lpSubTree); + lpSubTree->Release(); + } + else { + MAPI_TRACE0("GetStoreFolders: Error opening sub tree.\n"); + } + } + else { + MAPI_TRACE0("GetStoreFolders: Error getting entryID from sub tree property val.\n"); + } + } + else { + MAPI_TRACE0("GetStoreFolders: Error getting sub tree property.\n"); + } + } + else { + MAPI_TRACE0("GetStoreFolders: Error opening message store.\n"); + } + + return bResult; +} + +BOOL CMapiApi::GetStoreAddressFolders(ULONG cbEid, LPENTRYID lpEid, CMapiFolderList& folders) +{ + // Fill in the array with the folders in the given store + if (!m_initialized || !m_lpSession) { + MAPI_TRACE0("MAPI not initialized for GetStoreAddressFolders\n"); + return FALSE; + } + + m_lpMdb = NULL; + + CMsgStore * pStore = FindMessageStore(cbEid, lpEid); + BOOL bResult = FALSE; + LPSPropValue pVal; + + if (pStore && pStore->Open(m_lpSession, &m_lpMdb)) { + // Successful open, do the iteration of the store + pVal = GetMapiProperty(m_lpMdb, PR_IPM_SUBTREE_ENTRYID); + if (pVal) { + ULONG cbEntry; + LPENTRYID pEntry; + LPMAPIFOLDER lpSubTree = NULL; + + if (GetEntryIdFromProp(pVal, cbEntry, pEntry)) { + // Open up the folder! + bResult = OpenEntry(cbEntry, pEntry, (LPUNKNOWN *)&lpSubTree); + MAPIFreeBuffer(pEntry); + if (bResult && lpSubTree) { + // Iterate the subtree with the results going into the folder list + CGetStoreFoldersIter iterHandler(this, folders, 1, FALSE); + bResult = IterateHierarchy(&iterHandler, lpSubTree); + lpSubTree->Release(); + } + else { + MAPI_TRACE0("GetStoreAddressFolders: Error opening sub tree.\n"); + } + } + else { + MAPI_TRACE0("GetStoreAddressFolders: Error getting entryID from sub tree property val.\n"); + } + } + else { + MAPI_TRACE0("GetStoreAddressFolders: Error getting sub tree property.\n"); + } + } + else + MAPI_TRACE0("GetStoreAddressFolders: Error opening message store.\n"); + + return bResult; +} + + +BOOL CMapiApi::OpenStore(ULONG cbEid, LPENTRYID lpEid, LPMDB *ppMdb) +{ + if (!m_lpSession) { + MAPI_TRACE0("OpenStore called before a session was opened\n"); + return FALSE; + } + + CMsgStore * pStore = FindMessageStore(cbEid, lpEid); + if (pStore && pStore->Open(m_lpSession, ppMdb)) + return TRUE; + return FALSE; +} + + +BOOL CMapiApi::OpenEntry(ULONG cbEntry, LPENTRYID pEntryId, LPUNKNOWN *ppOpen) +{ + if (!m_lpMdb) { + MAPI_TRACE0("OpenEntry called before the message store is open\n"); + return FALSE; + } + + return OpenMdbEntry(m_lpMdb, cbEntry, pEntryId, ppOpen); +} + +BOOL CMapiApi::OpenMdbEntry(LPMDB lpMdb, ULONG cbEntry, LPENTRYID pEntryId, LPUNKNOWN *ppOpen) +{ + ULONG ulObjType; + HRESULT hr; + hr = m_lpSession->OpenEntry(cbEntry, + pEntryId, + NULL, + 0, + &ulObjType, + (LPUNKNOWN *) ppOpen); + if (FAILED(hr)) { + MAPI_TRACE2("OpenMdbEntry failed: 0x%lx, %d\n", (long)hr, (int)hr); + return FALSE; + } + return TRUE; +} + +enum { + ieidPR_ENTRYID = 0, + ieidPR_OBJECT_TYPE, + ieidMax +}; + +static const SizedSPropTagArray(ieidMax, ptaEid)= +{ + ieidMax, + { + PR_ENTRYID, + PR_OBJECT_TYPE, + } +}; + +BOOL CMapiApi::IterateContents(CMapiContentIter *pIter, LPMAPIFOLDER pFolder, ULONG flags) +{ + // flags can be 0 or MAPI_ASSOCIATED + // MAPI_ASSOCIATED is usually used for forms and views + + HRESULT hr; + LPMAPITABLE lpTable; + hr = pFolder->GetContentsTable(flags, &lpTable); + if (FAILED(hr)) { + MAPI_TRACE2("GetContentsTable failed: 0x%lx, %d\n", (long)hr, (int)hr); + return FALSE; + } + + ULONG rowCount; + hr = lpTable->GetRowCount(0, &rowCount); + if (!rowCount) { + MAPI_TRACE0(" Empty Table\n"); + } + + hr = lpTable->SetColumns((LPSPropTagArray)&ptaEid, 0); + if (FAILED(hr)) { + lpTable->Release(); + MAPI_TRACE2("SetColumns failed: 0x%lx, %d\n", (long)hr, (int)hr); + return FALSE; + } + + hr = lpTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL); + if (FAILED(hr)) { + lpTable->Release(); + MAPI_TRACE2("SeekRow failed: 0x%lx, %d\n", (long)hr, (int)hr); + return FALSE; + } + + int cNumRows = 0; + LPSRowSet lpRow; + BOOL keepGoing = TRUE; + BOOL bResult = TRUE; + do { + lpRow = NULL; + hr = lpTable->QueryRows(1, 0, &lpRow); + if(HR_FAILED(hr)) { + MAPI_TRACE2("QueryRows failed: 0x%lx, %d\n", (long)hr, (int)hr); + bResult = FALSE; + break; + } + + if(lpRow) { + cNumRows = lpRow->cRows; + if (cNumRows) { + LPENTRYID lpEID = (LPENTRYID) lpRow->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.lpb; + ULONG cbEID = lpRow->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.cb; + ULONG oType = lpRow->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.ul; + keepGoing = HandleContentsItem(oType, cbEID, lpEID); + MAPI_TRACE1(" ObjectType: %ld\n", oType); + } + FreeProws(lpRow); + } + + } while (SUCCEEDED(hr) && cNumRows && lpRow && keepGoing); + + lpTable->Release(); + return bResult; +} + +BOOL CMapiApi::HandleContentsItem(ULONG oType, ULONG cb, LPENTRYID pEntry) +{ + if (oType == MAPI_MESSAGE) { + LPMESSAGE pMsg; + if (OpenEntry(cb, pEntry, (LPUNKNOWN *) &pMsg)) { + LPSPropValue pVal; + pVal = GetMapiProperty(pMsg, PR_SUBJECT); + ReportStringProp("PR_SUBJECT:", pVal); + pVal = GetMapiProperty(pMsg, PR_DISPLAY_BCC); + ReportStringProp("PR_DISPLAY_BCC:", pVal); + pVal = GetMapiProperty(pMsg, PR_DISPLAY_CC); + ReportStringProp("PR_DISPLAY_CC:", pVal); + pVal = GetMapiProperty(pMsg, PR_DISPLAY_TO); + ReportStringProp("PR_DISPLAY_TO:", pVal); + pVal = GetMapiProperty(pMsg, PR_MESSAGE_CLASS); + ReportStringProp("PR_MESSAGE_CLASS:", pVal); + ListProperties(pMsg); + pMsg->Release(); + } + else { + MAPI_TRACE0(" Folder type - error opening\n"); + } + } + else + MAPI_TRACE1(" ObjectType: %ld\n", oType); + + return TRUE; +} + +void CMapiApi::ListProperties(LPMAPIPROP lpProp, BOOL getValues) +{ + LPSPropTagArray pArray; + HRESULT hr = lpProp->GetPropList(0, &pArray); + if (FAILED(hr)) { + MAPI_TRACE0(" Unable to retrieve property list\n"); + return; + } + ULONG count = 0; + LPMAPINAMEID FAR * lppPropNames; + SPropTagArray tagArray; + LPSPropTagArray lpTagArray = &tagArray; + tagArray.cValues = (ULONG)1; + nsCString desc; + for (ULONG i = 0; i < pArray->cValues; i++) { + GetPropTagName(pArray->aulPropTag[i], desc); + if (getValues) { + tagArray.aulPropTag[0] = pArray->aulPropTag[i]; + hr = lpProp->GetNamesFromIDs(&lpTagArray, nullptr, 0, &count, &lppPropNames); + if (hr == S_OK) + MAPIFreeBuffer(lppPropNames); + + LPSPropValue pVal = GetMapiProperty(lpProp, pArray->aulPropTag[i]); + if (pVal) { + desc += ", "; + ListPropertyValue(pVal, desc); + MAPIFreeBuffer(pVal); + } + } + MAPI_TRACE2(" Tag #%d: %s\n", (int) i, desc.get()); + } + + MAPIFreeBuffer(pArray); +} + +ULONG CMapiApi::GetEmailPropertyTag(LPMAPIPROP lpProp, LONG nameID) +{ +static GUID emailGUID = { + 0x00062004, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 +}; + + MAPINAMEID mapiNameID; + mapiNameID.lpguid = &emailGUID; + mapiNameID.ulKind = MNID_ID; + mapiNameID.Kind.lID = nameID; + + LPMAPINAMEID lpMapiNames = &mapiNameID; + LPSPropTagArray lpMailTagArray = nullptr; + + HRESULT result = lpProp->GetIDsFromNames(1L, &lpMapiNames, 0, &lpMailTagArray); + if (result == S_OK) + { + ULONG lTag = lpMailTagArray->aulPropTag[0]; + MAPIFreeBuffer(lpMailTagArray); + return lTag; + } + else + return 0L; +} + +BOOL CMapiApi::HandleHierarchyItem(ULONG oType, ULONG cb, LPENTRYID pEntry) +{ + if (oType == MAPI_FOLDER) { + LPMAPIFOLDER pFolder; + if (OpenEntry(cb, pEntry, (LPUNKNOWN *) &pFolder)) { + LPSPropValue pVal; + pVal = GetMapiProperty(pFolder, PR_DISPLAY_NAME); + ReportStringProp("Folder name:", pVal); + IterateContents(NULL, pFolder); + IterateHierarchy(NULL, pFolder); + pFolder->Release(); + } + else { + MAPI_TRACE0(" Folder type - error opening\n"); + } + } + else + MAPI_TRACE1(" ObjectType: %ld\n", oType); + + return TRUE; +} + +BOOL CMapiApi::IterateHierarchy(CMapiHierarchyIter *pIter, LPMAPIFOLDER pFolder, ULONG flags) +{ + // flags can be CONVENIENT_DEPTH or 0 + // CONVENIENT_DEPTH will return all depths I believe instead + // of just children + HRESULT hr; + LPMAPITABLE lpTable; + hr = pFolder->GetHierarchyTable(flags, &lpTable); + if (HR_FAILED(hr)) { + m_lastError = hr; + MAPI_TRACE2("IterateHierarchy: GetContentsTable failed: 0x%lx, %d\n", (long)hr, (int)hr); + return FALSE; + } + + ULONG rowCount; + hr = lpTable->GetRowCount(0, &rowCount); + if (!rowCount) { + lpTable->Release(); + return TRUE; + } + + hr = lpTable->SetColumns((LPSPropTagArray)&ptaEid, 0); + if (HR_FAILED(hr)) { + m_lastError = hr; + lpTable->Release(); + MAPI_TRACE2("IterateHierarchy: SetColumns failed: 0x%lx, %d\n", (long)hr, (int)hr); + return FALSE; + } + + hr = lpTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL); + if (HR_FAILED(hr)) { + m_lastError = hr; + lpTable->Release(); + MAPI_TRACE2("IterateHierarchy: SeekRow failed: 0x%lx, %d\n", (long)hr, (int)hr); + return FALSE; + } + + int cNumRows = 0; + LPSRowSet lpRow; + BOOL keepGoing = TRUE; + BOOL bResult = TRUE; + do { + lpRow = NULL; + hr = lpTable->QueryRows(1, 0, &lpRow); + + if(HR_FAILED(hr)) { + MAPI_TRACE2("QueryRows failed: 0x%lx, %d\n", (long)hr, (int)hr); + m_lastError = hr; + bResult = FALSE; + break; + } + + if(lpRow) { + cNumRows = lpRow->cRows; + + if (cNumRows) { + LPENTRYID lpEntry = (LPENTRYID) lpRow->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.lpb; + ULONG cb = lpRow->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.cb; + ULONG oType = lpRow->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.ul; + + if (pIter) + keepGoing = pIter->HandleHierarchyItem(oType, cb, lpEntry); + else + keepGoing = HandleHierarchyItem(oType, cb, lpEntry); + + } + FreeProws(lpRow); + } + } while (SUCCEEDED(hr) && cNumRows && lpRow && keepGoing); + + lpTable->Release(); + + if (bResult && !keepGoing) + bResult = FALSE; + + return bResult; +} + + +enum { + itblPR_DISPLAY_NAME, + itblPR_ENTRYID, + itblMax +}; + +static const SizedSPropTagArray(itblMax, ptaTbl)= +{ + itblMax, + { + PR_DISPLAY_NAME, + PR_ENTRYID, + } +}; + +BOOL CMapiApi::IterateStores(CMapiFolderList& stores) +{ + stores.ClearAll(); + + if (!m_lpSession) { + MAPI_TRACE0("IterateStores called before session is open\n"); + m_lastError = -1; + return FALSE; + } + + + HRESULT hr; + + /* -- Some Microsoft sample code just to see if things are working --- *//* + + ULONG cbEIDStore; + LPENTRYID lpEIDStore; + + hr = HrMAPIFindDefaultMsgStore(m_lpSession, &cbEIDStore, &lpEIDStore); + if (HR_FAILED(hr)) { + MAPI_TRACE0("Default message store not found\n"); + // MessageBoxW(NULL, L"Message Store Not Found", NULL, MB_OK); + } + else { + LPMDB lpStore; + MAPI_TRACE0("Default Message store FOUND\n"); + hr = m_lpSession->OpenMsgStore(NULL, cbEIDStore, + lpEIDStore, NULL, + MDB_NO_MAIL | MDB_NO_DIALOG, &lpStore); + if (HR_FAILED(hr)) { + MAPI_TRACE1("Unable to open default message store: 0x%lx\n", hr); + } + else { + MAPI_TRACE0("Default message store OPENED\n"); + lpStore->Release(); + } + } + */ + + + + LPMAPITABLE lpTable; + + hr = m_lpSession->GetMsgStoresTable(0, &lpTable); + if (FAILED(hr)) { + MAPI_TRACE0("GetMsgStoresTable failed\n"); + m_lastError = hr; + return FALSE; + } + + + ULONG rowCount; + hr = lpTable->GetRowCount(0, &rowCount); + MAPI_TRACE1("MsgStores Table rowCount: %ld\n", rowCount); + + hr = lpTable->SetColumns((LPSPropTagArray)&ptaTbl, 0); + if (FAILED(hr)) { + lpTable->Release(); + MAPI_TRACE2("SetColumns failed: 0x%lx, %d\n", (long)hr, (int)hr); + m_lastError = hr; + return FALSE; + } + + hr = lpTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL); + if (FAILED(hr)) { + lpTable->Release(); + MAPI_TRACE2("SeekRow failed: 0x%lx, %d\n", (long)hr, (int)hr); + m_lastError = hr; + return FALSE; + } + + int cNumRows = 0; + LPSRowSet lpRow; + BOOL keepGoing = TRUE; + BOOL bResult = TRUE; + do { + lpRow = NULL; + hr = lpTable->QueryRows(1, 0, &lpRow); + + if(HR_FAILED(hr)) { + MAPI_TRACE2("QueryRows failed: 0x%lx, %d\n", (long)hr, (int)hr); + bResult = FALSE; + m_lastError = hr; + break; + } + + if(lpRow) { + cNumRows = lpRow->cRows; + + if (cNumRows) { + LPCTSTR lpStr = (LPCTSTR) lpRow->aRow[0].lpProps[itblPR_DISPLAY_NAME].Value.LPSZ; + LPENTRYID lpEID = (LPENTRYID) lpRow->aRow[0].lpProps[itblPR_ENTRYID].Value.bin.lpb; + ULONG cbEID = lpRow->aRow[0].lpProps[itblPR_ENTRYID].Value.bin.cb; + + // In the future, GetStoreInfo needs to somehow return + // whether or not the store is from an IMAP server. + // Currently, GetStoreInfo opens the store and attempts + // to get the hierarchy tree. If the tree is empty or + // does not exist, then szContents will be zero. We'll + // assume that any store that doesn't have anything in + // it's hierarchy tree is not a store we want to import - + // there would be nothing to import from anyway! + // Currently, this does exclude IMAP server accounts + // which is the desired behaviour. + + int strLen = strlen(lpStr); + char16_t * pwszStr = (char16_t *) moz_xmalloc((strLen + 1) * sizeof(WCHAR)); + if (!pwszStr) { + // out of memory + FreeProws(lpRow); + lpTable->Release(); + return FALSE; + } + ::MultiByteToWideChar(CP_ACP, 0, lpStr, strlen(lpStr) + 1, wwc(pwszStr), (strLen + 1) * sizeof(WCHAR)); + CMapiFolder *pFolder = new CMapiFolder(pwszStr, cbEID, lpEID, 0, MAPI_STORE); + free(pwszStr); + + long szContents = 1; + GetStoreInfo(pFolder, &szContents); + + MAPI_TRACE1(" DisplayName: %s\n", lpStr); + if (szContents) + stores.AddItem(pFolder); + else { + delete pFolder; + MAPI_TRACE0(" ^^^^^ Not added to store list\n"); + } + + keepGoing = TRUE; + } + FreeProws(lpRow); + } + } while (SUCCEEDED(hr) && cNumRows && lpRow && keepGoing); + + lpTable->Release(); + + return bResult; +} + +void CMapiApi::GetStoreInfo(CMapiFolder *pFolder, long *pSzContents) +{ + HRESULT hr; + LPMDB lpMdb; + + if (pSzContents) + *pSzContents = 0; + + if (!OpenStore(pFolder->GetCBEntryID(), pFolder->GetEntryID(), &lpMdb)) + return; + + LPSPropValue pVal; + /* + pVal = GetMapiProperty(lpMdb, PR_DISPLAY_NAME); + ReportStringProp(" Message store name:", pVal); + pVal = GetMapiProperty(lpMdb, PR_MDB_PROVIDER); + ReportUIDProp(" Message store provider:", pVal); + pVal = GetMapiProperty(lpMdb, PR_COMMENT); + ReportStringProp(" Message comment:", pVal); + pVal = GetMapiProperty(lpMdb, PR_ACCESS_LEVEL); + ReportLongProp(" Message store Access Level:", pVal); + pVal = GetMapiProperty(lpMdb, PR_STORE_SUPPORT_MASK); + ReportLongProp(" Message store support mask:", pVal); + pVal = GetMapiProperty(lpMdb, PR_STORE_STATE); + ReportLongProp(" Message store state:", pVal); + pVal = GetMapiProperty(lpMdb, PR_OBJECT_TYPE); + ReportLongProp(" Message store object type:", pVal); + pVal = GetMapiProperty(lpMdb, PR_VALID_FOLDER_MASK); + ReportLongProp(" Message store valid folder mask:", pVal); + + pVal = GetMapiProperty(lpMdb, 0x8001001e); + ReportStringProp(" Message prop 0x8001001e:", pVal); + + // This key appears to be the OMI Account Manager account that corresponds + // to this message store. This is important for IMAP accounts + // since we may not want to import messages from an IMAP store! + // Seems silly if you ask me! + // In order to test this, we'll need the registry key to look under to determine + // if it contains the "IMAP Server" value, if it does then we are an + // IMAP store, if not, then we are a non-IMAP store - which may always mean + // a regular store that should be imported. + + pVal = GetMapiProperty(lpMdb, 0x80000003); + ReportLongProp(" Message prop 0x80000003:", pVal); + + // ListProperties(lpMdb); + */ + + pVal = GetMapiProperty(lpMdb, PR_IPM_SUBTREE_ENTRYID); + if (pVal) { + ULONG cbEntry; + LPENTRYID pEntry; + LPMAPIFOLDER lpSubTree = NULL; + + if (GetEntryIdFromProp(pVal, cbEntry, pEntry)) { + // Open up the folder! + ULONG ulObjType; + hr = lpMdb->OpenEntry(cbEntry, pEntry, NULL, 0, &ulObjType, (LPUNKNOWN *) &lpSubTree); + MAPIFreeBuffer(pEntry); + if (SUCCEEDED(hr) && lpSubTree) { + // Find out if there are any contents in the + // tree. + LPMAPITABLE lpTable; + hr = lpSubTree->GetHierarchyTable(0, &lpTable); + if (HR_FAILED(hr)) { + MAPI_TRACE2("GetStoreInfo: GetHierarchyTable failed: 0x%lx, %d\n", (long)hr, (int)hr); + } + else { + ULONG rowCount; + hr = lpTable->GetRowCount(0, &rowCount); + lpTable->Release(); + if (SUCCEEDED(hr) && pSzContents) + *pSzContents = (long) rowCount; + } + + lpSubTree->Release(); + } + } + } +} + + +void CMapiApi::ClearMessageStores(void) +{ + if (m_pStores) { + CMsgStore * pStore; + for (size_t i = 0; i < m_pStores->Length(); i++) { + pStore = m_pStores->ElementAt(i); + delete pStore; + } + m_pStores->Clear(); + } +} + +void CMapiApi::AddMessageStore(CMsgStore *pStore) +{ + if (m_pStores) + m_pStores->AppendElement(pStore); +} + +CMsgStore * CMapiApi::FindMessageStore(ULONG cbEid, LPENTRYID lpEid) +{ + if (!m_lpSession) { + MAPI_TRACE0("FindMessageStore called before session is open\n"); + m_lastError = -1; + return NULL; + } + + ULONG result; + HRESULT hr; + CMsgStore * pStore; + for (size_t i = 0; i < m_pStores->Length(); i++) { + pStore = m_pStores->ElementAt(i); + hr = m_lpSession->CompareEntryIDs(cbEid, lpEid, pStore->GetCBEntryID(), pStore->GetLPEntryID(), + 0, &result); + if (HR_FAILED(hr)) { + MAPI_TRACE2("CompareEntryIDs failed: 0x%lx, %d\n", (long)hr, (int)hr); + m_lastError = hr; + return NULL; + } + if (result) { + return pStore; + } + } + + pStore = new CMsgStore(cbEid, lpEid); + AddMessageStore(pStore); + return pStore; +} + +// -------------------------------------------------------------------- +// Utility stuff +// -------------------------------------------------------------------- + +LPSPropValue CMapiApi::GetMapiProperty(LPMAPIPROP pProp, ULONG tag) +{ + if (!pProp) + return NULL; + + int sz = CbNewSPropTagArray(1); + SPropTagArray *pTag = (SPropTagArray *) new char[sz]; + pTag->cValues = 1; + pTag->aulPropTag[0] = tag; + LPSPropValue lpProp = NULL; + ULONG cValues = 0; + HRESULT hr = pProp->GetProps(pTag, 0, &cValues, &lpProp); + delete [] pTag; + if (HR_FAILED(hr) || (cValues != 1)) { + if (lpProp) + MAPIFreeBuffer(lpProp); + return NULL; + } + else { + if (PROP_TYPE(lpProp->ulPropTag) == PT_ERROR) { + if (lpProp->Value.l == MAPI_E_NOT_FOUND) { + MAPIFreeBuffer(lpProp); + lpProp = NULL; + } + } + } + + return lpProp; +} + +BOOL CMapiApi::IsLargeProperty(LPSPropValue pVal) +{ + return ((PROP_TYPE(pVal->ulPropTag) == PT_ERROR) && (pVal->Value.l == E_OUTOFMEMORY)); +} + +// 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 + 2]; + if (pVal) { + ULONG sz; + hr = lpStream->Read(pVal, (ULONG) st.cbSize.QuadPart, &sz); + if (HR_FAILED(hr)) { + bResult = FALSE; + 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, nsCString& val) +{ + void* result; + if (!GetLargeProperty(pProp, tag, &result)) + return FALSE; + if (PROP_TYPE(tag) == PT_UNICODE) // unicode string + LossyCopyUTF16toASCII(nsDependentString(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; +} + +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) +{ + if (!pVal) + 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; + + default: + MAPI_TRACE0("EntryId not in BINARY prop value\n"); + bResult = FALSE; + break; + } + + if (pVal && delVal) + MAPIFreeBuffer(pVal); + + 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)) + LossyCopyUTF16toASCII(nsDependentString(pVal->Value.lpszW), val); + else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_NULL)) + val.Truncate(); + else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_ERROR)) { + val.Truncate(); + bResult = FALSE; + } + else { + if (pVal) { + MAPI_TRACE1("GetStringFromProp: invalid value, expecting string - %d\n", (int) PROP_TYPE(pVal->ulPropTag)); + } + else { + MAPI_TRACE0("GetStringFromProp: invalid value, expecting string, got null pointer\n"); + } + val.Truncate(); + bResult = FALSE; + } + if (pVal && delVal) + MAPIFreeBuffer(pVal); + + return bResult; +} + +BOOL CMapiApi::GetStringFromProp(LPSPropValue pVal, nsString& val, BOOL delVal) +{ + BOOL bResult = TRUE; + if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_STRING8)) { + CStrToUnicode((const char *)pVal->Value.lpszA, val); + } + else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_UNICODE)) { + val = (char16_t *) pVal->Value.lpszW; + } + else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_NULL)) { + val.Truncate(); + } + else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_ERROR)) { + val.Truncate(); + bResult = FALSE; + } + else { + if (pVal) { + MAPI_TRACE1("GetStringFromProp: invalid value, expecting string - %d\n", (int) PROP_TYPE(pVal->ulPropTag)); + } + else { + MAPI_TRACE0("GetStringFromProp: invalid value, expecting string, got null pointer\n"); + } + val.Truncate(); + bResult = FALSE; + } + if (pVal && delVal) + MAPIFreeBuffer(pVal); + + return bResult; +} + + +LONG CMapiApi::GetLongFromProp(LPSPropValue pVal, BOOL delVal) +{ + LONG val = 0; + if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_LONG)) { + val = pVal->Value.l; + } + else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_NULL)) { + val = 0; + } + else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_ERROR)) { + val = 0; + MAPI_TRACE0("GetLongFromProp: Error retrieving property\n"); + } + else { + MAPI_TRACE0("GetLongFromProp: invalid value, expecting long\n"); + } + if (pVal && delVal) + MAPIFreeBuffer(pVal); + + return val; +} + + +void CMapiApi::ReportUIDProp(const char *pTag, LPSPropValue pVal) +{ + if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_BINARY)) { + if (pVal->Value.bin.cb != 16) { + MAPI_TRACE1("%s - INVALID, expecting 16 bytes of binary data for UID\n", pTag); + } + else { + nsIID uid; + memcpy(&uid, pVal->Value.bin.lpb, 16); + char * pStr = uid.ToString(); + if (pStr) { + MAPI_TRACE2("%s %s\n", pTag, (const char *)pStr); + NS_Free(pStr); + } + } + } + else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_NULL)) { + MAPI_TRACE1("%s {NULL}\n", pTag); + } + else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_ERROR)) { + MAPI_TRACE1("%s {Error retrieving property}\n", pTag); + } + else { + MAPI_TRACE1("%s invalid value, expecting binary\n", pTag); + } + if (pVal) + MAPIFreeBuffer(pVal); +} + +void CMapiApi::ReportLongProp(const char *pTag, LPSPropValue pVal) +{ + if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_LONG)) { + nsCString num; + nsCString num2; + + num.AppendInt((int32_t) pVal->Value.l); + num2.AppendInt((int32_t) pVal->Value.l, 16); + MAPI_TRACE3("%s %s, 0x%s\n", pTag, num, num2); + } + else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_NULL)) { + MAPI_TRACE1("%s {NULL}\n", pTag); + } + else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_ERROR)) { + MAPI_TRACE1("%s {Error retrieving property}\n", pTag); + } + else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_ERROR)) { + MAPI_TRACE1("%s {Error retrieving property}\n", pTag); + } + else { + MAPI_TRACE1("%s invalid value, expecting long\n", pTag); + } + if (pVal) + MAPIFreeBuffer(pVal); +} + +void CMapiApi::ReportStringProp(const char *pTag, LPSPropValue pVal) +{ + if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_TSTRING)) { + nsCString val((LPCTSTR) (pVal->Value.LPSZ)); + MAPI_TRACE2("%s %s\n", pTag, val.get()); + } + else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_NULL)) { + MAPI_TRACE1("%s {NULL}\n", pTag); + } + else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_ERROR)) { + MAPI_TRACE1("%s {Error retrieving property}\n", pTag); + } + else { + MAPI_TRACE1("%s invalid value, expecting string\n", pTag); + } + if (pVal) + MAPIFreeBuffer(pVal); +} + +void CMapiApi::GetPropTagName(ULONG tag, nsCString& s) +{ + char numStr[256]; + PR_snprintf(numStr, 256, "0x%lx, %ld", tag, tag); + s = numStr; + switch(tag) { +#include "mapitagstrs.cpp" + } + s += ", data: "; + switch(PROP_TYPE(tag)) { + case PT_UNSPECIFIED: s += "PT_UNSPECIFIED"; break; + case PT_NULL: s += "PT_NULL"; break; + case PT_I2: s += "PT_I2"; break; + case PT_LONG: s += "PT_LONG"; break; + case PT_R4: s += "PT_R4"; break; + case PT_DOUBLE: s += "PT_DOUBLE"; break; + case PT_CURRENCY: s += "PT_CURRENCY"; break; + case PT_APPTIME: s += "PT_APPTIME"; break; + case PT_ERROR: s += "PT_ERROR"; break; + case PT_BOOLEAN: s += "PT_BOOLEAN"; break; + case PT_OBJECT: s += "PT_OBJECT"; break; + case PT_I8: s += "PT_I8"; break; + case PT_STRING8: s += "PT_STRING8"; break; + case PT_UNICODE: s += "PT_UNICODE"; break; + case PT_SYSTIME: s += "PT_SYSTIME"; break; + case PT_CLSID: s += "PT_CLSID"; break; + case PT_BINARY: s += "PT_BINARY"; break; + case PT_MV_I2: s += "PT_MV_I2"; break; + case PT_MV_LONG: s += "PT_MV_LONG"; break; + case PT_MV_R4: s += "PT_MV_R4"; break; + case PT_MV_DOUBLE: s += "PT_MV_DOUBLE"; break; + case PT_MV_CURRENCY: s += "PT_MV_CURRENCY"; break; + case PT_MV_APPTIME: s += "PT_MV_APPTIME"; break; + case PT_MV_SYSTIME: s += "PT_MV_SYSTIME"; break; + case PT_MV_STRING8: s += "PT_MV_STRING8"; break; + case PT_MV_BINARY: s += "PT_MV_BINARY"; break; + case PT_MV_UNICODE: s += "PT_MV_UNICODE"; break; + case PT_MV_CLSID: s += "PT_MV_CLSID"; break; + case PT_MV_I8: s += "PT_MV_I8"; break; + default: + s += "Unknown"; + } +} + +void CMapiApi::ListPropertyValue(LPSPropValue pVal, nsCString& s) +{ + nsCString strVal; + char nBuff[64]; + + s += "value: "; + switch (PROP_TYPE(pVal->ulPropTag)) { + case PT_STRING8: + GetStringFromProp(pVal, strVal, FALSE); + if (strVal.Length() > 60) { + strVal.SetLength(60); + strVal += "..."; + } + MsgReplaceSubstring(strVal, "\r", "\\r"); + MsgReplaceSubstring(strVal, "\n", "\\n"); + s += strVal; + break; + case PT_LONG: + s.AppendInt((int32_t) pVal->Value.l); + s += ", 0x"; + s.AppendInt((int32_t) pVal->Value.l, 16); + s += nBuff; + break; + case PT_BOOLEAN: + if (pVal->Value.b) + s += "True"; + else + s += "False"; + break; + case PT_NULL: + s += "--NULL--"; + break; + case PT_SYSTIME: { + /* + COleDateTime tm(pVal->Value.ft); + s += tm.Format(); + */ + s += "-- Figure out how to format time in mozilla, PT_SYSTIME --"; + } + break; + default: + s += "?"; + } +} + + + +// ------------------------------------------------------------------- +// Folder list stuff +// ------------------------------------------------------------------- +CMapiFolderList::CMapiFolderList() +{ +} + +CMapiFolderList::~CMapiFolderList() +{ + ClearAll(); +} + +void CMapiFolderList::AddItem(CMapiFolder *pFolder) +{ + EnsureUniqueName(pFolder); + GenerateFilePath(pFolder); + m_array.AppendElement(pFolder); +} + +void CMapiFolderList::ChangeName(nsString& name) +{ + if (name.IsEmpty()) { + name.AssignLiteral("1"); + return; + } + char16_t lastC = name.Last(); + if ((lastC >= '0') && (lastC <= '9')) { + lastC++; + if (lastC > '9') { + lastC = '1'; + name.SetCharAt(lastC, name.Length() - 1); + name.AppendLiteral("0"); + } + else { + name.SetCharAt(lastC, name.Length() - 1); + } + } + else { + name.AppendLiteral(" 2"); + } +} + +void CMapiFolderList::EnsureUniqueName(CMapiFolder *pFolder) +{ + // For everybody in the array before me with the SAME + // depth, my name must be unique + CMapiFolder * pCurrent; + int i; + BOOL done; + nsString name; + nsString cName; + + pFolder->GetDisplayName(name); + do { + done = TRUE; + i = m_array.Length() - 1; + while (i >= 0) { + pCurrent = GetAt(i); + if (pCurrent->GetDepth() == pFolder->GetDepth()) { + pCurrent->GetDisplayName(cName); + if (cName.Equals(name, nsCaseInsensitiveStringComparator())) { + ChangeName(name); + pFolder->SetDisplayName(name.get()); + done = FALSE; + break; + } + } + else if (pCurrent->GetDepth() < pFolder->GetDepth()) + break; + i--; + } + } while (!done); +} + +void CMapiFolderList::GenerateFilePath(CMapiFolder *pFolder) +{ + // A file path, includes all of my parent's path, plus mine + nsString name; + nsString path; + if (!pFolder->GetDepth()) { + pFolder->GetDisplayName(name); + pFolder->SetFilePath(name.get()); + return; + } + + CMapiFolder * pCurrent; + int i = m_array.Length() - 1; + while (i >= 0) { + pCurrent = GetAt(i); + if (pCurrent->GetDepth() == (pFolder->GetDepth() - 1)) { + pCurrent->GetFilePath(path); + path.AppendLiteral(".sbd\\"); + pFolder->GetDisplayName(name); + path += name; + pFolder->SetFilePath(path.get()); + return; + } + i--; + } + pFolder->GetDisplayName(name); + pFolder->SetFilePath(name.get()); +} + +void CMapiFolderList::ClearAll(void) +{ + CMapiFolder *pFolder; + for (size_t i = 0; i < m_array.Length(); i++) { + pFolder = GetAt(i); + delete pFolder; + } + m_array.Clear(); +} + +void CMapiFolderList::DumpList(void) +{ + CMapiFolder *pFolder; + nsString str; + int depth; + char prefix[256]; + + MAPI_TRACE0("Folder List ---------------------------------\n"); + for (size_t i = 0; i < m_array.Length(); i++) { + pFolder = GetAt(i); + depth = pFolder->GetDepth(); + pFolder->GetDisplayName(str); + depth *= 2; + if (depth > 255) + depth = 255; + memset(prefix, ' ', depth); + prefix[depth] = 0; +#ifdef MAPI_DEBUG + char *ansiStr = ToNewCString(str); + MAPI_TRACE2("%s%s: ", prefix, ansiStr); + NS_Free(ansiStr); +#endif + pFolder->GetFilePath(str); +#ifdef MAPI_DEBUG + ansiStr = ToNewCString(str); + MAPI_TRACE2("depth=%d, filePath=%s\n", pFolder->GetDepth(), ansiStr); + NS_Free(ansiStr); +#endif + } + MAPI_TRACE0("---------------------------------------------\n"); +} + + +CMapiFolder::CMapiFolder() +{ + m_objectType = MAPI_FOLDER; + m_cbEid = 0; + m_lpEid = NULL; + m_depth = 0; + m_doImport = TRUE; +} + +CMapiFolder::CMapiFolder(const char16_t *pDisplayName, ULONG cbEid, LPENTRYID lpEid, int depth, LONG oType) +{ + m_cbEid = 0; + m_lpEid = NULL; + SetDisplayName(pDisplayName); + SetEntryID(cbEid, lpEid); + SetDepth(depth); + SetObjectType(oType); + SetDoImport(TRUE); +} + +CMapiFolder::CMapiFolder(const CMapiFolder *pCopyFrom) +{ + m_lpEid = NULL; + m_cbEid = 0; + SetDoImport(pCopyFrom->GetDoImport()); + SetDisplayName(pCopyFrom->m_displayName.get()); + SetObjectType(pCopyFrom->GetObjectType()); + SetEntryID(pCopyFrom->GetCBEntryID(), pCopyFrom->GetEntryID()); + SetDepth(pCopyFrom->GetDepth()); + SetFilePath(pCopyFrom->m_mailFilePath.get()); +} + +CMapiFolder::~CMapiFolder() +{ + if (m_lpEid) + delete m_lpEid; +} + +void CMapiFolder::SetEntryID(ULONG cbEid, LPENTRYID lpEid) +{ + if (m_lpEid) + delete m_lpEid; + m_lpEid = NULL; + m_cbEid = cbEid; + if (cbEid) { + m_lpEid = new BYTE[cbEid]; + memcpy(m_lpEid, lpEid, cbEid); + } +} + +// --------------------------------------------------------------------- +// Message store stuff +// --------------------------------------------------------------------- + + +CMsgStore::CMsgStore(ULONG cbEid, LPENTRYID lpEid) +{ + m_lpEid = NULL; + m_lpMdb = NULL; + SetEntryID(cbEid, lpEid); +} + +CMsgStore::~CMsgStore() +{ + if (m_lpEid) + delete m_lpEid; + + if (m_lpMdb) { + ULONG flags = LOGOFF_NO_WAIT; + HRESULT hr = m_lpMdb->StoreLogoff(&flags); + m_lpMdb->Release(); + m_lpMdb = NULL; + } +} + +void CMsgStore::SetEntryID(ULONG cbEid, LPENTRYID lpEid) +{ + HRESULT hr; + + if (m_lpEid) + delete m_lpEid; + + m_lpEid = NULL; + if (cbEid) { + m_lpEid = new BYTE[cbEid]; + memcpy(m_lpEid, lpEid, cbEid); + } + m_cbEid = cbEid; + + if (m_lpMdb) { + ULONG flags = LOGOFF_NO_WAIT; + hr = m_lpMdb->StoreLogoff(&flags); + m_lpMdb->Release(); + m_lpMdb = NULL; + } +} + +BOOL CMsgStore::Open(LPMAPISESSION pSession, LPMDB *ppMdb) +{ + if (m_lpMdb) { + if (ppMdb) + *ppMdb = m_lpMdb; + return TRUE; + } + + BOOL bResult = TRUE; + HRESULT hr = pSession->OpenMsgStore(NULL, m_cbEid, (LPENTRYID)m_lpEid, NULL, MDB_NO_MAIL, &m_lpMdb); // MDB pointer + if (HR_FAILED(hr)) { + m_lpMdb = NULL; + MAPI_TRACE2("OpenMsgStore failed: 0x%lx, %d\n", (long)hr, (int)hr); + bResult = FALSE; + } + + if (ppMdb) + *ppMdb = m_lpMdb; + return bResult; +} + + + +// ------------------------------------------------------------ +// Contents Iterator +// ----------------------------------------------------------- + + +CMapiFolderContents::CMapiFolderContents(LPMDB lpMdb, ULONG cbEid, LPENTRYID lpEid) +{ + m_lpMdb = lpMdb; + m_fCbEid = cbEid; + m_fLpEid = new BYTE[cbEid]; + memcpy(m_fLpEid, lpEid, cbEid); + m_count = 0; + m_iterCount = 0; + m_failure = FALSE; + m_lastError = 0; + m_lpFolder = NULL; + m_lpTable = NULL; + m_lastLpEid = NULL; + m_lastCbEid = 0; +} + +CMapiFolderContents::~CMapiFolderContents() +{ + if (m_lastLpEid) + delete m_lastLpEid; + delete m_fLpEid; + if (m_lpTable) + m_lpTable->Release(); + if (m_lpFolder) + m_lpFolder->Release(); +} + + +BOOL CMapiFolderContents::SetUpIter(void) +{ + // First, open up the MAPIFOLDER object + ULONG ulObjType; + HRESULT hr; + hr = m_lpMdb->OpenEntry(m_fCbEid, (LPENTRYID) m_fLpEid, NULL, 0, &ulObjType, (LPUNKNOWN *) &m_lpFolder); + + if (FAILED(hr) || !m_lpFolder) { + m_lpFolder = NULL; + m_lastError = hr; + MAPI_TRACE2("CMapiFolderContents OpenEntry failed: 0x%lx, %d\n", (long)hr, (int)hr); + return FALSE; + } + + if (ulObjType != MAPI_FOLDER) { + m_lastError = -1; + MAPI_TRACE0("CMapiFolderContents - bad object type, not a folder.\n"); + return FALSE; + } + + + hr = m_lpFolder->GetContentsTable(0, &m_lpTable); + if (FAILED(hr) || !m_lpTable) { + m_lastError = hr; + m_lpTable = NULL; + MAPI_TRACE2("CMapiFolderContents - GetContentsTable failed: 0x%lx, %d\n", (long)hr, (int)hr); + return FALSE; + } + + hr = m_lpTable->GetRowCount(0, &m_count); + if (FAILED(hr)) { + m_lastError = hr; + MAPI_TRACE0("CMapiFolderContents - GetRowCount failed\n"); + return FALSE; + } + + hr = m_lpTable->SetColumns((LPSPropTagArray)&ptaEid, 0); + if (FAILED(hr)) { + m_lastError = hr; + MAPI_TRACE2("CMapiFolderContents - SetColumns failed: 0x%lx, %d\n", (long)hr, (int)hr); + return FALSE; + } + + hr = m_lpTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL); + if (FAILED(hr)) { + m_lastError = hr; + MAPI_TRACE2("CMapiFolderContents - SeekRow failed: 0x%lx, %d\n", (long)hr, (int)hr); + return FALSE; + } + + return TRUE; +} + + +BOOL CMapiFolderContents::GetNext(ULONG *pcbEid, LPENTRYID *ppEid, ULONG *poType, BOOL *pDone) +{ + *pDone = FALSE; + if (m_failure) + return FALSE; + if (!m_lpFolder) { + if (!SetUpIter()) { + m_failure = TRUE; + return FALSE; + } + if (!m_count) { + *pDone = TRUE; + return TRUE; + } + } + + int cNumRows = 0; + LPSRowSet lpRow = NULL; + HRESULT hr = m_lpTable->QueryRows(1, 0, &lpRow); + + if(HR_FAILED(hr)) { + m_lastError = hr; + m_failure = TRUE; + MAPI_TRACE2("CMapiFolderContents - QueryRows failed: 0x%lx, %d\n", (long)hr, (int)hr); + return FALSE; + } + + if(lpRow) { + cNumRows = lpRow->cRows; + if (cNumRows) { + LPENTRYID lpEID = (LPENTRYID) lpRow->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.lpb; + ULONG cbEID = lpRow->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.cb; + ULONG oType = lpRow->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.ul; + + if (m_lastCbEid != cbEID) { + if (m_lastLpEid) + delete m_lastLpEid; + m_lastLpEid = new BYTE[cbEID]; + m_lastCbEid = cbEID; + } + memcpy(m_lastLpEid, lpEID, cbEID); + + *ppEid = (LPENTRYID) m_lastLpEid; + *pcbEid = cbEID; + *poType = oType; + } + else + *pDone = TRUE; + CMapiApi::FreeProws(lpRow); + } + else + *pDone = TRUE; + + return TRUE; +} + |