1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "MapiDbgLog.h"
7 #include "MapiApi.h"
8 
9 #include <sstream>
10 #include "rtfMailDecoder.h"
11 
12 #include "prprf.h"
13 #include "nsMemory.h"
14 #include "nsMsgUtils.h"
15 #include "nsUnicharUtils.h"
16 #include "nsNativeCharsetUtils.h"
17 
18 int CMapiApi::m_clients = 0;
19 BOOL CMapiApi::m_initialized = false;
20 nsTArray<CMsgStore*>* CMapiApi::m_pStores = NULL;
21 LPMAPISESSION CMapiApi::m_lpSession = NULL;
22 LPMDB CMapiApi::m_lpMdb = NULL;
23 HRESULT CMapiApi::m_lastError;
24 /*
25 Type: 1, name: Calendar, class: IPF.Appointment
26 Type: 1, name: Contacts, class: IPF.Contact
27 Type: 1, name: Journal, class: IPF.Journal
28 Type: 1, name: Notes, class: IPF.StickyNote
29 Type: 1, name: Tasks, class: IPF.Task
30 Type: 1, name: Drafts, class: IPF.Note
31 */
32 
33 HINSTANCE CMapiApi::m_hMapi32 = NULL;
34 
35 LPMAPIUNINITIALIZE gpMapiUninitialize = NULL;
36 LPMAPIINITIALIZE gpMapiInitialize = NULL;
37 LPMAPIALLOCATEBUFFER gpMapiAllocateBuffer = NULL;
38 LPMAPIFREEBUFFER gpMapiFreeBuffer = NULL;
39 LPMAPILOGONEX gpMapiLogonEx = NULL;
40 LPOPENSTREAMONFILE gpMapiOpenStreamOnFile = NULL;
41 
42 typedef HRESULT(STDMETHODCALLTYPE WRAPCOMPRESSEDRTFSTREAM)(
43     LPSTREAM lpCompressedRTFStream, ULONG ulFlags,
44     LPSTREAM FAR* lpUncompressedRTFStream);
45 typedef WRAPCOMPRESSEDRTFSTREAM* LPWRAPCOMPRESSEDRTFSTREAM;
46 LPWRAPCOMPRESSEDRTFSTREAM gpWrapCompressedRTFStream = NULL;
47 
48 // WrapCompressedRTFStreamEx related stuff - see
49 // http://support.microsoft.com/kb/839560
50 typedef struct {
51   ULONG size;
52   ULONG ulFlags;
53   ULONG ulInCodePage;
54   ULONG ulOutCodePage;
55 } RTF_WCSINFO;
56 typedef struct {
57   ULONG size;
58   ULONG ulStreamFlags;
59 } RTF_WCSRETINFO;
60 
61 typedef HRESULT(STDMETHODCALLTYPE WRAPCOMPRESSEDRTFSTREAMEX)(
62     LPSTREAM lpCompressedRTFStream, CONST RTF_WCSINFO* pWCSInfo,
63     LPSTREAM* lppUncompressedRTFStream, RTF_WCSRETINFO* pRetInfo);
64 typedef WRAPCOMPRESSEDRTFSTREAMEX* LPWRAPCOMPRESSEDRTFSTREAMEX;
65 LPWRAPCOMPRESSEDRTFSTREAMEX gpWrapCompressedRTFStreamEx = NULL;
66 
LoadMapiEntryPoints(void)67 BOOL CMapiApi::LoadMapiEntryPoints(void) {
68   if (!(gpMapiUninitialize =
69             (LPMAPIUNINITIALIZE)GetProcAddress(m_hMapi32, "MAPIUninitialize")))
70     return FALSE;
71   if (!(gpMapiInitialize =
72             (LPMAPIINITIALIZE)GetProcAddress(m_hMapi32, "MAPIInitialize")))
73     return FALSE;
74   if (!(gpMapiAllocateBuffer = (LPMAPIALLOCATEBUFFER)GetProcAddress(
75             m_hMapi32, "MAPIAllocateBuffer")))
76     return FALSE;
77   if (!(gpMapiFreeBuffer =
78             (LPMAPIFREEBUFFER)GetProcAddress(m_hMapi32, "MAPIFreeBuffer")))
79     return FALSE;
80   if (!(gpMapiLogonEx =
81             (LPMAPILOGONEX)GetProcAddress(m_hMapi32, "MAPILogonEx")))
82     return FALSE;
83   if (!(gpMapiOpenStreamOnFile =
84             (LPOPENSTREAMONFILE)GetProcAddress(m_hMapi32, "OpenStreamOnFile")))
85     return FALSE;
86 
87   // Available from the Outlook 2002 post-SP3 hotfix
88   // (http://support.microsoft.com/kb/883924/) Exported by msmapi32.dll; so it's
89   // unavailable to us using mapi32.dll
90   gpWrapCompressedRTFStreamEx = (LPWRAPCOMPRESSEDRTFSTREAMEX)GetProcAddress(
91       m_hMapi32, "WrapCompressedRTFStreamEx");
92   // Available always
93   gpWrapCompressedRTFStream = (LPWRAPCOMPRESSEDRTFSTREAM)GetProcAddress(
94       m_hMapi32, "WrapCompressedRTFStream");
95 
96   return TRUE;
97 }
98 
99 // Gets the PR_RTF_COMPRESSED tag property
100 // Codepage is used only if the WrapCompressedRTFStreamEx is available
GetRTFPropertyDecodedAsUTF16(LPMAPIPROP pProp,nsString & val,unsigned long & nativeBodyType,unsigned long codepage)101 BOOL CMapiApi::GetRTFPropertyDecodedAsUTF16(LPMAPIPROP pProp, nsString& val,
102                                             unsigned long& nativeBodyType,
103                                             unsigned long codepage) {
104   if (!m_hMapi32 || !(gpWrapCompressedRTFStreamEx || gpWrapCompressedRTFStream))
105     return FALSE;  // Fallback to the default processing
106 
107   LPSTREAM icstream = 0;   // for the compressed stream
108   LPSTREAM iunstream = 0;  // for the uncompressed stream
109   HRESULT hr =
110       pProp->OpenProperty(PR_RTF_COMPRESSED, &IID_IStream,
111                           STGM_READ | STGM_DIRECT, 0, (LPUNKNOWN*)&icstream);
112   if (HR_FAILED(hr)) return FALSE;
113 
114   if (gpWrapCompressedRTFStreamEx) {  // Impossible - we use mapi32.dll!
115     RTF_WCSINFO wcsinfo = {0};
116     RTF_WCSRETINFO retinfo = {0};
117 
118     retinfo.size = sizeof(RTF_WCSRETINFO);
119 
120     wcsinfo.size = sizeof(RTF_WCSINFO);
121     wcsinfo.ulFlags = MAPI_NATIVE_BODY;
122     wcsinfo.ulInCodePage = codepage;
123     wcsinfo.ulOutCodePage = CP_UTF8;
124 
125     if (HR_SUCCEEDED(hr = gpWrapCompressedRTFStreamEx(icstream, &wcsinfo,
126                                                       &iunstream, &retinfo)))
127       nativeBodyType = retinfo.ulStreamFlags;
128   } else {  // mapi32.dll
129     gpWrapCompressedRTFStream(icstream, 0, &iunstream);
130   }
131   icstream->Release();
132 
133   if (iunstream) {  // Succeeded
134     std::string streamData;
135     // Stream.Stat doesn't work for this stream!
136     bool done = false;
137     while (!done) {
138       // I think 10K is a good guess to minimize the number of reads while
139       // keeping memory usage low
140       const int bufsize = 10240;
141       char buf[bufsize];
142       ULONG read;
143       hr = iunstream->Read(buf, bufsize, &read);
144       done = (read < bufsize) || (hr != S_OK);
145       if (read) streamData.append(buf, read);
146     }
147     iunstream->Release();
148     // if rtf -> convert to plain text.
149     if (!gpWrapCompressedRTFStreamEx ||
150         (nativeBodyType == MAPI_NATIVE_BODY_TYPE_RTF)) {
151       std::stringstream s(streamData);
152       CRTFMailDecoder decoder;
153       DecodeRTF(s, decoder);
154       if (decoder.mode() == CRTFMailDecoder::mHTML)
155         nativeBodyType = MAPI_NATIVE_BODY_TYPE_HTML;
156       else if (decoder.mode() == CRTFMailDecoder::mText)
157         nativeBodyType = MAPI_NATIVE_BODY_TYPE_PLAINTEXT;
158       else
159         nativeBodyType = MAPI_NATIVE_BODY_TYPE_RTF;
160       val.Assign(decoder.text(), decoder.textSize());
161     } else {  // WrapCompressedRTFStreamEx available and original type is not
162               // rtf
163       CopyUTF8toUTF16(nsDependentCString(streamData.c_str()), val);
164     }
165     return TRUE;
166   }
167   return FALSE;
168 }
169 
MAPIUninitialize(void)170 void CMapiApi::MAPIUninitialize(void) {
171   if (m_hMapi32 && gpMapiUninitialize) (*gpMapiUninitialize)();
172 }
173 
MAPIInitialize(LPVOID lpInit)174 HRESULT CMapiApi::MAPIInitialize(LPVOID lpInit) {
175   return (m_hMapi32 && gpMapiInitialize) ? (*gpMapiInitialize)(lpInit)
176                                          : MAPI_E_NOT_INITIALIZED;
177 }
178 
MAPIAllocateBuffer(ULONG cbSize,LPVOID FAR * lppBuffer)179 SCODE CMapiApi::MAPIAllocateBuffer(ULONG cbSize, LPVOID FAR* lppBuffer) {
180   return (m_hMapi32 && gpMapiAllocateBuffer)
181              ? (*gpMapiAllocateBuffer)(cbSize, lppBuffer)
182              : MAPI_E_NOT_INITIALIZED;
183 }
184 
MAPIFreeBuffer(LPVOID lpBuff)185 ULONG CMapiApi::MAPIFreeBuffer(LPVOID lpBuff) {
186   return (m_hMapi32 && gpMapiFreeBuffer) ? (*gpMapiFreeBuffer)(lpBuff)
187                                          : MAPI_E_NOT_INITIALIZED;
188 }
189 
MAPILogonEx(ULONG ulUIParam,LPTSTR lpszProfileName,LPTSTR lpszPassword,FLAGS flFlags,LPMAPISESSION FAR * lppSession)190 HRESULT CMapiApi::MAPILogonEx(ULONG ulUIParam, LPTSTR lpszProfileName,
191                               LPTSTR lpszPassword, FLAGS flFlags,
192                               LPMAPISESSION FAR* lppSession) {
193   return (m_hMapi32 && gpMapiLogonEx)
194              ? (*gpMapiLogonEx)(ulUIParam, lpszProfileName, lpszPassword,
195                                 flFlags, lppSession)
196              : MAPI_E_NOT_INITIALIZED;
197 }
198 
OpenStreamOnFile(LPALLOCATEBUFFER lpAllocateBuffer,LPFREEBUFFER lpFreeBuffer,ULONG ulFlags,LPCTSTR lpszFileName,LPTSTR lpszPrefix,LPSTREAM FAR * lppStream)199 HRESULT CMapiApi::OpenStreamOnFile(LPALLOCATEBUFFER lpAllocateBuffer,
200                                    LPFREEBUFFER lpFreeBuffer, ULONG ulFlags,
201                                    LPCTSTR lpszFileName, LPTSTR lpszPrefix,
202                                    LPSTREAM FAR* lppStream) {
203   return (m_hMapi32 && gpMapiOpenStreamOnFile)
204              ? (*gpMapiOpenStreamOnFile)(lpAllocateBuffer, lpFreeBuffer,
205                                          ulFlags, lpszFileName, lpszPrefix,
206                                          lppStream)
207              : MAPI_E_NOT_INITIALIZED;
208 }
209 
FreeProws(LPSRowSet prows)210 void CMapiApi::FreeProws(LPSRowSet prows) {
211   ULONG irow;
212   if (!prows) return;
213   for (irow = 0; irow < prows->cRows; ++irow)
214     MAPIFreeBuffer(prows->aRow[irow].lpProps);
215   MAPIFreeBuffer(prows);
216 }
217 
LoadMapi(void)218 BOOL CMapiApi::LoadMapi(void) {
219   if (m_hMapi32) return TRUE;
220 
221   HINSTANCE hInst = ::LoadLibraryW(L"MAPI32.DLL");
222   if (!hInst) return FALSE;
223   FARPROC pProc = GetProcAddress(hInst, "MAPIGetNetscapeVersion");
224   if (pProc) {
225     ::FreeLibrary(hInst);
226     hInst = ::LoadLibraryW(L"MAPI32BAK.DLL");
227     if (!hInst) return FALSE;
228   }
229 
230   m_hMapi32 = hInst;
231   return LoadMapiEntryPoints();
232 }
233 
UnloadMapi(void)234 void CMapiApi::UnloadMapi(void) {
235   if (m_hMapi32) ::FreeLibrary(m_hMapi32);
236   m_hMapi32 = NULL;
237 }
238 
CMapiApi()239 CMapiApi::CMapiApi() {
240   m_clients++;
241   LoadMapi();
242   if (!m_pStores) m_pStores = new nsTArray<CMsgStore*>();
243 }
244 
~CMapiApi()245 CMapiApi::~CMapiApi() {
246   m_clients--;
247   if (!m_clients) {
248     HRESULT hr;
249 
250     ClearMessageStores();
251     delete m_pStores;
252     m_pStores = NULL;
253 
254     m_lpMdb = NULL;
255 
256     if (m_lpSession) {
257       hr = m_lpSession->Logoff(NULL, 0, 0);
258       if (FAILED(hr)) {
259         MAPI_TRACE2("Logoff failed: 0x%lx, %d\n", (long)hr, (int)hr);
260       }
261       m_lpSession->Release();
262       m_lpSession = NULL;
263     }
264 
265     if (m_initialized) {
266       MAPIUninitialize();
267       m_initialized = FALSE;
268     }
269 
270     UnloadMapi();
271   }
272 }
273 
CStrToUnicode(const char * pStr,nsString & result)274 void CMapiApi::CStrToUnicode(const char* pStr, nsString& result) {
275   NS_CopyNativeToUnicode(nsDependentCString(pStr), result);
276 }
277 
Initialize(void)278 BOOL CMapiApi::Initialize(void) {
279   if (m_initialized) return TRUE;
280 
281   HRESULT hr;
282 
283   hr = MAPIInitialize(NULL);
284 
285   if (FAILED(hr)) {
286     MAPI_TRACE2("MAPI Initialize failed: 0x%lx, %d\n", (long)hr, (int)hr);
287     return FALSE;
288   }
289 
290   m_initialized = TRUE;
291   MAPI_TRACE0("MAPI Initialized\n");
292 
293   return TRUE;
294 }
295 
LogOn(void)296 BOOL CMapiApi::LogOn(void) {
297   if (!m_initialized) {
298     MAPI_TRACE0("Tried to LogOn before initializing MAPI\n");
299     return FALSE;
300   }
301 
302   if (m_lpSession) return TRUE;
303 
304   HRESULT hr;
305 
306   hr = MAPILogonEx(
307       0,     // might need to be passed in HWND
308       NULL,  // profile name, 64 char max (LPTSTR)
309       NULL,  // profile password, 64 char max (LPTSTR)
310       // MAPI_NEW_SESSION | MAPI_NO_MAIL | MAPI_LOGON_UI |
311       // MAPI_EXPLICIT_PROFILE, MAPI_NEW_SESSION | MAPI_NO_MAIL | MAPI_LOGON_UI,
312       // MAPI_NO_MAIL | MAPI_LOGON_UI,
313       MAPI_NO_MAIL | MAPI_USE_DEFAULT | MAPI_EXTENDED | MAPI_NEW_SESSION,
314       &m_lpSession);
315 
316   if (FAILED(hr)) {
317     m_lpSession = NULL;
318     MAPI_TRACE2("LogOn failed: 0x%lx, %d\n", (long)hr, (int)hr);
319     return FALSE;
320   }
321 
322   MAPI_TRACE0("MAPI Logged on\n");
323   return TRUE;
324 }
325 
326 class CGetStoreFoldersIter : public CMapiHierarchyIter {
327  public:
328   CGetStoreFoldersIter(CMapiApi* pApi, CMapiFolderList& folders, int depth,
329                        BOOL isMail = TRUE);
330 
331   virtual BOOL HandleHierarchyItem(ULONG oType, ULONG cb, LPENTRYID pEntry);
332 
333  protected:
334   BOOL ExcludeFolderClass(const char16_t* pName);
335 
336   BOOL m_isMail;
337   CMapiApi* m_pApi;
338   CMapiFolderList* m_pList;
339   int m_depth;
340 };
341 
CGetStoreFoldersIter(CMapiApi * pApi,CMapiFolderList & folders,int depth,BOOL isMail)342 CGetStoreFoldersIter::CGetStoreFoldersIter(CMapiApi* pApi,
343                                            CMapiFolderList& folders, int depth,
344                                            BOOL isMail) {
345   m_pApi = pApi;
346   m_pList = &folders;
347   m_depth = depth;
348   m_isMail = isMail;
349 }
350 
ExcludeFolderClass(const char16_t * pName)351 BOOL CGetStoreFoldersIter::ExcludeFolderClass(const char16_t* pName) {
352   BOOL bResult;
353   nsDependentString pNameStr(pName);
354   if (m_isMail) {
355     bResult = FALSE;
356     if (pNameStr.EqualsLiteral("IPF.Appointment"))
357       bResult = TRUE;
358     else if (pNameStr.EqualsLiteral("IPF.Contact"))
359       bResult = TRUE;
360     else if (pNameStr.EqualsLiteral("IPF.Journal"))
361       bResult = TRUE;
362     else if (pNameStr.EqualsLiteral("IPF.StickyNote"))
363       bResult = TRUE;
364     else if (pNameStr.EqualsLiteral("IPF.Task"))
365       bResult = TRUE;
366     // Skip IMAP folders
367     else if (pNameStr.EqualsLiteral("IPF.Imap"))
368       bResult = TRUE;
369     // else if (!stricmp(pName, "IPF.Note"))
370     //  bResult = TRUE;
371   } else {
372     bResult = TRUE;
373     if (pNameStr.EqualsLiteral("IPF.Contact")) bResult = FALSE;
374   }
375 
376   return bResult;
377 }
378 
HandleHierarchyItem(ULONG oType,ULONG cb,LPENTRYID pEntry)379 BOOL CGetStoreFoldersIter::HandleHierarchyItem(ULONG oType, ULONG cb,
380                                                LPENTRYID pEntry) {
381   if (oType == MAPI_FOLDER) {
382     LPMAPIFOLDER pFolder;
383     if (m_pApi->OpenEntry(cb, pEntry, (LPUNKNOWN*)&pFolder)) {
384       LPSPropValue pVal;
385       nsString name;
386 
387       pVal = m_pApi->GetMapiProperty(pFolder, PR_CONTAINER_CLASS);
388       if (pVal)
389         m_pApi->GetStringFromProp(pVal, name);
390       else
391         name.Truncate();
392 
393       if ((name.IsEmpty() && m_isMail) || (!ExcludeFolderClass(name.get()))) {
394         pVal = m_pApi->GetMapiProperty(pFolder, PR_DISPLAY_NAME);
395         m_pApi->GetStringFromProp(pVal, name);
396         CMapiFolder* pNewFolder =
397             new CMapiFolder(name.get(), cb, pEntry, m_depth);
398         m_pList->AddItem(pNewFolder);
399 
400         pVal = m_pApi->GetMapiProperty(pFolder, PR_FOLDER_TYPE);
401         MAPI_TRACE2("Type: %d, name: %s\n", m_pApi->GetLongFromProp(pVal),
402                     name.get());
403         // m_pApi->ListProperties(pFolder);
404 
405         CGetStoreFoldersIter nextIter(m_pApi, *m_pList, m_depth + 1, m_isMail);
406         m_pApi->IterateHierarchy(&nextIter, pFolder);
407       }
408       pFolder->Release();
409     } else {
410       MAPI_TRACE0(
411           "GetStoreFolders - HandleHierarchyItem: Error opening folder "
412           "entry.\n");
413       return FALSE;
414     }
415   } else
416     MAPI_TRACE1(
417         "GetStoreFolders - HandleHierarchyItem: Unhandled ObjectType: %ld\n",
418         oType);
419   return TRUE;
420 }
421 
GetStoreFolders(ULONG cbEid,LPENTRYID lpEid,CMapiFolderList & folders,int startDepth)422 BOOL CMapiApi::GetStoreFolders(ULONG cbEid, LPENTRYID lpEid,
423                                CMapiFolderList& folders, int startDepth) {
424   // Fill in the array with the folders in the given store
425   if (!m_initialized || !m_lpSession) {
426     MAPI_TRACE0("MAPI not initialized for GetStoreFolders\n");
427     return FALSE;
428   }
429 
430   m_lpMdb = NULL;
431 
432   CMsgStore* pStore = FindMessageStore(cbEid, lpEid);
433   BOOL bResult = FALSE;
434   LPSPropValue pVal;
435 
436   if (pStore && pStore->Open(m_lpSession, &m_lpMdb)) {
437     // Successful open, do the iteration of the store
438     pVal = GetMapiProperty(m_lpMdb, PR_IPM_SUBTREE_ENTRYID);
439     if (pVal) {
440       ULONG cbEntry;
441       LPENTRYID pEntry;
442       LPMAPIFOLDER lpSubTree = NULL;
443 
444       if (GetEntryIdFromProp(pVal, cbEntry, pEntry)) {
445         // Open up the folder!
446         bResult = OpenEntry(cbEntry, pEntry, (LPUNKNOWN*)&lpSubTree);
447         MAPIFreeBuffer(pEntry);
448         if (bResult && lpSubTree) {
449           // Iterate the subtree with the results going into the folder list
450           CGetStoreFoldersIter iterHandler(this, folders, startDepth);
451           bResult = IterateHierarchy(&iterHandler, lpSubTree);
452           lpSubTree->Release();
453         } else {
454           MAPI_TRACE0("GetStoreFolders: Error opening sub tree.\n");
455         }
456       } else {
457         MAPI_TRACE0(
458             "GetStoreFolders: Error getting entryID from sub tree property "
459             "val.\n");
460       }
461     } else {
462       MAPI_TRACE0("GetStoreFolders: Error getting sub tree property.\n");
463     }
464   } else {
465     MAPI_TRACE0("GetStoreFolders: Error opening message store.\n");
466   }
467 
468   return bResult;
469 }
470 
GetStoreAddressFolders(ULONG cbEid,LPENTRYID lpEid,CMapiFolderList & folders)471 BOOL CMapiApi::GetStoreAddressFolders(ULONG cbEid, LPENTRYID lpEid,
472                                       CMapiFolderList& folders) {
473   // Fill in the array with the folders in the given store
474   if (!m_initialized || !m_lpSession) {
475     MAPI_TRACE0("MAPI not initialized for GetStoreAddressFolders\n");
476     return FALSE;
477   }
478 
479   m_lpMdb = NULL;
480 
481   CMsgStore* pStore = FindMessageStore(cbEid, lpEid);
482   BOOL bResult = FALSE;
483   LPSPropValue pVal;
484 
485   if (pStore && pStore->Open(m_lpSession, &m_lpMdb)) {
486     // Successful open, do the iteration of the store
487     pVal = GetMapiProperty(m_lpMdb, PR_IPM_SUBTREE_ENTRYID);
488     if (pVal) {
489       ULONG cbEntry;
490       LPENTRYID pEntry;
491       LPMAPIFOLDER lpSubTree = NULL;
492 
493       if (GetEntryIdFromProp(pVal, cbEntry, pEntry)) {
494         // Open up the folder!
495         bResult = OpenEntry(cbEntry, pEntry, (LPUNKNOWN*)&lpSubTree);
496         MAPIFreeBuffer(pEntry);
497         if (bResult && lpSubTree) {
498           // Iterate the subtree with the results going into the folder list
499           CGetStoreFoldersIter iterHandler(this, folders, 1, FALSE);
500           bResult = IterateHierarchy(&iterHandler, lpSubTree);
501           lpSubTree->Release();
502         } else {
503           MAPI_TRACE0("GetStoreAddressFolders: Error opening sub tree.\n");
504         }
505       } else {
506         MAPI_TRACE0(
507             "GetStoreAddressFolders: Error getting entryID from sub tree "
508             "property val.\n");
509       }
510     } else {
511       MAPI_TRACE0("GetStoreAddressFolders: Error getting sub tree property.\n");
512     }
513   } else
514     MAPI_TRACE0("GetStoreAddressFolders: Error opening message store.\n");
515 
516   return bResult;
517 }
518 
OpenStore(ULONG cbEid,LPENTRYID lpEid,LPMDB * ppMdb)519 BOOL CMapiApi::OpenStore(ULONG cbEid, LPENTRYID lpEid, LPMDB* ppMdb) {
520   if (!m_lpSession) {
521     MAPI_TRACE0("OpenStore called before a session was opened\n");
522     return FALSE;
523   }
524 
525   CMsgStore* pStore = FindMessageStore(cbEid, lpEid);
526   if (pStore && pStore->Open(m_lpSession, ppMdb)) return TRUE;
527   return FALSE;
528 }
529 
OpenEntry(ULONG cbEntry,LPENTRYID pEntryId,LPUNKNOWN * ppOpen)530 BOOL CMapiApi::OpenEntry(ULONG cbEntry, LPENTRYID pEntryId, LPUNKNOWN* ppOpen) {
531   if (!m_lpMdb) {
532     MAPI_TRACE0("OpenEntry called before the message store is open\n");
533     return FALSE;
534   }
535 
536   return OpenMdbEntry(m_lpMdb, cbEntry, pEntryId, ppOpen);
537 }
538 
OpenMdbEntry(LPMDB lpMdb,ULONG cbEntry,LPENTRYID pEntryId,LPUNKNOWN * ppOpen)539 BOOL CMapiApi::OpenMdbEntry(LPMDB lpMdb, ULONG cbEntry, LPENTRYID pEntryId,
540                             LPUNKNOWN* ppOpen) {
541   ULONG ulObjType;
542   HRESULT hr;
543   hr = m_lpSession->OpenEntry(cbEntry, pEntryId, NULL, 0, &ulObjType,
544                               (LPUNKNOWN*)ppOpen);
545   if (FAILED(hr)) {
546     MAPI_TRACE2("OpenMdbEntry failed: 0x%lx, %d\n", (long)hr, (int)hr);
547     return FALSE;
548   }
549   return TRUE;
550 }
551 
552 enum { ieidPR_ENTRYID = 0, ieidPR_OBJECT_TYPE, ieidMax };
553 
554 static const SizedSPropTagArray(ieidMax, ptaEid) = {ieidMax,
555                                                     {
556                                                         PR_ENTRYID,
557                                                         PR_OBJECT_TYPE,
558                                                     }};
559 
IterateContents(CMapiContentIter * pIter,LPMAPIFOLDER pFolder,ULONG flags)560 BOOL CMapiApi::IterateContents(CMapiContentIter* pIter, LPMAPIFOLDER pFolder,
561                                ULONG flags) {
562   // flags can be 0 or MAPI_ASSOCIATED
563   // MAPI_ASSOCIATED is usually used for forms and views
564 
565   HRESULT hr;
566   LPMAPITABLE lpTable;
567   hr = pFolder->GetContentsTable(flags, &lpTable);
568   if (FAILED(hr)) {
569     MAPI_TRACE2("GetContentsTable failed: 0x%lx, %d\n", (long)hr, (int)hr);
570     return FALSE;
571   }
572 
573   ULONG rowCount;
574   hr = lpTable->GetRowCount(0, &rowCount);
575   if (!rowCount) {
576     MAPI_TRACE0("  Empty Table\n");
577   }
578 
579   hr = lpTable->SetColumns((LPSPropTagArray)&ptaEid, 0);
580   if (FAILED(hr)) {
581     lpTable->Release();
582     MAPI_TRACE2("SetColumns failed: 0x%lx, %d\n", (long)hr, (int)hr);
583     return FALSE;
584   }
585 
586   hr = lpTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
587   if (FAILED(hr)) {
588     lpTable->Release();
589     MAPI_TRACE2("SeekRow failed: 0x%lx, %d\n", (long)hr, (int)hr);
590     return FALSE;
591   }
592 
593   int cNumRows = 0;
594   LPSRowSet lpRow;
595   BOOL keepGoing = TRUE;
596   BOOL bResult = TRUE;
597   do {
598     lpRow = NULL;
599     hr = lpTable->QueryRows(1, 0, &lpRow);
600     if (HR_FAILED(hr)) {
601       MAPI_TRACE2("QueryRows failed: 0x%lx, %d\n", (long)hr, (int)hr);
602       bResult = FALSE;
603       break;
604     }
605 
606     if (lpRow) {
607       cNumRows = lpRow->cRows;
608       if (cNumRows) {
609         LPENTRYID lpEID =
610             (LPENTRYID)lpRow->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.lpb;
611         ULONG cbEID = lpRow->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.cb;
612         ULONG oType = lpRow->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.ul;
613         keepGoing = HandleContentsItem(oType, cbEID, lpEID);
614         MAPI_TRACE1("    ObjectType: %ld\n", oType);
615       }
616       FreeProws(lpRow);
617     }
618 
619   } while (SUCCEEDED(hr) && cNumRows && lpRow && keepGoing);
620 
621   lpTable->Release();
622   return bResult;
623 }
624 
HandleContentsItem(ULONG oType,ULONG cb,LPENTRYID pEntry)625 BOOL CMapiApi::HandleContentsItem(ULONG oType, ULONG cb, LPENTRYID pEntry) {
626   if (oType == MAPI_MESSAGE) {
627     LPMESSAGE pMsg;
628     if (OpenEntry(cb, pEntry, (LPUNKNOWN*)&pMsg)) {
629       LPSPropValue pVal;
630       pVal = GetMapiProperty(pMsg, PR_SUBJECT);
631       ReportStringProp("PR_SUBJECT:", pVal);
632       pVal = GetMapiProperty(pMsg, PR_DISPLAY_BCC);
633       ReportStringProp("PR_DISPLAY_BCC:", pVal);
634       pVal = GetMapiProperty(pMsg, PR_DISPLAY_CC);
635       ReportStringProp("PR_DISPLAY_CC:", pVal);
636       pVal = GetMapiProperty(pMsg, PR_DISPLAY_TO);
637       ReportStringProp("PR_DISPLAY_TO:", pVal);
638       pVal = GetMapiProperty(pMsg, PR_MESSAGE_CLASS);
639       ReportStringProp("PR_MESSAGE_CLASS:", pVal);
640       ListProperties(pMsg);
641       pMsg->Release();
642     } else {
643       MAPI_TRACE0("    Folder type - error opening\n");
644     }
645   } else
646     MAPI_TRACE1("    ObjectType: %ld\n", oType);
647 
648   return TRUE;
649 }
650 
ListProperties(LPMAPIPROP lpProp,BOOL getValues)651 void CMapiApi::ListProperties(LPMAPIPROP lpProp, BOOL getValues) {
652   LPSPropTagArray pArray;
653   HRESULT hr = lpProp->GetPropList(0, &pArray);
654   if (FAILED(hr)) {
655     MAPI_TRACE0("    Unable to retrieve property list\n");
656     return;
657   }
658   ULONG count = 0;
659   LPMAPINAMEID FAR* lppPropNames;
660   SPropTagArray tagArray;
661   LPSPropTagArray lpTagArray = &tagArray;
662   tagArray.cValues = (ULONG)1;
663   nsCString desc;
664   for (ULONG i = 0; i < pArray->cValues; i++) {
665     GetPropTagName(pArray->aulPropTag[i], desc);
666     if (getValues) {
667       tagArray.aulPropTag[0] = pArray->aulPropTag[i];
668       hr = lpProp->GetNamesFromIDs(&lpTagArray, nullptr, 0, &count,
669                                    &lppPropNames);
670       if (hr == S_OK) MAPIFreeBuffer(lppPropNames);
671 
672       LPSPropValue pVal = GetMapiProperty(lpProp, pArray->aulPropTag[i]);
673       if (pVal) {
674         desc += ", ";
675         ListPropertyValue(pVal, desc);
676         MAPIFreeBuffer(pVal);
677       }
678     }
679     MAPI_TRACE2("    Tag #%d: %s\n", (int)i, desc.get());
680   }
681 
682   MAPIFreeBuffer(pArray);
683 }
684 
GetEmailPropertyTag(LPMAPIPROP lpProp,LONG nameID)685 ULONG CMapiApi::GetEmailPropertyTag(LPMAPIPROP lpProp, LONG nameID) {
686   static GUID emailGUID = {0x00062004,
687                            0x0000,
688                            0x0000,
689                            {0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
690 
691   MAPINAMEID mapiNameID;
692   mapiNameID.lpguid = &emailGUID;
693   mapiNameID.ulKind = MNID_ID;
694   mapiNameID.Kind.lID = nameID;
695 
696   LPMAPINAMEID lpMapiNames = &mapiNameID;
697   LPSPropTagArray lpMailTagArray = nullptr;
698 
699   HRESULT result =
700       lpProp->GetIDsFromNames(1L, &lpMapiNames, 0, &lpMailTagArray);
701   if (result == S_OK) {
702     ULONG lTag = lpMailTagArray->aulPropTag[0];
703     MAPIFreeBuffer(lpMailTagArray);
704     return lTag;
705   } else
706     return 0L;
707 }
708 
HandleHierarchyItem(ULONG oType,ULONG cb,LPENTRYID pEntry)709 BOOL CMapiApi::HandleHierarchyItem(ULONG oType, ULONG cb, LPENTRYID pEntry) {
710   if (oType == MAPI_FOLDER) {
711     LPMAPIFOLDER pFolder;
712     if (OpenEntry(cb, pEntry, (LPUNKNOWN*)&pFolder)) {
713       LPSPropValue pVal;
714       pVal = GetMapiProperty(pFolder, PR_DISPLAY_NAME);
715       ReportStringProp("Folder name:", pVal);
716       IterateContents(NULL, pFolder);
717       IterateHierarchy(NULL, pFolder);
718       pFolder->Release();
719     } else {
720       MAPI_TRACE0("    Folder type - error opening\n");
721     }
722   } else
723     MAPI_TRACE1("    ObjectType: %ld\n", oType);
724 
725   return TRUE;
726 }
727 
IterateHierarchy(CMapiHierarchyIter * pIter,LPMAPIFOLDER pFolder,ULONG flags)728 BOOL CMapiApi::IterateHierarchy(CMapiHierarchyIter* pIter, LPMAPIFOLDER pFolder,
729                                 ULONG flags) {
730   // flags can be CONVENIENT_DEPTH or 0
731   // CONVENIENT_DEPTH will return all depths I believe instead
732   // of just children
733   HRESULT hr;
734   LPMAPITABLE lpTable;
735   hr = pFolder->GetHierarchyTable(flags, &lpTable);
736   if (HR_FAILED(hr)) {
737     m_lastError = hr;
738     MAPI_TRACE2("IterateHierarchy: GetContentsTable failed: 0x%lx, %d\n",
739                 (long)hr, (int)hr);
740     return FALSE;
741   }
742 
743   ULONG rowCount;
744   hr = lpTable->GetRowCount(0, &rowCount);
745   if (!rowCount) {
746     lpTable->Release();
747     return TRUE;
748   }
749 
750   hr = lpTable->SetColumns((LPSPropTagArray)&ptaEid, 0);
751   if (HR_FAILED(hr)) {
752     m_lastError = hr;
753     lpTable->Release();
754     MAPI_TRACE2("IterateHierarchy: SetColumns failed: 0x%lx, %d\n", (long)hr,
755                 (int)hr);
756     return FALSE;
757   }
758 
759   hr = lpTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
760   if (HR_FAILED(hr)) {
761     m_lastError = hr;
762     lpTable->Release();
763     MAPI_TRACE2("IterateHierarchy: SeekRow failed: 0x%lx, %d\n", (long)hr,
764                 (int)hr);
765     return FALSE;
766   }
767 
768   int cNumRows = 0;
769   LPSRowSet lpRow;
770   BOOL keepGoing = TRUE;
771   BOOL bResult = TRUE;
772   do {
773     lpRow = NULL;
774     hr = lpTable->QueryRows(1, 0, &lpRow);
775 
776     if (HR_FAILED(hr)) {
777       MAPI_TRACE2("QueryRows failed: 0x%lx, %d\n", (long)hr, (int)hr);
778       m_lastError = hr;
779       bResult = FALSE;
780       break;
781     }
782 
783     if (lpRow) {
784       cNumRows = lpRow->cRows;
785 
786       if (cNumRows) {
787         LPENTRYID lpEntry =
788             (LPENTRYID)lpRow->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.lpb;
789         ULONG cb = lpRow->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.cb;
790         ULONG oType = lpRow->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.ul;
791 
792         if (pIter)
793           keepGoing = pIter->HandleHierarchyItem(oType, cb, lpEntry);
794         else
795           keepGoing = HandleHierarchyItem(oType, cb, lpEntry);
796       }
797       FreeProws(lpRow);
798     }
799   } while (SUCCEEDED(hr) && cNumRows && lpRow && keepGoing);
800 
801   lpTable->Release();
802 
803   if (bResult && !keepGoing) bResult = FALSE;
804 
805   return bResult;
806 }
807 
808 enum { itblPR_DISPLAY_NAME, itblPR_ENTRYID, itblMax };
809 
810 static const SizedSPropTagArray(itblMax, ptaTbl) = {itblMax,
811                                                     {
812                                                         PR_DISPLAY_NAME,
813                                                         PR_ENTRYID,
814                                                     }};
815 
IterateStores(CMapiFolderList & stores)816 BOOL CMapiApi::IterateStores(CMapiFolderList& stores) {
817   stores.ClearAll();
818 
819   if (!m_lpSession) {
820     MAPI_TRACE0("IterateStores called before session is open\n");
821     m_lastError = E_UNEXPECTED;
822     return FALSE;
823   }
824 
825   HRESULT hr;
826 
827   /* -- Some Microsoft sample code just to see if things are working --- */ /*
828 
829    ULONG    cbEIDStore;
830    LPENTRYID  lpEIDStore;
831 
832      hr = HrMAPIFindDefaultMsgStore(m_lpSession, &cbEIDStore, &lpEIDStore);
833      if (HR_FAILED(hr)) {
834          MAPI_TRACE0("Default message store not found\n");
835      // MessageBoxW(NULL, L"Message Store Not Found", NULL, MB_OK);
836      }
837    else {
838      LPMDB  lpStore;
839      MAPI_TRACE0("Default Message store FOUND\n");
840      hr = m_lpSession->OpenMsgStore(NULL, cbEIDStore,
841                                    lpEIDStore, NULL,
842                                    MDB_NO_MAIL | MDB_NO_DIALOG, &lpStore);
843      if (HR_FAILED(hr)) {
844        MAPI_TRACE1("Unable to open default message store: 0x%lx\n", hr);
845      }
846      else {
847        MAPI_TRACE0("Default message store OPENED\n");
848        lpStore->Release();
849      }
850     }
851       */
852 
853   LPMAPITABLE lpTable;
854 
855   hr = m_lpSession->GetMsgStoresTable(0, &lpTable);
856   if (FAILED(hr)) {
857     MAPI_TRACE0("GetMsgStoresTable failed\n");
858     m_lastError = hr;
859     return FALSE;
860   }
861 
862   ULONG rowCount;
863   hr = lpTable->GetRowCount(0, &rowCount);
864   MAPI_TRACE1("MsgStores Table rowCount: %ld\n", rowCount);
865 
866   hr = lpTable->SetColumns((LPSPropTagArray)&ptaTbl, 0);
867   if (FAILED(hr)) {
868     lpTable->Release();
869     MAPI_TRACE2("SetColumns failed: 0x%lx, %d\n", (long)hr, (int)hr);
870     m_lastError = hr;
871     return FALSE;
872   }
873 
874   hr = lpTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
875   if (FAILED(hr)) {
876     lpTable->Release();
877     MAPI_TRACE2("SeekRow failed: 0x%lx, %d\n", (long)hr, (int)hr);
878     m_lastError = hr;
879     return FALSE;
880   }
881 
882   int cNumRows = 0;
883   LPSRowSet lpRow;
884   BOOL keepGoing = TRUE;
885   BOOL bResult = TRUE;
886   do {
887     lpRow = NULL;
888     hr = lpTable->QueryRows(1, 0, &lpRow);
889 
890     if (HR_FAILED(hr)) {
891       MAPI_TRACE2("QueryRows failed: 0x%lx, %d\n", (long)hr, (int)hr);
892       bResult = FALSE;
893       m_lastError = hr;
894       break;
895     }
896 
897     if (lpRow) {
898       cNumRows = lpRow->cRows;
899 
900       if (cNumRows) {
901         LPCTSTR lpStr =
902             (LPCTSTR)lpRow->aRow[0].lpProps[itblPR_DISPLAY_NAME].Value.LPSZ;
903         LPENTRYID lpEID =
904             (LPENTRYID)lpRow->aRow[0].lpProps[itblPR_ENTRYID].Value.bin.lpb;
905         ULONG cbEID = lpRow->aRow[0].lpProps[itblPR_ENTRYID].Value.bin.cb;
906 
907         // In the future, GetStoreInfo needs to somehow return
908         // whether or not the store is from an IMAP server.
909         // Currently, GetStoreInfo opens the store and attempts
910         // to get the hierarchy tree.  If the tree is empty or
911         // does not exist, then szContents will be zero.  We'll
912         // assume that any store that doesn't have anything in
913         // it's hierarchy tree is not a store we want to import -
914         // there would be nothing to import from anyway!
915         // Currently, this does exclude IMAP server accounts
916         // which is the desired behaviour.
917 
918         int strLen = strlen(lpStr);
919         char16_t* pwszStr =
920             (char16_t*)moz_xmalloc((strLen + 1) * sizeof(WCHAR));
921         if (!pwszStr) {
922           // out of memory
923           FreeProws(lpRow);
924           lpTable->Release();
925           return FALSE;
926         }
927         ::MultiByteToWideChar(CP_ACP, 0, lpStr, strlen(lpStr) + 1,
928                               reinterpret_cast<wchar_t*>(pwszStr),
929                               (strLen + 1) * sizeof(WCHAR));
930         CMapiFolder* pFolder =
931             new CMapiFolder(pwszStr, cbEID, lpEID, 0, MAPI_STORE);
932         free(pwszStr);
933 
934         long szContents = 1;
935         GetStoreInfo(pFolder, &szContents);
936 
937         MAPI_TRACE1("    DisplayName: %s\n", lpStr);
938         if (szContents)
939           stores.AddItem(pFolder);
940         else {
941           delete pFolder;
942           MAPI_TRACE0("    ^^^^^ Not added to store list\n");
943         }
944 
945         keepGoing = TRUE;
946       }
947       FreeProws(lpRow);
948     }
949   } while (SUCCEEDED(hr) && cNumRows && lpRow && keepGoing);
950 
951   lpTable->Release();
952 
953   return bResult;
954 }
955 
GetStoreInfo(CMapiFolder * pFolder,long * pSzContents)956 void CMapiApi::GetStoreInfo(CMapiFolder* pFolder, long* pSzContents) {
957   HRESULT hr;
958   LPMDB lpMdb;
959 
960   if (pSzContents) *pSzContents = 0;
961 
962   if (!OpenStore(pFolder->GetCBEntryID(), pFolder->GetEntryID(), &lpMdb))
963     return;
964 
965   LPSPropValue pVal;
966   /*
967   pVal = GetMapiProperty(lpMdb, PR_DISPLAY_NAME);
968   ReportStringProp("    Message store name:", pVal);
969   pVal = GetMapiProperty(lpMdb, PR_MDB_PROVIDER);
970   ReportUIDProp("    Message store provider:", pVal);
971   pVal = GetMapiProperty(lpMdb, PR_COMMENT);
972   ReportStringProp("    Message comment:", pVal);
973   pVal = GetMapiProperty(lpMdb, PR_ACCESS_LEVEL);
974   ReportLongProp("    Message store Access Level:", pVal);
975   pVal = GetMapiProperty(lpMdb, PR_STORE_SUPPORT_MASK);
976   ReportLongProp("    Message store support mask:", pVal);
977   pVal = GetMapiProperty(lpMdb, PR_STORE_STATE);
978   ReportLongProp("    Message store state:", pVal);
979   pVal = GetMapiProperty(lpMdb, PR_OBJECT_TYPE);
980   ReportLongProp("    Message store object type:", pVal);
981   pVal = GetMapiProperty(lpMdb, PR_VALID_FOLDER_MASK);
982   ReportLongProp("    Message store valid folder mask:", pVal);
983 
984   pVal = GetMapiProperty(lpMdb, 0x8001001e);
985   ReportStringProp("    Message prop 0x8001001e:", pVal);
986 
987   // This key appears to be the OMI Account Manager account that corresponds
988   // to this message store.  This is important for IMAP accounts
989   // since we may not want to import messages from an IMAP store!
990   // Seems silly if you ask me!
991   // In order to test this, we'll need the registry key to look under to
992   determine
993   // if it contains the "IMAP Server" value, if it does then we are an
994   // IMAP store, if not, then we are a non-IMAP store - which may always mean
995   // a regular store that should be imported.
996 
997   pVal = GetMapiProperty(lpMdb, 0x80000003);
998   ReportLongProp("    Message prop 0x80000003:", pVal);
999 
1000   // ListProperties(lpMdb);
1001   */
1002 
1003   pVal = GetMapiProperty(lpMdb, PR_IPM_SUBTREE_ENTRYID);
1004   if (pVal) {
1005     ULONG cbEntry;
1006     LPENTRYID pEntry;
1007     LPMAPIFOLDER lpSubTree = NULL;
1008 
1009     if (GetEntryIdFromProp(pVal, cbEntry, pEntry)) {
1010       // Open up the folder!
1011       ULONG ulObjType;
1012       hr = lpMdb->OpenEntry(cbEntry, pEntry, NULL, 0, &ulObjType,
1013                             (LPUNKNOWN*)&lpSubTree);
1014       MAPIFreeBuffer(pEntry);
1015       if (SUCCEEDED(hr) && lpSubTree) {
1016         // Find out if there are any contents in the
1017         // tree.
1018         LPMAPITABLE lpTable;
1019         hr = lpSubTree->GetHierarchyTable(0, &lpTable);
1020         if (HR_FAILED(hr)) {
1021           MAPI_TRACE2("GetStoreInfo: GetHierarchyTable failed: 0x%lx, %d\n",
1022                       (long)hr, (int)hr);
1023         } else {
1024           ULONG rowCount;
1025           hr = lpTable->GetRowCount(0, &rowCount);
1026           lpTable->Release();
1027           if (SUCCEEDED(hr) && pSzContents) *pSzContents = (long)rowCount;
1028         }
1029 
1030         lpSubTree->Release();
1031       }
1032     }
1033   }
1034 }
1035 
ClearMessageStores(void)1036 void CMapiApi::ClearMessageStores(void) {
1037   if (m_pStores) {
1038     CMsgStore* pStore;
1039     for (size_t i = 0; i < m_pStores->Length(); i++) {
1040       pStore = m_pStores->ElementAt(i);
1041       delete pStore;
1042     }
1043     m_pStores->Clear();
1044   }
1045 }
1046 
AddMessageStore(CMsgStore * pStore)1047 void CMapiApi::AddMessageStore(CMsgStore* pStore) {
1048   if (m_pStores) m_pStores->AppendElement(pStore);
1049 }
1050 
FindMessageStore(ULONG cbEid,LPENTRYID lpEid)1051 CMsgStore* CMapiApi::FindMessageStore(ULONG cbEid, LPENTRYID lpEid) {
1052   if (!m_lpSession) {
1053     MAPI_TRACE0("FindMessageStore called before session is open\n");
1054     m_lastError = E_UNEXPECTED;
1055     return NULL;
1056   }
1057 
1058   ULONG result;
1059   HRESULT hr;
1060   CMsgStore* pStore;
1061   for (size_t i = 0; i < m_pStores->Length(); i++) {
1062     pStore = m_pStores->ElementAt(i);
1063     hr = m_lpSession->CompareEntryIDs(cbEid, lpEid, pStore->GetCBEntryID(),
1064                                       pStore->GetLPEntryID(), 0, &result);
1065     if (HR_FAILED(hr)) {
1066       MAPI_TRACE2("CompareEntryIDs failed: 0x%lx, %d\n", (long)hr, (int)hr);
1067       m_lastError = hr;
1068       return NULL;
1069     }
1070     if (result) {
1071       return pStore;
1072     }
1073   }
1074 
1075   pStore = new CMsgStore(cbEid, lpEid);
1076   AddMessageStore(pStore);
1077   return pStore;
1078 }
1079 
1080 // --------------------------------------------------------------------
1081 // Utility stuff
1082 // --------------------------------------------------------------------
1083 
GetMapiProperty(LPMAPIPROP pProp,ULONG tag)1084 LPSPropValue CMapiApi::GetMapiProperty(LPMAPIPROP pProp, ULONG tag) {
1085   if (!pProp) return NULL;
1086 
1087   int sz = CbNewSPropTagArray(1);
1088   SPropTagArray* pTag = (SPropTagArray*)new char[sz];
1089   pTag->cValues = 1;
1090   pTag->aulPropTag[0] = tag;
1091   LPSPropValue lpProp = NULL;
1092   ULONG cValues = 0;
1093   HRESULT hr = pProp->GetProps(pTag, 0, &cValues, &lpProp);
1094   delete[] pTag;
1095   if (HR_FAILED(hr) || (cValues != 1)) {
1096     if (lpProp) MAPIFreeBuffer(lpProp);
1097     return NULL;
1098   } else {
1099     if (PROP_TYPE(lpProp->ulPropTag) == PT_ERROR) {
1100       if (lpProp->Value.l == MAPI_E_NOT_FOUND) {
1101         MAPIFreeBuffer(lpProp);
1102         lpProp = NULL;
1103       }
1104     }
1105   }
1106 
1107   return lpProp;
1108 }
1109 
IsLargeProperty(LPSPropValue pVal)1110 BOOL CMapiApi::IsLargeProperty(LPSPropValue pVal) {
1111   return ((PROP_TYPE(pVal->ulPropTag) == PT_ERROR) &&
1112           (pVal->Value.l == E_OUTOFMEMORY));
1113 }
1114 
1115 // The output buffer (result) must be freed with operator delete[]
GetLargeProperty(LPMAPIPROP pProp,ULONG tag,void ** result)1116 BOOL CMapiApi::GetLargeProperty(LPMAPIPROP pProp, ULONG tag, void** result) {
1117   LPSTREAM lpStream;
1118   HRESULT hr =
1119       pProp->OpenProperty(tag, &IID_IStream, 0, 0, (LPUNKNOWN*)&lpStream);
1120   if (HR_FAILED(hr)) return FALSE;
1121   STATSTG st;
1122   BOOL bResult = TRUE;
1123   hr = lpStream->Stat(&st, STATFLAG_NONAME);
1124   if (HR_FAILED(hr))
1125     bResult = FALSE;
1126   else {
1127     if (!st.cbSize.QuadPart) st.cbSize.QuadPart = 1;
1128     char* pVal = new char[(int)st.cbSize.QuadPart + 2];
1129     if (pVal) {
1130       ULONG sz;
1131       hr = lpStream->Read(pVal, (ULONG)st.cbSize.QuadPart, &sz);
1132       if (HR_FAILED(hr)) {
1133         bResult = FALSE;
1134         delete[] pVal;
1135       } else {
1136         // Just in case it's a UTF16 string
1137         pVal[(int)st.cbSize.QuadPart] = pVal[(int)st.cbSize.QuadPart + 1] = 0;
1138         *result = pVal;
1139       }
1140     } else
1141       bResult = FALSE;
1142   }
1143 
1144   lpStream->Release();
1145 
1146   return bResult;
1147 }
1148 
GetLargeStringProperty(LPMAPIPROP pProp,ULONG tag,nsCString & val)1149 BOOL CMapiApi::GetLargeStringProperty(LPMAPIPROP pProp, ULONG tag,
1150                                       nsCString& val) {
1151   void* result;
1152   if (!GetLargeProperty(pProp, tag, &result)) return FALSE;
1153   if (PROP_TYPE(tag) == PT_UNICODE)  // unicode string
1154     LossyCopyUTF16toASCII(nsDependentString(static_cast<wchar_t*>(result)),
1155                           val);
1156   else  // either PT_STRING8 or some other binary - use as is
1157     val.Assign(static_cast<char*>(result));
1158   // Despite being used as wchar_t*, result it allocated as "new char[]" in
1159   // GetLargeProperty().
1160   delete[] static_cast<char*>(result);
1161   return TRUE;
1162 }
1163 
GetLargeStringProperty(LPMAPIPROP pProp,ULONG tag,nsString & val)1164 BOOL CMapiApi::GetLargeStringProperty(LPMAPIPROP pProp, ULONG tag,
1165                                       nsString& val) {
1166   void* result;
1167   if (!GetLargeProperty(pProp, tag, &result)) return FALSE;
1168   if (PROP_TYPE(tag) == PT_UNICODE)  // We already get the unicode string
1169     val.Assign(static_cast<wchar_t*>(result));
1170   else  // either PT_STRING8 or some other binary
1171     CStrToUnicode(static_cast<char*>(result), val);
1172   // Despite being used as wchar_t*, result it allocated as "new char[]" in
1173   // GetLargeProperty().
1174   delete[] static_cast<char*>(result);
1175   return TRUE;
1176 }
1177 // If the value is a string, get it...
GetEntryIdFromProp(LPSPropValue pVal,ULONG & cbEntryId,LPENTRYID & lpEntryId,BOOL delVal)1178 BOOL CMapiApi::GetEntryIdFromProp(LPSPropValue pVal, ULONG& cbEntryId,
1179                                   LPENTRYID& lpEntryId, BOOL delVal) {
1180   if (!pVal) return FALSE;
1181 
1182   BOOL bResult = TRUE;
1183   switch (PROP_TYPE(pVal->ulPropTag)) {
1184     case PT_BINARY:
1185       cbEntryId = pVal->Value.bin.cb;
1186       MAPIAllocateBuffer(cbEntryId, (LPVOID*)&lpEntryId);
1187       memcpy(lpEntryId, pVal->Value.bin.lpb, cbEntryId);
1188       break;
1189 
1190     default:
1191       MAPI_TRACE0("EntryId not in BINARY prop value\n");
1192       bResult = FALSE;
1193       break;
1194   }
1195 
1196   if (pVal && delVal) MAPIFreeBuffer(pVal);
1197 
1198   return bResult;
1199 }
1200 
GetStringFromProp(LPSPropValue pVal,nsCString & val,BOOL delVal)1201 BOOL CMapiApi::GetStringFromProp(LPSPropValue pVal, nsCString& val,
1202                                  BOOL delVal) {
1203   BOOL bResult = TRUE;
1204   if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_STRING8))
1205     val = pVal->Value.lpszA;
1206   else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_UNICODE))
1207     LossyCopyUTF16toASCII(nsDependentString(pVal->Value.lpszW), val);
1208   else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_NULL))
1209     val.Truncate();
1210   else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_ERROR)) {
1211     val.Truncate();
1212     bResult = FALSE;
1213   } else {
1214     if (pVal) {
1215       MAPI_TRACE1("GetStringFromProp: invalid value, expecting string - %d\n",
1216                   (int)PROP_TYPE(pVal->ulPropTag));
1217     } else {
1218       MAPI_TRACE0(
1219           "GetStringFromProp: invalid value, expecting string, got null "
1220           "pointer\n");
1221     }
1222     val.Truncate();
1223     bResult = FALSE;
1224   }
1225   if (pVal && delVal) MAPIFreeBuffer(pVal);
1226 
1227   return bResult;
1228 }
1229 
GetStringFromProp(LPSPropValue pVal,nsString & val,BOOL delVal)1230 BOOL CMapiApi::GetStringFromProp(LPSPropValue pVal, nsString& val,
1231                                  BOOL delVal) {
1232   BOOL bResult = TRUE;
1233   if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_STRING8)) {
1234     CStrToUnicode((const char*)pVal->Value.lpszA, val);
1235   } else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_UNICODE)) {
1236     val = (char16_t*)pVal->Value.lpszW;
1237   } else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_NULL)) {
1238     val.Truncate();
1239   } else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_ERROR)) {
1240     val.Truncate();
1241     bResult = FALSE;
1242   } else {
1243     if (pVal) {
1244       MAPI_TRACE1("GetStringFromProp: invalid value, expecting string - %d\n",
1245                   (int)PROP_TYPE(pVal->ulPropTag));
1246     } else {
1247       MAPI_TRACE0(
1248           "GetStringFromProp: invalid value, expecting string, got null "
1249           "pointer\n");
1250     }
1251     val.Truncate();
1252     bResult = FALSE;
1253   }
1254   if (pVal && delVal) MAPIFreeBuffer(pVal);
1255 
1256   return bResult;
1257 }
1258 
GetLongFromProp(LPSPropValue pVal,BOOL delVal)1259 LONG CMapiApi::GetLongFromProp(LPSPropValue pVal, BOOL delVal) {
1260   LONG val = 0;
1261   if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_LONG)) {
1262     val = pVal->Value.l;
1263   } else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_NULL)) {
1264     val = 0;
1265   } else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_ERROR)) {
1266     val = 0;
1267     MAPI_TRACE0("GetLongFromProp: Error retrieving property\n");
1268   } else {
1269     MAPI_TRACE0("GetLongFromProp: invalid value, expecting long\n");
1270   }
1271   if (pVal && delVal) MAPIFreeBuffer(pVal);
1272 
1273   return val;
1274 }
1275 
ReportUIDProp(const char * pTag,LPSPropValue pVal)1276 void CMapiApi::ReportUIDProp(const char* pTag, LPSPropValue pVal) {
1277   if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_BINARY)) {
1278     if (pVal->Value.bin.cb != 16) {
1279       MAPI_TRACE1("%s - INVALID, expecting 16 bytes of binary data for UID\n",
1280                   pTag);
1281     } else {
1282       nsIID uid;
1283       memcpy(&uid, pVal->Value.bin.lpb, 16);
1284       char* pStr = uid.ToString();
1285       if (pStr) {
1286         MAPI_TRACE2("%s %s\n", pTag, (const char*)pStr);
1287         free(pStr);
1288       }
1289     }
1290   } else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_NULL)) {
1291     MAPI_TRACE1("%s {NULL}\n", pTag);
1292   } else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_ERROR)) {
1293     MAPI_TRACE1("%s {Error retrieving property}\n", pTag);
1294   } else {
1295     MAPI_TRACE1("%s invalid value, expecting binary\n", pTag);
1296   }
1297   if (pVal) MAPIFreeBuffer(pVal);
1298 }
1299 
ReportLongProp(const char * pTag,LPSPropValue pVal)1300 void CMapiApi::ReportLongProp(const char* pTag, LPSPropValue pVal) {
1301   if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_LONG)) {
1302     nsCString num;
1303     nsCString num2;
1304 
1305     num.AppendInt((int32_t)pVal->Value.l);
1306     num2.AppendInt((int32_t)pVal->Value.l, 16);
1307     MAPI_TRACE3("%s %s, 0x%s\n", pTag, num, num2);
1308   } else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_NULL)) {
1309     MAPI_TRACE1("%s {NULL}\n", pTag);
1310   } else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_ERROR)) {
1311     MAPI_TRACE1("%s {Error retrieving property}\n", pTag);
1312   } else {
1313     MAPI_TRACE1("%s invalid value, expecting long\n", pTag);
1314   }
1315   if (pVal) MAPIFreeBuffer(pVal);
1316 }
1317 
ReportStringProp(const char * pTag,LPSPropValue pVal)1318 void CMapiApi::ReportStringProp(const char* pTag, LPSPropValue pVal) {
1319   if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_TSTRING)) {
1320     nsCString val((LPCTSTR)(pVal->Value.LPSZ));
1321     MAPI_TRACE2("%s %s\n", pTag, val.get());
1322   } else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_NULL)) {
1323     MAPI_TRACE1("%s {NULL}\n", pTag);
1324   } else if (pVal && (PROP_TYPE(pVal->ulPropTag) == PT_ERROR)) {
1325     MAPI_TRACE1("%s {Error retrieving property}\n", pTag);
1326   } else {
1327     MAPI_TRACE1("%s invalid value, expecting string\n", pTag);
1328   }
1329   if (pVal) MAPIFreeBuffer(pVal);
1330 }
1331 
GetPropTagName(ULONG tag,nsCString & s)1332 void CMapiApi::GetPropTagName(ULONG tag, nsCString& s) {
1333   char numStr[256];
1334   PR_snprintf(numStr, 256, "0x%lx, %ld", tag, tag);
1335   s = numStr;
1336   switch (tag) {
1337 #include "MapiTagStrs.cpp"
1338   }
1339   s += ", data: ";
1340   switch (PROP_TYPE(tag)) {
1341     case PT_UNSPECIFIED:
1342       s += "PT_UNSPECIFIED";
1343       break;
1344     case PT_NULL:
1345       s += "PT_NULL";
1346       break;
1347     case PT_I2:
1348       s += "PT_I2";
1349       break;
1350     case PT_LONG:
1351       s += "PT_LONG";
1352       break;
1353     case PT_R4:
1354       s += "PT_R4";
1355       break;
1356     case PT_DOUBLE:
1357       s += "PT_DOUBLE";
1358       break;
1359     case PT_CURRENCY:
1360       s += "PT_CURRENCY";
1361       break;
1362     case PT_APPTIME:
1363       s += "PT_APPTIME";
1364       break;
1365     case PT_ERROR:
1366       s += "PT_ERROR";
1367       break;
1368     case PT_BOOLEAN:
1369       s += "PT_BOOLEAN";
1370       break;
1371     case PT_OBJECT:
1372       s += "PT_OBJECT";
1373       break;
1374     case PT_I8:
1375       s += "PT_I8";
1376       break;
1377     case PT_STRING8:
1378       s += "PT_STRING8";
1379       break;
1380     case PT_UNICODE:
1381       s += "PT_UNICODE";
1382       break;
1383     case PT_SYSTIME:
1384       s += "PT_SYSTIME";
1385       break;
1386     case PT_CLSID:
1387       s += "PT_CLSID";
1388       break;
1389     case PT_BINARY:
1390       s += "PT_BINARY";
1391       break;
1392     case PT_MV_I2:
1393       s += "PT_MV_I2";
1394       break;
1395     case PT_MV_LONG:
1396       s += "PT_MV_LONG";
1397       break;
1398     case PT_MV_R4:
1399       s += "PT_MV_R4";
1400       break;
1401     case PT_MV_DOUBLE:
1402       s += "PT_MV_DOUBLE";
1403       break;
1404     case PT_MV_CURRENCY:
1405       s += "PT_MV_CURRENCY";
1406       break;
1407     case PT_MV_APPTIME:
1408       s += "PT_MV_APPTIME";
1409       break;
1410     case PT_MV_SYSTIME:
1411       s += "PT_MV_SYSTIME";
1412       break;
1413     case PT_MV_STRING8:
1414       s += "PT_MV_STRING8";
1415       break;
1416     case PT_MV_BINARY:
1417       s += "PT_MV_BINARY";
1418       break;
1419     case PT_MV_UNICODE:
1420       s += "PT_MV_UNICODE";
1421       break;
1422     case PT_MV_CLSID:
1423       s += "PT_MV_CLSID";
1424       break;
1425     case PT_MV_I8:
1426       s += "PT_MV_I8";
1427       break;
1428     default:
1429       s += "Unknown";
1430   }
1431 }
1432 
ListPropertyValue(LPSPropValue pVal,nsCString & s)1433 void CMapiApi::ListPropertyValue(LPSPropValue pVal, nsCString& s) {
1434   nsCString strVal;
1435   char nBuff[64];
1436 
1437   s += "value: ";
1438   switch (PROP_TYPE(pVal->ulPropTag)) {
1439     case PT_STRING8:
1440       GetStringFromProp(pVal, strVal, FALSE);
1441       if (strVal.Length() > 60) {
1442         strVal.SetLength(60);
1443         strVal += "...";
1444       }
1445       strVal.ReplaceSubstring("\r", "\\r");
1446       strVal.ReplaceSubstring("\n", "\\n");
1447       s += strVal;
1448       break;
1449     case PT_LONG:
1450       s.AppendInt((int32_t)pVal->Value.l);
1451       s += ", 0x";
1452       s.AppendInt((int32_t)pVal->Value.l, 16);
1453       s += nBuff;
1454       break;
1455     case PT_BOOLEAN:
1456       if (pVal->Value.b)
1457         s += "True";
1458       else
1459         s += "False";
1460       break;
1461     case PT_NULL:
1462       s += "--NULL--";
1463       break;
1464     case PT_SYSTIME: {
1465       /*
1466       COleDateTime  tm(pVal->Value.ft);
1467       s += tm.Format();
1468       */
1469       s += "-- Figure out how to format time in mozilla, PT_SYSTIME --";
1470     } break;
1471     default:
1472       s += "?";
1473   }
1474 }
1475 
1476 // -------------------------------------------------------------------
1477 // Folder list stuff
1478 // -------------------------------------------------------------------
CMapiFolderList()1479 CMapiFolderList::CMapiFolderList() {}
1480 
~CMapiFolderList()1481 CMapiFolderList::~CMapiFolderList() { ClearAll(); }
1482 
AddItem(CMapiFolder * pFolder)1483 void CMapiFolderList::AddItem(CMapiFolder* pFolder) {
1484   EnsureUniqueName(pFolder);
1485   GenerateFilePath(pFolder);
1486   m_array.AppendElement(pFolder);
1487 }
1488 
ChangeName(nsString & name)1489 void CMapiFolderList::ChangeName(nsString& name) {
1490   if (name.IsEmpty()) {
1491     name.Assign('1');
1492     return;
1493   }
1494   char16_t lastC = name.Last();
1495   if ((lastC >= '0') && (lastC <= '9')) {
1496     lastC++;
1497     if (lastC > '9') {
1498       lastC = '1';
1499       name.SetCharAt(lastC, name.Length() - 1);
1500       name.Append('0');
1501     } else {
1502       name.SetCharAt(lastC, name.Length() - 1);
1503     }
1504   } else {
1505     name.AppendLiteral(" 2");
1506   }
1507 }
1508 
EnsureUniqueName(CMapiFolder * pFolder)1509 void CMapiFolderList::EnsureUniqueName(CMapiFolder* pFolder) {
1510   // For everybody in the array before me with the SAME
1511   // depth, my name must be unique
1512   CMapiFolder* pCurrent;
1513   int i;
1514   BOOL done;
1515   nsString name;
1516   nsString cName;
1517 
1518   pFolder->GetDisplayName(name);
1519   do {
1520     done = TRUE;
1521     i = m_array.Length() - 1;
1522     while (i >= 0) {
1523       pCurrent = GetAt(i);
1524       if (pCurrent->GetDepth() == pFolder->GetDepth()) {
1525         pCurrent->GetDisplayName(cName);
1526         if (cName.Equals(name, nsCaseInsensitiveStringComparator)) {
1527           ChangeName(name);
1528           pFolder->SetDisplayName(name.get());
1529           done = FALSE;
1530           break;
1531         }
1532       } else if (pCurrent->GetDepth() < pFolder->GetDepth())
1533         break;
1534       i--;
1535     }
1536   } while (!done);
1537 }
1538 
GenerateFilePath(CMapiFolder * pFolder)1539 void CMapiFolderList::GenerateFilePath(CMapiFolder* pFolder) {
1540   // A file path, includes all of my parent's path, plus mine
1541   nsString name;
1542   nsString path;
1543   if (!pFolder->GetDepth()) {
1544     pFolder->GetDisplayName(name);
1545     pFolder->SetFilePath(name.get());
1546     return;
1547   }
1548 
1549   CMapiFolder* pCurrent;
1550   int i = m_array.Length() - 1;
1551   while (i >= 0) {
1552     pCurrent = GetAt(i);
1553     if (pCurrent->GetDepth() == (pFolder->GetDepth() - 1)) {
1554       pCurrent->GetFilePath(path);
1555       path.AppendLiteral(".sbd\\");
1556       pFolder->GetDisplayName(name);
1557       path += name;
1558       pFolder->SetFilePath(path.get());
1559       return;
1560     }
1561     i--;
1562   }
1563   pFolder->GetDisplayName(name);
1564   pFolder->SetFilePath(name.get());
1565 }
1566 
ClearAll(void)1567 void CMapiFolderList::ClearAll(void) {
1568   CMapiFolder* pFolder;
1569   for (size_t i = 0; i < m_array.Length(); i++) {
1570     pFolder = GetAt(i);
1571     delete pFolder;
1572   }
1573   m_array.Clear();
1574 }
1575 
DumpList(void)1576 void CMapiFolderList::DumpList(void) {
1577   CMapiFolder* pFolder;
1578   nsString str;
1579   int depth;
1580   char prefix[256];
1581 
1582   MAPI_TRACE0("Folder List ---------------------------------\n");
1583   for (size_t i = 0; i < m_array.Length(); i++) {
1584     pFolder = GetAt(i);
1585     depth = pFolder->GetDepth();
1586     pFolder->GetDisplayName(str);
1587     depth *= 2;
1588     if (depth > 255) depth = 255;
1589     memset(prefix, ' ', depth);
1590     prefix[depth] = 0;
1591 #ifdef MAPI_DEBUG
1592     char* ansiStr = ToNewCString(str);
1593     MAPI_TRACE2("%s%s: ", prefix, ansiStr);
1594     free(ansiStr);
1595 #endif
1596     pFolder->GetFilePath(str);
1597 #ifdef MAPI_DEBUG
1598     ansiStr = ToNewCString(str);
1599     MAPI_TRACE2("depth=%d, filePath=%s\n", pFolder->GetDepth(), ansiStr);
1600     free(ansiStr);
1601 #endif
1602   }
1603   MAPI_TRACE0("---------------------------------------------\n");
1604 }
1605 
CMapiFolder()1606 CMapiFolder::CMapiFolder() {
1607   m_objectType = MAPI_FOLDER;
1608   m_cbEid = 0;
1609   m_lpEid = NULL;
1610   m_depth = 0;
1611   m_doImport = TRUE;
1612 }
1613 
CMapiFolder(const char16_t * pDisplayName,ULONG cbEid,LPENTRYID lpEid,int depth,LONG oType)1614 CMapiFolder::CMapiFolder(const char16_t* pDisplayName, ULONG cbEid,
1615                          LPENTRYID lpEid, int depth, LONG oType) {
1616   m_cbEid = 0;
1617   m_lpEid = NULL;
1618   SetDisplayName(pDisplayName);
1619   SetEntryID(cbEid, lpEid);
1620   SetDepth(depth);
1621   SetObjectType(oType);
1622   SetDoImport(TRUE);
1623 }
1624 
CMapiFolder(const CMapiFolder * pCopyFrom)1625 CMapiFolder::CMapiFolder(const CMapiFolder* pCopyFrom) {
1626   m_lpEid = NULL;
1627   m_cbEid = 0;
1628   SetDoImport(pCopyFrom->GetDoImport());
1629   SetDisplayName(pCopyFrom->m_displayName.get());
1630   SetObjectType(pCopyFrom->GetObjectType());
1631   SetEntryID(pCopyFrom->GetCBEntryID(), pCopyFrom->GetEntryID());
1632   SetDepth(pCopyFrom->GetDepth());
1633   SetFilePath(pCopyFrom->m_mailFilePath.get());
1634 }
1635 
~CMapiFolder()1636 CMapiFolder::~CMapiFolder() {
1637   if (m_lpEid) delete m_lpEid;
1638 }
1639 
SetEntryID(ULONG cbEid,LPENTRYID lpEid)1640 void CMapiFolder::SetEntryID(ULONG cbEid, LPENTRYID lpEid) {
1641   if (m_lpEid) delete m_lpEid;
1642   m_lpEid = NULL;
1643   m_cbEid = cbEid;
1644   if (cbEid) {
1645     m_lpEid = new BYTE[cbEid];
1646     memcpy(m_lpEid, lpEid, cbEid);
1647   }
1648 }
1649 
1650 // ---------------------------------------------------------------------
1651 // Message store stuff
1652 // ---------------------------------------------------------------------
1653 
CMsgStore(ULONG cbEid,LPENTRYID lpEid)1654 CMsgStore::CMsgStore(ULONG cbEid, LPENTRYID lpEid) {
1655   m_lpEid = NULL;
1656   m_lpMdb = NULL;
1657   SetEntryID(cbEid, lpEid);
1658 }
1659 
~CMsgStore()1660 CMsgStore::~CMsgStore() {
1661   if (m_lpEid) delete m_lpEid;
1662 
1663   if (m_lpMdb) {
1664     ULONG flags = LOGOFF_NO_WAIT;
1665     m_lpMdb->StoreLogoff(&flags);
1666     m_lpMdb->Release();
1667     m_lpMdb = NULL;
1668   }
1669 }
1670 
SetEntryID(ULONG cbEid,LPENTRYID lpEid)1671 void CMsgStore::SetEntryID(ULONG cbEid, LPENTRYID lpEid) {
1672   if (m_lpEid) delete m_lpEid;
1673 
1674   m_lpEid = NULL;
1675   if (cbEid) {
1676     m_lpEid = new BYTE[cbEid];
1677     memcpy(m_lpEid, lpEid, cbEid);
1678   }
1679   m_cbEid = cbEid;
1680 
1681   if (m_lpMdb) {
1682     ULONG flags = LOGOFF_NO_WAIT;
1683     m_lpMdb->StoreLogoff(&flags);
1684     m_lpMdb->Release();
1685     m_lpMdb = NULL;
1686   }
1687 }
1688 
Open(LPMAPISESSION pSession,LPMDB * ppMdb)1689 BOOL CMsgStore::Open(LPMAPISESSION pSession, LPMDB* ppMdb) {
1690   if (m_lpMdb) {
1691     if (ppMdb) *ppMdb = m_lpMdb;
1692     return TRUE;
1693   }
1694 
1695   BOOL bResult = TRUE;
1696   HRESULT hr = pSession->OpenMsgStore(NULL, m_cbEid, (LPENTRYID)m_lpEid, NULL,
1697                                       MDB_NO_MAIL, &m_lpMdb);  // MDB pointer
1698   if (HR_FAILED(hr)) {
1699     m_lpMdb = NULL;
1700     MAPI_TRACE2("OpenMsgStore failed: 0x%lx, %d\n", (long)hr, (int)hr);
1701     bResult = FALSE;
1702   }
1703 
1704   if (ppMdb) *ppMdb = m_lpMdb;
1705   return bResult;
1706 }
1707 
1708 // ------------------------------------------------------------
1709 // Contents Iterator
1710 // -----------------------------------------------------------
1711 
CMapiFolderContents(LPMDB lpMdb,ULONG cbEid,LPENTRYID lpEid)1712 CMapiFolderContents::CMapiFolderContents(LPMDB lpMdb, ULONG cbEid,
1713                                          LPENTRYID lpEid) {
1714   m_lpMdb = lpMdb;
1715   m_fCbEid = cbEid;
1716   m_fLpEid = new BYTE[cbEid];
1717   memcpy(m_fLpEid, lpEid, cbEid);
1718   m_count = 0;
1719   m_iterCount = 0;
1720   m_failure = FALSE;
1721   m_lastError = 0;
1722   m_lpFolder = NULL;
1723   m_lpTable = NULL;
1724   m_lastLpEid = NULL;
1725   m_lastCbEid = 0;
1726 }
1727 
~CMapiFolderContents()1728 CMapiFolderContents::~CMapiFolderContents() {
1729   if (m_lastLpEid) delete m_lastLpEid;
1730   delete m_fLpEid;
1731   if (m_lpTable) m_lpTable->Release();
1732   if (m_lpFolder) m_lpFolder->Release();
1733 }
1734 
SetUpIter(void)1735 BOOL CMapiFolderContents::SetUpIter(void) {
1736   // First, open up the MAPIFOLDER object
1737   ULONG ulObjType;
1738   HRESULT hr;
1739   hr = m_lpMdb->OpenEntry(m_fCbEid, (LPENTRYID)m_fLpEid, NULL, 0, &ulObjType,
1740                           (LPUNKNOWN*)&m_lpFolder);
1741 
1742   if (FAILED(hr) || !m_lpFolder) {
1743     m_lpFolder = NULL;
1744     m_lastError = hr;
1745     MAPI_TRACE2("CMapiFolderContents OpenEntry failed: 0x%lx, %d\n", (long)hr,
1746                 (int)hr);
1747     return FALSE;
1748   }
1749 
1750   if (ulObjType != MAPI_FOLDER) {
1751     m_lastError = E_UNEXPECTED;
1752     MAPI_TRACE0("CMapiFolderContents - bad object type, not a folder.\n");
1753     return FALSE;
1754   }
1755 
1756   hr = m_lpFolder->GetContentsTable(0, &m_lpTable);
1757   if (FAILED(hr) || !m_lpTable) {
1758     m_lastError = hr;
1759     m_lpTable = NULL;
1760     MAPI_TRACE2("CMapiFolderContents - GetContentsTable failed: 0x%lx, %d\n",
1761                 (long)hr, (int)hr);
1762     return FALSE;
1763   }
1764 
1765   hr = m_lpTable->GetRowCount(0, &m_count);
1766   if (FAILED(hr)) {
1767     m_lastError = hr;
1768     MAPI_TRACE0("CMapiFolderContents - GetRowCount failed\n");
1769     return FALSE;
1770   }
1771 
1772   hr = m_lpTable->SetColumns((LPSPropTagArray)&ptaEid, 0);
1773   if (FAILED(hr)) {
1774     m_lastError = hr;
1775     MAPI_TRACE2("CMapiFolderContents - SetColumns failed: 0x%lx, %d\n",
1776                 (long)hr, (int)hr);
1777     return FALSE;
1778   }
1779 
1780   hr = m_lpTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
1781   if (FAILED(hr)) {
1782     m_lastError = hr;
1783     MAPI_TRACE2("CMapiFolderContents - SeekRow failed: 0x%lx, %d\n", (long)hr,
1784                 (int)hr);
1785     return FALSE;
1786   }
1787 
1788   return TRUE;
1789 }
1790 
GetNext(ULONG * pcbEid,LPENTRYID * ppEid,ULONG * poType,BOOL * pDone)1791 BOOL CMapiFolderContents::GetNext(ULONG* pcbEid, LPENTRYID* ppEid,
1792                                   ULONG* poType, BOOL* pDone) {
1793   *pDone = FALSE;
1794   if (m_failure) return FALSE;
1795   if (!m_lpFolder) {
1796     if (!SetUpIter()) {
1797       m_failure = TRUE;
1798       return FALSE;
1799     }
1800     if (!m_count) {
1801       *pDone = TRUE;
1802       return TRUE;
1803     }
1804   }
1805 
1806   int cNumRows = 0;
1807   LPSRowSet lpRow = NULL;
1808   HRESULT hr = m_lpTable->QueryRows(1, 0, &lpRow);
1809 
1810   if (HR_FAILED(hr)) {
1811     m_lastError = hr;
1812     m_failure = TRUE;
1813     MAPI_TRACE2("CMapiFolderContents - QueryRows failed: 0x%lx, %d\n", (long)hr,
1814                 (int)hr);
1815     return FALSE;
1816   }
1817 
1818   if (lpRow) {
1819     cNumRows = lpRow->cRows;
1820     if (cNumRows) {
1821       LPENTRYID lpEID =
1822           (LPENTRYID)lpRow->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.lpb;
1823       ULONG cbEID = lpRow->aRow[0].lpProps[ieidPR_ENTRYID].Value.bin.cb;
1824       ULONG oType = lpRow->aRow[0].lpProps[ieidPR_OBJECT_TYPE].Value.ul;
1825 
1826       if (m_lastCbEid != cbEID) {
1827         if (m_lastLpEid) delete m_lastLpEid;
1828         m_lastLpEid = new BYTE[cbEID];
1829         m_lastCbEid = cbEID;
1830       }
1831       memcpy(m_lastLpEid, lpEID, cbEID);
1832 
1833       *ppEid = (LPENTRYID)m_lastLpEid;
1834       *pcbEid = cbEID;
1835       *poType = oType;
1836     } else
1837       *pDone = TRUE;
1838     CMapiApi::FreeProws(lpRow);
1839   } else
1840     *pDone = TRUE;
1841 
1842   return TRUE;
1843 }
1844