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
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);
|
|
}
|