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.
 
 
 
 
 
 

4197 lines
136 KiB

/*
* h t t p s e r v . cpp
*
* Author: Greg Friedman
*
* Purpose: Derives from IMessageServer to implement HTTPMail-specific
* store communication.
*
* Copyright (C) Microsoft Corp. 1998.
*/
#include "pch.hxx"
#include "httpserv.h"
#include "httputil.h"
#include "storutil.h"
#include "serverq.h"
#include "tmap.h"
#include "acctcach.h"
#include "urlmon.h"
#include "useragnt.h"
#include "spoolapi.h"
#include "demand.h"
#define CCHMAX_RES 255
static const char s_szHTTPMailServerWndClass[] = "HTTPMailWndClass";
#define AssertSingleThreaded AssertSz(m_dwThreadId == GetCurrentThreadId(), "Multi-threading makes me sad.")
// explicit template instantiations
template class TMap<FOLDERID, CSimpleString>;
template class TPair<FOLDERID, CSimpleString>;
const UINT WM_HTTP_BEGIN_OP = WM_USER;
// SOT_SYNCING_STORE
static const HTTPSTATEFUNCS c_rgpfnSyncStore[] =
{
{ &CHTTPMailServer::Connect, NULL },
{ &CHTTPMailServer::GetMsgFolderRoot, &CHTTPMailServer::HandleGetMsgFolderRoot },
{ &CHTTPMailServer::ListFolders, &CHTTPMailServer::HandleListFolders },
{ &CHTTPMailServer::PurgeFolders, NULL }
};
// SOT_SYNC_FOLDER
static const HTTPSTATEFUNCS c_rgpfnSyncFolder[] =
{
{ &CHTTPMailServer::Connect, NULL },
{ &CHTTPMailServer::GetMsgFolderRoot, &CHTTPMailServer::HandleGetMsgFolderRoot },
{ &CHTTPMailServer::AutoListFolders, &CHTTPMailServer::HandleListFolders },
{ &CHTTPMailServer::PurgeFolders, NULL },
{ &CHTTPMailServer::BuildFolderUrl, NULL },
{ &CHTTPMailServer::ListHeaders, &CHTTPMailServer::HandleListHeaders },
{ &CHTTPMailServer::PurgeMessages, NULL },
{ &CHTTPMailServer::ResetMessageCounts, NULL }
};
// SOT_GET_MESSAGE
static const HTTPSTATEFUNCS c_rgpfnGetMessage[] =
{
{ &CHTTPMailServer::Connect, NULL },
{ &CHTTPMailServer::GetMsgFolderRoot, &CHTTPMailServer::HandleGetMsgFolderRoot },
{ &CHTTPMailServer::AutoListFolders, &CHTTPMailServer::HandleListFolders },
{ &CHTTPMailServer::PurgeFolders, NULL },
{ &CHTTPMailServer::BuildFolderUrl, NULL },
{ &CHTTPMailServer::GetMessage, &CHTTPMailServer::HandleGetMessage }
};
// SOT_CREATE_FOLDER
static const HTTPSTATEFUNCS c_rgpfnCreateFolder[] =
{
{ &CHTTPMailServer::Connect, NULL },
{ &CHTTPMailServer::GetMsgFolderRoot, &CHTTPMailServer::HandleGetMsgFolderRoot },
{ &CHTTPMailServer::AutoListFolders, &CHTTPMailServer::HandleListFolders },
{ &CHTTPMailServer::PurgeFolders, NULL },
{ &CHTTPMailServer::CreateFolder, &CHTTPMailServer::HandleCreateFolder }
};
// SOT_RENAME_FOLDER
static const HTTPSTATEFUNCS c_rgpfnRenameFolder[] =
{
{ &CHTTPMailServer::Connect, NULL },
{ &CHTTPMailServer::GetMsgFolderRoot, &CHTTPMailServer::HandleGetMsgFolderRoot },
{ &CHTTPMailServer::AutoListFolders, &CHTTPMailServer::HandleListFolders },
{ &CHTTPMailServer::PurgeFolders, NULL },
{ &CHTTPMailServer::RenameFolder, &CHTTPMailServer::HandleRenameFolder }
};
// SOT_DELETE_FOLDER
static const HTTPSTATEFUNCS c_rgpfnDeleteFolder[] =
{
{ &CHTTPMailServer::Connect, NULL },
{ &CHTTPMailServer::GetMsgFolderRoot, &CHTTPMailServer::HandleGetMsgFolderRoot },
{ &CHTTPMailServer::AutoListFolders, &CHTTPMailServer::HandleListFolders },
{ &CHTTPMailServer::PurgeFolders, NULL },
{ &CHTTPMailServer::DeleteFolder, &CHTTPMailServer::HandleDeleteFolder }
};
// SOT_SET_MESSAGEFLAGS
static const HTTPSTATEFUNCS c_rgpfnSetMessageFlags[] =
{
{ &CHTTPMailServer::Connect, NULL },
{ &CHTTPMailServer::GetMsgFolderRoot, &CHTTPMailServer::HandleGetMsgFolderRoot },
{ &CHTTPMailServer::AutoListFolders, &CHTTPMailServer::HandleListFolders },
{ &CHTTPMailServer::PurgeFolders, NULL },
{ &CHTTPMailServer::BuildFolderUrl, NULL },
{ &CHTTPMailServer::SetMessageFlags, &CHTTPMailServer::HandleMemberErrors},
{ &CHTTPMailServer::ApplyFlagsToStore, NULL }
};
// SOT_DELETING_MESSAGES
static const HTTPSTATEFUNCS c_rgpfnDeleteMessages[] =
{
{ &CHTTPMailServer::Connect, NULL },
{ &CHTTPMailServer::GetMsgFolderRoot, &CHTTPMailServer::HandleGetMsgFolderRoot },
{ &CHTTPMailServer::AutoListFolders, &CHTTPMailServer::HandleListFolders },
{ &CHTTPMailServer::PurgeFolders, NULL },
{ &CHTTPMailServer::BuildFolderUrl, NULL },
{ &CHTTPMailServer::DeleteMessages, &CHTTPMailServer::HandleMemberErrors },
{ &CHTTPMailServer::DeleteFallbackToMove, &CHTTPMailServer::HandleDeleteFallbackToMove },
{ &CHTTPMailServer::PurgeDeletedFromStore, NULL }
};
// SOT_PUT_MESSAGE
static const HTTPSTATEFUNCS c_rgpfnPutMessage[] =
{
{ &CHTTPMailServer::Connect, NULL },
{ &CHTTPMailServer::GetMsgFolderRoot, &CHTTPMailServer::HandleGetMsgFolderRoot },
{ &CHTTPMailServer::AutoListFolders, &CHTTPMailServer::HandleListFolders },
{ &CHTTPMailServer::PurgeFolders, NULL },
{ &CHTTPMailServer::PutMessage, &CHTTPMailServer::HandlePutMessage },
{ &CHTTPMailServer::AddPutMessage, NULL }
};
// SOT_COPYMOVE_MESSAGES (copying or moving one message)
static const HTTPSTATEFUNCS c_rgpfnCopyMoveMessage[] =
{
{ &CHTTPMailServer::Connect, NULL },
{ &CHTTPMailServer::GetMsgFolderRoot, &CHTTPMailServer::HandleGetMsgFolderRoot },
{ &CHTTPMailServer::AutoListFolders, &CHTTPMailServer::HandleListFolders },
{ &CHTTPMailServer::PurgeFolders, NULL },
{ &CHTTPMailServer::BuildFolderUrl, NULL },
{ &CHTTPMailServer::CopyMoveMessage, &CHTTPMailServer::HandleCopyMoveMessage }
};
// SOT_COPYMOVE_MESSAGES (moving multiple messages)
static const HTTPSTATEFUNCS c_rgpfnBatchCopyMoveMessages[] =
{
{ &CHTTPMailServer::Connect, NULL },
{ &CHTTPMailServer::GetMsgFolderRoot, &CHTTPMailServer::HandleGetMsgFolderRoot },
{ &CHTTPMailServer::AutoListFolders, &CHTTPMailServer::HandleListFolders },
{ &CHTTPMailServer::PurgeFolders, NULL },
{ &CHTTPMailServer::BuildFolderUrl, NULL },
{ &CHTTPMailServer::BatchCopyMoveMessages, &CHTTPMailServer::HandleBatchCopyMoveMessages},
{ &CHTTPMailServer::FinalizeBatchCopyMove, NULL }
};
// SOT_GET_ADURL (gets ad url from Hotmail)
static const HTTPSTATEFUNCS c_rgpfnGetAdUrl[] =
{
{ &CHTTPMailServer::Connect, NULL },
{ &CHTTPMailServer::GetAdBarUrlFromServer, NULL }
};
// SOT_GET_HTTP_MINPOLLINGINTERVAL (gets Minimum polling interval from http server)
static const HTTPSTATEFUNCS c_rgpfnGetMinPollingInterval[] =
{
{ &CHTTPMailServer::Connect, NULL },
{ &CHTTPMailServer::GetMinPollingInterval, NULL }
};
class CFolderList
{
public:
// Public factory function.
static HRESULT Create(IMessageStore *pStore, FOLDERID idRoot, CFolderList **ppFolderList);
private:
// Constructor is private. Use "Create" to instantiate.
CFolderList();
~CFolderList();
private:
// unimplemented copy constructor/assignment operator
CFolderList(const CFolderList& other);
CFolderList& operator=(const CFolderList& other);
public:
ULONG AddRef(void);
ULONG Release(void);
FOLDERID FindAndRemove(LPSTR pszUrlComponent, DWORD *pcMessages, DWORD *pcUnread);
FOLDERID FindAndRemove(SPECIALFOLDER tySpecial, DWORD *pcMessages, DWORD *pcUnread);
void PurgeRemainingFromStore(void);
private:
typedef struct tagFOLDERLISTNODE
{
LPSTR pszUrlComponent;
FLDRFLAGS dwFlags;
FOLDERID idFolder;
SPECIALFOLDER tySpecial;
DWORD cMessages;
DWORD cUnread;
tagFOLDERLISTNODE *pflnNext;
} FOLDERLISTNODE, *LPFOLDERLISTNODE;
LPFOLDERLISTNODE _AllocNode(void)
{
LPFOLDERLISTNODE pflnNode = new FOLDERLISTNODE;
if (pflnNode)
{
pflnNode->pszUrlComponent = NULL;
pflnNode->dwFlags = 0;
pflnNode->idFolder = FOLDERID_INVALID;
pflnNode->tySpecial = FOLDER_NOTSPECIAL;
pflnNode->cMessages = 0;
pflnNode->cUnread = 0;
pflnNode->pflnNext = NULL;
}
return pflnNode;
}
HRESULT HrInitialize(IMessageStore *pStore, FOLDERID idRoot);
void _FreeNode(LPFOLDERLISTNODE pflnNode)
{
if (pflnNode)
{
SafeMemFree(pflnNode->pszUrlComponent);
delete pflnNode;
}
}
void _FreeList(void);
private:
ULONG m_cRef;
IMessageStore *m_pStore;
LPFOLDERLISTNODE m_pflnList;
};
//----------------------------------------------------------------------
// CFolderList::Create
//----------------------------------------------------------------------
HRESULT CFolderList::Create(IMessageStore *pStore, FOLDERID idRoot, CFolderList **ppFolderList)
{
HRESULT hr = S_OK;
CFolderList *pFolderList = NULL;
if (NULL == pStore || FOLDERID_INVALID == idRoot || NULL == ppFolderList)
{
hr = TraceResult(E_INVALIDARG);
goto exit;
}
*ppFolderList = NULL;
pFolderList = new CFolderList();
if (!pFolderList)
{
hr = E_OUTOFMEMORY;
goto exit;
}
IF_FAILEXIT(hr = pFolderList->HrInitialize(pStore, idRoot));
*ppFolderList = pFolderList;
pFolderList = NULL;
exit:
SafeRelease(pFolderList);
return hr;
}
//----------------------------------------------------------------------
// CFolderList::CFolderList
//----------------------------------------------------------------------
CFolderList::CFolderList(void) :
m_cRef(1),
m_pStore(NULL),
m_pflnList(NULL)
{
// nothing to do
}
//----------------------------------------------------------------------
// CFolderList::~CFolderList
//----------------------------------------------------------------------
CFolderList::~CFolderList(void)
{
_FreeList();
SafeRelease(m_pStore);
}
//----------------------------------------------------------------------
// CFolderList::AddRef
//----------------------------------------------------------------------
ULONG CFolderList::AddRef(void)
{
return (++m_cRef);
}
//----------------------------------------------------------------------
// CFolderList::Release
//----------------------------------------------------------------------
ULONG CFolderList::Release(void)
{
if (0 == --m_cRef)
{
delete this;
return 0;
}
else
return m_cRef;
}
//----------------------------------------------------------------------
// CFolderList::_FreeList
//----------------------------------------------------------------------
void CFolderList::_FreeList(void)
{
LPFOLDERLISTNODE pflnDeleteMe;
while (m_pflnList)
{
pflnDeleteMe = m_pflnList;
m_pflnList = m_pflnList->pflnNext;
_FreeNode(pflnDeleteMe);
}
}
//----------------------------------------------------------------------
// CFolderList::HrInitialize
//----------------------------------------------------------------------
HRESULT CFolderList::HrInitialize(IMessageStore *pStore, FOLDERID idRoot)
{
HRESULT hr=S_OK;
IEnumerateFolders *pFldrEnum = NULL;
FOLDERINFO fi;
FOLDERLISTNODE flnDummyHead= { NULL, 0, 0, NULL };
LPFOLDERLISTNODE pflnTail = &flnDummyHead;
LPFOLDERLISTNODE pflnNewNode = NULL;
if (NULL == pStore)
{
hr = TraceResult(E_INVALIDARG);
return hr;
}
if (NULL != m_pflnList)
{
hr = TraceResult(ERROR_ALREADY_INITIALIZED);
return hr;
}
m_pStore = pStore;
m_pStore->AddRef();
// this function assumes that the folder list is flat.
// it needs to be modified to support a hierarchical store.
IF_FAILEXIT(hr = pStore->EnumChildren(idRoot, FALSE, &pFldrEnum));
pFldrEnum->Reset();
// build a linked list of folder nodes
while (S_OK == pFldrEnum->Next(1, &fi, NULL))
{
pflnNewNode = _AllocNode();
if (NULL == pflnNewNode)
{
hr = TraceResult(E_OUTOFMEMORY);
pStore->FreeRecord(&fi);
_FreeList();
goto exit;
}
pflnNewNode->pszUrlComponent = PszDupA(fi.pszUrlComponent);
pflnNewNode->dwFlags = fi.dwFlags;
pflnNewNode->idFolder = fi.idFolder;
pflnNewNode->tySpecial = fi.tySpecial;
pflnNewNode->cMessages = fi.cMessages;
pflnNewNode->cUnread = fi.cUnread;
pflnTail->pflnNext = pflnNewNode;
pflnTail = pflnNewNode;
pflnNewNode = NULL;
pStore->FreeRecord(&fi);
}
m_pflnList = flnDummyHead.pflnNext;
exit:
ReleaseObj(pFldrEnum);
return hr;
}
//----------------------------------------------------------------------
// CFolderList::FindAndRemove
//----------------------------------------------------------------------
FOLDERID CFolderList::FindAndRemove(LPSTR pszUrlComponent,
DWORD *pcMessages,
DWORD *pcUnread)
{
LPFOLDERLISTNODE pflnPrev = NULL;
LPFOLDERLISTNODE pflnCur = m_pflnList;
FOLDERID idFound = FOLDERID_INVALID;
if (NULL == pszUrlComponent)
return FOLDERID_INVALID;
if (pcMessages)
*pcMessages = 0;
if (pcUnread)
*pcUnread = 0;
while (pflnCur)
{
if ((NULL != pflnCur->pszUrlComponent) && (0 == lstrcmp(pflnCur->pszUrlComponent, pszUrlComponent)))
{
if (NULL == pflnPrev)
m_pflnList = pflnCur->pflnNext;
else
pflnPrev->pflnNext = pflnCur->pflnNext;
idFound = pflnCur->idFolder;
if (pcMessages)
*pcMessages = pflnCur->cMessages;
if (pcUnread)
*pcUnread = pflnCur->cUnread;
_FreeNode(pflnCur);
break;
}
pflnPrev = pflnCur;
pflnCur = pflnCur->pflnNext;
}
return idFound;
}
//----------------------------------------------------------------------
// CFolderList::FindAndRemove
//----------------------------------------------------------------------
FOLDERID CFolderList::FindAndRemove(SPECIALFOLDER tySpecial,
DWORD *pcMessages,
DWORD *pcUnread)
{
LPFOLDERLISTNODE pflnPrev = NULL;
LPFOLDERLISTNODE pflnCur = m_pflnList;
FOLDERID idFound = FOLDERID_INVALID;
if (FOLDER_NOTSPECIAL == tySpecial)
return FOLDERID_INVALID;
if (pcMessages)
*pcMessages = 0;
if (pcUnread)
*pcUnread = 0;
while (pflnCur)
{
if (pflnCur->tySpecial == tySpecial)
{
if (NULL == pflnPrev)
m_pflnList = pflnCur->pflnNext;
else
pflnPrev->pflnNext = pflnCur->pflnNext;
idFound = pflnCur->idFolder;
if (pcMessages)
*pcMessages = pflnCur->cMessages;
if (pcUnread)
*pcUnread = pflnCur->cUnread;
_FreeNode(pflnCur);
break;
}
pflnPrev = pflnCur;
pflnCur = pflnCur->pflnNext;
}
return idFound;
}
//----------------------------------------------------------------------
// CFolderList::PurgeRemainingFromStore
//----------------------------------------------------------------------
void CFolderList::PurgeRemainingFromStore(void)
{
HRESULT hr = S_OK;
LPFOLDERLISTNODE pflnCur = m_pflnList;
LPFOLDERLISTNODE pflnDeleteMe = NULL;
// take ownership of the list
m_pflnList = NULL;
while (pflnCur)
{
m_pStore->DeleteFolder(pflnCur->idFolder, DELETE_FOLDER_DELETESPECIAL | DELETE_FOLDER_NOTRASHCAN, NULL);
pflnDeleteMe = pflnCur;
pflnCur = pflnCur->pflnNext;
_FreeNode(pflnDeleteMe);
}
}
//----------------------------------------------------------------------
// FreeNewMessageInfo
//----------------------------------------------------------------------
static void __cdecl FreeNewMessageInfo(LPVOID pnmi)
{
Assert(NULL != pnmi);
SafeMemFree(((LPNEWMESSAGEINFO)pnmi)->pszUrlComponent);
MemFree(pnmi);
}
#ifndef NOHTTPMAIL
//----------------------------------------------------------------------
// CreateHTTPMailStore (factory function)
//----------------------------------------------------------------------
HRESULT CreateHTTPMailStore(IUnknown *pUnkOuter, IUnknown **ppUnknown)
{
HRESULT hr = S_OK;
// Trace
TraceCall("CreateHTTPMailStore");
// Invalid Args
Assert(NULL != ppUnknown);
if (NULL == ppUnknown)
return E_INVALIDARG;
// Initialize
*ppUnknown = NULL;
// Create me
CHTTPMailServer *pNew = new CHTTPMailServer();
if (NULL == pNew)
return TraceResult(E_OUTOFMEMORY);
// Cast to unknown
//*ppUnknown = SAFECAST(pNew, IMessageServer *);
hr = CreateServerQueue(pNew, (IMessageServer **)ppUnknown);
pNew->Release(); // Since we're not returning this ptr, bump down refcount
// Done
return hr;
}
#endif
//----------------------------------------------------------------------
// CHTTPMailServer::CHTTPMailServer
//----------------------------------------------------------------------
CHTTPMailServer::CHTTPMailServer(void) :
m_cRef(1),
m_hwnd(NULL),
m_pStore(NULL),
m_pFolder(NULL),
m_pTransport(NULL),
m_pszFldrLeafName(NULL),
m_pszMsgFolderRoot(NULL),
m_idServer(FOLDERID_INVALID),
m_idFolder(FOLDERID_INVALID),
m_tySpecialFolder(FOLDER_NOTSPECIAL),
m_pszFolderUrl(NULL),
m_fConnected(FALSE),
m_pTransport2(NULL),
m_pAccount(NULL)
{
_FreeOperation(FALSE);
ZeroMemory(&m_rInetServerInfo, sizeof(INETSERVER));
m_szAccountName[0] = '\0';
m_szAccountId[0] = '\0';
m_op.pszAdUrl = NULL;
#ifdef DEBUG
m_dwThreadId = GetCurrentThreadId();
#endif // DEBUG
}
//----------------------------------------------------------------------
// CHTTPMailServer::~CHTTPMailServer
//----------------------------------------------------------------------
CHTTPMailServer::~CHTTPMailServer(void)
{
// Close the window
if ((NULL != m_hwnd) && (FALSE != IsWindow(m_hwnd)))
SendMessage(m_hwnd, WM_CLOSE, 0, 0);
ZeroMemory(&m_rInetServerInfo, sizeof(m_rInetServerInfo)); // Done for security
SafeRelease(m_pStore);
SafeRelease(m_pFolder);
SafeRelease(m_pTransport);
SafeRelease(m_pTransport2);
SafeRelease(m_pAccount);
SafeMemFree(m_pszFldrLeafName);
SafeMemFree(m_pszMsgFolderRoot);
SafeMemFree(m_pszFolderUrl);
}
//----------------------------------------------------------------------
// IUnknown Members
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// CHTTPMailServer::QueryInterface
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::QueryInterface(REFIID riid, LPVOID *ppv)
{
HRESULT hr = S_OK;
TraceCall("CHTTPMailServer::QueryInterface");
if (NULL == ppv)
{
hr = E_INVALIDARG;
goto exit;
}
if (IID_IUnknown == riid || IID_IMessageServer == riid)
*ppv = (IMessageServer *)this;
else if (IID_ITransportCallback == riid)
*ppv = (ITransportCallback *)this;
else if (IID_IHTTPMailCallback == riid)
*ppv = (IHTTPMailCallback *)this;
else
{
*ppv = NULL;
hr = E_NOINTERFACE;
goto exit;
}
// the interface was found. addref it
((IUnknown *)*ppv)->AddRef();
exit:
// done
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::AddRef
//----------------------------------------------------------------------
STDMETHODIMP_(ULONG) CHTTPMailServer::AddRef(void)
{
TraceCall("CHTTPMailServer::AddRef");
return InterlockedIncrement(&m_cRef);
}
//----------------------------------------------------------------------
// CHTTPMailServer::AddRef
//----------------------------------------------------------------------
STDMETHODIMP_(ULONG) CHTTPMailServer::Release(void)
{
TraceCall("CHTTPMailServer::Release");
ULONG cRef = InterlockedDecrement(&m_cRef);
Assert(((LONG)cRef) >= 0);
if (0 == cRef)
delete this;
return cRef;
}
//----------------------------------------------------------------------
// IMessageServer Members
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// CHTTPMailServer::Initialize
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::Initialize( IMessageStore *pStore,
FOLDERID idStoreRoot,
IMessageFolder *pFolder,
FOLDERID idFolder)
{
HRESULT hr = S_OK;
FOLDERINFO fi;
AssertSingleThreaded;
if (NULL == pStore || FOLDERID_INVALID == idStoreRoot)
return TraceResult(E_INVALIDARG);
if (!_CreateWnd())
return E_FAIL;
m_idServer = idStoreRoot;
m_idFolder = idFolder;
ReplaceInterface(m_pFolder, pFolder);
ReplaceInterface(m_pStore, pStore);
if (FAILED(hr = m_pStore->GetFolderInfo(idStoreRoot, &fi)))
goto exit;
Assert(!!(fi.dwFlags & FOLDER_SERVER));
StrCpyN(m_szAccountId, fi.pszAccountId, ARRAYSIZE(m_szAccountId));
m_pStore->FreeRecord(&fi);
// if we were passed a valid folder id, check to see if this folder is special?
// we might get passed a bad folder id when we are syncing the store.
if (FOLDERID_INVALID != idFolder)
{
if (FAILED(hr = m_pStore->GetFolderInfo(idFolder, &fi)))
goto exit;
m_tySpecialFolder = fi.tySpecial;
m_pStore->FreeRecord(&fi);
}
exit:
return hr;
}
STDMETHODIMP CHTTPMailServer::ResetFolder( IMessageFolder *pFolder,
FOLDERID idFolder)
{
return(E_NOTIMPL);
}
//----------------------------------------------------------------------
// CHTTPMailServer::SetIdleCallback
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::SetIdleCallback(IStoreCallback *pDefaultCallback)
{
return E_NOTIMPL;
}
//----------------------------------------------------------------------
// CHTTPMailServer::SynchronizeFolder
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::SynchronizeFolder(
SYNCFOLDERFLAGS dwFlags,
DWORD cHeaders,
IStoreCallback *pCallback)
{
TraceCall("CHTTPMailServer::SynchronizeFolder");
AssertSingleThreaded;
Assert(NULL != pCallback);
Assert(SOT_INVALID == m_op.tyOperation);
Assert(NULL != m_pStore);
if (NULL == pCallback)
return E_INVALIDARG;
m_op.tyOperation = SOT_SYNC_FOLDER;
m_op.iState = 0;
m_op.pfnState = c_rgpfnSyncFolder;
m_op.cState = ARRAYSIZE(c_rgpfnSyncFolder);
m_op.dwSyncFlags = dwFlags;
m_op.pCallback = pCallback;
m_op.pCallback->AddRef();
return _BeginDeferredOperation();
}
//----------------------------------------------------------------------
// CHTTPMailServer::GetMessage
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::GetMessage(
MESSAGEID idMessage,
IStoreCallback *pCallback)
{
HRESULT hr = S_OK;
TraceCall("CHTTPMailServer::GetMessage");
AssertSingleThreaded;
Assert(NULL != pCallback);
Assert(SOT_INVALID == m_op.tyOperation);
Assert(NULL != m_pStore);
if (NULL == pCallback)
return E_INVALIDARG;
if (FAILED(hr = CreatePersistentWriteStream(m_pFolder, &m_op.pMessageStream, &m_op.faStream)))
goto exit;
m_op.tyOperation = SOT_GET_MESSAGE;
m_op.pfnState = c_rgpfnGetMessage;
m_op.iState = 0;
m_op.cState = ARRAYSIZE(c_rgpfnGetMessage);
m_op.pCallback = pCallback;
m_op.pCallback->AddRef();
m_op.idMessage = idMessage;
hr = _BeginDeferredOperation();
exit:
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::PutMessage
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::PutMessage(
FOLDERID idFolder,
MESSAGEFLAGS dwFlags,
LPFILETIME pftReceived,
IStream *pStream,
IStoreCallback *pCallback)
{
TraceCall("CHTTPMailServer::PutMessage");
AssertSingleThreaded;
Assert(NULL != pCallback);
Assert(SOT_INVALID == m_op.tyOperation);
Assert(NULL != m_pStore);
if (NULL == pStream || NULL == pCallback)
return E_INVALIDARG;
if (FOLDER_MSNPROMO == m_tySpecialFolder)
return SP_E_HTTP_CANTMODIFYMSNFOLDER;
m_op.tyOperation = SOT_PUT_MESSAGE;
m_op.pfnState = c_rgpfnPutMessage;
m_op.iState = 0;
m_op.cState = ARRAYSIZE(c_rgpfnPutMessage);
m_op.pCallback = pCallback;
m_op.pCallback->AddRef();
m_op.idFolder = idFolder;
m_op.pMessageStream = pStream;
m_op.pMessageStream->AddRef();
m_op.dwMsgFlags = dwFlags;
return _BeginDeferredOperation();
}
//----------------------------------------------------------------------
// CHTTPMailServer::CopyMessages
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::CopyMessages(
IMessageFolder *pDest,
COPYMESSAGEFLAGS dwOptions,
LPMESSAGEIDLIST pList,
LPADJUSTFLAGS pFlags,
IStoreCallback *pCallback)
{
HRESULT hr = S_OK;
FOLDERID idFolder;
FOLDERINFO fi = {0};
LPFOLDERINFO pfiFree = NULL;
TraceCall("CHTTPMailServer::CopyMessages");
if (NULL == pDest)
return E_INVALIDARG;
Assert(NULL != m_pStore);
// disallow moving or copying into the msn promo folder
IF_FAILEXIT(hr = pDest->GetFolderId(&idFolder));
IF_FAILEXIT(hr = m_pStore->GetFolderInfo(idFolder, &fi));
pfiFree = &fi;
if (FOLDER_MSNPROMO == fi.tySpecial)
{
hr = TraceResult(SP_E_HTTP_CANTMODIFYMSNFOLDER);
goto exit;
}
// convert moves out of the promo folder into copies
if (FOLDER_MSNPROMO == m_tySpecialFolder)
dwOptions = (dwOptions & ~COPY_MESSAGE_MOVE);
hr = _DoCopyMoveMessages(SOT_COPYMOVE_MESSAGE, pDest, dwOptions, pList, pCallback);
exit:
if (NULL != pfiFree)
m_pStore->FreeRecord(pfiFree);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::DeleteMessages
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::DeleteMessages(DELETEMESSAGEFLAGS dwOptions,
LPMESSAGEIDLIST pList,
IStoreCallback *pCallback)
{
TraceCall("CHTTPMailServer::DeleteMessages");
AssertSingleThreaded;
Assert(NULL == pList || pList->cMsgs > 0);
Assert(SOT_INVALID == m_op.tyOperation);
Assert(NULL != m_pStore);
// we don't allow messages to be deleted out of the msnpromo folder
if (FOLDER_MSNPROMO == m_tySpecialFolder)
{
// this is a hack. we test this flag to determine that the
//operation is being performed as the last phase of a move
// into a local folder. when this is the case, we fail
// silently.
if (!!(DELETE_MESSAGE_MAYIGNORENOTRASH & dwOptions))
return S_OK;
else
return SP_E_HTTP_CANTMODIFYMSNFOLDER;
}
if ((NULL !=pList && 0 == pList->cMsgs) || NULL == pCallback)
return E_INVALIDARG;
HRESULT hr = S_OK;
IMessageFolder *pDeletedItems = NULL;
m_op.dwDelMsgFlags = dwOptions;
// if the current folder is the deleted items folder, then delete the
// messages, otherwise move them to deleted items
if (FOLDER_DELETED != m_tySpecialFolder && !(dwOptions & DELETE_MESSAGE_NOTRASHCAN))
{
// find the deleted items folder
if (SUCCEEDED(m_pStore->OpenSpecialFolder(m_idServer, NULL, FOLDER_DELETED, &pDeletedItems)) && NULL != pDeletedItems)
{
hr = _DoCopyMoveMessages(SOT_DELETING_MESSAGES, pDeletedItems, COPY_MESSAGE_MOVE, pList, pCallback);
goto exit;
}
}
// handle the case where the messages are not in the deleted items folder
// if pList is null, apply the operation to the entire folder
if (NULL != pList)
hr = CloneMessageIDList(pList, &m_op.pIDList);
else
hr = m_pFolder->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &m_op.hRowSet);
if (FAILED(hr))
goto exit;
m_op.tyOperation = SOT_DELETING_MESSAGES;
m_op.pfnState = c_rgpfnDeleteMessages;
m_op.iState = 0;
m_op.cState = ARRAYSIZE(c_rgpfnDeleteMessages);
m_op.pCallback = pCallback;
m_op.pCallback->AddRef();
hr = _BeginDeferredOperation();
exit:
SafeRelease(pDeletedItems);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::SetMessageFlags
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::SetMessageFlags(
LPMESSAGEIDLIST pList,
LPADJUSTFLAGS pFlags,
SETMESSAGEFLAGSFLAGS dwFlags,
IStoreCallback *pCallback)
{
TraceCall("CHTTPMailServer::SetMessageFlags");
AssertSingleThreaded;
Assert(NULL == pList || pList->cMsgs > 0);
Assert(NULL != pCallback);
Assert(m_op.tyOperation == SOT_INVALID);
Assert(m_pStore != NULL);
if ((NULL !=pList && 0 == pList->cMsgs) || NULL == pFlags || NULL == pCallback)
return E_INVALIDARG;
// the only remote flag supported by httpmail is the "read" flag
// it is an error to attempt to set or unset any other flag
Assert(0 == (pFlags->dwRemove & ~ARF_READ));
Assert(0 == (pFlags->dwAdd & ~ARF_READ));
Assert((ARF_READ == (ARF_READ & pFlags->dwRemove)) || (ARF_READ == (ARF_READ & pFlags->dwAdd)));
HRESULT hr = S_OK;
// if pList is null, apply the operation to the entire folder
if (NULL != pList)
hr = CloneMessageIDList(pList, &m_op.pIDList);
else
hr = m_pFolder->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &m_op.hRowSet);
if (FAILED(hr))
return hr;
m_op.tyOperation = SOT_SET_MESSAGEFLAGS;
m_op.pfnState = c_rgpfnSetMessageFlags;
m_op.cState = ARRAYSIZE(c_rgpfnSetMessageFlags);
m_op.iState = 0;
m_op.pCallback = pCallback;
m_op.pCallback->AddRef();
m_op.fMarkRead = !!(pFlags->dwAdd & ARF_READ);
m_op.dwSetFlags = dwFlags;
return _BeginDeferredOperation();
}
//----------------------------------------------------------------------
// CHTTPMailServer::GetServerMessageFlags
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::GetServerMessageFlags(MESSAGEFLAGS *pFlags)
{
if (NULL == pFlags)
return E_INVALIDARG;
*pFlags = ARF_READ;
return S_OK;
}
//----------------------------------------------------------------------
// CHTTPMailServer::SynchronizeStore
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::SynchronizeStore(
FOLDERID idParent,
SYNCSTOREFLAGS dwFlags,
IStoreCallback *pCallback)
{
TraceCall("CHTTPMailServer::SynchronizeStore");
AssertSingleThreaded;
Assert(pCallback != NULL);
Assert(m_op.tyOperation == SOT_INVALID);
Assert(m_pStore != NULL);
if (NULL == pCallback)
return E_INVALIDARG;
m_op.tyOperation = SOT_SYNCING_STORE;
m_op.pfnState = c_rgpfnSyncStore;
m_op.iState = 0;
m_op.cState = ARRAYSIZE(c_rgpfnSyncStore);
m_op.pCallback = pCallback;
m_op.pCallback->AddRef();
return _BeginDeferredOperation();
}
//----------------------------------------------------------------------
// CHTTPMailServer::CreateFolder
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::CreateFolder(
FOLDERID idParent,
SPECIALFOLDER tySpecial,
LPCSTR pszName,
FLDRFLAGS dwFlags,
IStoreCallback *pCallback)
{
TraceCall("CHTTPMailServer::CreateFolder");
AssertSingleThreaded;
Assert(NULL != pCallback);
Assert(m_op.tyOperation == SOT_INVALID);
Assert(NULL != m_pStore);
// why would we be called to create a special folder?
Assert(FOLDER_NOTSPECIAL == tySpecial);
if (NULL == pCallback || NULL == pszName)
return E_INVALIDARG;
// hotmail doesn't support hierarchical folders.
Assert(m_idServer == idParent);
if (m_idServer != idParent)
return E_FAIL;
m_op.pszFolderName = PszDupA(pszName);
if (NULL == m_op.pszFolderName)
return E_OUTOFMEMORY;
m_op.tyOperation = SOT_CREATE_FOLDER;
m_op.pfnState = c_rgpfnCreateFolder;
m_op.iState = 0;
m_op.cState = ARRAYSIZE(c_rgpfnCreateFolder);
m_op.pCallback = pCallback;
m_op.pCallback->AddRef();
m_op.dwFldrFlags = dwFlags;
return _BeginDeferredOperation();
}
//----------------------------------------------------------------------
// CHTTPMailServer::MoveFolder
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::MoveFolder(FOLDERID idFolder, FOLDERID idParentNew, IStoreCallback *pCallback)
{
return E_NOTIMPL;
}
//----------------------------------------------------------------------
// CHTTPMailServer::RenameFolder
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::RenameFolder(FOLDERID idFolder,
LPCSTR pszName,
IStoreCallback *pCallback)
{
TraceCall("CHTTPMailServer::RenameFolder");
AssertSingleThreaded;
Assert(NULL != pCallback);
Assert(SOT_INVALID == m_op.tyOperation);
Assert(NULL != m_pStore);
Assert(NULL != pszName);
// don't allow the user to rename the promo folder
if (FOLDER_MSNPROMO == m_tySpecialFolder)
return SP_E_HTTP_CANTMODIFYMSNFOLDER;
if (NULL == pszName || NULL == pCallback)
return E_INVALIDARG;
m_op.pszFolderName = PszDupA(pszName);
if (NULL == m_op.pszFolderName)
{
TraceResult(E_OUTOFMEMORY);
return E_OUTOFMEMORY;
}
m_op.idFolder = idFolder;
m_op.tyOperation = SOT_RENAME_FOLDER;
m_op.iState = 0;
m_op.pfnState = c_rgpfnRenameFolder;
m_op.cState = ARRAYSIZE(c_rgpfnRenameFolder);
m_op.pCallback = pCallback;
m_op.pCallback->AddRef();
return _BeginDeferredOperation();
}
//----------------------------------------------------------------------
// CHTTPMailServer::DeleteFolder
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::DeleteFolder(FOLDERID idFolder,
DELETEFOLDERFLAGS dwFlags,
IStoreCallback *pCallback)
{
TraceCall("CHTTPMailServer::DeleteFolder");
AssertSingleThreaded;
Assert(NULL != pCallback);
Assert(SOT_INVALID == m_op.tyOperation);
Assert(NULL != m_pStore);
Assert(FOLDERID_INVALID != idFolder);
// don't allow the user to delete the msn promo folder
if (FOLDER_MSNPROMO == m_tySpecialFolder)
return SP_E_HTTP_CANTMODIFYMSNFOLDER;
// we don't support hierarchical folders - if we are asked to delete
// the children of a folder, just return immediately
if (!!(DELETE_FOLDER_CHILDRENONLY & dwFlags))
return S_OK;
if (NULL == pCallback || FOLDERID_INVALID == idFolder)
return E_INVALIDARG;
m_op.idFolder = idFolder;
m_op.tyOperation = SOT_DELETE_FOLDER;
m_op.iState = 0;
m_op.pfnState = c_rgpfnDeleteFolder;
m_op.cState = ARRAYSIZE(c_rgpfnDeleteFolder);
m_op.pCallback = pCallback;
m_op.pCallback->AddRef();
return _BeginDeferredOperation();
}
//----------------------------------------------------------------------
// CHTTPMailServer::SubscribeToFolder
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::SubscribeToFolder(FOLDERID idFolder, BOOL fSubscribe, IStoreCallback *pCallback)
{
return E_NOTIMPL;
}
//----------------------------------------------------------------------
// CHTTPMailServer::Close
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::Close(DWORD dwFlags)
{
// if we are processing a command, cancel it
Cancel(CT_CANCEL);
if (dwFlags & MSGSVRF_DROP_CONNECTION)
_SetConnected(FALSE);
if (dwFlags & MSGSVRF_HANDS_OFF_SERVER)
{
if (m_pTransport)
{
m_pTransport->DropConnection();
m_pTransport->HandsOffCallback();
m_pTransport->Release();
m_pTransport = NULL;
}
}
return S_OK;
}
//----------------------------------------------------------------------
// CHTTPMailServer::GetFolderCounts
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::GetFolderCounts(FOLDERID idFolder, IStoreCallback *pCallback)
{
return E_NOTIMPL;
}
//----------------------------------------------------------------------
// CHTTPMailServer::GetNewGroups
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::GetNewGroups(LPSYSTEMTIME pSysTime, IStoreCallback *pCallback)
{
return E_NOTIMPL;
}
//----------------------------------------------------------------------
// CHTTPMailServer::OnLogonPrompt
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::OnLogonPrompt(
LPINETSERVER pInetServer,
IInternetTransport *pTransport)
{
HRESULT hr = S_OK;
LPSTR pszCachedPassword = NULL;
INETSERVER rInetServer;
TraceCall("CHTTPMailServer::OnLogonPrompt");
AssertSingleThreaded;
Assert(pInetServer != NULL);
Assert(pTransport != NULL);
Assert(m_op.tyOperation != SOT_INVALID);
Assert(m_op.pCallback != NULL);
// pull password out of the cache
GetAccountPropStrA(m_szAccountId, CAP_PASSWORD, &pszCachedPassword);
if (NULL != pszCachedPassword && 0 != lstrcmp(pszCachedPassword, pInetServer->szPassword))
{
StrCpyN(pInetServer->szPassword, pszCachedPassword, ARRAYSIZE(pInetServer->szPassword));
goto exit;
}
hr = m_op.pCallback->OnLogonPrompt(pInetServer, IXP_HTTPMail);
if (S_OK == hr)
{
// cache the password
HrCacheAccountPropStrA(m_szAccountId, CAP_PASSWORD, pInetServer->szPassword);
// copy the new username and password into our local server info
StrCpyN(m_rInetServerInfo.szPassword, pInetServer->szPassword, ARRAYSIZE(m_rInetServerInfo.szPassword));
StrCpyN(m_rInetServerInfo.szUserName, pInetServer->szUserName, ARRAYSIZE(m_rInetServerInfo.szUserName));
}
exit:
SafeMemFree(pszCachedPassword);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::OnPrompt
//----------------------------------------------------------------------
STDMETHODIMP_(INT) CHTTPMailServer::OnPrompt(
HRESULT hrError,
LPCTSTR pszText,
LPCTSTR pszCaption,
UINT uType,
IInternetTransport *pTransport)
{
return 0;
}
//----------------------------------------------------------------------
// CHTTPMailServer::OnStatus
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::OnStatus(
IXPSTATUS ixpstatus,
IInternetTransport *pTransport)
{
// Stack
TraceCall("CHTTPMailServer::OnStatus");
AssertSingleThreaded;
// If we were disconnected, then clean up some internal state.
if (IXP_DISCONNECTED == ixpstatus)
{
if (m_op.tyOperation != SOT_INVALID)
{
Assert(m_op.pCallback != NULL);
if (m_op.fCancel && !m_op.fNotifiedComplete)
{
IXPRESULT rIxpResult;
// Fake an IXPRESULT
ZeroMemory(&rIxpResult, sizeof(rIxpResult));
rIxpResult.hrResult = STORE_E_OPERATION_CANCELED;
// Return meaningful error information
_FillStoreError(&m_op.error, &rIxpResult);
Assert(STORE_E_OPERATION_CANCELED == m_op.error.hrResult);
m_op.fNotifiedComplete = TRUE;
m_op.pCallback->OnComplete(m_op.tyOperation, m_op.error.hrResult, NULL, &m_op.error);
_FreeOperation();
}
}
m_fConnected = FALSE;
}
return(S_OK);
}
//----------------------------------------------------------------------
// CHTTPMailServer::OnError
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::OnError(
IXPSTATUS ixpstatus,
LPIXPRESULT pIxpResult,
IInternetTransport *pTransport)
{
return E_NOTIMPL;
}
//----------------------------------------------------------------------
// CHTTPMailServer::OnProgress
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::OnProgress(
DWORD dwIncrement,
DWORD dwCurrent,
DWORD dwMaximum,
IInternetTransport *pTransport)
{
return E_NOTIMPL;
}
//----------------------------------------------------------------------
// CHTTPMailServer::OnCommand
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::OnCommand(
CMDTYPE cmdtype,
LPSTR pszLine,
HRESULT hrResponse,
IInternetTransport *pTransport)
{
return E_NOTIMPL;
}
//----------------------------------------------------------------------
// CHTTPMailServer::OnTimeout
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::OnTimeout(
DWORD *pdwTimeout,
IInternetTransport *pTransport)
{
return E_NOTIMPL;
}
//----------------------------------------------------------------------
// CHTTPMailServer::OnResponse
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::OnResponse(
LPHTTPMAILRESPONSE pResponse)
{
HRESULT hr = S_OK;
HRESULT hrResponse;
HRESULT hrSaved;
BOOL fInvokeResponseHandler = TRUE;
AssertSingleThreaded;
Assert(SOT_INVALID != m_op.tyOperation);
if (!m_op.fCancel && !m_op.fNotifiedComplete &&
(SOT_GET_ADURL == m_op.tyOperation || SOT_GET_HTTP_MINPOLLINGINTERVAL == m_op.tyOperation))
{
STOREOPERATIONINFO StoreInfo = {0};
m_op.fNotifiedComplete = TRUE;
if (SOT_GET_ADURL == m_op.tyOperation)
{
StoreInfo.pszUrl = pResponse->rGetPropInfo.pszProp;
pResponse->rGetPropInfo.pszProp = NULL;
}
if (SOT_GET_HTTP_MINPOLLINGINTERVAL == m_op.tyOperation)
{
StoreInfo.dwMinPollingInterval = pResponse->rGetPropInfo.dwProp;
}
m_op.pCallback->OnComplete(m_op.tyOperation, pResponse->rIxpResult.hrResult, &StoreInfo, NULL);
_FreeOperation();
MemFree(StoreInfo.pszUrl);
goto cleanup;
}
if (FAILED(pResponse->rIxpResult.hrResult))
{
Assert(pResponse->fDone);
// Hotmail hack. Hotmail does not support deleting message. This interferes with operations
// such as moving messages from a hotmail folder into the local store. we attempt to send
// a delete to the server, since we don't know whether or not the server supports the command.
// if it fails, we check the delete messages flag to determine if we are allowed to fallback
// to a move operation.
if (SOT_DELETING_MESSAGES == m_op.tyOperation &&
(HTTPMAIL_DELETE == pResponse->command || HTTPMAIL_BDELETE == pResponse->command) &&
IXP_E_HTTP_METHOD_NOT_ALLOW == pResponse->rIxpResult.hrResult &&
FOLDER_DELETED != m_tySpecialFolder &&
!!(m_op.dwDelMsgFlags & DELETE_MESSAGE_MAYIGNORENOTRASH))
{
m_op.fFallbackToMove = TRUE;
fInvokeResponseHandler = FALSE;
// cache the fact that this acct doesn't support msg delete so we don't
// have to go through this nonsense again
HrCacheAccountPropStrA(m_szAccountId, CAP_HTTPNOMESSAGEDELETES, "TRUE");
}
else
{
hrSaved = pResponse->rIxpResult.hrResult;
if (IXP_E_HTTP_ROOT_PROP_NOT_FOUND == hrSaved)
pResponse->rIxpResult.hrResult = SP_E_HTTP_SERVICEDOESNTWORK;
else if ((HTTPMAIL_DELETE == pResponse->command || HTTPMAIL_BDELETE == pResponse->command) && IXP_E_HTTP_METHOD_NOT_ALLOW == hrSaved)
pResponse->rIxpResult.hrResult = SP_E_HTTP_NODELETESUPPORT;
_FillStoreError(&m_op.error, &pResponse->rIxpResult);
pResponse->rIxpResult.hrResult = hrSaved;
if (!m_op.fNotifiedComplete)
{
m_op.fNotifiedComplete = TRUE;
m_op.pCallback->OnComplete(m_op.tyOperation, m_op.error.hrResult, NULL, &m_op.error);
_FreeOperation();
}
return S_OK;
}
}
Assert(NULL != m_op.pfnState[m_op.iState].pfnResp);
// by default, state advances occur when the the response indicates
// that io is done. response functions can override this behavior
// by setting fStateWillAdvance to FALSE to maintain the current state.
m_op.fStateWillAdvance = pResponse->fDone;
// invoke the response function
if (fInvokeResponseHandler)
hr = (this->*(m_op.pfnState[m_op.iState].pfnResp))(pResponse);
cleanup:
if (FAILED(hr))
{
if (_FConnected())
{
m_pTransport->DropConnection();
m_pTransport->HandsOffCallback();
SafeRelease(m_pTransport);
}
if (!m_op.fNotifiedComplete)
{
m_op.fNotifiedComplete = TRUE;
m_op.pCallback->OnComplete(m_op.tyOperation, hr, NULL, NULL);
_FreeOperation();
}
}
else if (SUCCEEDED(hr) && m_op.fStateWillAdvance)
{
m_op.iState++;
_DoOperation();
}
return S_OK;
}
//----------------------------------------------------------------------
// CHTTPMailServer::GetParentWindow
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::GetParentWindow(HWND *phwndParent)
{
HRESULT hr = E_FAIL;
AssertSingleThreaded;
if (m_op.tyOperation != SOT_INVALID && NULL != m_op.pCallback)
hr = m_op.pCallback->GetParentWindow(0, phwndParent);
return hr;
}
//----------------------------------------------------------------------
// IOperationCancel Members
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// CHTTPMailServer::Cancel
//----------------------------------------------------------------------
STDMETHODIMP CHTTPMailServer::Cancel(CANCELTYPE tyCancel)
{
if (m_op.tyOperation != SOT_INVALID)
{
m_op.fCancel = TRUE;
_Disconnect();
}
return S_OK;
}
//----------------------------------------------------------------------
// CHTTPMailServer Implementation
//----------------------------------------------------------------------
// --------------------------------------------------------------------------------
// CHTTPMailServer::_CreateWnd
// --------------------------------------------------------------------------------
BOOL CHTTPMailServer::_CreateWnd()
{
WNDCLASS wc;
IxpAssert(!m_hwnd);
if (m_hwnd)
return TRUE;
if (!GetClassInfo(g_hInst, s_szHTTPMailServerWndClass, &wc))
{
wc.style = 0;
wc.lpfnWndProc = CHTTPMailServer::_WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = g_hInst;
wc.hIcon = NULL;
wc.hCursor = NULL;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = s_szHTTPMailServerWndClass;
RegisterClass(&wc);
}
m_hwnd = CreateWindowEx(WS_EX_TOPMOST,
s_szHTTPMailServerWndClass,
s_szHTTPMailServerWndClass,
WS_POPUP,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
g_hInst,
(LPVOID)this);
return (NULL != m_hwnd);
}
// --------------------------------------------------------------------------------
// CHTTPMailServer::_WndProc
// --------------------------------------------------------------------------------
LRESULT CALLBACK CHTTPMailServer::_WndProc(HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
CHTTPMailServer *pThis = (CHTTPMailServer*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
LRESULT lr = 0;
switch (msg)
{
case WM_NCCREATE:
IxpAssert(!pThis);
pThis = (CHTTPMailServer*)((LPCREATESTRUCT)lParam)->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LPARAM)pThis);
lr = DefWindowProc(hwnd, msg, wParam, lParam);
break;
case WM_HTTP_BEGIN_OP:
IxpAssert(pThis);
pThis->_DoOperation();
break;
default:
lr = DefWindowProc(hwnd, msg, wParam, lParam);
break;
}
return lr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::_BeginDeferredOperation
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::_BeginDeferredOperation(void)
{
return (PostMessage(m_hwnd, WM_HTTP_BEGIN_OP, 0, 0) ? E_PENDING : E_FAIL);
}
//----------------------------------------------------------------------
// CHTTPMailServer::HandleGetMsgFolderRoot
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::HandleGetMsgFolderRoot(LPHTTPMAILRESPONSE pResponse)
{
HRESULT hr = S_OK;
Assert(HTTPMAIL_GETPROP == pResponse->command);
Assert(NULL == m_pszMsgFolderRoot);
Assert(HTTPMAIL_PROP_MSGFOLDERROOT == pResponse->rGetPropInfo.type);
if (NULL == pResponse->rGetPropInfo.pszProp)
{
hr = E_FAIL;
goto exit;
}
m_pszMsgFolderRoot = pResponse->rGetPropInfo.pszProp;
pResponse->rGetPropInfo.pszProp = NULL;
// add it to the account data cache
HrCacheAccountPropStrA(m_szAccountId, CAP_HTTPMAILMSGFOLDERROOT, m_pszMsgFolderRoot);
exit:
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::HandleListFolders
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::HandleListFolders(LPHTTPMAILRESPONSE pResponse)
{
HRESULT hr = S_OK;
SPECIALFOLDER tySpecial = FOLDER_NOTSPECIAL;
FOLDERINFO fiNewFolder;
FOLDERID idFound = FOLDERID_INVALID;
LPHTTPMEMBERINFOLIST pMemberList = &pResponse->rMemberInfoList;
LPHTTPMEMBERINFO pMemberInfo;
CHAR szUrlComponent[MAX_PATH];
DWORD dwUrlComponentLen;
CHAR szSpecialFolder[CCHMAX_STRINGRES];
DWORD cMessages;
DWORD cUnread;
FOLDERINFO fi = {0};
for (ULONG ulIndex = 0; ulIndex < pMemberList->cMemberInfo; ++ulIndex)
{
idFound = FOLDERID_INVALID;
pMemberInfo = &pMemberList->prgMemberInfo[ulIndex];
// skip anything that isn't a folder
if (!pMemberInfo->fIsFolder)
continue;
dwUrlComponentLen = ARRAYSIZE(szUrlComponent);
IF_FAILEXIT(hr = Http_NameFromUrl(pMemberInfo->pszHref, szUrlComponent, &dwUrlComponentLen));
// [shaheedp] Bug# 84477
// If szUrlComponent is null, then we should not be adding this folder to the store.
if (!(*szUrlComponent))
{
hr = E_FAIL;
goto exit;
}
// if we've found a reserved folder, translate the httpmail
// special folder constant into the equivalent oe store
// special folder type.
tySpecial = _TranslateHTTPSpecialFolderType(pMemberInfo->tySpecial);
if (FOLDER_NOTSPECIAL != tySpecial)
idFound = m_op.pFolderList->FindAndRemove(tySpecial, &cMessages, &cUnread);
// if the folder wasn't found, try to find it by name
if (FOLDERID_INVALID == idFound)
{
idFound = m_op.pFolderList->FindAndRemove(szUrlComponent, &cMessages, &cUnread);
// if it still wasn't found, then add it
if (FOLDERID_INVALID == idFound)
{
// fill in the folderinfo
ZeroMemory(&fiNewFolder, sizeof(FOLDERINFO));
fiNewFolder.idParent = m_idServer;
fiNewFolder.tySpecial = tySpecial;
fiNewFolder.tyFolder = FOLDER_HTTPMAIL;
fiNewFolder.pszName = pMemberInfo->pszDisplayName;
if (FOLDER_NOTSPECIAL != tySpecial)
{
if (_LoadSpecialFolderName(tySpecial, szSpecialFolder, sizeof(szSpecialFolder)))
fiNewFolder.pszName = szSpecialFolder;
}
fiNewFolder.pszUrlComponent = szUrlComponent;
fiNewFolder.dwFlags = (FOLDER_SUBSCRIBED | FOLDER_NOCHILDCREATE);
// message counts
fiNewFolder.cMessages = pMemberInfo->dwVisibleCount;
fiNewFolder.cUnread = pMemberInfo->dwUnreadCount;
if (tySpecial == FOLDER_INBOX)
fiNewFolder.dwFlags |= FOLDER_DOWNLOADALL;
// Add the folder to the store
IF_FAILEXIT(hr = m_pStore->CreateFolder(NOFLAGS, &fiNewFolder, NULL));
}
}
// if the folder was found, update its message counts
if (FOLDERID_INVALID != idFound)
{
if (SUCCEEDED(hr = m_pStore->GetFolderInfo(idFound, &fi)))
{
BOOL bUpdate = FALSE;
// only update folders that changed. always update the MSN_PROMO folder.
// [shaheedp] Bug# 84477
// If the folder's pszUrlComponent was null or it is different, then reset it.
if ((fi.pszUrlComponent == NULL) ||
(lstrcmpi(fi.pszUrlComponent, szUrlComponent)))
{
bUpdate = TRUE;
fi.pszUrlComponent = szUrlComponent;
}
if ((FOLDER_MSNPROMO == tySpecial) ||
(cMessages != pMemberInfo->dwVisibleCount) ||
(cUnread != pMemberInfo->dwUnreadCount))
{
fi.cMessages = pMemberInfo->dwVisibleCount;
// special handling for the promo folder - messages on the
// server are never marked unread.
if (FOLDER_MSNPROMO == fi.tySpecial)
{
// we attempt to approximate the number of unread msgs in
// the promo folder. we assume that the server will not track
// the read/unread state of promo messages. we figure out how
// many messages were read before we got the new counts, and
// subtract that number from the current number of visible
// messages to get the unread count. this number will not always
// be accurate, but since all we know is the counts, and we
// don't know how the count changed (additions vs deletions),
// this is the best we can do and it always errors on the side
// of a too-small count to avoid bothering the user with
// unread counts when no unread msgs exist.
DWORD dwReadMessages = cMessages - cUnread;
if (fi.cMessages > dwReadMessages)
fi.cUnread = fi.cMessages - dwReadMessages;
else
fi.cUnread = 0;
}
else
fi.cUnread = pMemberInfo->dwUnreadCount;
if (cMessages != fi.cMessages || cUnread != fi.cUnread)
bUpdate = TRUE;
}
if (bUpdate)
m_pStore->UpdateRecord(&fi);
m_pStore->FreeRecord(&fi);
}
}
// if we are syncing the store, notify the client of our progress
if (SOT_SYNCING_STORE == m_op.tyOperation && NULL != m_op.pCallback)
m_op.pCallback->OnProgress(
SOT_SYNCING_STORE,
0,
0,
m_rInetServerInfo.szServerName);
}
IF_FAILEXIT(hr = m_pAccount->SetPropSz(AP_HTTPMAIL_ROOTTIMESTAMP, pMemberList->pszRootTimeStamp));
IF_FAILEXIT(hr = m_pAccount->SetPropSz(AP_HTTPMAIL_ROOTINBOXTIMESTAMP, pMemberList->pszFolderTimeStamp));
exit:
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::HandleGetMessage
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::HandleGetMessage(LPHTTPMAILRESPONSE pResponse)
{
HRESULT hr;
IF_FAILEXIT(hr = m_op.pMessageStream->Write(
pResponse->rGetInfo.pvBody,
pResponse->rGetInfo.cbIncrement,
NULL));
if (m_op.pCallback && pResponse->rGetInfo.cbTotal > 0)
{
m_op.pCallback->OnProgress(m_op.tyOperation,
pResponse->rGetInfo.cbCurrent,
pResponse->rGetInfo.cbTotal,
NULL);
}
// if not done yet, bail out
if (!pResponse->fDone)
goto exit;
// we're done...write the stream out
hr = Http_SetMessageStream(m_pFolder, m_op.idMessage, m_op.pMessageStream, &m_op.faStream, FALSE);
exit:
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::_DoOperation
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::_DoOperation(void)
{
HRESULT hr = S_OK;
STOREOPERATIONINFO soi = { sizeof(STOREOPERATIONINFO), MESSAGEID_INVALID };
STOREOPERATIONINFO *psoi = NULL;
BOOL fCallComplete = TRUE;
if (m_op.tyOperation == SOT_INVALID)
return E_FAIL;
Assert(m_op.tyOperation != SOT_INVALID);
Assert(m_op.pfnState != NULL);
Assert(m_op.cState > 0);
Assert(m_op.iState <= m_op.cState);
if (m_op.iState == 0)
{
if (m_op.tyOperation == SOT_GET_MESSAGE)
{
// provide message id on get message start
soi.idMessage = m_op.idMessage;
psoi = &soi;
}
if (m_op.tyOperation == SOT_GET_ADURL)
m_op.pszAdUrl = NULL;
m_op.pCallback->OnBegin(m_op.tyOperation, psoi, (IOperationCancel *)this);
}
while (m_op.iState < m_op.cState)
{
hr = (this->*(m_op.pfnState[m_op.iState].pfnOp))();
if (FAILED(hr))
break;
m_op.iState++;
}
if ((m_op.iState == m_op.cState) || (FAILED(hr) && hr != E_PENDING))
{
LPSTOREERROR perr = NULL;
// provide message id
if (m_op.tyOperation == SOT_PUT_MESSAGE && MESSAGEID_INVALID != m_op.idPutMessage)
{
soi.idMessage = m_op.idPutMessage;
psoi = &soi;
}
switch (m_op.tyOperation)
{
case SOT_GET_ADURL:
{
if (SUCCEEDED(hr))
{
psoi = &soi;
psoi->pszUrl = m_op.pszAdUrl;
}
else
{
psoi = NULL;
fCallComplete = FALSE;
}
perr = NULL;
break;
}
case SOT_GET_HTTP_MINPOLLINGINTERVAL:
{
if (SUCCEEDED(hr))
{
psoi = &soi;
psoi->dwMinPollingInterval = m_op.dwMinPollingInterval;
}
else
{
psoi = NULL;
fCallComplete = FALSE;
}
perr = NULL;
break;
}
default:
{
if (FAILED(hr))
{
IXPRESULT rIxpResult;
// Fake an IXPRESULT
ZeroMemory(&rIxpResult, sizeof(rIxpResult));
rIxpResult.hrResult = hr;
// Return meaningful error information
_FillStoreError(&m_op.error, &rIxpResult);
Assert(m_op.error.hrResult == hr);
}
else
m_op.error.hrResult = hr;
if (!m_op.fNotifiedComplete)
perr = &m_op.error;
break;
}
}
if (!m_op.fNotifiedComplete && fCallComplete)
{
m_op.fNotifiedComplete = TRUE;
m_op.pCallback->OnComplete(m_op.tyOperation, hr, psoi, perr);
_FreeOperation();
}
}
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::_FreeOperation
//----------------------------------------------------------------------
void CHTTPMailServer::_FreeOperation(BOOL fValidState)
{
if (fValidState)
{
if (m_op.pCallback != NULL)
m_op.pCallback->Release();
if (m_op.pFolderList != NULL)
m_op.pFolderList->Release();
if (m_op.pMessageFolder)
m_op.pMessageFolder->Release();
SafeMemFree(m_op.pszProblem);
if (0 != m_op.faStream)
{
Assert(m_pFolder);
m_pFolder->DeleteStream(m_op.faStream);
}
if (m_op.pMessageStream)
m_op.pMessageStream->Release();
if (m_op.pmapMessageId)
delete m_op.pmapMessageId;
if (m_op.psaNewMessages)
delete m_op.psaNewMessages;
if (m_op.pPropPatchRequest)
m_op.pPropPatchRequest->Release();
SafeMemFree(m_op.pszDestFolderUrl);
SafeMemFree(m_op.pszDestUrl);
SafeMemFree(m_op.pIDList);
if (NULL != m_op.hRowSet)
m_pFolder->CloseRowset(&m_op.hRowSet);
SafeMemFree(m_op.pszFolderName);
if (m_op.pTargets)
Http_FreeTargetList(m_op.pTargets);
SafeMemFree(m_op.pszAdUrl);
}
ZeroMemory(&m_op, sizeof(HTTPOPERATION));
m_op.tyOperation = SOT_INVALID;
m_op.idPutMessage = MESSAGEID_INVALID;
}
//----------------------------------------------------------------------
// CHTTPMailServer::Connect
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::Connect()
{
HRESULT hr = S_OK;
INETSERVER rInetServerInfo;
BOOL fInetInit = FALSE;
LPSTR pszCache = NULL;
AssertSingleThreaded;
Assert(m_op.pCallback != NULL);
if (!m_pAccount)
{
IF_FAILEXIT(hr = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, m_szAccountId, &m_pAccount));
IF_FAILEXIT(hr = _LoadAccountInfo(m_pAccount));
}
if (_FConnected())
{
Assert(m_pTransport != NULL);
IF_FAILEXIT(hr = m_pTransport->InetServerFromAccount(m_pAccount, &rInetServerInfo));
// compare the current account from the account whose data we saved.
// if the account has changed, drop the connection, and reconnect
Assert(m_rInetServerInfo.szServerName[0] != 0);
if (m_rInetServerInfo.rasconntype == rInetServerInfo.rasconntype &&
m_rInetServerInfo.dwPort == rInetServerInfo.dwPort &&
m_rInetServerInfo.fSSL == rInetServerInfo.fSSL &&
m_rInetServerInfo.fTrySicily == rInetServerInfo.fTrySicily &&
m_rInetServerInfo.dwTimeout == rInetServerInfo.dwTimeout &&
0 == lstrcmp(m_rInetServerInfo.szUserName, rInetServerInfo.szUserName) &&
('\0' == rInetServerInfo.szPassword[0] ||
0 == lstrcmp(m_rInetServerInfo.szPassword, rInetServerInfo.szPassword)) &&
0 == lstrcmp(m_rInetServerInfo.szServerName, rInetServerInfo.szServerName) &&
0 == lstrcmp(m_rInetServerInfo.szConnectoid, rInetServerInfo.szConnectoid))
{
goto exit;
}
fInetInit = TRUE;
// synchronously drop the connection
m_pTransport->DropConnection();
}
hr = m_op.pCallback->CanConnect(m_szAccountId, NOFLAGS);
if (S_OK != hr)
{
if (hr == S_FALSE)
hr = HR_E_USER_CANCEL_CONNECT;
goto exit;
}
if (NULL == m_pTransport)
IF_FAILEXIT(hr = _LoadTransport());
// initialize the server info if we haven't already
if (!fInetInit)
IF_FAILEXIT(hr = m_pTransport->InetServerFromAccount(m_pAccount, &m_rInetServerInfo));
else
CopyMemory(&m_rInetServerInfo, &rInetServerInfo, sizeof(INETSERVER));
GetAccountPropStrA(m_szAccountId, CAP_PASSWORD, &pszCache);
if (NULL != pszCache)
{
StrCpyN(m_rInetServerInfo.szPassword, pszCache, sizeof(m_rInetServerInfo.szPassword));
SafeMemFree(pszCache);
}
// connect to the server. the transport won't actually connect until
// a command is issued.
IF_FAILEXIT(hr = m_pTransport->Connect(&m_rInetServerInfo, TRUE, FALSE));
_SetConnected(TRUE);
exit:
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::GetMsgFolderRoot
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::GetMsgFolderRoot(void)
{
HRESULT hr = S_OK;
// bail if we've already got it
if (NULL != m_pszMsgFolderRoot)
goto exit;
// try to pull it out of the account data cache
if (GetAccountPropStrA(m_szAccountId, CAP_HTTPMAILMSGFOLDERROOT, &m_pszMsgFolderRoot))
goto exit;
if (SUCCEEDED(hr = m_pTransport->GetProperty(HTTPMAIL_PROP_MSGFOLDERROOT, &m_pszMsgFolderRoot)))
{
Assert(NULL != m_pszMsgFolderRoot);
if (NULL == m_pszMsgFolderRoot)
{
hr = E_FAIL;
goto exit;
}
// add it to the account data cache
HrCacheAccountPropStrA(m_szAccountId, CAP_HTTPMAILMSGFOLDERROOT, m_pszMsgFolderRoot);
}
exit:
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::BuildFolderUrl
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::BuildFolderUrl(void)
{
HRESULT hr = S_OK;
FOLDERINFO fi;
LPFOLDERINFO fiFree = NULL;
// just bail if we've already got it
if (NULL != m_pszFolderUrl)
goto exit;
Assert(NULL != m_pszMsgFolderRoot);
if (NULL == m_pszMsgFolderRoot)
{
hr = TraceResult(E_UNEXPECTED);
goto exit;
}
if (FAILED(hr = m_pStore->GetFolderInfo(m_idFolder, &fi)))
goto exit;
fiFree = &fi;
Assert(fi.pszUrlComponent);
hr = _BuildUrl(fi.pszUrlComponent, NULL, &m_pszFolderUrl);
exit:
if (fiFree)
m_pStore->FreeRecord(fiFree);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::ListFolders
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::ListFolders(void)
{
HRESULT hr = S_OK;
CHAR szRootTimeStamp[CCHMAX_RES];
CHAR szInboxTimeStamp[CCHMAX_RES];
Assert(NULL == m_op.pFolderList);
// cache a value that the account has been synced.
HrCacheAccountPropStrA(m_szAccountId, CAP_HTTPAUTOSYNCEDFOLDERS, c_szTrue);
// build the folder list
IF_FAILEXIT(hr = CFolderList::Create(m_pStore, m_idServer, &m_op.pFolderList));
hr = m_pAccount->GetPropSz(AP_HTTPMAIL_ROOTTIMESTAMP, szRootTimeStamp, ARRAYSIZE(szRootTimeStamp));
if (FAILED(hr))
*szRootTimeStamp = 0;
hr = m_pAccount->GetPropSz(AP_HTTPMAIL_ROOTINBOXTIMESTAMP, szInboxTimeStamp, ARRAYSIZE(szInboxTimeStamp));
if (FAILED(hr))
*szInboxTimeStamp = 0;
// execute the listfolders command
IF_FAILEXIT(hr = m_pTransport2->RootMemberInfo(m_pszMsgFolderRoot, HTTP_MEMBERINFO_FOLDERPROPS,
1, FALSE, 0, szRootTimeStamp, szInboxTimeStamp));
hr = E_PENDING;
exit:
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::AutoListFolders
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::AutoListFolders(void)
{
LPSTR pszAutoSynced = NULL;
HRESULT hr = S_OK;
// look for a cached property that indicates that the folder list
// for this server has been synchronized at least once this session
if (GetAccountPropStrA(m_szAccountId, CAP_HTTPAUTOSYNCEDFOLDERS, &pszAutoSynced))
goto exit;
// initiate the sync
hr = ListFolders();
exit:
SafeMemFree(pszAutoSynced);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::ListHeaders
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::ListHeaders(void)
{
HRESULT hr = S_OK;
TABLEINDEX index;
CHAR szTimeStamp[CCHMAX_RES];
Assert(NULL != m_pszFolderUrl);
Assert(NULL != m_pTransport);
Assert(NULL != m_pStore);
Assert(NULL == m_op.psaNewMessages);
// look for an index on pszMessageID
if ( FAILED(m_pFolder->GetIndexInfo(IINDEX_HTTPURL, NULL, &index)) ||
(CompareTableIndexes(&index, &g_HttpUrlIndex) != S_OK) )
{
// the index didn't exist - create it
IF_FAILEXIT(hr = m_pFolder->ModifyIndex(IINDEX_HTTPURL, NULL, &g_HttpUrlIndex));
}
IF_FAILEXIT(hr = _CreateMessageIDMap(&m_op.pmapMessageId));
IF_FAILEXIT(hr = CSortedArray::Create(NULL, FreeNewMessageInfo, &m_op.psaNewMessages));
hr = m_pAccount->GetPropSz(AP_HTTPMAIL_INBOXTIMESTAMP, szTimeStamp, ARRAYSIZE(szTimeStamp));
if (FAILED(hr))
*szTimeStamp = 0;
// For now we are passing in null folder name. This is meant for future purposes.
IF_FAILEXIT(hr = m_pTransport2->FolderMemberInfo(m_pszFolderUrl, HTTP_MEMBERINFO_MESSAGEPROPS,
1, FALSE, 0, szTimeStamp, NULL));
hr = E_PENDING;
exit:
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::HandleListHeaders
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::HandleListHeaders(LPHTTPMAILRESPONSE pResponse)
{
HRESULT hr = S_OK;
LPHTTPMEMBERINFOLIST pMemberList = &pResponse->rMemberInfoList;
LPHTTPMEMBERINFO pMemberInfo;
TPair<CSimpleString, MARKEDMESSAGE> *pFoundPair = NULL;
CSimpleString ss;
char szUrlComponent[MAX_PATH];
DWORD dwUrlComponentLen;
Assert(NULL != m_op.pmapMessageId);
for (ULONG ulIndex = 0; ulIndex < pMemberList->cMemberInfo; ++ulIndex)
{
pMemberInfo = &pMemberList->prgMemberInfo[ulIndex];
// skip folders
if (pMemberInfo->fIsFolder)
continue;
dwUrlComponentLen = MAX_PATH;
if (FAILED(hr = Http_NameFromUrl(pMemberInfo->pszHref, szUrlComponent, &dwUrlComponentLen)))
goto exit;
// look for the message by its server-assigned id in the local map
if (FAILED(hr = ss.SetString(szUrlComponent)))
goto exit;
pFoundPair = m_op.pmapMessageId->Find(ss);
// if the message was found, synchronize its read state, otherwise
// add the new message to the store
if (pFoundPair)
{
pFoundPair->m_value.fMarked = TRUE;
// if not syncing the msn promo folder, adopt the server's read state
if (FOLDER_MSNPROMO != m_tySpecialFolder)
{
if ((!!(pFoundPair->m_value.dwFlags & ARF_READ)) != pMemberInfo->fRead)
hr = _MarkMessageRead(pFoundPair->m_value.idMessage, pMemberInfo->fRead);
}
}
else
{
if (FAILED(hr = Http_AddMessageToFolder(m_pFolder,
m_szAccountId,
pMemberInfo,
FOLDER_DRAFT == m_tySpecialFolder ? ARF_UNSENT : NOFLAGS,
pMemberInfo->pszHref,
NULL)))
{
if (DB_E_DUPLICATE == hr)
hr = S_OK;
else
goto exit;
}
}
// update our message and unread message counts
m_op.cMessages++;
// if syncing the promo folder, no headers on the server will
// ever appear to be read.
if (FOLDER_MSNPROMO == m_tySpecialFolder)
{
if (!pFoundPair || !(pFoundPair->m_value.dwFlags & ARF_READ))
m_op.cUnread++;
}
else if (!pMemberInfo->fRead)
m_op.cUnread++;
}
if (pMemberList->pszFolderTimeStamp)
{
IF_FAILEXIT(hr = m_pAccount->SetPropSz(AP_HTTPMAIL_INBOXTIMESTAMP, pMemberList->pszFolderTimeStamp));
}
exit:
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::_Disconnect
//----------------------------------------------------------------------
void CHTTPMailServer::_Disconnect(void)
{
if (m_pTransport)
m_pTransport->DropConnection();
}
//----------------------------------------------------------------------
// CHTTPMailServer::_BuildUrl
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::_BuildUrl(LPCSTR pszFolderComponent,
LPCSTR pszNameComponent,
LPSTR *ppszUrl)
{
HRESULT hr = S_OK;
DWORD cchMsgFolderRoot = 0;
DWORD cchFolderComponent = 0;
DWORD cchNameComponent = 0;
DWORD cchWritten = 0;
LPSTR pszUrl = NULL;
CHAR chSlash = '/';
Assert(NULL != m_pszMsgFolderRoot);
Assert(NULL != ppszUrl);
*ppszUrl = NULL;
if (NULL == m_pszMsgFolderRoot)
{
hr = E_UNEXPECTED;
goto exit;
}
cchMsgFolderRoot = lstrlen(m_pszMsgFolderRoot);
if (pszFolderComponent)
cchFolderComponent = lstrlen(pszFolderComponent);
if (pszNameComponent)
cchNameComponent = lstrlen(pszNameComponent);
// add three bytes - two for trailing slashes and one for the eos
if (!MemAlloc((void **)&pszUrl, cchMsgFolderRoot + cchFolderComponent + cchNameComponent + 3))
{
hr = E_OUTOFMEMORY;
goto exit;
}
*ppszUrl = pszUrl;
CopyMemory(pszUrl, m_pszMsgFolderRoot, cchMsgFolderRoot);
cchWritten = cchMsgFolderRoot;
// make sure the msg folder root is terminated with a '/'
if (chSlash != pszUrl[cchWritten - 1])
pszUrl[cchWritten++] = chSlash;
if (cchFolderComponent)
{
CopyMemory(&pszUrl[cchWritten], pszFolderComponent, cchFolderComponent);
cchWritten += cchFolderComponent;
if (chSlash != pszUrl[cchWritten - 1])
pszUrl[cchWritten++] = chSlash;
}
if (cchNameComponent)
{
CopyMemory(&pszUrl[cchWritten], pszNameComponent, cchNameComponent);
cchWritten += cchNameComponent;
}
// null terminate the string
pszUrl[cchWritten] = 0;
exit:
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::_BuildMessageUrl
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::_BuildMessageUrl(LPCSTR pszFolderUrl,
LPSTR pszNameComponent,
LPSTR *ppszUrl)
{
DWORD cchFolderUrlLen;
DWORD cchNameComponentLen;
if (NULL != ppszUrl)
*ppszUrl = NULL;
if (NULL == pszFolderUrl || NULL == pszNameComponent || NULL == ppszUrl)
return E_INVALIDARG;
cchFolderUrlLen = lstrlen(pszFolderUrl);
cchNameComponentLen = lstrlen(pszNameComponent);
// allocate two extra bytes - one for the '/' delimeter and one for the eos
DWORD cchTotal = (cchFolderUrlLen + cchNameComponentLen + 2);
if (!MemAlloc((void **)ppszUrl, cchTotal * sizeof((*ppszUrl)[0])))
return E_OUTOFMEMORY;
if ('/' == pszFolderUrl[cchFolderUrlLen - 1])
wnsprintf(*ppszUrl, cchTotal, "%s%s", pszFolderUrl, pszNameComponent);
else
wnsprintf(*ppszUrl, cchTotal, "%s/%s", pszFolderUrl, pszNameComponent);
return S_OK;
}
//----------------------------------------------------------------------
// CHTTPMailServer::_MarkMessageRead
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::_MarkMessageRead(MESSAGEID id, BOOL fRead)
{
HRESULT hr;
MESSAGEINFO mi = {0};
BOOL fFoundRecord = FALSE;
ZeroMemory(&mi, sizeof(MESSAGEINFO));
mi.idMessage = id;
// find the message in the database
if (FAILED(hr = GetMessageInfo(m_pFolder, id, &mi)))
goto exit;
fFoundRecord = TRUE;
if (fRead)
mi.dwFlags |= ARF_READ;
else
mi.dwFlags &= ~ARF_READ;
hr = m_pFolder->UpdateRecord(&mi);
exit:
if (fFoundRecord)
m_pFolder->FreeRecord(&mi);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::CreateFolder
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::CreateFolder(void)
{
HRESULT hr = S_OK;
CHAR szEncodedName[MAX_PATH];
DWORD cb = sizeof(szEncodedName);
Assert(NULL != m_pTransport);
Assert(NULL != m_op.pszFolderName);
IF_FAILEXIT(hr = UrlEscapeA(m_op.pszFolderName,
szEncodedName,
&cb,
URL_ESCAPE_UNSAFE | URL_ESCAPE_PERCENT | URL_ESCAPE_SEGMENT_ONLY));
IF_FAILEXIT(hr = _BuildUrl(szEncodedName, NULL, &m_op.pszDestFolderUrl));
IF_FAILEXIT(hr = m_pTransport->CommandMKCOL(m_op.pszDestFolderUrl, 0));
hr = E_PENDING;
exit:
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::RenameFolder
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::RenameFolder(void)
{
HRESULT hr = S_OK;
FOLDERINFO fi = {0};
LPFOLDERINFO pfiFree = NULL;
LPSTR pszSourceUrl = NULL;
LPSTR pszDestUrl = NULL;
CHAR szEncodedName[MAX_PATH];
DWORD cb = sizeof(szEncodedName);
// build the source url
IF_FAILEXIT(hr = m_pStore->GetFolderInfo(m_op.idFolder, &fi));
pfiFree = &fi;
IF_FAILEXIT(hr = _BuildUrl(fi.pszUrlComponent, NULL, &pszSourceUrl));
// escape the new folder name
IF_FAILEXIT(hr = UrlEscapeA(m_op.pszFolderName,
szEncodedName,
&cb,
URL_ESCAPE_UNSAFE | URL_ESCAPE_PERCENT | URL_ESCAPE_SEGMENT_ONLY));
// build the destination url
IF_FAILEXIT(hr = _BuildUrl(szEncodedName, NULL, &pszDestUrl));
// send the MOVE command to the transport
IF_FAILEXIT(hr = m_pTransport->CommandMOVE(pszSourceUrl, pszDestUrl, TRUE, 0));
hr = E_PENDING;
exit:
if (pfiFree)
m_pStore->FreeRecord(pfiFree);
SafeMemFree(pszSourceUrl);
SafeMemFree(pszDestUrl);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::DeleteFolder
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::DeleteFolder(void)
{
HRESULT hr = S_OK;
FOLDERINFO fi = {0};
LPFOLDERINFO pfiFree = NULL;
LPSTR pszUrl = NULL;
// build the folder's url
if (FAILED(hr = m_pStore->GetFolderInfo(m_op.idFolder, &fi)))
goto exit;
pfiFree = &fi;
if (FAILED(hr = _BuildUrl(fi.pszUrlComponent, NULL, &pszUrl)))
goto exit;
// send the delete command to the transport
hr = m_pTransport->CommandDELETE(pszUrl, 0);
if (SUCCEEDED(hr))
hr = E_PENDING;
exit:
if (pfiFree)
m_pStore->FreeRecord(pfiFree);
SafeMemFree(pszUrl);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::HandleCreateFolder
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::HandleCreateFolder(LPHTTPMAILRESPONSE pResponse)
{
FOLDERINFO fi;
CHAR szUrlComponent[MAX_PATH];
DWORD dwUrlComponentLen = MAX_PATH;
HRESULT hr = pResponse->rIxpResult.hrResult;
if (SUCCEEDED(hr))
{
// if the server specified a location, use it. otherwise, use
// the url that we included in the request
if (NULL != pResponse->rMkColInfo.pszLocation)
IF_FAILEXIT(hr = Http_NameFromUrl(pResponse->rMkColInfo.pszLocation, szUrlComponent, &dwUrlComponentLen));
else
IF_FAILEXIT(hr = Http_NameFromUrl(m_op.pszDestFolderUrl, szUrlComponent, &dwUrlComponentLen));
// [shaheedp] Bug# 84477
// If szUrlComponent is null, then we should not be adding this folder to the store.
if (!(*szUrlComponent))
{
hr = E_FAIL;
goto exit;
}
ZeroMemory(&fi, sizeof(FOLDERINFO));
fi.idParent = m_idServer;
fi.tySpecial = FOLDER_NOTSPECIAL;
fi.tyFolder = FOLDER_HTTPMAIL;
fi.pszName = m_op.pszFolderName;
fi.pszUrlComponent = szUrlComponent;
fi.dwFlags = (FOLDER_SUBSCRIBED | FOLDER_NOCHILDCREATE);
m_pStore->CreateFolder(NOFLAGS, &fi, NULL);
}
exit:
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::HandleRenameFolder
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::HandleRenameFolder(LPHTTPMAILRESPONSE pResponse)
{
HRESULT hr = S_OK;
char szUrlComponent[MAX_PATH];
DWORD dwUrlComponentLen = MAX_PATH;
FOLDERINFO fi = {0};
LPFOLDERINFO pfiFree = NULL;
// REVIEW: if the server doesn't return a response, return an error
Assert(NULL != pResponse->rCopyMoveInfo.pszLocation);
if (NULL != pResponse->rCopyMoveInfo.pszLocation)
{
if (FAILED(hr = Http_NameFromUrl(pResponse->rCopyMoveInfo.pszLocation, szUrlComponent, &dwUrlComponentLen)))
goto exit;
if (FAILED(hr = m_pStore->GetFolderInfo(m_op.idFolder, &fi)))
goto exit;
pfiFree = &fi;
fi.pszName = m_op.pszFolderName;
fi.pszUrlComponent = szUrlComponent;
hr = m_pStore->UpdateRecord(&fi);
}
exit:
if (NULL != pfiFree)
m_pStore->FreeRecord(pfiFree);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::HandleDeleteFolder
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::HandleDeleteFolder(LPHTTPMAILRESPONSE pResponse)
{
return m_pStore->DeleteFolder(m_op.idFolder, DELETE_FOLDER_NOTRASHCAN, NULL);
}
//----------------------------------------------------------------------
// CHTTPMailServer::PurgeFolders
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::PurgeFolders(void)
{
// the folder list will be null if we didn't need
// to do an auto-sync
if (NULL != m_op.pFolderList)
m_op.pFolderList->PurgeRemainingFromStore();
return S_OK;
}
//----------------------------------------------------------------------
// CHTTPMailServer::PurgeMessages
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::PurgeMessages(void)
{
TPair<CSimpleString, MARKEDMESSAGE> *pPair;
MESSAGEID idMessage = MESSAGEID_INVALID;
MESSAGEIDLIST rIdList = { 1, 1, &idMessage };
Assert(NULL != m_op.pmapMessageId);
long lMapLength = m_op.pmapMessageId->GetLength();
for (long lIndex = 0; lIndex < lMapLength; lIndex++)
{
pPair = m_op.pmapMessageId->GetItemAt(lIndex);
if (NULL != pPair && !pPair->m_value.fMarked)
{
idMessage = pPair->m_value.idMessage;
m_pFolder->DeleteMessages(DELETE_MESSAGE_NOTRASHCAN | DELETE_MESSAGE_NOPROMPT, &rIdList, NULL, NULL /* m_op.pCallback */);
}
}
// don't need the map anymore
SafeDelete(m_op.pmapMessageId);
return S_OK;
}
//----------------------------------------------------------------------
// CHTTPMailServer::ResetMessageCounts
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::ResetMessageCounts(void)
{
HRESULT hr = S_OK;
FOLDERINFO fi;
LPFOLDERINFO pfiFree = NULL;
// find the folder
IF_FAILEXIT(hr = m_pStore->GetFolderInfo(m_idFolder, &fi));
pfiFree = &fi;
// update the counts
if (fi.cMessages != m_op.cMessages || fi.cUnread != m_op.cUnread)
{
fi.cMessages = m_op.cMessages;
fi.cUnread = m_op.cUnread;
}
hr = m_pStore->UpdateRecord(&fi);
exit:
if (pfiFree)
m_pStore->FreeRecord(pfiFree);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::GetMessage
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::GetMessage(void)
{
HRESULT hr = S_OK;
MESSAGEINFO mi = {0};
BOOL fFoundRecord = FALSE;
LPCSTR rgszAcceptTypes[] = { c_szAcceptTypeRfc822, c_szAcceptTypeWildcard, NULL };
LPSTR pszUrl = NULL;
TCHAR szRes[CCHMAX_STRINGRES];
// pull the message info out of the store
if (FAILED(hr = GetMessageInfo(m_pFolder, m_op.idMessage, &mi)))
goto exit;
fFoundRecord = TRUE;
Assert(mi.pszUrlComponent);
if (FAILED(hr =_BuildMessageUrl(m_pszFolderUrl, mi.pszUrlComponent, &pszUrl)))
goto exit;
AthLoadString(idsRequestingArt, szRes, ARRAYSIZE(szRes));
if (m_op.pCallback)
m_op.pCallback->OnProgress(m_op.tyOperation, 0, 0, szRes);
if (FAILED(hr = m_pTransport->CommandGET(pszUrl, rgszAcceptTypes, FALSE, 0)))
goto exit;
hr = E_PENDING;
exit:
if (fFoundRecord)
m_pFolder->FreeRecord(&mi);
SafeMemFree(pszUrl);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::CreateSetFlagsRequest
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::CreateSetFlagsRequest(void)
{
HRESULT hr;
hr = CoCreateInstance(CLSID_IPropPatchRequest, NULL, CLSCTX_INPROC_SERVER, IID_IPropPatchRequest, (LPVOID *)&m_op.pPropPatchRequest);
if (FAILED(hr))
goto exit;
if (m_op.fMarkRead)
hr = m_op.pPropPatchRequest->SetProperty(DAVNAMESPACE_HTTPMAIL, "read", "1");
else
hr = m_op.pPropPatchRequest->SetProperty(DAVNAMESPACE_HTTPMAIL, "read", "0");
exit:
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::SetMessageFlags
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::SetMessageFlags(void)
{
HRESULT hr = S_OK;
LPHTTPTARGETLIST pTargets = NULL;
LPSTR pszMessageUrl = NULL;
ADJUSTFLAGS af;
af.dwAdd = m_op.fMarkRead ? ARF_READ : 0;
af.dwRemove = !m_op.fMarkRead ? ARF_READ : 0;
// if we have a rowset, we shouldn't have an ID List.
Assert(NULL == m_op.hRowSet || NULL == m_op.pIDList);
IF_FAILEXIT(hr = _HrBuildMapAndTargets(m_op.pIDList, m_op.hRowSet, &af, m_op.dwSetFlags, &m_op.pmapMessageId, &pTargets));
// if the folder is the msnpromo folder, advance to the next state.
// don't actually send the command to the server
if (FOLDER_MSNPROMO == m_tySpecialFolder)
goto exit;
// if there is only one target, build a complete url for the target
// and invoke the non-batch version of the command. if there are no targets,
// return S_OK, and don't send any commands to the xport
if (1 == pTargets->cTarget)
{
IF_FAILEXIT(hr = _BuildMessageUrl(m_pszFolderUrl, const_cast<char *>(pTargets->prgTarget[0]), &pszMessageUrl));
IF_FAILEXIT(hr = m_pTransport->MarkRead(pszMessageUrl, NULL, m_op.fMarkRead, 0));
hr = E_PENDING;
}
else if (pTargets->cTarget > 0)
{
IF_FAILEXIT(hr = m_pTransport->MarkRead(m_pszFolderUrl, pTargets, m_op.fMarkRead, 0));
hr = E_PENDING;
}
exit:
if (pTargets)
Http_FreeTargetList(pTargets);
SafeMemFree(pszMessageUrl);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::ApplyFlagsToStore
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::ApplyFlagsToStore(void)
{
HRESULT hr = S_OK;
TPair<CSimpleString, MARKEDMESSAGE> *pPair;
long lMapLength = m_op.pmapMessageId->GetLength();
ADJUSTFLAGS af;
BOOL fFoundMarked = FALSE;
long lIndex;
af.dwAdd = m_op.fMarkRead ? ARF_READ : 0;
af.dwRemove = !m_op.fMarkRead ? ARF_READ : 0;
// if the operation was requested on the entire folder,
// check to see if anything failed. if it did, then
// build up an IDList so we can mark only the msgs that
// were successfully modified
if (NULL != m_op.hRowSet)
{
Assert(NULL == m_op.pIDList);
for (lIndex = 0; lIndex < lMapLength && !fFoundMarked; lIndex++)
{
pPair = m_op.pmapMessageId->GetItemAt(lIndex);
Assert(NULL != pPair);
if (pPair && pPair->m_value.fMarked)
fFoundMarked = TRUE;
}
// if no messages were marked, apply the operation to the entire folder
if (!fFoundMarked)
{
hr = m_pFolder->SetMessageFlags(NULL, &af, NULL, NULL);
// we're done
goto exit;
}
// if one or more msgs were marked, allocate an idlist
if (fFoundMarked)
{
// allocate the list structure
if (!MemAlloc((void **)&m_op.pIDList, sizeof(MESSAGEIDLIST)))
{
hr = TrapError(E_OUTOFMEMORY);
goto exit;
}
ZeroMemory(m_op.pIDList, sizeof(MESSAGEIDLIST));
// allocate storage
if (!MemAlloc((void **)&m_op.pIDList->prgidMsg, sizeof(MESSAGEID) * lMapLength))
{
hr = TrapError(E_OUTOFMEMORY);
goto exit;
}
m_op.pIDList->cAllocated = lMapLength;
m_op.pIDList->cMsgs = 0;
}
}
// we need to apply the setflags operation to the local store. we
// can't just pass the message id list that we got originally,
// because some of the operation may have failed. instead, we
// rebuild the id list (in place, since we know the successful
// operations will never outnumber the attempted operations),
// and send that into the store
Assert(NULL != m_op.pIDList);
Assert(NULL != m_op.pmapMessageId);
Assert(m_op.pIDList->cMsgs >= (DWORD)lMapLength);
m_op.pIDList->cMsgs = 0;
for (lIndex = 0; lIndex < lMapLength; lIndex++)
{
pPair = m_op.pmapMessageId->GetItemAt(lIndex);
Assert(NULL != pPair);
// if the item isn't marked, then it was successfully modified
if (pPair && !pPair->m_value.fMarked)
m_op.pIDList->prgidMsg[m_op.pIDList->cMsgs++] = pPair->m_value.idMessage;
}
// if the resulting id list contains at least one message, perform the operation
if (m_op.pIDList->cMsgs > 0)
hr = m_pFolder->SetMessageFlags(m_op.pIDList, &af, NULL, NULL);
// todo: alert user if the operation failed in part
exit:
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::HandleMemberErrors
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::HandleMemberErrors(LPHTTPMAILRESPONSE pResponse)
{
HRESULT hr = S_OK;
LPHTTPMEMBERERROR pme = NULL;
CHAR szUrlComponent[MAX_PATH];
DWORD dwComponentBytes;
CSimpleString ss;
TPair<CSimpleString, MARKEDMESSAGE> *pFoundPair = NULL;
// loop through the response looking for errors. we ignore
// per-item success.
for (DWORD dw = 0; dw < pResponse->rMemberErrorList.cMemberError; dw++)
{
pme = &pResponse->rMemberErrorList.prgMemberError[dw];
if (SUCCEEDED(pme->hrResult))
continue;
Assert(NULL != pme->pszHref);
if (NULL == pme->pszHref)
continue;
dwComponentBytes = ARRAYSIZE(szUrlComponent);
if (FAILED(Http_NameFromUrl(pme->pszHref, szUrlComponent, &dwComponentBytes)))
continue;
IF_FAILEXIT(hr = ss.SetString(szUrlComponent));
// find and mark the found message
pFoundPair = m_op.pmapMessageId->Find(ss);
Assert(NULL != pFoundPair);
if (NULL != pFoundPair)
pFoundPair->m_value.fMarked = TRUE;
}
exit:
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::DeleteMessages
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::DeleteMessages(void)
{
HRESULT hr = S_OK;
LPSTR pszMessageUrl = NULL;
LPSTR pszNoDeleteSupport = NULL;
// if we have a rowset, we shouldn't have an ID List
Assert(NULL == m_op.hRowSet || NULL == m_op.pIDList);
IF_FAILEXIT(hr = _HrBuildMapAndTargets(m_op.pIDList, m_op.hRowSet, NULL, 0, &m_op.pmapMessageId, &m_op.pTargets));
// look for an already cached property that indicates that the server
// isn't going to support deleting messages (Hotmail doesn't)
if (GetAccountPropStrA(m_szAccountId, CAP_HTTPNOMESSAGEDELETES, &pszNoDeleteSupport))
{
if (!!(DELETE_MESSAGE_MAYIGNORENOTRASH & m_op.dwDelMsgFlags))
m_op.fFallbackToMove = TRUE;
else
hr = SP_E_HTTP_NODELETESUPPORT;
goto exit;
}
// if there is only one target, build a complete url for the target,
// and call the non-batch version of the command. if there are no targets,
// return S_OK and don't issue any commands
if (!m_op.pTargets)
{
hr = E_FAIL;
goto exit;
}
if (1 == m_op.pTargets->cTarget)
{
IF_FAILEXIT(hr = _BuildMessageUrl(m_pszFolderUrl, const_cast<char *>(m_op.pTargets->prgTarget[0]), &pszMessageUrl));
IF_FAILEXIT(hr = m_pTransport->CommandDELETE(pszMessageUrl, 0));
}
else
IF_FAILEXIT(hr = m_pTransport->CommandBDELETE(m_pszFolderUrl, m_op.pTargets, 0));
hr = E_PENDING;
exit:
SafeMemFree(pszNoDeleteSupport);
SafeMemFree(pszMessageUrl);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::DeleteFallbackToMove
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::DeleteFallbackToMove(void)
{
HRESULT hr = S_OK;
IMessageFolder *pDeletedItems = NULL;
if (!m_op.fFallbackToMove)
goto exit;
// find the deleted items folder
IF_FAILEXIT(hr = m_pStore->OpenSpecialFolder(m_idServer, NULL, FOLDER_DELETED, &pDeletedItems));
if (NULL == pDeletedItems)
{
hr = TraceResult(IXP_E_HTTP_NOT_FOUND);
goto exit;
}
IF_FAILEXIT(hr = pDeletedItems->GetFolderId(&m_op.idFolder));
Assert(NULL == m_op.pMessageFolder);
// should already have a targets list by this point
Assert(NULL != m_op.pTargets);
m_op.pMessageFolder = pDeletedItems;
pDeletedItems = NULL;
m_op.dwOptions = COPY_MESSAGE_MOVE;
if (1 == m_op.pTargets->cTarget)
hr = CopyMoveMessage();
else
hr = BatchCopyMoveMessages();
exit:
SafeRelease(pDeletedItems);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::HandleDeleteFallbackToMove
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::HandleDeleteFallbackToMove(LPHTTPMAILRESPONSE pResponse)
{
HRESULT hr = S_OK;
if (1 == m_op.pTargets->cTarget)
hr = HandleCopyMoveMessage(pResponse);
else
hr = HandleBatchCopyMoveMessages(pResponse);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::PutMessage
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::PutMessage(void)
{
HRESULT hr = S_OK;
FOLDERINFO fi;
LPFOLDERINFO pfiFree = NULL;
IF_FAILEXIT(hr = m_pStore->GetFolderInfo(m_op.idFolder, &fi));
pfiFree = &fi;
IF_FAILEXIT(hr = _BuildUrl(fi.pszUrlComponent, NULL, &m_op.pszDestFolderUrl));
IF_FAILEXIT(hr = m_pTransport->CommandPOST(m_op.pszDestFolderUrl, m_op.pMessageStream, c_szAcceptTypeRfc822, 0));
hr = E_PENDING;
exit:
//SafeMemFree(pv);
if (pfiFree)
m_pStore->FreeRecord(pfiFree);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::BatchCopyMoveMessages
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::BatchCopyMoveMessages(void)
{
HRESULT hr = S_OK;
FOLDERINFO fi = {0};
LPFOLDERINFO pfiFree = NULL;
// build the destination folder urls
IxpAssert(NULL == m_op.pszDestFolderUrl);
if (FAILED(hr = m_pStore->GetFolderInfo(m_op.idFolder, &fi)))
{
TraceResult(hr);
goto exit;
}
pfiFree = &fi;
if (FAILED(hr = _BuildUrl(fi.pszUrlComponent, NULL, &m_op.pszDestFolderUrl)))
goto exit;
Assert(NULL == m_op.pTargets || m_op.fFallbackToMove);
// build the target list and the message id map
if (NULL == m_op.pTargets)
IF_FAILEXIT(hr = _HrBuildMapAndTargets(m_op.pIDList, NULL, NULL, 0, &m_op.pmapMessageId, &m_op.pTargets));
if (!!(m_op.dwOptions & COPY_MESSAGE_MOVE))
hr = m_pTransport->CommandBMOVE(m_pszFolderUrl, m_op.pTargets, m_op.pszDestFolderUrl, NULL, TRUE, 0);
else
hr = m_pTransport->CommandBCOPY(m_pszFolderUrl, m_op.pTargets, m_op.pszDestFolderUrl, NULL, TRUE, 0);
if (SUCCEEDED(hr))
hr = E_PENDING;
exit:
if (pfiFree)
m_pStore->FreeRecord(pfiFree);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::CopyMoveMessage
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::CopyMoveMessage(void)
{
HRESULT hr = S_OK;
FOLDERINFO fi = {0};
LPFOLDERINFO pfiFree = NULL;
// build the destination folder urls
IxpAssert(NULL == m_op.pszDestFolderUrl);
if (FAILED(hr = m_pStore->GetFolderInfo(m_op.idFolder, &fi)))
{
TraceResult(hr);
goto exit;
}
pfiFree = &fi;
if (FAILED(hr = _BuildUrl(fi.pszUrlComponent, NULL, &m_op.pszDestFolderUrl)))
goto exit;
hr = _CopyMoveNextMessage();
exit:
if (pfiFree)
m_pStore->FreeRecord(pfiFree);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::FinalizeBatchCopyMove
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::FinalizeBatchCopyMove(void)
{
HRESULT hr = S_OK;
if (NOFLAGS != m_op.dwCopyMoveErrorFlags)
{
hr = E_FAIL;
if (HTTPCOPYMOVE_OUTOFSPACE == m_op.dwCopyMoveErrorFlags)
m_op.pszProblem = AthLoadString(idsHttpBatchCopyNoStorage, NULL, 0);
else
m_op.pszProblem = AthLoadString(idsHttpBatchCopyErrors, NULL , 0);
}
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::PurgeDeletedFromStore
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::PurgeDeletedFromStore(void)
{
HRESULT hr = S_OK;
TPair<CSimpleString, MARKEDMESSAGE> *pPair;
long lMapLength = m_op.pmapMessageId->GetLength();
BOOL fFoundMarked = FALSE;
long lIndex;
if (m_op.fFallbackToMove)
goto exit;
// if the operation was requested on the entire folder,
// check to see if anything failed. if it did, then build
// up an IDList so we can mark only the msgs that were
// successfully modified
if (NULL != m_op.hRowSet)
{
Assert(NULL == m_op.pIDList);
for (lIndex = 0; lIndex < lMapLength && !fFoundMarked; lIndex++)
{
pPair = m_op.pmapMessageId->GetItemAt(lIndex);
Assert(NULL != pPair);
if (pPair && pPair->m_value.fMarked)
fFoundMarked = TRUE;
}
// if no messages were marked, apply the operation to the entire folder
if (!fFoundMarked)
{
hr = m_pFolder->DeleteMessages(DELETE_MESSAGE_NOTRASHCAN | DELETE_MESSAGE_NOPROMPT, NULL, NULL, NULL);
// we're done
goto exit;
}
// if one or more msgs were marked, allocate an idlist
if (fFoundMarked)
{
// allocate the list structure
if (!MemAlloc((void **)&m_op.pIDList, sizeof(MESSAGEIDLIST)))
{
hr = TrapError(E_OUTOFMEMORY);
goto exit;
}
ZeroMemory(m_op.pIDList, sizeof(MESSAGEIDLIST));
// allocate storage
if (!MemAlloc((void **)&m_op.pIDList->prgidMsg, sizeof(MESSAGEID) * lMapLength))
{
hr = TrapError(E_OUTOFMEMORY);
goto exit;
}
m_op.pIDList->cAllocated = lMapLength;
m_op.pIDList->cMsgs = 0;
}
}
// apply the delete operation to the local store. we can't just pass
// the message id list that we got originally, because some of the operation
// may have failed. instead, we rebuild the id list (in place, since we know
// the successful operations will never outnumber the attempted operations,
// and send that to the store.
Assert(NULL != m_op.pIDList);
Assert(NULL != m_op.pmapMessageId);
Assert(m_op.pIDList->cMsgs >= (DWORD)lMapLength);
// set the idlist count to 0. we will re-populate the
// idlist with ids from the messageID map. We know that
// the idlist is the same size as the map, so there won't
// be an overflow problem.
m_op.pIDList->cMsgs = 0;
for (lIndex = 0; lIndex < lMapLength; lIndex++)
{
pPair = m_op.pmapMessageId->GetItemAt(lIndex);
Assert(NULL != pPair);
// if the item isn't marked, then it was successfully modified
if (pPair && !pPair->m_value.fMarked)
m_op.pIDList->prgidMsg[m_op.pIDList->cMsgs++] = pPair->m_value.idMessage;
}
// if the resulting id list contains at least one message, perform the operation
if (m_op.pIDList->cMsgs > 0)
hr = m_pFolder->DeleteMessages(DELETE_MESSAGE_NOTRASHCAN | DELETE_MESSAGE_NOPROMPT, m_op.pIDList, NULL, NULL);
// todo: alert user if the operation failed in part
exit:
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::HandlePutMessage
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::HandlePutMessage(LPHTTPMAILRESPONSE pResponse)
{
HRESULT hr = S_OK;
if (!pResponse->fDone && m_op.pCallback)
{
m_op.pCallback->OnProgress(m_op.tyOperation,
pResponse->rPostInfo.cbCurrent,
pResponse->rPostInfo.cbTotal,
NULL);
}
if (pResponse->fDone)
{
Assert(NULL != pResponse->rPostInfo.pszLocation);
if (NULL == pResponse->rPostInfo.pszLocation)
{
hr = E_FAIL;
goto exit;
}
// assume ownership of the location url
m_op.pszDestUrl = pResponse->rPostInfo.pszLocation;
pResponse->rPostInfo.pszLocation = NULL;
}
exit:
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::AddPutMessage
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::AddPutMessage()
{
HRESULT hr = S_OK;
IMessageFolder *pFolder = NULL;
MESSAGEID idMessage;
MESSAGEFLAGS dwFlags;
// adopt some of the flags of the message we are putting
dwFlags = m_op.dwMsgFlags & (ARF_READ | ARF_SIGNED | ARF_ENCRYPTED | ARF_HASATTACH | ARF_VOICEMAIL);
if (!!(m_op.dwMsgFlags & ARF_UNSENT) || FOLDER_DRAFT == m_tySpecialFolder)
dwFlags |= ARF_UNSENT;
IF_FAILEXIT(hr = m_pStore->OpenFolder(m_op.idFolder, NULL, NOFLAGS, &pFolder));
IF_FAILEXIT(hr = Http_AddMessageToFolder(pFolder, m_szAccountId, NULL, dwFlags, m_op.pszDestUrl, &idMessage));
IF_FAILEXIT(hr = Http_SetMessageStream(pFolder, idMessage, m_op.pMessageStream, NULL, TRUE));
m_op.idPutMessage = idMessage;
exit:
SafeRelease(pFolder);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::HandleBatchCopyMoveMessages
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::HandleBatchCopyMoveMessages(LPHTTPMAILRESPONSE pResponse)
{
HRESULT hr = S_OK;
CHAR szUrlComponent[MAX_PATH];
DWORD dwComponentBytes;
LPHTTPMAILBCOPYMOVE pCopyMove;
CSimpleString ss;
TPair<CSimpleString, MARKEDMESSAGE> *pFoundPair = NULL;
HLOCK hLockNotify = NULL;
BOOL fDeleteOriginal = !!(m_op.dwOptions & COPY_MESSAGE_MOVE);
// This forces all notifications to be queued (this is good since you do segmented deletes)
m_pFolder->LockNotify(0, &hLockNotify);
for (DWORD dw = 0; dw < pResponse->rBCopyMoveList.cBCopyMove; dw++)
{
pCopyMove = &pResponse->rBCopyMoveList.prgBCopyMove[dw];
if (FAILED(pCopyMove->hrResult))
{
if (IXP_E_HTTP_INSUFFICIENT_STORAGE == pCopyMove->hrResult)
m_op.dwCopyMoveErrorFlags |= HTTPCOPYMOVE_OUTOFSPACE;
else
m_op.dwCopyMoveErrorFlags |= HTTPCOPYMOVE_ERROR;
continue;
}
Assert(NULL != pCopyMove->pszHref);
if (pCopyMove->pszHref)
{
dwComponentBytes = ARRAYSIZE(szUrlComponent);
if (FAILED(Http_NameFromUrl(pCopyMove->pszHref, szUrlComponent, &dwComponentBytes)))
continue;
if (FAILED(ss.SetString(szUrlComponent)))
goto exit;
pFoundPair = m_op.pmapMessageId->Find(ss);
Assert(NULL != pFoundPair);
if (NULL == pFoundPair)
continue;
// move the message and, if the move succeeds, mark the success
if (SUCCEEDED(_CopyMoveLocalMessage(pFoundPair->m_value.idMessage, m_op.pMessageFolder, pCopyMove->pszLocation, fDeleteOriginal)))
pFoundPair->m_value.fMarked = TRUE;
}
}
exit:
m_pFolder->UnlockNotify(&hLockNotify);
m_op.lIndex += pResponse->rBCopyMoveList.cBCopyMove;
if (m_op.pCallback)
m_op.pCallback->OnProgress(m_op.tyOperation, m_op.lIndex + 1, m_op.pmapMessageId->GetLength(), NULL);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::HandleCopyMessage
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::HandleCopyMoveMessage(LPHTTPMAILRESPONSE pResponse)
{
HRESULT hr = S_OK;
BOOL fDeleteOriginal = !!(m_op.dwOptions & COPY_MESSAGE_MOVE);
hr = _CopyMoveLocalMessage(m_op.pIDList->prgidMsg[m_op.dwIndex - 1],
m_op.pMessageFolder,
pResponse->rCopyMoveInfo.pszLocation,
fDeleteOriginal);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::_CopyMoveLocalMessage
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::_CopyMoveLocalMessage(MESSAGEID idMessage,
IMessageFolder* pDestFolder,
LPSTR pszUrl,
BOOL fMoveSource)
{
HRESULT hr = S_OK;
MESSAGEINFO miSource, miDest;
char szUrlComponent[MAX_PATH];
DWORD dwUrlComponentLen = MAX_PATH;
IStream *pStream = NULL;
LPMESSAGEINFO pmiFreeSource = NULL;
MESSAGEIDLIST rIdList = { 1, 1, &idMessage };
ZeroMemory(&miDest, sizeof(MESSAGEINFO));
if (FAILED(hr = GetMessageInfo(m_pFolder, idMessage, &miSource)))
goto exit;
pmiFreeSource = &miSource;
// get the store to generate an id
if (FAILED(hr = pDestFolder->GenerateId((DWORD *)&miDest.idMessage)))
goto exit;
// if the response specified a destination, use it. otherwise, assume
// that the url component did not change
if (pszUrl)
{
if (FAILED(hr = Http_NameFromUrl(pszUrl, szUrlComponent, &dwUrlComponentLen)))
goto exit;
}
else if (miSource.pszUrlComponent)
{
StrCpyN(szUrlComponent, miSource.pszUrlComponent, ARRAYSIZE(szUrlComponent));
}
miDest.dwFlags = miSource.dwFlags;
miDest.dwFlags &= ~(ARF_HASBODY | ARF_DELETED_OFFLINE);
miDest.pszSubject = miSource.pszSubject;
miDest.pszNormalSubj = miSource.pszNormalSubj;
miDest.pszDisplayFrom = miSource.pszDisplayFrom;
miDest.ftReceived = miSource.ftReceived;
miDest.pszUrlComponent = szUrlComponent;
miDest.pszEmailTo = miSource.pszEmailTo;
// add it to the database
if (FAILED(hr = m_op.pMessageFolder->InsertRecord(&miDest)))
goto exit;
// normalize the result code
hr = S_OK;
if (0 != miSource.faStream)
{
FILEADDRESS faDst;
IStream *pStmDst;
Assert(!!(miSource.dwFlags & ARF_HASBODY));
if (FAILED(hr = m_pFolder->CopyStream(m_op.pMessageFolder, miSource.faStream, &faDst)))
goto exit;
if (FAILED(hr = m_op.pMessageFolder->OpenStream(ACCESS_READ, faDst, &pStmDst)))
goto exit;
if (FAILED(hr = m_op.pMessageFolder->SetMessageStream(miDest.idMessage, pStmDst)))
{
pStmDst->Release();
goto exit;
}
pStmDst->Release();
}
if (fMoveSource)
hr = m_pFolder->DeleteMessages(DELETE_MESSAGE_NOTRASHCAN | DELETE_MESSAGE_NOPROMPT, &rIdList, NULL, NULL);
exit:
SafeRelease(pStream);
if (NULL != pmiFreeSource)
m_pFolder->FreeRecord(pmiFreeSource);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::_CopyMoveNextMessage
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::_CopyMoveNextMessage(void)
{
HRESULT hr = S_OK;
MESSAGEINFO mi = {0};
MESSAGEINFO *pmiFree = NULL;
char szUrlComponent[MAX_PATH];
DWORD dwUrlComponentLen = MAX_PATH;
LPSTR pszSourceUrl = NULL;
LPSTR pszDestUrl = NULL;
// return success when the index meets the count
if (m_op.dwIndex == m_op.pIDList->cMsgs)
goto exit;
if (FAILED(hr = GetMessageInfo(m_pFolder, m_op.pIDList->prgidMsg[m_op.dwIndex], &mi)))
goto exit;
pmiFree = &mi;
++m_op.dwIndex;
Assert(mi.pszUrlComponent);
if (NULL == mi.pszUrlComponent)
{
hr = ERROR_INTERNET_INVALID_URL;
goto exit;
}
// build the source url
if (FAILED(hr = _BuildMessageUrl(m_pszFolderUrl, mi.pszUrlComponent, &pszSourceUrl)))
goto exit;
// build the destination url
if (FAILED(hr = _BuildMessageUrl(m_op.pszDestFolderUrl, mi.pszUrlComponent, &pszDestUrl)))
goto exit;
if (!!(m_op.dwOptions & COPY_MESSAGE_MOVE))
hr = m_pTransport->CommandMOVE(pszSourceUrl, pszDestUrl, TRUE, 0);
else
hr = m_pTransport->CommandCOPY(pszSourceUrl, pszDestUrl, TRUE, 0);
if (SUCCEEDED(hr))
hr = E_PENDING;
exit:
if (NULL != pmiFree)
m_pFolder->FreeRecord(pmiFree);
SafeMemFree(pszSourceUrl);
SafeMemFree(pszDestUrl);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::_DoCopyMoveMessages
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::_DoCopyMoveMessages(STOREOPERATIONTYPE sot,
IMessageFolder *pDest,
COPYMESSAGEFLAGS dwOptions,
LPMESSAGEIDLIST pList,
IStoreCallback *pCallback)
{
HRESULT hr = S_OK;
AssertSingleThreaded;
Assert(NULL == pList || pList->cMsgs > 0);
Assert(SOT_INVALID == m_op.tyOperation);
Assert(NULL != m_pStore);
if ((NULL == pList) || (0 == pList->cMsgs) || (NULL == pDest) || (NULL == pCallback))
return E_INVALIDARG;
if (FAILED(hr = pDest->GetFolderId(&m_op.idFolder)))
goto exit;
if (FAILED(hr = CloneMessageIDList(pList, &m_op.pIDList)))
{
m_op.idFolder = FOLDERID_INVALID;
goto exit;
}
m_op.tyOperation = sot;
if (1 == pList->cMsgs)
{
m_op.pfnState = c_rgpfnCopyMoveMessage;
m_op.cState = ARRAYSIZE(c_rgpfnCopyMoveMessage);
}
else
{
m_op.pfnState = c_rgpfnBatchCopyMoveMessages;
m_op.cState = ARRAYSIZE(c_rgpfnBatchCopyMoveMessages);
}
m_op.dwOptions = dwOptions;
m_op.iState = 0;
m_op.pCallback = pCallback;
m_op.pCallback->AddRef();
m_op.pMessageFolder = pDest;
m_op.pMessageFolder->AddRef();
hr = _BeginDeferredOperation();
exit:
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::_LoadAccountInfo
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::_LoadAccountInfo(IImnAccount *pAcct)
{
HRESULT hr = S_OK;
FOLDERINFO fi;
FOLDERINFO *pfiFree = NULL;;
Assert(NULL != pAcct);
Assert(FOLDERID_INVALID != m_idServer);
Assert(NULL != m_pStore);
Assert(NULL != g_pAcctMan);
// free data associated with the account. if we connected to
// a transport, and then disconnected, we might be reconnecting
// with stale data left around.
SafeMemFree(m_pszFldrLeafName);
IF_FAILEXIT(hr = m_pStore->GetFolderInfo(m_idServer, &fi));
pfiFree = &fi;
m_pszFldrLeafName = PszDupA(fi.pszName);
if (NULL == m_pszFldrLeafName)
{
hr = TraceResult(E_OUTOFMEMORY);
goto exit;
}
// failure of the account name is recoverable
pAcct->GetPropSz(AP_ACCOUNT_NAME, m_szAccountName, sizeof(m_szAccountName));
exit:
if (pfiFree)
m_pStore->FreeRecord(pfiFree);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::_LoadTransport
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::_LoadTransport(void)
{
HRESULT hr = S_OK;
char szLogFilePath[MAX_PATH];
char *pszLogFilePath = NULL;
LPSTR pszUserAgent = NULL;
Assert(NULL == m_pTransport);
// Create and initialize HTTPMail transport
hr = CoCreateInstance(CLSID_IHTTPMailTransport, NULL, CLSCTX_INPROC_SERVER, IID_IHTTPMailTransport, (LPVOID *)&m_pTransport);
if (FAILED(hr))
{
TraceResult(hr);
goto exit;
}
IF_FAILEXIT(hr = m_pTransport->QueryInterface(IID_IHTTPMailTransport2, (LPVOID*)&m_pTransport2));
// check if logging is enabled
if (DwGetOption(OPT_MAIL_LOGHTTPMAIL))
{
char szDirectory[MAX_PATH];
char szLogFileName[MAX_PATH];
DWORD cb;
*szDirectory = 0;
// get the log filename
cb = GetOption(OPT_MAIL_HTTPMAILLOGFILE, szLogFileName, sizeof(szLogFileName) / sizeof(TCHAR));
if (0 == cb)
{
// push the defaults into the registry
StrCpyN(szLogFileName, c_szDefaultHTTPMailLog, ARRAYSIZE(szLogFileName));
SetOption(OPT_MAIL_HTTPMAILLOGFILE,
(void *)c_szDefaultHTTPMailLog,
lstrlen(c_szDefaultHTTPMailLog) + sizeof(TCHAR),
NULL,
0);
}
m_pStore->GetDirectory(szDirectory, ARRAYSIZE(szDirectory));
PathCombineA(szLogFilePath, szDirectory, szLogFileName);
pszLogFilePath = szLogFilePath;
}
pszUserAgent = GetOEUserAgentString();
if (FAILED(hr = m_pTransport->InitNew(pszUserAgent, pszLogFilePath, this)))
{
TraceResult(hr);
goto exit;
}
exit:
SafeMemFree(pszUserAgent);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::_TranslateHTTPSpecialFolderType
//----------------------------------------------------------------------
SPECIALFOLDER CHTTPMailServer::_TranslateHTTPSpecialFolderType(HTTPMAILSPECIALFOLDER tySpecial)
{
SPECIALFOLDER tyOESpecial;
switch (tySpecial)
{
case HTTPMAIL_SF_INBOX:
tyOESpecial = FOLDER_INBOX;
break;
case HTTPMAIL_SF_DELETEDITEMS:
tyOESpecial = FOLDER_DELETED;
break;
case HTTPMAIL_SF_DRAFTS:
tyOESpecial = FOLDER_DRAFT;
break;
case HTTPMAIL_SF_OUTBOX:
tyOESpecial = FOLDER_OUTBOX;
break;
case HTTPMAIL_SF_SENTITEMS:
tyOESpecial = FOLDER_SENT;
break;
case HTTPMAIL_SF_MSNPROMO:
tyOESpecial = FOLDER_MSNPROMO;
break;
case HTTPMAIL_SF_BULKMAIL:
tyOESpecial = FOLDER_BULKMAIL;
break;
default:
tyOESpecial = FOLDER_NOTSPECIAL;
break;
}
return tyOESpecial;
}
//----------------------------------------------------------------------
// CHTTPMailServer::_LoadSpecialFolderName
//----------------------------------------------------------------------
BOOL CHTTPMailServer::_LoadSpecialFolderName(SPECIALFOLDER tySpecial,
LPSTR pszName,
DWORD cbBuffer)
{
BOOL fResult = TRUE;
UINT uID;
switch (tySpecial)
{
case FOLDER_INBOX:
uID = idsInbox;
break;
case FOLDER_DELETED:
uID = idsDeletedItems;
break;
case FOLDER_DRAFT:
uID = idsDraft;
break;
case FOLDER_OUTBOX:
uID = idsOutbox;
break;
case FOLDER_SENT:
uID = idsSentItems;
break;
case FOLDER_MSNPROMO:
uID = idsMsnPromo;
break;
case FOLDER_BULKMAIL:
uID = idsJunkFolderName;
break;
default:
fResult = FALSE;
break;
}
if (fResult && (0 == LoadString(g_hLocRes, uID, pszName, cbBuffer)))
fResult = FALSE;
return fResult;
}
//----------------------------------------------------------------------
// CHTTPMailServer::_CreateMessageIDMap
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::_CreateMessageIDMap(TMap<CSimpleString, MARKEDMESSAGE> **ppMap)
{
HRESULT hr = S_OK;
TMap<CSimpleString, MARKEDMESSAGE> *pMap = NULL;
HROWSET hRowSet = NULL;
MESSAGEINFO mi;
CSimpleString ss;
MARKEDMESSAGE markedID = { 0, 0, FALSE };
if (NULL == m_pStore || NULL == ppMap)
return E_INVALIDARG;
*ppMap = NULL;
pMap = new TMap<CSimpleString, MARKEDMESSAGE>;
if (NULL == pMap)
{
hr = E_OUTOFMEMORY;
goto exit;
}
ZeroMemory(&mi, sizeof(MESSAGEINFO));
if (FAILED(hr = m_pFolder->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowSet)))
goto exit;
// iterate through the messages
while (S_OK == m_pFolder->QueryRowset(hRowSet, 1, (LPVOID *)&mi, NULL))
{
// add the message's info to the map
markedID.idMessage = mi.idMessage;
markedID.dwFlags = mi.dwFlags;
hr = ss.SetString(mi.pszUrlComponent);
if (FAILED(hr))
{
m_pFolder->FreeRecord(&mi);
goto exit;
}
hr = pMap->Add(ss, markedID);
// Free
m_pFolder->FreeRecord(&mi);
if (FAILED(hr))
goto exit;
}
// the map was built successfully
*ppMap = pMap;
pMap = NULL;
exit:
if (NULL != hRowSet)
m_pFolder->CloseRowset(&hRowSet);
if (NULL != pMap)
delete pMap;
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::_HrBuildMapAndTargets
//----------------------------------------------------------------------
HRESULT CHTTPMailServer::_HrBuildMapAndTargets(LPMESSAGEIDLIST pList,
HROWSET hRowSet,
LPADJUSTFLAGS pFlags,
SETMESSAGEFLAGSFLAGS dwFlags,
TMap<CSimpleString, MARKEDMESSAGE> **ppMap,
LPHTTPTARGETLIST *ppTargets)
{
HRESULT hr = S_OK;
TMap<CSimpleString, MARKEDMESSAGE> *pMap = NULL;
LPHTTPTARGETLIST pTargets = NULL;
MESSAGEINFO mi = { 0 };
LPMESSAGEINFO pmiFree = NULL;
CSimpleString ss;
MARKEDMESSAGE markedID = { 0, 0, FALSE };
BOOL fSkipRead = (pFlags && !!(pFlags->dwAdd & ARF_READ));
BOOL fSkipUnread = (pFlags && !!(pFlags->dwRemove & ARF_READ));
DWORD cMsgs;
DWORD dwIndex = 0;
if ((NULL == pList && NULL == hRowSet) || NULL == ppMap || NULL == ppTargets)
return E_INVALIDARG;
// expect either a list or a rowset, but not both
Assert(NULL == pList || NULL == hRowSet);
// if using a rowset, determine the rowcount
if (NULL != hRowSet)
{
IF_FAILEXIT(hr = m_pFolder->GetRecordCount(0, &cMsgs));
// seek the first row
IF_FAILEXIT(hr = m_pFolder->SeekRowset(hRowSet, SEEK_ROWSET_BEGIN, 0, NULL));
}
else
cMsgs = pList->cMsgs;
*ppMap = NULL;
*ppTargets = NULL;
pMap = new TMap<CSimpleString, MARKEDMESSAGE>;
if (NULL == pMap)
{
hr = TrapError(E_OUTOFMEMORY);
goto exit;
}
if (!MemAlloc((void **)&pTargets, sizeof(HTTPTARGETLIST)))
{
hr = TrapError(E_OUTOFMEMORY);
goto exit;
}
pTargets->cTarget = 0;
pTargets->prgTarget = NULL;
// allocate enough space for all of the targets
if (!MemAlloc((void **)&pTargets->prgTarget, sizeof(LPCSTR) * cMsgs))
{
hr = TrapError(E_OUTOFMEMORY);
goto exit;
}
ZeroMemory(pTargets->prgTarget, sizeof(LPCSTR) * cMsgs);
while (TRUE)
{
// fetch the next message
if (NULL != pList)
{
if (dwIndex == pList->cMsgs)
break;
hr = GetMessageInfo(m_pFolder, pList->prgidMsg[dwIndex++], &mi);
// if the record wasn't found, just skip it
if (DB_E_NOTFOUND == hr)
goto next;
if (FAILED(hr))
break;
}
else
{
// bail out if the number of targets is the same as the rowcount
// we expected. this will prevent us from overflowing the target
// array if the rowcount changes while we are building up our target
// list.
if (pTargets->cTarget == cMsgs)
break;
if (S_OK != m_pFolder->QueryRowset(hRowSet, 1, (LPVOID *)&mi, NULL))
break;
}
pmiFree = &mi;
// respect control flags, if they exist
if (0 == (dwFlags & SET_MESSAGE_FLAGS_FORCE) && ((fSkipRead && !!(mi.dwFlags & ARF_READ)) || (fSkipUnread && !(mi.dwFlags & ARF_READ))))
goto next;
Assert(NULL != mi.pszUrlComponent);
if (NULL == mi.pszUrlComponent)
{
hr = TrapError(ERROR_INTERNET_INVALID_URL);
goto exit;
}
// add the url component to the target list
pTargets->prgTarget[pTargets->cTarget] = PszDupA(mi.pszUrlComponent);
if (NULL == pTargets->prgTarget[pTargets->cTarget])
{
hr = TrapError(E_OUTOFMEMORY);
goto exit;
}
pTargets->cTarget++;
// add the url and the message id to the map
markedID.idMessage = mi.idMessage;
markedID.dwFlags = mi.dwFlags;
if (FAILED(hr = ss.SetString(mi.pszUrlComponent)))
goto exit;
if (FAILED(hr = pMap->Add(ss, markedID)))
goto exit;
next:
if (pmiFree)
{
m_pFolder->FreeRecord(pmiFree);
pmiFree = NULL;
}
hr = S_OK;
}
*ppMap = pMap;
pMap = NULL;
*ppTargets = pTargets;
pTargets = NULL;
exit:
if (pmiFree)
m_pFolder->FreeRecord(pmiFree);
if (pTargets)
Http_FreeTargetList(pTargets);
SafeDelete(pMap);
return hr;
}
//----------------------------------------------------------------------
// CHTTPMailServer::_FillStoreError
//----------------------------------------------------------------------
void CHTTPMailServer::_FillStoreError(LPSTOREERROR pErrorInfo,
IXPRESULT *pResult)
{
TraceCall("CHTTPMailServer::FillStoreError");
Assert(m_cRef >= 0); // Can be called during destruction
Assert(NULL != pErrorInfo);
//TODO: Fill in pszFolder
// Fill out the STOREERROR structure
ZeroMemory(pErrorInfo, sizeof(*pErrorInfo));
if (IXP_E_USER_CANCEL == pResult->hrResult)
pErrorInfo->hrResult = STORE_E_OPERATION_CANCELED;
else
pErrorInfo->hrResult = pResult->hrResult;
pErrorInfo->uiServerError = pResult->uiServerError;
pErrorInfo->hrServerError = pResult->hrServerError;
pErrorInfo->dwSocketError = pResult->dwSocketError;
pErrorInfo->pszProblem = (NULL != m_op.pszProblem) ? m_op.pszProblem : pResult->pszProblem;
pErrorInfo->pszDetails = pResult->pszResponse;
pErrorInfo->pszAccount = m_rInetServerInfo.szAccount;
pErrorInfo->pszServer = m_rInetServerInfo.szServerName;
pErrorInfo->pszFolder = NULL;
pErrorInfo->pszUserName = m_rInetServerInfo.szUserName;
pErrorInfo->pszProtocol = "HTTPMail";
pErrorInfo->pszConnectoid = m_rInetServerInfo.szConnectoid;
pErrorInfo->rasconntype = m_rInetServerInfo.rasconntype;
pErrorInfo->ixpType = IXP_HTTPMail;
pErrorInfo->dwPort = m_rInetServerInfo.dwPort;
pErrorInfo->fSSL = m_rInetServerInfo.fSSL;
pErrorInfo->fTrySicily = m_rInetServerInfo.fTrySicily;
pErrorInfo->dwFlags = 0;
}
STDMETHODIMP CHTTPMailServer::GetAdBarUrl(IStoreCallback *pCallback)
{
TraceCall("CHTTPMailServer::GetAdBarUrl");
AssertSingleThreaded;
Assert(NULL != pCallback);
Assert(SOT_INVALID == m_op.tyOperation);
Assert(NULL != m_pStore);
if (NULL == pCallback)
return E_INVALIDARG;
m_op.tyOperation = SOT_GET_ADURL;
m_op.iState = 0;
m_op.pfnState = c_rgpfnGetAdUrl;
m_op.cState = ARRAYSIZE(c_rgpfnGetAdUrl);
m_op.pCallback = pCallback;
m_op.pCallback->AddRef();
return _BeginDeferredOperation();
}
HRESULT CHTTPMailServer::GetAdBarUrlFromServer()
{
HRESULT hr = S_OK;
LPSTR pszUrl = NULL;
hr = m_pTransport->GetProperty(HTTPMAIL_PROP_ADBAR, &pszUrl);
if (hr == S_OK)
m_op.pszAdUrl = pszUrl;
return hr;
}
STDMETHODIMP CHTTPMailServer::GetMinPollingInterval(IStoreCallback *pCallback)
{
TraceCall("CHTTPMailServer::GetMinPollingInterval");
AssertSingleThreaded;
Assert(NULL != pCallback);
Assert(SOT_INVALID == m_op.tyOperation);
Assert(NULL != m_pStore);
if (NULL == pCallback)
return E_INVALIDARG;
m_op.tyOperation = SOT_GET_HTTP_MINPOLLINGINTERVAL;
m_op.iState = 0;
m_op.pfnState = c_rgpfnGetMinPollingInterval;
m_op.cState = ARRAYSIZE(c_rgpfnGetMinPollingInterval);
m_op.pCallback = pCallback;
m_op.pCallback->AddRef();
return _BeginDeferredOperation();
}
HRESULT CHTTPMailServer::GetMinPollingInterval()
{
DWORD dwDone = FALSE;
DWORD dwPollingInterval = 0;
HRESULT hr = S_OK;
hr = m_pTransport->GetPropertyDw(HTTPMAIL_PROP_MAXPOLLINGINTERVAL, &dwPollingInterval);
if (hr == S_OK)
m_op.dwMinPollingInterval = dwPollingInterval;
return hr;
}