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

/*
* imaputil.cpp
*
* Purpose:
* Implements IMAP utility functions
*
* Owner:
* Raych
*
* Copyright (C) Microsoft Corp. 1996
*/
//---------------------------------------------------------------------------
// Includes
//---------------------------------------------------------------------------
#include "pch.hxx"
#include "imapute.h"
#include "storutil.h"
#include "imapsync.h"
//---------------------------------------------------------------------------
// Forward Declarations
//---------------------------------------------------------------------------
DWORD ImapUtil_ReverseSentence(LPSTR pszSentence, char cDelimiter);
void ImapUtil_ReverseString(LPSTR pszStart, LPSTR pszEnd);
//---------------------------------------------------------------------------
// Module Constants
//---------------------------------------------------------------------------
const char c_szIMAP_MSG_ANSWERED[] = "Answered";
const char c_szIMAP_MSG_FLAGGED[] = "Flagged";
const char c_szIMAP_MSG_DELETED[] = "Deleted";
const char c_szIMAP_MSG_DRAFT[] = "Draft";
const char c_szIMAP_MSG_SEEN[] = "Seen";
const char c_szBACKSLASH[] = "\\";
typedef struct tagIMFToStr_LUT {
IMAP_MSGFLAGS imfValue;
LPCSTR pszValue;
} IMFTOSTR_LUT;
const IMFTOSTR_LUT g_IMFToStringLUT[] = {
{IMAP_MSG_ANSWERED, c_szIMAP_MSG_ANSWERED},
{IMAP_MSG_FLAGGED, c_szIMAP_MSG_FLAGGED},
{IMAP_MSG_DELETED, c_szIMAP_MSG_DELETED},
{IMAP_MSG_SEEN, c_szIMAP_MSG_SEEN},
{IMAP_MSG_DRAFT, c_szIMAP_MSG_DRAFT}};
//---------------------------------------------------------------------------
// Functions
//---------------------------------------------------------------------------
//***************************************************************************
// Function: ImapUtil_MsgFlagsToString
//
// Purpose:
// This function converts a IMAP_MSGFLAGS register to its string
// equivalent. For instance, IMAP_MSG_SEEN is converted to "(\Seen)".
//
// Arguments:
// IMAP_MSGFLAGS imfSource [in] - IMAP_MSGFLAGS register to convert to
// string.
// LPSTR *ppszDestination [out] - the string equivalent is returned here.
// If imfSource is 0, NULL is returned here. Otherwise, a string buffer
// is returned which the caller must MemFree when he is done with it.
// DWORD *pdwLengthOfDestination [out] - the length of *ppszDestination.
// Pass in NULL if not interested.
//
// Returns:
// HRESULT indicating success or failure. Remember that it is possible
// for a successful HRESULT to be returned, even is *ppszDestination is NULL.
//***************************************************************************
HRESULT ImapUtil_MsgFlagsToString(IMAP_MSGFLAGS imfSource,
LPSTR *ppszDestination,
DWORD *pdwLengthOfDestination)
{
CByteStream bstmOutput;
HRESULT hrResult;
const IMFTOSTR_LUT *pCurrent;
const IMFTOSTR_LUT *pLastEntry;
BOOL fFirstFlag;
TraceCall("ImapUtil_MsgFlagsToString");
Assert(NULL != ppszDestination);
AssertSz(0 == (imfSource & ~IMAP_MSG_ALLFLAGS), "Quit feeding me garbage.");
// Codify assumptions
Assert(IMAP_MSG_ALLFLAGS == 0x0000001F);
*ppszDestination = NULL;
if (NULL != pdwLengthOfDestination)
*pdwLengthOfDestination = 0;
if (0 == (imfSource & IMAP_MSG_ALLFLAGS))
return S_FALSE; // Nothing to do here!
hrResult = bstmOutput.Write("(", 1, NULL);
if (FAILED(hrResult))
goto exit;
fFirstFlag = TRUE;
pCurrent = g_IMFToStringLUT;
pLastEntry = pCurrent + sizeof(g_IMFToStringLUT)/sizeof(IMFTOSTR_LUT) - 1;
while (pCurrent <= pLastEntry) {
if (imfSource & pCurrent->imfValue) {
// Prepend a space to flag, if necessary
if (FALSE == fFirstFlag) {
hrResult = bstmOutput.Write(g_szSpace, 1, NULL);
if (FAILED(hrResult))
goto exit;
}
else
fFirstFlag = FALSE;
// Output the backslash
hrResult = bstmOutput.Write(c_szBACKSLASH,
sizeof(c_szBACKSLASH) - 1, NULL);
if (FAILED(hrResult))
goto exit;
// Output string associated with this IMAP flag
hrResult = bstmOutput.Write(pCurrent->pszValue,
lstrlen(pCurrent->pszValue), NULL);
if (FAILED(hrResult))
goto exit;
} // if (imfSource & pCurrent->imfValue)
// Advance current pointer
pCurrent += 1;
} // while
hrResult = bstmOutput.Write(")", 1, NULL);
if (FAILED(hrResult))
goto exit;
hrResult = bstmOutput.HrAcquireStringA(pdwLengthOfDestination,
ppszDestination, ACQ_DISPLACE);
exit:
return hrResult;
} // IMAPMsgFlagsToString
//***************************************************************************
// Function: ImapUtil_FolderIDToPath
//
// Purpose:
// This function takes the given FolderID and returns the full path
// (including prefix) to the folder. The caller may also choose to append
// a string to the path.
//
// Arguments:
// FolderID idFolder [in] - FolderID to convert into a full path.
// char **ppszPath [out] - a full path to idFolder is returned here.
// LPDWORD pdwPathLen [out] - if non-NULL, the length of *ppszPath is
// returned here.
// char *pcHierarchyChar [out] - the hierarchy char used to interpret
// *ppszPath is returned here.
// CFolderCache *pFldrCache [in] - a CFolderCache to use to generate
// the path.
// LPCSTR pszAppendStr [in] - this can be NULL if the caller does not need
// to append a string to the path. Otherwise, a hierarchy character is
// appended to the path and this string is appended after the HC. This
// argument is typically used to tack a wildcard to the end of the path.
// LPCSTR pszRootFldrPrefix [in] - the root folder prefix for this IMAP
// account. If this is NULL, this function will find out for itself.
//
// Returns:
// HRESULT indicating success or failure.
//***************************************************************************
HRESULT ImapUtil_FolderIDToPath(FOLDERID idServer, FOLDERID idFolder, char **ppszPath,
LPDWORD pdwPathLen, char *pcHierarchyChar,
IMessageStore *pFldrCache, LPCSTR pszAppendStr,
LPCSTR pszRootFldrPrefix)
{
FOLDERINFO fiPath;
HRESULT hrResult;
CByteStream bstmPath;
DWORD dwLengthOfPath;
LPSTR pszEnd;
char szRootFldrPrefix[MAX_PATH];
char szAccount[CCHMAX_ACCOUNT_NAME];
BOOL fAppendStrHC = FALSE,
fFreeFldrInfo = FALSE;
BOOL fSpecialFldr = FALSE;
DWORD dwLen;
TraceCall("ImapUtil_FolderIDToPath");
if (FOLDERID_INVALID == idFolder || FOLDERID_INVALID == idServer)
{
hrResult = TraceResult(E_INVALIDARG);
goto exit;
}
// Build full path to current folder in reverse (leaf->root)
// Limited buffer overflow risk since user input limited to MAX_PATH
// Start off with target folder (leaf) and return its HC if so requested
hrResult = pFldrCache->GetFolderInfo(idFolder, &fiPath);
if (FAILED(hrResult))
{
TraceResult(hrResult);
goto exit;
}
fFreeFldrInfo = TRUE;
GetFolderAccountId(&fiPath, szAccount, ARRAYSIZE(szAccount));
if (NULL != pcHierarchyChar)
{
Assert((BYTE)INVALID_HIERARCHY_CHAR != fiPath.bHierarchy);
*pcHierarchyChar = (char) fiPath.bHierarchy;
}
// Append anything the user asked us to (will be at end of str after reversal)
if (NULL != pszAppendStr)
{
char szBuf[MAX_PATH + 1];
// First, have to reverse the append string itself, in case it contains HC's
Assert(lstrlen(pszAppendStr) < ARRAYSIZE(szBuf));
StrCpyN(szBuf, pszAppendStr, ARRAYSIZE(szBuf));
dwLen = ImapUtil_ReverseSentence(szBuf, fiPath.bHierarchy);
hrResult = bstmPath.Write(szBuf, dwLen, NULL);
if (FAILED(hrResult))
{
TraceResult(hrResult);
goto exit;
}
fAppendStrHC = TRUE;
}
// Check if user gave us a root folder prefix: otherwise we need to load it ourselves
if (NULL == pszRootFldrPrefix)
{
ImapUtil_LoadRootFldrPrefix(szAccount, szRootFldrPrefix, sizeof(szRootFldrPrefix));
pszRootFldrPrefix = szRootFldrPrefix;
}
else
// Copy to our buffer because we're going to reverse the RFP
StrCpyN(szRootFldrPrefix, pszRootFldrPrefix, ARRAYSIZE(szRootFldrPrefix));
// Proceed to root
while (FALSE == fSpecialFldr && idServer != fiPath.idFolder)
{
LPSTR pszFolderName;
Assert(FOLDERID_INVALID != fiPath.idFolder);
Assert(FOLDERID_ROOT != fiPath.idParent);
if (fAppendStrHC)
{
// Separate append str from path with HC
Assert((BYTE)INVALID_HIERARCHY_CHAR != fiPath.bHierarchy);
hrResult = bstmPath.Write(&fiPath.bHierarchy, sizeof(fiPath.bHierarchy), NULL);
if (FAILED(hrResult))
{
TraceResult(hrResult);
goto exit;
}
fAppendStrHC = FALSE;
}
// Expand folder name to full path if this is a special folder
if (FOLDER_NOTSPECIAL != fiPath.tySpecial)
{
char szSpecialFldrPath[MAX_PATH * 2 + 2]; // Room for HC and null-term
fSpecialFldr = TRUE;
hrResult = ImapUtil_SpecialFldrTypeToPath(szAccount, fiPath.tySpecial,
szRootFldrPrefix, fiPath.bHierarchy, szSpecialFldrPath, sizeof(szSpecialFldrPath));
if (FAILED(hrResult))
{
TraceResult(hrResult);
goto exit;
}
// Reverse special folder path so we can append it. It will be reversed back to normal
// There should be no trailing HC's
//Assert((BYTE)INVALID_HIERARCHY_CHAR != fiPath.bHierarchy);
dwLen = ImapUtil_ReverseSentence(szSpecialFldrPath, fiPath.bHierarchy);
Assert(dwLen == 0 || fiPath.bHierarchy !=
*(CharPrev(szSpecialFldrPath, szSpecialFldrPath + dwLen)));
pszFolderName = szSpecialFldrPath;
}
else
pszFolderName = fiPath.pszName;
// Write folder name to stream
hrResult = bstmPath.Write(pszFolderName, lstrlen(pszFolderName), NULL);
if (FAILED(hrResult))
{
TraceResult(hrResult);
goto exit;
}
//Assert((BYTE)INVALID_HIERARCHY_CHAR != fiPath.bHierarchy);
hrResult = bstmPath.Write(&fiPath.bHierarchy, sizeof(fiPath.bHierarchy), NULL);
if (FAILED(hrResult))
{
TraceResult(hrResult);
goto exit;
}
pFldrCache->FreeRecord(&fiPath);
fFreeFldrInfo = FALSE;
hrResult = pFldrCache->GetFolderInfo(fiPath.idParent, &fiPath);
if (FAILED(hrResult))
{
TraceResult(hrResult);
goto exit;
}
fFreeFldrInfo = TRUE;
} // while
if (FALSE == fSpecialFldr && '\0' != szRootFldrPrefix[0])
{
if (fAppendStrHC)
{
// Separate append str from path with HC
Assert((BYTE)INVALID_HIERARCHY_CHAR != fiPath.bHierarchy);
hrResult = bstmPath.Write(&fiPath.bHierarchy, sizeof(fiPath.bHierarchy), NULL);
if (FAILED(hrResult))
{
TraceResult(hrResult);
goto exit;
}
fAppendStrHC = FALSE;
}
// Reverse root folder path so we can append it. It will be reversed back to normal
// There should be no trailing HC's (ImapUtil_LoadRootFldrPrefix guarantees this)
Assert((BYTE)INVALID_HIERARCHY_CHAR != fiPath.bHierarchy);
dwLen = ImapUtil_ReverseSentence(szRootFldrPrefix, fiPath.bHierarchy);
Assert(dwLen == 0 || fiPath.bHierarchy !=
*(CharPrev(szRootFldrPrefix, szRootFldrPrefix + dwLen)));
hrResult = bstmPath.Write(szRootFldrPrefix, dwLen, NULL);
if (FAILED(hrResult))
{
TraceResult(hrResult);
goto exit;
}
}
// OK, path won't get any larger. Acquire mem buffer so we can reverse it
hrResult = bstmPath.HrAcquireStringA(&dwLengthOfPath, ppszPath, ACQ_DISPLACE);
if (FAILED(hrResult))
{
TraceResult(hrResult);
goto exit;
}
// Blow away trailing hierarchy character or it becomes leading HC
pszEnd = CharPrev(*ppszPath, *ppszPath + dwLengthOfPath);
Assert('%' == *pszEnd || (BYTE)INVALID_HIERARCHY_CHAR != fiPath.bHierarchy);
if (*pszEnd == (char) fiPath.bHierarchy)
*pszEnd = '\0';
// Reverse the 'sentence' (HC is delimiter) to get path
dwLen = ImapUtil_ReverseSentence(*ppszPath, fiPath.bHierarchy);
if (NULL != pdwPathLen)
*pdwPathLen = dwLen;
exit:
if (fFreeFldrInfo)
pFldrCache->FreeRecord(&fiPath);
return hrResult;
} // ImapUtil_FolderIDToPath
//***************************************************************************
// Function: ImapUtil_ReverseSentence
//
// Purpose:
// This function reverses the words in the given sentence, where words are
// separated by the given delimiter. For instance, "one two three" with space
// as the delimiter is returned as "three two one".
//
// Arguments:
// LPSTR pszSentence [in/out] - the sentence to be reversed. The sentence
// is reversed in place.
// char cDelimiter [in] - the character separating the words in the
// sentence.
//
// Returns:
// DWORD indicating length of reversed sentence.
//***************************************************************************
DWORD ImapUtil_ReverseSentence(LPSTR pszSentence, char cDelimiter)
{
LPSTR pszStartWord, psz;
Assert(NULL != pszSentence);
BOOL fFoundDelimiter;
BOOL fSkipByte = FALSE;
TraceCall("ImapUtil_ReverseSentence");
if ('\0' == cDelimiter)
return 0; // Nothing to reverse
// Check if first character is a delimiter
if (cDelimiter != *pszSentence) {
pszStartWord = pszSentence;
psz = pszSentence;
fFoundDelimiter = FALSE;
}
else {
// Skip first delimiter char (it will be reversed at end of fn)
pszStartWord = pszSentence + 1;
psz = pszSentence + 1;
fFoundDelimiter = TRUE;
}
// First, reverse each word in the sentence
while (1) {
char cCurrent = *psz;
if (fSkipByte) {
fSkipByte = FALSE;
if ('\0' != cCurrent)
psz += 1;
continue;
}
if (cDelimiter == cCurrent || '\0' == cCurrent) {
// We've gone past a word! Reverse it!
ImapUtil_ReverseString(pszStartWord, psz - 1);
pszStartWord = psz + 1; // Set us up for next word
fFoundDelimiter = TRUE;
}
if ('\0' == cCurrent)
break;
else {
if (IsDBCSLeadByteEx(GetACP(), cCurrent))
fSkipByte = TRUE;
psz += 1;
}
} // while (1)
// Now reverse the entire sentence string (psz points to null-terminator)
if (fFoundDelimiter && psz > pszSentence)
ImapUtil_ReverseString(pszSentence, psz - 1);
return (DWORD) (psz - pszSentence);
} // ImapUtil_ReverseSentence
//***************************************************************************
// Function: ImapUtil_ReverseString
//
// Purpose:
// This function reverses the given string in-place
//
// Arguments:
// LPSTR pszStart [in/out] - start of the string to be reversed.
// LPSTR pszEnd [in/out] - the end of the string to be reversed.
//***************************************************************************
void ImapUtil_ReverseString(LPSTR pszStart, LPSTR pszEnd)
{
TraceCall("ImapUtil_ReverseString");
Assert(NULL != pszStart);
Assert(NULL != pszEnd);
while (pszStart < pszEnd) {
char cTemp;
// Swap characters
cTemp = *pszStart;
*pszStart = *pszEnd;
*pszEnd = cTemp;
// Advance pointers
pszStart += 1;
pszEnd -= 1;
} // while
} // ImapUtil_ReverseString
//***************************************************************************
// Function: ImapUtil_SpecialFldrTypeToPath
//
// Purpose:
// This function returns the path for the given special folder type.
//
// Arguments:
// LPSTR pszAccountID [in] - ID of IMAP account where special folder resides.
// SPECIALFOLDER sfType [in] - the special folder whose path should be returned
// (eg, FOLDER_SENT).
// LPCSTR pszRootFldrPrefix [in] - the root folder prefix for this IMAP
// account. If this is NULL, this function will find out for itself.
// LPSTR pszPath [out] - pointer to a buffer to receieve the special folder
// path.
// DWORD dwSizeOfPath [in] - size of buffer pointed to by pszPath.
//
// Returns:
// HRESULT indicating success or failure. This can include:
//
// STORE_E_NOREMOTESPECIALFLDR: indicates the given special folder has
// been disabled by the user for this IMAP server.
//***************************************************************************
HRESULT ImapUtil_SpecialFldrTypeToPath(LPCSTR pszAccountID, SPECIALFOLDER sfType,
LPSTR pszRootFldrPrefix, char cHierarchyChar,
LPSTR pszPath, DWORD dwSizeOfPath)
{
HRESULT hrResult = E_FAIL;
IImnAccount *pAcct = NULL;
DWORD dw;
int iLen;
TraceCall("ImapUtil_SpecialFldrTypeToPath");
AssertSz(dwSizeOfPath >= MAX_PATH * 2 + 2, "RFP + Special Folder Path = Big Buffer, Dude"); // Room for HC, null-term
*pszPath = '\0'; // Initialize
switch (sfType)
{
case FOLDER_INBOX:
StrCpyN(pszPath, c_szINBOX, dwSizeOfPath);
hrResult = S_OK;
break;
case FOLDER_SENT:
case FOLDER_DRAFT:
Assert(g_pAcctMan);
if (g_pAcctMan)
{
hrResult = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, pszAccountID, &pAcct);
}
if (FAILED(hrResult))
break;
hrResult = pAcct->GetPropDw(AP_IMAP_SVRSPECIALFLDRS, &dw);
if (FAILED(hrResult))
break;
else if (FALSE == dw) {
hrResult = STORE_E_NOREMOTESPECIALFLDR;
break;
}
// First prepend the root folder prefix
// Check if user gave us a root folder prefix: otherwise we need to load it ourselves
if (NULL == pszRootFldrPrefix)
ImapUtil_LoadRootFldrPrefix(pszAccountID, pszPath, dwSizeOfPath);
else
StrCpyN(pszPath, pszRootFldrPrefix, dwSizeOfPath);
iLen = lstrlen(pszPath);
if (iLen > 0 && (DWORD)iLen + 1 < dwSizeOfPath)
{
pszPath[iLen] = cHierarchyChar;
iLen += 1;
pszPath[iLen] = '\0';
}
hrResult = pAcct->GetPropSz(FOLDER_SENT == sfType ?
AP_IMAP_SENTITEMSFLDR : AP_IMAP_DRAFTSFLDR, pszPath + iLen,
dwSizeOfPath - iLen);
break;
case FOLDER_DELETED:
case FOLDER_ERRORS:
case FOLDER_JUNK:
case FOLDER_MSNPROMO:
case FOLDER_OUTBOX:
case FOLDER_BULKMAIL:
hrResult = STORE_E_NOREMOTESPECIALFLDR;
break;
default:
AssertSz(FALSE, "Invalid special folder type!");
hrResult = E_INVALIDARG;
break;
} // switch (sfType)
if (NULL != pAcct)
pAcct->Release();
// Check for blank path
if (SUCCEEDED(hrResult) && '\0' == *pszPath)
hrResult = STORE_E_NOREMOTESPECIALFLDR;
return hrResult;
} // ImapUtil_SpecialFldrTypeToPath
//***************************************************************************
// Function: ImapUtil_LoadRootFldrPrefix
//
// Purpose:
// This function loads the "Root Folder Path" option from the account
// manager. The Root Folder Path (prefix) identifies the parent of all of
// the user's folders. Thus, the Root Folder Path forms a prefix for all
// mailboxes which are not INBOX.
//
// Arguments:
// LPCTSTR pszAccountID [in] - ID of the account
// LPSTR pszRootFolderPrefix [out] - destination for Root Folder Path
// DWORD dwSizeofPrefixBuffer [in] - size of buffer pointed to by
// pszRootFolderPrefix.
//***************************************************************************
void ImapUtil_LoadRootFldrPrefix(LPCTSTR pszAccountID,
LPSTR pszRootFolderPrefix,
DWORD dwSizeofPrefixBuffer)
{
IImnAccount *pAcct = NULL;
HRESULT hrResult = E_UNEXPECTED;
LPSTR pLastChar;
Assert(g_pAcctMan);
Assert(NULL != pszAccountID);
Assert(NULL != pszRootFolderPrefix);
Assert(0 != dwSizeofPrefixBuffer);
if (!g_pAcctMan)
return;
// Initialize variables
pAcct = NULL;
pszRootFolderPrefix[0] = '\0'; // If we can't find a prefix, default to NONE
// Get the prefix from the account manager
hrResult = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, pszAccountID, &pAcct);
if (FAILED(hrResult))
goto exit;
hrResult = pAcct->GetPropSz(AP_IMAP_ROOT_FOLDER, pszRootFolderPrefix,
dwSizeofPrefixBuffer);
if (FAILED(hrResult))
goto exit;
// OK, we now have the root folder prefix. Strip trailing hierarchy chars,
// since we probably don't know server HC when we try to list the prefix
pLastChar = CharPrev(pszRootFolderPrefix, pszRootFolderPrefix + lstrlen(pszRootFolderPrefix));
while (pLastChar >= pszRootFolderPrefix &&
('/' == *pLastChar || '\\' == *pLastChar || '.' == *pLastChar)) {
*pLastChar = '\0'; // Bye-bye, potential hierarchy char
pLastChar = CharPrev(pszRootFolderPrefix, pLastChar);
} // while
exit:
if (NULL != pAcct)
pAcct->Release();
} // ImapUtil_LoadRootFldrPrefix
//***************************************************************************
// Function: ImapUtil_GetSpecialFolderType
//
// Purpose:
// This function takes the given account name and folder path, and
// determines whether the path points to a special IMAP folder. Note that
// although it is possible for a path to represent more than one type of
// IMAP special folder, only ONE special folder type is returned (based
// on evaluation order).
//
// Arguments:
// LPSTR pszAccountID [in] - ID of the IMAP account whose special folder
// paths we want to compare pszFullPath to.
// LPSTR pszFullPath [in] - path to a potential special folder residing on
// the pszAccountID account.
// char cHierarchyChar [in] - hierarchy char used to interpret pszFullPath.
// LPSTR pszRootFldrPrefix [in] - the root folder prefix for this IMAP
// account. If this is NULL, this function will find out for itself.
// SPECIALFOLDER *psfType [out] - the special folder type of given folder
// (eg, FOLDER_NOTSPECIAL, FOLDER_SENT). Pass NULL if not interested.
//
// Returns:
// LPSTR pointing to leaf name of special folder path. For instance, if
// the Drafts folder is set to "one/two/three/Drafts" and this function is
// called to process "one/two/three/Drafts/foo", then this function will
// return "Drafts/foo". If no match is found, NULL is returned.
//***************************************************************************
LPSTR ImapUtil_GetSpecialFolderType(LPSTR pszAccountID, LPSTR pszFullPath,
char cHierarchyChar, LPSTR pszRootFldrPrefix,
SPECIALFOLDER *psfType)
{
HRESULT hrResult;
SPECIALFOLDER sfType = FOLDER_NOTSPECIAL;
BOOL fSpecialFldrPrefix = FALSE;
IImnAccount *pAccount = NULL;
DWORD dw;
int iLeafNameOffset = 0;
int iTmp;
int iLen;
char sz[MAX_PATH * 2 + 2]; // Room for HC plus null-term
Assert(INVALID_HIERARCHY_CHAR != cHierarchyChar);
Assert(g_pAcctMan);
if (!g_pAcctMan)
goto exit;
// First check if this is INBOX or one of its children
iLen = lstrlen(c_szInbox);
if (0 == StrCmpNI(pszFullPath, c_szInbox, iLen) &&
(cHierarchyChar == pszFullPath[iLen] || '\0' == pszFullPath[iLen]))
{
fSpecialFldrPrefix = TRUE;
iLeafNameOffset = 0; // "INBOX" is always the leaf name
if ('\0' == pszFullPath[iLen])
{
sfType = FOLDER_INBOX; // Exact match for "INBOX"
goto exit;
}
}
hrResult = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, pszAccountID, &pAccount);
if (FAILED(hrResult))
goto exit;
#ifdef DEBUG
hrResult = pAccount->GetServerTypes(&dw);
Assert(SUCCEEDED(hrResult) && (SRV_IMAP & dw));
#endif // DEBUG
hrResult = pAccount->GetPropDw(AP_IMAP_SVRSPECIALFLDRS, &dw);
if (SUCCEEDED(hrResult) && dw)
{
int iLenRFP;
// Check if user gave us a root folder prefix: otherwise we need to load it ourselves
if (NULL == pszRootFldrPrefix)
ImapUtil_LoadRootFldrPrefix(pszAccountID, sz, sizeof(sz));
else
StrCpyN(sz, pszRootFldrPrefix, ARRAYSIZE(sz));
iLenRFP = lstrlen(sz);
if (iLenRFP > 0 && (DWORD)iLenRFP + 1 < sizeof(sz))
{
sz[iLenRFP] = cHierarchyChar;
iLenRFP += 1;
sz[iLenRFP] = '\0';
}
hrResult = pAccount->GetPropSz(AP_IMAP_SENTITEMSFLDR, sz + iLenRFP, sizeof(sz) - iLenRFP);
if (SUCCEEDED(hrResult))
{
iLen = lstrlen(sz);
if (0 == StrCmpNI(sz, pszFullPath, iLen) &&
(cHierarchyChar == pszFullPath[iLen] || '\0' == pszFullPath[iLen]))
{
fSpecialFldrPrefix = TRUE;
iTmp = (int) (ImapUtil_ExtractLeafName(sz, cHierarchyChar) - sz);
iLeafNameOffset = max(iTmp, iLeafNameOffset);
if ('\0' == pszFullPath[iLen])
{
sfType = FOLDER_SENT; // Exact match for Sent Items
goto exit;
}
}
}
hrResult = pAccount->GetPropSz(AP_IMAP_DRAFTSFLDR, sz + iLenRFP, sizeof(sz) - iLenRFP);
if (SUCCEEDED(hrResult))
{
iLen = lstrlen(sz);
if (0 == StrCmpNI(sz, pszFullPath, iLen) &&
(cHierarchyChar == pszFullPath[iLen] || '\0' == pszFullPath[iLen]))
{
fSpecialFldrPrefix = TRUE;
iTmp = (int) (ImapUtil_ExtractLeafName(sz, cHierarchyChar) - sz);
iLeafNameOffset = max(iTmp, iLeafNameOffset);
if ('\0' == pszFullPath[iLen])
{
sfType = FOLDER_DRAFT; // Exact match for Drafts folder
goto exit;
}
}
}
} // if (AP_IMAP_SVRSPECIALFLDRS)
exit:
if (NULL != pAccount)
pAccount->Release();
if (NULL != psfType)
*psfType = sfType;
if (fSpecialFldrPrefix)
return pszFullPath + iLeafNameOffset;
else
return NULL;
} // ImapUtil_GetSpecialFolderType
//***************************************************************************
// Function: ImapUtil_ExtractLeafName
//
// Purpose:
// This function takes an IMAP folder path and extracts the leaf node name.
//
// Arguments:
// LPSTR pszFolderPath [in] - a string containing the IMAP folder path.
// char cHierarchyChar [in] - the hierarchy char used in pszFolderPath.
//
// Returns:
// A pointer to the leaf node name in pszFolderPath. The default return
// value is pszFolderPath, if no hierarchy characters were found.
//***************************************************************************
LPSTR ImapUtil_ExtractLeafName(LPSTR pszFolderPath, char cHierarchyChar)
{
LPSTR pszLastHierarchyChar, p;
// Find out where the last hierarchy character lives
pszLastHierarchyChar = pszFolderPath;
p = pszFolderPath;
while ('\0' != *p) {
if (cHierarchyChar == *p)
pszLastHierarchyChar = p;
p += 1;
}
// Adjust pszLastHierarchyChar to point to leaf name
if (cHierarchyChar == *pszLastHierarchyChar)
return pszLastHierarchyChar + 1;
else
return pszFolderPath;
} // ImapUtil_ExtractLeafName
HRESULT ImapUtil_UIDToMsgSeqNum(IIMAPTransport *pIMAPTransport, DWORD_PTR dwUID,
LPDWORD pdwMsgSeqNum)
{
HRESULT hrTemp;
DWORD *pdwMsgSeqNumToUIDArray = NULL;
DWORD dwHighestMsgSeqNum;
DWORD dw;
BOOL fFound = FALSE;
TraceCall("ImapUtil_UIDToMsgSeqNum");
if (NULL == pIMAPTransport || 0 == dwUID)
{
TraceResult(E_INVALIDARG);
goto exit;
}
// Quickly check the highest MSN
hrTemp = pIMAPTransport->GetHighestMsgSeqNum(&dwHighestMsgSeqNum);
if (FAILED(hrTemp) || 0 == dwHighestMsgSeqNum)
{
TraceError(hrTemp);
goto exit;
}
// OK, no more laziness, we gotta do a linear search now
hrTemp = pIMAPTransport->GetMsgSeqNumToUIDArray(&pdwMsgSeqNumToUIDArray,
&dwHighestMsgSeqNum);
if (FAILED(hrTemp))
{
TraceResult(hrTemp);
goto exit;
}
Assert(dwHighestMsgSeqNum > 0);
for (dw = 0; dw < dwHighestMsgSeqNum; dw++)
{
// Look for match or overrun
if (0 != pdwMsgSeqNumToUIDArray[dw] && dwUID <= pdwMsgSeqNumToUIDArray[dw])
{
if (dwUID == pdwMsgSeqNumToUIDArray[dw])
{
if (NULL != pdwMsgSeqNum)
*pdwMsgSeqNum = dw + 1;
fFound = TRUE;
}
break;
}
} // for
exit:
SafeMemFree(pdwMsgSeqNumToUIDArray);
if (fFound)
return S_OK;
else
return E_FAIL;
} // ImapUtil_UIDToMsgSeqNum
// *** REMOVE THIS after Beta-2! This sets the AP_IMAP_DIRTY flag if no IMAP special folders
// found after OE4->OE5 migration. We can then prompt user to refresh folder list.
void ImapUtil_B2SetDirtyFlag(void)
{
IImnAccountManager *pAcctMan = NULL;
IImnEnumAccounts *pAcctEnum = NULL;
IImnAccount *pAcct = NULL;
HRESULT hrResult;
TraceCall("ImapUtil_B2SetDirtyFlag");
// Enumerate through all accounts. Set AP_IMAP_DIRTY flag on all IMAP accounts
hrResult = HrCreateAccountManager(&pAcctMan);
if (FAILED(hrResult))
{
TraceResult(hrResult);
goto exit;
}
hrResult = pAcctMan->Init(NULL);
if (FAILED(hrResult))
{
TraceResult(hrResult);
goto exit;
}
hrResult = pAcctMan->Enumerate(SRV_IMAP, &pAcctEnum);
if (FAILED(hrResult))
{
TraceResult(hrResult);
goto exit;
}
hrResult = pAcctEnum->GetNext(&pAcct);
while (SUCCEEDED(hrResult))
{
DWORD dwIMAPDirty;
hrResult = pAcct->GetPropDw(AP_IMAP_DIRTY, &dwIMAPDirty);
if (FAILED(hrResult))
{
TraceResult(hrResult);
dwIMAPDirty = 0;
}
// Mark this IMAP account as dirty so we prompt user to refresh folder list
dwIMAPDirty |= (IMAP_FLDRLIST_DIRTY | IMAP_OE4MIGRATE_DIRTY);
hrResult = pAcct->SetPropDw(AP_IMAP_DIRTY, dwIMAPDirty);
TraceError(hrResult); // Record but otherwise ignore result
hrResult = pAcct->SaveChanges();
TraceError(hrResult); // Record but otherwise ignore result
// Get next account
SafeRelease(pAcct);
hrResult = pAcctEnum->GetNext(&pAcct);
}
exit:
SafeRelease(pAcctMan);
SafeRelease(pAcctEnum);
}