/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 #include "rtfMailDecoder.h" #include "prprf.h" #include "nsMemory.h" #include "nsMsgUtils.h" #include "nsUnicharUtils.h" #include "nsNativeCharsetUtils.h" int CMapiApi::m_clients = 0; BOOL CMapiApi::m_initialized = false; nsTArray* CMapiApi::m_pStores = NULL; LPMAPISESSION CMapiApi::m_lpSession = NULL; LPMDB CMapiApi::m_lpMdb = NULL; HRESULT CMapiApi::m_lastError; /* 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, LPCTSTR 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 = ::LoadLibraryW(L"MAPI32.DLL"); if (!hInst) return FALSE; FARPROC pProc = GetProcAddress(hInst, "MAPIGetNetscapeVersion"); if (pProc) { ::FreeLibrary(hInst); hInst = ::LoadLibraryW(L"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(); } 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(); } } void CMapiApi::CStrToUnicode(const char* pStr, nsString& result) { NS_CopyNativeToUnicode(nsDependentCString(pStr), result); } 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 = E_UNEXPECTED; 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, reinterpret_cast(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 = E_UNEXPECTED; 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(result)), val); else // either PT_STRING8 or some other binary - use as is val.Assign(static_cast(result)); // Despite being used as wchar_t*, result it allocated as "new char[]" in // GetLargeProperty(). delete[] static_cast(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(result)); else // either PT_STRING8 or some other binary CStrToUnicode(static_cast(result), val); // Despite being used as wchar_t*, result it allocated as "new char[]" in // GetLargeProperty(). delete[] static_cast(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); const char* pStr = uid.ToString().get(); if (pStr) { MAPI_TRACE2("%s %s\n", pTag, 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 { 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 += "..."; } strVal.ReplaceSubstring("\r", "\\r"); strVal.ReplaceSubstring("\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.Assign('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.Append('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); free(ansiStr); #endif pFolder->GetFilePath(str); #ifdef MAPI_DEBUG ansiStr = ToNewCString(str); MAPI_TRACE2("depth=%d, filePath=%s\n", pFolder->GetDepth(), ansiStr); 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; m_lpMdb->StoreLogoff(&flags); m_lpMdb->Release(); m_lpMdb = NULL; } } void CMsgStore::SetEntryID(ULONG cbEid, LPENTRYID lpEid) { 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; 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 = E_UNEXPECTED; 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; }