Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

968 lines
32 KiB

  1. /*
  2. * imaputil.cpp
  3. *
  4. * Purpose:
  5. * Implements IMAP utility functions
  6. *
  7. * Owner:
  8. * Raych
  9. *
  10. * Copyright (C) Microsoft Corp. 1996
  11. */
  12. //---------------------------------------------------------------------------
  13. // Includes
  14. //---------------------------------------------------------------------------
  15. #include "pch.hxx"
  16. #include "imapute.h"
  17. #include "storutil.h"
  18. #include "imapsync.h"
  19. //---------------------------------------------------------------------------
  20. // Forward Declarations
  21. //---------------------------------------------------------------------------
  22. DWORD ImapUtil_ReverseSentence(LPSTR pszSentence, char cDelimiter);
  23. void ImapUtil_ReverseString(LPSTR pszStart, LPSTR pszEnd);
  24. //---------------------------------------------------------------------------
  25. // Module Constants
  26. //---------------------------------------------------------------------------
  27. const char c_szIMAP_MSG_ANSWERED[] = "Answered";
  28. const char c_szIMAP_MSG_FLAGGED[] = "Flagged";
  29. const char c_szIMAP_MSG_DELETED[] = "Deleted";
  30. const char c_szIMAP_MSG_DRAFT[] = "Draft";
  31. const char c_szIMAP_MSG_SEEN[] = "Seen";
  32. const char c_szBACKSLASH[] = "\\";
  33. typedef struct tagIMFToStr_LUT {
  34. IMAP_MSGFLAGS imfValue;
  35. LPCSTR pszValue;
  36. } IMFTOSTR_LUT;
  37. const IMFTOSTR_LUT g_IMFToStringLUT[] = {
  38. {IMAP_MSG_ANSWERED, c_szIMAP_MSG_ANSWERED},
  39. {IMAP_MSG_FLAGGED, c_szIMAP_MSG_FLAGGED},
  40. {IMAP_MSG_DELETED, c_szIMAP_MSG_DELETED},
  41. {IMAP_MSG_SEEN, c_szIMAP_MSG_SEEN},
  42. {IMAP_MSG_DRAFT, c_szIMAP_MSG_DRAFT}};
  43. //---------------------------------------------------------------------------
  44. // Functions
  45. //---------------------------------------------------------------------------
  46. //***************************************************************************
  47. // Function: ImapUtil_MsgFlagsToString
  48. //
  49. // Purpose:
  50. // This function converts a IMAP_MSGFLAGS register to its string
  51. // equivalent. For instance, IMAP_MSG_SEEN is converted to "(\Seen)".
  52. //
  53. // Arguments:
  54. // IMAP_MSGFLAGS imfSource [in] - IMAP_MSGFLAGS register to convert to
  55. // string.
  56. // LPSTR *ppszDestination [out] - the string equivalent is returned here.
  57. // If imfSource is 0, NULL is returned here. Otherwise, a string buffer
  58. // is returned which the caller must MemFree when he is done with it.
  59. // DWORD *pdwLengthOfDestination [out] - the length of *ppszDestination.
  60. // Pass in NULL if not interested.
  61. //
  62. // Returns:
  63. // HRESULT indicating success or failure. Remember that it is possible
  64. // for a successful HRESULT to be returned, even is *ppszDestination is NULL.
  65. //***************************************************************************
  66. HRESULT ImapUtil_MsgFlagsToString(IMAP_MSGFLAGS imfSource,
  67. LPSTR *ppszDestination,
  68. DWORD *pdwLengthOfDestination)
  69. {
  70. CByteStream bstmOutput;
  71. HRESULT hrResult;
  72. const IMFTOSTR_LUT *pCurrent;
  73. const IMFTOSTR_LUT *pLastEntry;
  74. BOOL fFirstFlag;
  75. TraceCall("ImapUtil_MsgFlagsToString");
  76. Assert(NULL != ppszDestination);
  77. AssertSz(0 == (imfSource & ~IMAP_MSG_ALLFLAGS), "Quit feeding me garbage.");
  78. // Codify assumptions
  79. Assert(IMAP_MSG_ALLFLAGS == 0x0000001F);
  80. *ppszDestination = NULL;
  81. if (NULL != pdwLengthOfDestination)
  82. *pdwLengthOfDestination = 0;
  83. if (0 == (imfSource & IMAP_MSG_ALLFLAGS))
  84. return S_FALSE; // Nothing to do here!
  85. hrResult = bstmOutput.Write("(", 1, NULL);
  86. if (FAILED(hrResult))
  87. goto exit;
  88. fFirstFlag = TRUE;
  89. pCurrent = g_IMFToStringLUT;
  90. pLastEntry = pCurrent + sizeof(g_IMFToStringLUT)/sizeof(IMFTOSTR_LUT) - 1;
  91. while (pCurrent <= pLastEntry) {
  92. if (imfSource & pCurrent->imfValue) {
  93. // Prepend a space to flag, if necessary
  94. if (FALSE == fFirstFlag) {
  95. hrResult = bstmOutput.Write(g_szSpace, 1, NULL);
  96. if (FAILED(hrResult))
  97. goto exit;
  98. }
  99. else
  100. fFirstFlag = FALSE;
  101. // Output the backslash
  102. hrResult = bstmOutput.Write(c_szBACKSLASH,
  103. sizeof(c_szBACKSLASH) - 1, NULL);
  104. if (FAILED(hrResult))
  105. goto exit;
  106. // Output string associated with this IMAP flag
  107. hrResult = bstmOutput.Write(pCurrent->pszValue,
  108. lstrlen(pCurrent->pszValue), NULL);
  109. if (FAILED(hrResult))
  110. goto exit;
  111. } // if (imfSource & pCurrent->imfValue)
  112. // Advance current pointer
  113. pCurrent += 1;
  114. } // while
  115. hrResult = bstmOutput.Write(")", 1, NULL);
  116. if (FAILED(hrResult))
  117. goto exit;
  118. hrResult = bstmOutput.HrAcquireStringA(pdwLengthOfDestination,
  119. ppszDestination, ACQ_DISPLACE);
  120. exit:
  121. return hrResult;
  122. } // IMAPMsgFlagsToString
  123. //***************************************************************************
  124. // Function: ImapUtil_FolderIDToPath
  125. //
  126. // Purpose:
  127. // This function takes the given FolderID and returns the full path
  128. // (including prefix) to the folder. The caller may also choose to append
  129. // a string to the path.
  130. //
  131. // Arguments:
  132. // FolderID idFolder [in] - FolderID to convert into a full path.
  133. // char **ppszPath [out] - a full path to idFolder is returned here.
  134. // LPDWORD pdwPathLen [out] - if non-NULL, the length of *ppszPath is
  135. // returned here.
  136. // char *pcHierarchyChar [out] - the hierarchy char used to interpret
  137. // *ppszPath is returned here.
  138. // CFolderCache *pFldrCache [in] - a CFolderCache to use to generate
  139. // the path.
  140. // LPCSTR pszAppendStr [in] - this can be NULL if the caller does not need
  141. // to append a string to the path. Otherwise, a hierarchy character is
  142. // appended to the path and this string is appended after the HC. This
  143. // argument is typically used to tack a wildcard to the end of the path.
  144. // LPCSTR pszRootFldrPrefix [in] - the root folder prefix for this IMAP
  145. // account. If this is NULL, this function will find out for itself.
  146. //
  147. // Returns:
  148. // HRESULT indicating success or failure.
  149. //***************************************************************************
  150. HRESULT ImapUtil_FolderIDToPath(FOLDERID idServer, FOLDERID idFolder, char **ppszPath,
  151. LPDWORD pdwPathLen, char *pcHierarchyChar,
  152. IMessageStore *pFldrCache, LPCSTR pszAppendStr,
  153. LPCSTR pszRootFldrPrefix)
  154. {
  155. FOLDERINFO fiPath;
  156. HRESULT hrResult;
  157. CByteStream bstmPath;
  158. DWORD dwLengthOfPath;
  159. LPSTR pszEnd;
  160. char szRootFldrPrefix[MAX_PATH];
  161. char szAccount[CCHMAX_ACCOUNT_NAME];
  162. BOOL fAppendStrHC = FALSE,
  163. fFreeFldrInfo = FALSE;
  164. BOOL fSpecialFldr = FALSE;
  165. DWORD dwLen;
  166. TraceCall("ImapUtil_FolderIDToPath");
  167. if (FOLDERID_INVALID == idFolder || FOLDERID_INVALID == idServer)
  168. {
  169. hrResult = TraceResult(E_INVALIDARG);
  170. goto exit;
  171. }
  172. // Build full path to current folder in reverse (leaf->root)
  173. // Limited buffer overflow risk since user input limited to MAX_PATH
  174. // Start off with target folder (leaf) and return its HC if so requested
  175. hrResult = pFldrCache->GetFolderInfo(idFolder, &fiPath);
  176. if (FAILED(hrResult))
  177. {
  178. TraceResult(hrResult);
  179. goto exit;
  180. }
  181. fFreeFldrInfo = TRUE;
  182. GetFolderAccountId(&fiPath, szAccount, ARRAYSIZE(szAccount));
  183. if (NULL != pcHierarchyChar)
  184. {
  185. Assert((BYTE)INVALID_HIERARCHY_CHAR != fiPath.bHierarchy);
  186. *pcHierarchyChar = (char) fiPath.bHierarchy;
  187. }
  188. // Append anything the user asked us to (will be at end of str after reversal)
  189. if (NULL != pszAppendStr)
  190. {
  191. char szBuf[MAX_PATH + 1];
  192. // First, have to reverse the append string itself, in case it contains HC's
  193. Assert(lstrlen(pszAppendStr) < ARRAYSIZE(szBuf));
  194. StrCpyN(szBuf, pszAppendStr, ARRAYSIZE(szBuf));
  195. dwLen = ImapUtil_ReverseSentence(szBuf, fiPath.bHierarchy);
  196. hrResult = bstmPath.Write(szBuf, dwLen, NULL);
  197. if (FAILED(hrResult))
  198. {
  199. TraceResult(hrResult);
  200. goto exit;
  201. }
  202. fAppendStrHC = TRUE;
  203. }
  204. // Check if user gave us a root folder prefix: otherwise we need to load it ourselves
  205. if (NULL == pszRootFldrPrefix)
  206. {
  207. ImapUtil_LoadRootFldrPrefix(szAccount, szRootFldrPrefix, sizeof(szRootFldrPrefix));
  208. pszRootFldrPrefix = szRootFldrPrefix;
  209. }
  210. else
  211. // Copy to our buffer because we're going to reverse the RFP
  212. StrCpyN(szRootFldrPrefix, pszRootFldrPrefix, ARRAYSIZE(szRootFldrPrefix));
  213. // Proceed to root
  214. while (FALSE == fSpecialFldr && idServer != fiPath.idFolder)
  215. {
  216. LPSTR pszFolderName;
  217. Assert(FOLDERID_INVALID != fiPath.idFolder);
  218. Assert(FOLDERID_ROOT != fiPath.idParent);
  219. if (fAppendStrHC)
  220. {
  221. // Separate append str from path with HC
  222. Assert((BYTE)INVALID_HIERARCHY_CHAR != fiPath.bHierarchy);
  223. hrResult = bstmPath.Write(&fiPath.bHierarchy, sizeof(fiPath.bHierarchy), NULL);
  224. if (FAILED(hrResult))
  225. {
  226. TraceResult(hrResult);
  227. goto exit;
  228. }
  229. fAppendStrHC = FALSE;
  230. }
  231. // Expand folder name to full path if this is a special folder
  232. if (FOLDER_NOTSPECIAL != fiPath.tySpecial)
  233. {
  234. char szSpecialFldrPath[MAX_PATH * 2 + 2]; // Room for HC and null-term
  235. fSpecialFldr = TRUE;
  236. hrResult = ImapUtil_SpecialFldrTypeToPath(szAccount, fiPath.tySpecial,
  237. szRootFldrPrefix, fiPath.bHierarchy, szSpecialFldrPath, sizeof(szSpecialFldrPath));
  238. if (FAILED(hrResult))
  239. {
  240. TraceResult(hrResult);
  241. goto exit;
  242. }
  243. // Reverse special folder path so we can append it. It will be reversed back to normal
  244. // There should be no trailing HC's
  245. //Assert((BYTE)INVALID_HIERARCHY_CHAR != fiPath.bHierarchy);
  246. dwLen = ImapUtil_ReverseSentence(szSpecialFldrPath, fiPath.bHierarchy);
  247. Assert(dwLen == 0 || fiPath.bHierarchy !=
  248. *(CharPrev(szSpecialFldrPath, szSpecialFldrPath + dwLen)));
  249. pszFolderName = szSpecialFldrPath;
  250. }
  251. else
  252. pszFolderName = fiPath.pszName;
  253. // Write folder name to stream
  254. hrResult = bstmPath.Write(pszFolderName, lstrlen(pszFolderName), NULL);
  255. if (FAILED(hrResult))
  256. {
  257. TraceResult(hrResult);
  258. goto exit;
  259. }
  260. //Assert((BYTE)INVALID_HIERARCHY_CHAR != fiPath.bHierarchy);
  261. hrResult = bstmPath.Write(&fiPath.bHierarchy, sizeof(fiPath.bHierarchy), NULL);
  262. if (FAILED(hrResult))
  263. {
  264. TraceResult(hrResult);
  265. goto exit;
  266. }
  267. pFldrCache->FreeRecord(&fiPath);
  268. fFreeFldrInfo = FALSE;
  269. hrResult = pFldrCache->GetFolderInfo(fiPath.idParent, &fiPath);
  270. if (FAILED(hrResult))
  271. {
  272. TraceResult(hrResult);
  273. goto exit;
  274. }
  275. fFreeFldrInfo = TRUE;
  276. } // while
  277. if (FALSE == fSpecialFldr && '\0' != szRootFldrPrefix[0])
  278. {
  279. if (fAppendStrHC)
  280. {
  281. // Separate append str from path with HC
  282. Assert((BYTE)INVALID_HIERARCHY_CHAR != fiPath.bHierarchy);
  283. hrResult = bstmPath.Write(&fiPath.bHierarchy, sizeof(fiPath.bHierarchy), NULL);
  284. if (FAILED(hrResult))
  285. {
  286. TraceResult(hrResult);
  287. goto exit;
  288. }
  289. fAppendStrHC = FALSE;
  290. }
  291. // Reverse root folder path so we can append it. It will be reversed back to normal
  292. // There should be no trailing HC's (ImapUtil_LoadRootFldrPrefix guarantees this)
  293. Assert((BYTE)INVALID_HIERARCHY_CHAR != fiPath.bHierarchy);
  294. dwLen = ImapUtil_ReverseSentence(szRootFldrPrefix, fiPath.bHierarchy);
  295. Assert(dwLen == 0 || fiPath.bHierarchy !=
  296. *(CharPrev(szRootFldrPrefix, szRootFldrPrefix + dwLen)));
  297. hrResult = bstmPath.Write(szRootFldrPrefix, dwLen, NULL);
  298. if (FAILED(hrResult))
  299. {
  300. TraceResult(hrResult);
  301. goto exit;
  302. }
  303. }
  304. // OK, path won't get any larger. Acquire mem buffer so we can reverse it
  305. hrResult = bstmPath.HrAcquireStringA(&dwLengthOfPath, ppszPath, ACQ_DISPLACE);
  306. if (FAILED(hrResult))
  307. {
  308. TraceResult(hrResult);
  309. goto exit;
  310. }
  311. // Blow away trailing hierarchy character or it becomes leading HC
  312. pszEnd = CharPrev(*ppszPath, *ppszPath + dwLengthOfPath);
  313. Assert('%' == *pszEnd || (BYTE)INVALID_HIERARCHY_CHAR != fiPath.bHierarchy);
  314. if (*pszEnd == (char) fiPath.bHierarchy)
  315. *pszEnd = '\0';
  316. // Reverse the 'sentence' (HC is delimiter) to get path
  317. dwLen = ImapUtil_ReverseSentence(*ppszPath, fiPath.bHierarchy);
  318. if (NULL != pdwPathLen)
  319. *pdwPathLen = dwLen;
  320. exit:
  321. if (fFreeFldrInfo)
  322. pFldrCache->FreeRecord(&fiPath);
  323. return hrResult;
  324. } // ImapUtil_FolderIDToPath
  325. //***************************************************************************
  326. // Function: ImapUtil_ReverseSentence
  327. //
  328. // Purpose:
  329. // This function reverses the words in the given sentence, where words are
  330. // separated by the given delimiter. For instance, "one two three" with space
  331. // as the delimiter is returned as "three two one".
  332. //
  333. // Arguments:
  334. // LPSTR pszSentence [in/out] - the sentence to be reversed. The sentence
  335. // is reversed in place.
  336. // char cDelimiter [in] - the character separating the words in the
  337. // sentence.
  338. //
  339. // Returns:
  340. // DWORD indicating length of reversed sentence.
  341. //***************************************************************************
  342. DWORD ImapUtil_ReverseSentence(LPSTR pszSentence, char cDelimiter)
  343. {
  344. LPSTR pszStartWord, psz;
  345. Assert(NULL != pszSentence);
  346. BOOL fFoundDelimiter;
  347. BOOL fSkipByte = FALSE;
  348. TraceCall("ImapUtil_ReverseSentence");
  349. if ('\0' == cDelimiter)
  350. return 0; // Nothing to reverse
  351. // Check if first character is a delimiter
  352. if (cDelimiter != *pszSentence) {
  353. pszStartWord = pszSentence;
  354. psz = pszSentence;
  355. fFoundDelimiter = FALSE;
  356. }
  357. else {
  358. // Skip first delimiter char (it will be reversed at end of fn)
  359. pszStartWord = pszSentence + 1;
  360. psz = pszSentence + 1;
  361. fFoundDelimiter = TRUE;
  362. }
  363. // First, reverse each word in the sentence
  364. while (1) {
  365. char cCurrent = *psz;
  366. if (fSkipByte) {
  367. fSkipByte = FALSE;
  368. if ('\0' != cCurrent)
  369. psz += 1;
  370. continue;
  371. }
  372. if (cDelimiter == cCurrent || '\0' == cCurrent) {
  373. // We've gone past a word! Reverse it!
  374. ImapUtil_ReverseString(pszStartWord, psz - 1);
  375. pszStartWord = psz + 1; // Set us up for next word
  376. fFoundDelimiter = TRUE;
  377. }
  378. if ('\0' == cCurrent)
  379. break;
  380. else {
  381. if (IsDBCSLeadByteEx(GetACP(), cCurrent))
  382. fSkipByte = TRUE;
  383. psz += 1;
  384. }
  385. } // while (1)
  386. // Now reverse the entire sentence string (psz points to null-terminator)
  387. if (fFoundDelimiter && psz > pszSentence)
  388. ImapUtil_ReverseString(pszSentence, psz - 1);
  389. return (DWORD) (psz - pszSentence);
  390. } // ImapUtil_ReverseSentence
  391. //***************************************************************************
  392. // Function: ImapUtil_ReverseString
  393. //
  394. // Purpose:
  395. // This function reverses the given string in-place
  396. //
  397. // Arguments:
  398. // LPSTR pszStart [in/out] - start of the string to be reversed.
  399. // LPSTR pszEnd [in/out] - the end of the string to be reversed.
  400. //***************************************************************************
  401. void ImapUtil_ReverseString(LPSTR pszStart, LPSTR pszEnd)
  402. {
  403. TraceCall("ImapUtil_ReverseString");
  404. Assert(NULL != pszStart);
  405. Assert(NULL != pszEnd);
  406. while (pszStart < pszEnd) {
  407. char cTemp;
  408. // Swap characters
  409. cTemp = *pszStart;
  410. *pszStart = *pszEnd;
  411. *pszEnd = cTemp;
  412. // Advance pointers
  413. pszStart += 1;
  414. pszEnd -= 1;
  415. } // while
  416. } // ImapUtil_ReverseString
  417. //***************************************************************************
  418. // Function: ImapUtil_SpecialFldrTypeToPath
  419. //
  420. // Purpose:
  421. // This function returns the path for the given special folder type.
  422. //
  423. // Arguments:
  424. // LPSTR pszAccountID [in] - ID of IMAP account where special folder resides.
  425. // SPECIALFOLDER sfType [in] - the special folder whose path should be returned
  426. // (eg, FOLDER_SENT).
  427. // LPCSTR pszRootFldrPrefix [in] - the root folder prefix for this IMAP
  428. // account. If this is NULL, this function will find out for itself.
  429. // LPSTR pszPath [out] - pointer to a buffer to receieve the special folder
  430. // path.
  431. // DWORD dwSizeOfPath [in] - size of buffer pointed to by pszPath.
  432. //
  433. // Returns:
  434. // HRESULT indicating success or failure. This can include:
  435. //
  436. // STORE_E_NOREMOTESPECIALFLDR: indicates the given special folder has
  437. // been disabled by the user for this IMAP server.
  438. //***************************************************************************
  439. HRESULT ImapUtil_SpecialFldrTypeToPath(LPCSTR pszAccountID, SPECIALFOLDER sfType,
  440. LPSTR pszRootFldrPrefix, char cHierarchyChar,
  441. LPSTR pszPath, DWORD dwSizeOfPath)
  442. {
  443. HRESULT hrResult = E_FAIL;
  444. IImnAccount *pAcct = NULL;
  445. DWORD dw;
  446. int iLen;
  447. TraceCall("ImapUtil_SpecialFldrTypeToPath");
  448. AssertSz(dwSizeOfPath >= MAX_PATH * 2 + 2, "RFP + Special Folder Path = Big Buffer, Dude"); // Room for HC, null-term
  449. *pszPath = '\0'; // Initialize
  450. switch (sfType)
  451. {
  452. case FOLDER_INBOX:
  453. StrCpyN(pszPath, c_szINBOX, dwSizeOfPath);
  454. hrResult = S_OK;
  455. break;
  456. case FOLDER_SENT:
  457. case FOLDER_DRAFT:
  458. Assert(g_pAcctMan);
  459. if (g_pAcctMan)
  460. {
  461. hrResult = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, pszAccountID, &pAcct);
  462. }
  463. if (FAILED(hrResult))
  464. break;
  465. hrResult = pAcct->GetPropDw(AP_IMAP_SVRSPECIALFLDRS, &dw);
  466. if (FAILED(hrResult))
  467. break;
  468. else if (FALSE == dw) {
  469. hrResult = STORE_E_NOREMOTESPECIALFLDR;
  470. break;
  471. }
  472. // First prepend the root folder prefix
  473. // Check if user gave us a root folder prefix: otherwise we need to load it ourselves
  474. if (NULL == pszRootFldrPrefix)
  475. ImapUtil_LoadRootFldrPrefix(pszAccountID, pszPath, dwSizeOfPath);
  476. else
  477. StrCpyN(pszPath, pszRootFldrPrefix, dwSizeOfPath);
  478. iLen = lstrlen(pszPath);
  479. if (iLen > 0 && (DWORD)iLen + 1 < dwSizeOfPath)
  480. {
  481. pszPath[iLen] = cHierarchyChar;
  482. iLen += 1;
  483. pszPath[iLen] = '\0';
  484. }
  485. hrResult = pAcct->GetPropSz(FOLDER_SENT == sfType ?
  486. AP_IMAP_SENTITEMSFLDR : AP_IMAP_DRAFTSFLDR, pszPath + iLen,
  487. dwSizeOfPath - iLen);
  488. break;
  489. case FOLDER_DELETED:
  490. case FOLDER_ERRORS:
  491. case FOLDER_JUNK:
  492. case FOLDER_MSNPROMO:
  493. case FOLDER_OUTBOX:
  494. case FOLDER_BULKMAIL:
  495. hrResult = STORE_E_NOREMOTESPECIALFLDR;
  496. break;
  497. default:
  498. AssertSz(FALSE, "Invalid special folder type!");
  499. hrResult = E_INVALIDARG;
  500. break;
  501. } // switch (sfType)
  502. if (NULL != pAcct)
  503. pAcct->Release();
  504. // Check for blank path
  505. if (SUCCEEDED(hrResult) && '\0' == *pszPath)
  506. hrResult = STORE_E_NOREMOTESPECIALFLDR;
  507. return hrResult;
  508. } // ImapUtil_SpecialFldrTypeToPath
  509. //***************************************************************************
  510. // Function: ImapUtil_LoadRootFldrPrefix
  511. //
  512. // Purpose:
  513. // This function loads the "Root Folder Path" option from the account
  514. // manager. The Root Folder Path (prefix) identifies the parent of all of
  515. // the user's folders. Thus, the Root Folder Path forms a prefix for all
  516. // mailboxes which are not INBOX.
  517. //
  518. // Arguments:
  519. // LPCTSTR pszAccountID [in] - ID of the account
  520. // LPSTR pszRootFolderPrefix [out] - destination for Root Folder Path
  521. // DWORD dwSizeofPrefixBuffer [in] - size of buffer pointed to by
  522. // pszRootFolderPrefix.
  523. //***************************************************************************
  524. void ImapUtil_LoadRootFldrPrefix(LPCTSTR pszAccountID,
  525. LPSTR pszRootFolderPrefix,
  526. DWORD dwSizeofPrefixBuffer)
  527. {
  528. IImnAccount *pAcct = NULL;
  529. HRESULT hrResult = E_UNEXPECTED;
  530. LPSTR pLastChar;
  531. Assert(g_pAcctMan);
  532. Assert(NULL != pszAccountID);
  533. Assert(NULL != pszRootFolderPrefix);
  534. Assert(0 != dwSizeofPrefixBuffer);
  535. if (!g_pAcctMan)
  536. return;
  537. // Initialize variables
  538. pAcct = NULL;
  539. pszRootFolderPrefix[0] = '\0'; // If we can't find a prefix, default to NONE
  540. // Get the prefix from the account manager
  541. hrResult = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, pszAccountID, &pAcct);
  542. if (FAILED(hrResult))
  543. goto exit;
  544. hrResult = pAcct->GetPropSz(AP_IMAP_ROOT_FOLDER, pszRootFolderPrefix,
  545. dwSizeofPrefixBuffer);
  546. if (FAILED(hrResult))
  547. goto exit;
  548. // OK, we now have the root folder prefix. Strip trailing hierarchy chars,
  549. // since we probably don't know server HC when we try to list the prefix
  550. pLastChar = CharPrev(pszRootFolderPrefix, pszRootFolderPrefix + lstrlen(pszRootFolderPrefix));
  551. while (pLastChar >= pszRootFolderPrefix &&
  552. ('/' == *pLastChar || '\\' == *pLastChar || '.' == *pLastChar)) {
  553. *pLastChar = '\0'; // Bye-bye, potential hierarchy char
  554. pLastChar = CharPrev(pszRootFolderPrefix, pLastChar);
  555. } // while
  556. exit:
  557. if (NULL != pAcct)
  558. pAcct->Release();
  559. } // ImapUtil_LoadRootFldrPrefix
  560. //***************************************************************************
  561. // Function: ImapUtil_GetSpecialFolderType
  562. //
  563. // Purpose:
  564. // This function takes the given account name and folder path, and
  565. // determines whether the path points to a special IMAP folder. Note that
  566. // although it is possible for a path to represent more than one type of
  567. // IMAP special folder, only ONE special folder type is returned (based
  568. // on evaluation order).
  569. //
  570. // Arguments:
  571. // LPSTR pszAccountID [in] - ID of the IMAP account whose special folder
  572. // paths we want to compare pszFullPath to.
  573. // LPSTR pszFullPath [in] - path to a potential special folder residing on
  574. // the pszAccountID account.
  575. // char cHierarchyChar [in] - hierarchy char used to interpret pszFullPath.
  576. // LPSTR pszRootFldrPrefix [in] - the root folder prefix for this IMAP
  577. // account. If this is NULL, this function will find out for itself.
  578. // SPECIALFOLDER *psfType [out] - the special folder type of given folder
  579. // (eg, FOLDER_NOTSPECIAL, FOLDER_SENT). Pass NULL if not interested.
  580. //
  581. // Returns:
  582. // LPSTR pointing to leaf name of special folder path. For instance, if
  583. // the Drafts folder is set to "one/two/three/Drafts" and this function is
  584. // called to process "one/two/three/Drafts/foo", then this function will
  585. // return "Drafts/foo". If no match is found, NULL is returned.
  586. //***************************************************************************
  587. LPSTR ImapUtil_GetSpecialFolderType(LPSTR pszAccountID, LPSTR pszFullPath,
  588. char cHierarchyChar, LPSTR pszRootFldrPrefix,
  589. SPECIALFOLDER *psfType)
  590. {
  591. HRESULT hrResult;
  592. SPECIALFOLDER sfType = FOLDER_NOTSPECIAL;
  593. BOOL fSpecialFldrPrefix = FALSE;
  594. IImnAccount *pAccount = NULL;
  595. DWORD dw;
  596. int iLeafNameOffset = 0;
  597. int iTmp;
  598. int iLen;
  599. char sz[MAX_PATH * 2 + 2]; // Room for HC plus null-term
  600. Assert(INVALID_HIERARCHY_CHAR != cHierarchyChar);
  601. Assert(g_pAcctMan);
  602. if (!g_pAcctMan)
  603. goto exit;
  604. // First check if this is INBOX or one of its children
  605. iLen = lstrlen(c_szInbox);
  606. if (0 == StrCmpNI(pszFullPath, c_szInbox, iLen) &&
  607. (cHierarchyChar == pszFullPath[iLen] || '\0' == pszFullPath[iLen]))
  608. {
  609. fSpecialFldrPrefix = TRUE;
  610. iLeafNameOffset = 0; // "INBOX" is always the leaf name
  611. if ('\0' == pszFullPath[iLen])
  612. {
  613. sfType = FOLDER_INBOX; // Exact match for "INBOX"
  614. goto exit;
  615. }
  616. }
  617. hrResult = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, pszAccountID, &pAccount);
  618. if (FAILED(hrResult))
  619. goto exit;
  620. #ifdef DEBUG
  621. hrResult = pAccount->GetServerTypes(&dw);
  622. Assert(SUCCEEDED(hrResult) && (SRV_IMAP & dw));
  623. #endif // DEBUG
  624. hrResult = pAccount->GetPropDw(AP_IMAP_SVRSPECIALFLDRS, &dw);
  625. if (SUCCEEDED(hrResult) && dw)
  626. {
  627. int iLenRFP;
  628. // Check if user gave us a root folder prefix: otherwise we need to load it ourselves
  629. if (NULL == pszRootFldrPrefix)
  630. ImapUtil_LoadRootFldrPrefix(pszAccountID, sz, sizeof(sz));
  631. else
  632. StrCpyN(sz, pszRootFldrPrefix, ARRAYSIZE(sz));
  633. iLenRFP = lstrlen(sz);
  634. if (iLenRFP > 0 && (DWORD)iLenRFP + 1 < sizeof(sz))
  635. {
  636. sz[iLenRFP] = cHierarchyChar;
  637. iLenRFP += 1;
  638. sz[iLenRFP] = '\0';
  639. }
  640. hrResult = pAccount->GetPropSz(AP_IMAP_SENTITEMSFLDR, sz + iLenRFP, sizeof(sz) - iLenRFP);
  641. if (SUCCEEDED(hrResult))
  642. {
  643. iLen = lstrlen(sz);
  644. if (0 == StrCmpNI(sz, pszFullPath, iLen) &&
  645. (cHierarchyChar == pszFullPath[iLen] || '\0' == pszFullPath[iLen]))
  646. {
  647. fSpecialFldrPrefix = TRUE;
  648. iTmp = (int) (ImapUtil_ExtractLeafName(sz, cHierarchyChar) - sz);
  649. iLeafNameOffset = max(iTmp, iLeafNameOffset);
  650. if ('\0' == pszFullPath[iLen])
  651. {
  652. sfType = FOLDER_SENT; // Exact match for Sent Items
  653. goto exit;
  654. }
  655. }
  656. }
  657. hrResult = pAccount->GetPropSz(AP_IMAP_DRAFTSFLDR, sz + iLenRFP, sizeof(sz) - iLenRFP);
  658. if (SUCCEEDED(hrResult))
  659. {
  660. iLen = lstrlen(sz);
  661. if (0 == StrCmpNI(sz, pszFullPath, iLen) &&
  662. (cHierarchyChar == pszFullPath[iLen] || '\0' == pszFullPath[iLen]))
  663. {
  664. fSpecialFldrPrefix = TRUE;
  665. iTmp = (int) (ImapUtil_ExtractLeafName(sz, cHierarchyChar) - sz);
  666. iLeafNameOffset = max(iTmp, iLeafNameOffset);
  667. if ('\0' == pszFullPath[iLen])
  668. {
  669. sfType = FOLDER_DRAFT; // Exact match for Drafts folder
  670. goto exit;
  671. }
  672. }
  673. }
  674. } // if (AP_IMAP_SVRSPECIALFLDRS)
  675. exit:
  676. if (NULL != pAccount)
  677. pAccount->Release();
  678. if (NULL != psfType)
  679. *psfType = sfType;
  680. if (fSpecialFldrPrefix)
  681. return pszFullPath + iLeafNameOffset;
  682. else
  683. return NULL;
  684. } // ImapUtil_GetSpecialFolderType
  685. //***************************************************************************
  686. // Function: ImapUtil_ExtractLeafName
  687. //
  688. // Purpose:
  689. // This function takes an IMAP folder path and extracts the leaf node name.
  690. //
  691. // Arguments:
  692. // LPSTR pszFolderPath [in] - a string containing the IMAP folder path.
  693. // char cHierarchyChar [in] - the hierarchy char used in pszFolderPath.
  694. //
  695. // Returns:
  696. // A pointer to the leaf node name in pszFolderPath. The default return
  697. // value is pszFolderPath, if no hierarchy characters were found.
  698. //***************************************************************************
  699. LPSTR ImapUtil_ExtractLeafName(LPSTR pszFolderPath, char cHierarchyChar)
  700. {
  701. LPSTR pszLastHierarchyChar, p;
  702. // Find out where the last hierarchy character lives
  703. pszLastHierarchyChar = pszFolderPath;
  704. p = pszFolderPath;
  705. while ('\0' != *p) {
  706. if (cHierarchyChar == *p)
  707. pszLastHierarchyChar = p;
  708. p += 1;
  709. }
  710. // Adjust pszLastHierarchyChar to point to leaf name
  711. if (cHierarchyChar == *pszLastHierarchyChar)
  712. return pszLastHierarchyChar + 1;
  713. else
  714. return pszFolderPath;
  715. } // ImapUtil_ExtractLeafName
  716. HRESULT ImapUtil_UIDToMsgSeqNum(IIMAPTransport *pIMAPTransport, DWORD_PTR dwUID,
  717. LPDWORD pdwMsgSeqNum)
  718. {
  719. HRESULT hrTemp;
  720. DWORD *pdwMsgSeqNumToUIDArray = NULL;
  721. DWORD dwHighestMsgSeqNum;
  722. DWORD dw;
  723. BOOL fFound = FALSE;
  724. TraceCall("ImapUtil_UIDToMsgSeqNum");
  725. if (NULL == pIMAPTransport || 0 == dwUID)
  726. {
  727. TraceResult(E_INVALIDARG);
  728. goto exit;
  729. }
  730. // Quickly check the highest MSN
  731. hrTemp = pIMAPTransport->GetHighestMsgSeqNum(&dwHighestMsgSeqNum);
  732. if (FAILED(hrTemp) || 0 == dwHighestMsgSeqNum)
  733. {
  734. TraceError(hrTemp);
  735. goto exit;
  736. }
  737. // OK, no more laziness, we gotta do a linear search now
  738. hrTemp = pIMAPTransport->GetMsgSeqNumToUIDArray(&pdwMsgSeqNumToUIDArray,
  739. &dwHighestMsgSeqNum);
  740. if (FAILED(hrTemp))
  741. {
  742. TraceResult(hrTemp);
  743. goto exit;
  744. }
  745. Assert(dwHighestMsgSeqNum > 0);
  746. for (dw = 0; dw < dwHighestMsgSeqNum; dw++)
  747. {
  748. // Look for match or overrun
  749. if (0 != pdwMsgSeqNumToUIDArray[dw] && dwUID <= pdwMsgSeqNumToUIDArray[dw])
  750. {
  751. if (dwUID == pdwMsgSeqNumToUIDArray[dw])
  752. {
  753. if (NULL != pdwMsgSeqNum)
  754. *pdwMsgSeqNum = dw + 1;
  755. fFound = TRUE;
  756. }
  757. break;
  758. }
  759. } // for
  760. exit:
  761. SafeMemFree(pdwMsgSeqNumToUIDArray);
  762. if (fFound)
  763. return S_OK;
  764. else
  765. return E_FAIL;
  766. } // ImapUtil_UIDToMsgSeqNum
  767. // *** REMOVE THIS after Beta-2! This sets the AP_IMAP_DIRTY flag if no IMAP special folders
  768. // found after OE4->OE5 migration. We can then prompt user to refresh folder list.
  769. void ImapUtil_B2SetDirtyFlag(void)
  770. {
  771. IImnAccountManager *pAcctMan = NULL;
  772. IImnEnumAccounts *pAcctEnum = NULL;
  773. IImnAccount *pAcct = NULL;
  774. HRESULT hrResult;
  775. TraceCall("ImapUtil_B2SetDirtyFlag");
  776. // Enumerate through all accounts. Set AP_IMAP_DIRTY flag on all IMAP accounts
  777. hrResult = HrCreateAccountManager(&pAcctMan);
  778. if (FAILED(hrResult))
  779. {
  780. TraceResult(hrResult);
  781. goto exit;
  782. }
  783. hrResult = pAcctMan->Init(NULL);
  784. if (FAILED(hrResult))
  785. {
  786. TraceResult(hrResult);
  787. goto exit;
  788. }
  789. hrResult = pAcctMan->Enumerate(SRV_IMAP, &pAcctEnum);
  790. if (FAILED(hrResult))
  791. {
  792. TraceResult(hrResult);
  793. goto exit;
  794. }
  795. hrResult = pAcctEnum->GetNext(&pAcct);
  796. while (SUCCEEDED(hrResult))
  797. {
  798. DWORD dwIMAPDirty;
  799. hrResult = pAcct->GetPropDw(AP_IMAP_DIRTY, &dwIMAPDirty);
  800. if (FAILED(hrResult))
  801. {
  802. TraceResult(hrResult);
  803. dwIMAPDirty = 0;
  804. }
  805. // Mark this IMAP account as dirty so we prompt user to refresh folder list
  806. dwIMAPDirty |= (IMAP_FLDRLIST_DIRTY | IMAP_OE4MIGRATE_DIRTY);
  807. hrResult = pAcct->SetPropDw(AP_IMAP_DIRTY, dwIMAPDirty);
  808. TraceError(hrResult); // Record but otherwise ignore result
  809. hrResult = pAcct->SaveChanges();
  810. TraceError(hrResult); // Record but otherwise ignore result
  811. // Get next account
  812. SafeRelease(pAcct);
  813. hrResult = pAcctEnum->GetNext(&pAcct);
  814. }
  815. exit:
  816. SafeRelease(pAcctMan);
  817. SafeRelease(pAcctEnum);
  818. }