|
|
#include "pch.hxx"
#include "store.h"
#include "instance.h"
#include "msgfldr.h"
#include "secutil.h"
#include "storutil.h"
#include "fldrsync.h"
#include <conman.h>
#include "shlwapip.h"
#include "sync.h"
static char c_szFolderSyncWndClass[] = "Outlook Express FolderSync Window Class";
static const PFNCOPYFUNC c_rgpfnCopyMsgs[] = { &CFolderSync::CopyOpen, &CFolderSync::CopySave, &CFolderSync::CopySave2, &CFolderSync::CopyDelete, &CFolderSync::CopyDelete2, };
//--------------------------------------------------------------------------
// CFolderSync::CFolderSync
//--------------------------------------------------------------------------
CFolderSync::CFolderSync(void) { g_pInstance->DllAddRef(); m_cRef = 1; m_pLocalStore = NULL; m_pFldr = NULL; m_tyFolder = FOLDER_INVALID; m_idFolder = FOLDERID_INVALID; m_idServer = FOLDERID_INVALID; m_szAcctId[0] = 0; m_pServer = NULL; m_fConManAdvise = FALSE; m_hwnd = NULL; m_pCopy = NULL; m_fImap = FALSE; }
//--------------------------------------------------------------------------
// CFolderSync::~CFolderSync
//--------------------------------------------------------------------------
CFolderSync::~CFolderSync(void) { Assert(m_pCopy == NULL);
if (m_fConManAdvise && g_pConMan != NULL) g_pConMan->Unadvise((IConnectionNotify *)this);
if (m_hwnd != NULL) { if (GetWindowThreadProcessId(m_hwnd, NULL) == GetCurrentThreadId()) { DestroyWindow(m_hwnd); } else { SetWindowLongPtr(m_hwnd, GWLP_USERDATA, NULL); PostMessage(m_hwnd, WM_CLOSE, 0, 0L); } }
if (m_pServer) { m_pServer->Close(MSGSVRF_HANDS_OFF_SERVER); m_pServer->Release(); m_pServer = NULL; }
SafeRelease(m_pLocalStore); SafeRelease(m_pFldr); // Release the Dll
g_pInstance->DllRelease(); }
//--------------------------------------------------------------------------
// CFolderSync::QueryInterface
//--------------------------------------------------------------------------
STDMETHODIMP CFolderSync::QueryInterface(REFIID riid, LPVOID *ppv) { if (IID_IUnknown == riid) *ppv = (IMessageFolder *)this; else if (IID_IMessageFolder == riid) *ppv = (IMessageFolder *)this; else if (IID_IDatabase == riid) *ppv = (IDatabase *)this; else if (IID_IServiceProvider == riid) *ppv = (IServiceProvider *)this; else { *ppv = NULL; return(E_NOINTERFACE); }
// AddRef It
((IUnknown *)*ppv)->AddRef();
return(S_OK); }
//--------------------------------------------------------------------------
// CFolderSync::AddRef
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CFolderSync::AddRef(void) { return InterlockedIncrement(&m_cRef); }
//--------------------------------------------------------------------------
// CFolderSync::Release
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CFolderSync::Release(void) { LONG cRef = InterlockedDecrement(&m_cRef); if (0 == cRef) delete this; return (ULONG)cRef; }
//--------------------------------------------------------------------------
// CFolderSync::Initialize
//--------------------------------------------------------------------------
STDMETHODIMP CFolderSync::Initialize(IMessageStore *pStore, IMessageServer *pServer, OPENFOLDERFLAGS dwFlags, FOLDERID idFolder) { Assert(FALSE); return(E_NOTIMPL); }
//--------------------------------------------------------------------------
// CFolderSync::SetOwner
//--------------------------------------------------------------------------
STDMETHODIMP CFolderSync::SetOwner(IStoreCallback *pDefaultCallback) { HRESULT hrResult;
TraceCall("CFolderSync::SetOwner");
if (NULL == m_pServer) { hrResult = E_FAIL; goto exit; }
hrResult = m_pServer->SetIdleCallback(pDefaultCallback); if (FAILED(hrResult)) { TraceResult(hrResult); goto exit; }
exit: return hrResult; }
//--------------------------------------------------------------------------
// CFolderSync::Synchronize
//--------------------------------------------------------------------------
STDMETHODIMP CFolderSync::Synchronize(SYNCFOLDERFLAGS dwFlags, DWORD cHeaders, IStoreCallback *pCallback) { BOOL fOffline; HRESULT hr;
// async
// if online, then synchronize folder
// if offline, no op (fail)
if (m_pServer == NULL) { // local folders don't sync.
// NOTE - This isn't failure - there's just nothing to do.
return (S_FALSE); }
Assert(pCallback != NULL); if (pCallback == NULL) return(E_INVALIDARG); if(g_pConMan->IsAccountDisabled((LPSTR)m_szAcctId)) hr = S_FALSE; else hr = m_pServer->SynchronizeFolder(dwFlags, cHeaders, pCallback); return(hr); }
//--------------------------------------------------------------------------
// CFolderSync::GetFolderId
//--------------------------------------------------------------------------
STDMETHODIMP CFolderSync::GetFolderId(LPFOLDERID pidFolder) { Assert(NULL != pidFolder);
// local store operation only
*pidFolder = m_idFolder;
return(S_OK); }
//--------------------------------------------------------------------------
// CFolderSync::GetMessageFolderId
//--------------------------------------------------------------------------
STDMETHODIMP CFolderSync::GetMessageFolderId(MESSAGEID idMessage, LPFOLDERID pidFolder) { return(m_pFldr->GetMessageFolderId(idMessage, pidFolder)); }
//--------------------------------------------------------------------------
// CFolderSync::OpenMessage
//--------------------------------------------------------------------------
STDMETHODIMP CFolderSync::OpenMessage(MESSAGEID idMessage, OPENMESSAGEFLAGS dwFlags, IMimeMessage **ppMessage, IStoreCallback *pCallback) { HRESULT hr; BOOL fOffline; FILEADDRESS faStart;
Assert(NULL != ppMessage);
// async
// if message is already downloaded
// open message in local store
// else
// if online, download message
// if offline, fail
hr = m_pFldr->OpenMessage(idMessage, dwFlags, ppMessage, NULL); if (SUCCEEDED(hr)) return(hr);
if (hr == STORE_E_NOBODY && m_pServer != NULL && !ISFLAGSET(dwFlags, OPEN_MESSAGE_CACHEDONLY)) { Assert(pCallback != NULL); if (pCallback == NULL) return(E_INVALIDARG);
hr = m_pServer->GetMessage(idMessage, pCallback); }
return(hr); }
//--------------------------------------------------------------------------
// CFolderSync::SaveMessage
//--------------------------------------------------------------------------
STDMETHODIMP CFolderSync::SaveMessage(LPMESSAGEID pidMessage, SAVEMESSAGEFLAGS dwOptions, MESSAGEFLAGS dwFlags, IStream *pStreamIn, IMimeMessage *pMessage, IStoreCallback *pCallback) { HRESULT hr; IStream *pStream; DWORD dwOffline; LPFILETIME pftRecv; PROPVARIANT rVariant; Assert(NULL != pMessage);
// save message to local store
// if online, upload message
// if offline, log transaction
if (m_pServer == NULL) { hr = m_pFldr->SaveMessage(pidMessage, dwOptions, dwFlags, pStreamIn, pMessage, NULL); return(hr); }
hr = _Offline(&dwOffline); if (SUCCEEDED(hr)) { if (dwOffline == CONN_STATE_NOT_CONNECTED) { hr = E_NOT_ONLINE; } else if (dwOffline == CONN_STATE_OFFLINE) { hr = g_pSync->CreateMessage(m_pFldr, pidMessage, dwOptions, dwFlags, pStreamIn, pMessage); } else { Assert(dwOffline == CONN_STATE_CONNECTED); Assert(pCallback != NULL); if (pCallback == NULL) return(E_INVALIDARG); // the idStream can legally by null in the case of saving a draft
if (NULL == pStreamIn) hr = pMessage->GetMessageSource(&pStream, COMMIT_ONLYIFDIRTY); else { pStream = pStreamIn; pStream->AddRef(); }
if (SUCCEEDED(hr)) { rVariant.vt = VT_FILETIME; if (SUCCEEDED(pMessage->GetProp(PIDTOSTR(PID_ATT_RECVTIME), 0, &rVariant))) pftRecv = &rVariant.filetime; else pftRecv = NULL; hr = m_pServer->PutMessage(m_idFolder, dwFlags, pftRecv, pStream, pCallback); pStream->Release(); } } } return(hr); }
//--------------------------------------------------------------------------
// CFolderSync::SetMessageStream
//--------------------------------------------------------------------------
STDMETHODIMP CFolderSync::SetMessageStream(MESSAGEID idMessage, IStream *pStream) { // pass through to the local store
Assert (NULL != m_pFldr); Assert(NULL != pStream);
if (NULL == pStream) return E_INVALIDARG;
return m_pFldr->SetMessageStream(idMessage, pStream); }
//--------------------------------------------------------------------------
// CFolderSync::SetMessageFlags
//--------------------------------------------------------------------------
STDMETHODIMP CFolderSync::SetMessageFlags(LPMESSAGEIDLIST pList, LPADJUSTFLAGS pFlags, LPRESULTLIST pResults, IStoreCallback *pCallback) { INewsStore *pNewsStore; ADJUSTFLAGS localFlags, svrFlags; MESSAGEFLAGS flags; DWORD dwOffline; HRESULT hr;
Assert(NULL != pFlags); Assert(NULL == pList || 0 != pList->cMsgs);
// async
// save message flags to local store
// if online, upload message flags if necessary (some flags are irrelevant to server)
// if offline, log transaction if necessary
if (m_pServer == NULL) return(m_pFldr->SetMessageFlags(pList, pFlags, pResults, NULL));
hr = _Offline(&dwOffline); if (SUCCEEDED(hr)) { hr = m_pServer->GetServerMessageFlags(&flags); if (SUCCEEDED(hr)) { Assert(hr == S_OK || hr == S_FALSE);
// hr == S_FALSE
// this server doesn't have any flags that need to hit the server
localFlags = *pFlags;
if (hr == S_OK) { svrFlags = *pFlags; svrFlags.dwAdd &= flags; svrFlags.dwRemove &= flags;
localFlags.dwAdd &= ~flags; localFlags.dwRemove &= ~flags;
if (0 != svrFlags.dwAdd || 0 != svrFlags.dwRemove) { if (dwOffline == CONN_STATE_NOT_CONNECTED) { return(E_NOT_ONLINE); } else if (dwOffline == CONN_STATE_OFFLINE) { hr = g_pSync->SetMessageFlags(m_pFldr, pList, &svrFlags); } else { Assert(dwOffline == CONN_STATE_CONNECTED);
Assert(pCallback != NULL); if (pCallback == NULL) return(E_INVALIDARG);
hr = m_pServer->SetMessageFlags(pList, &svrFlags, 0, pCallback); } } }
if (0 != localFlags.dwAdd || 0 != localFlags.dwRemove) { hr = m_pFldr->SetMessageFlags(pList, &localFlags, pResults, NULL);
// mark news crossposts as read
if (m_tyFolder == FOLDER_NEWS && (!!(localFlags.dwAdd & ARF_READ) || !!(localFlags.dwRemove & ARF_READ))) { IServiceProvider *pService;
if (SUCCEEDED(m_pServer->QueryInterface(IID_IServiceProvider, (void **)&pService))) { if (SUCCEEDED(pService->QueryService(SID_MessageServer, IID_INewsStore, (void **)&pNewsStore))) { pNewsStore->MarkCrossposts(pList, !!(localFlags.dwAdd & ARF_READ));
pNewsStore->Release(); }
pService->Release(); } } } } }
return(hr); }
//--------------------------------------------------------------------------
// CFolderSync::DeleteMessages
//--------------------------------------------------------------------------
STDMETHODIMP CFolderSync::DeleteMessages(DELETEMESSAGEFLAGS dwOptions, LPMESSAGEIDLIST pList, LPRESULTLIST pResults, IStoreCallback *pCallback) { ADJUSTFLAGS afFlags; DWORD dwOffline; HRESULT hr;
Assert(NULL == pList || pList->cMsgs > 0);
// if online, delete messages from server and local store
// if offline, log transaction
if (m_pServer == NULL || m_tyFolder == FOLDER_NEWS) return(m_pFldr->DeleteMessages(dwOptions, pList, pResults, pCallback));
hr = _Offline(&dwOffline); if (SUCCEEDED(hr)) { if (dwOffline == CONN_STATE_NOT_CONNECTED) { hr = E_NOT_ONLINE; } else if (dwOffline == CONN_STATE_OFFLINE) { // [PaulHi] 4/8/99 Raid 63339
// Deleting from an HTTP folder (other than the 'deleted' folder)
// translates into a message move from the source folder to the
// 'deleted' folder. If offline we want the message copy to take
// place and then cache the message 'copy' just as would happen with
// an offline drag/drop copy operation.
FOLDERINFO fldrinfo; BOOL bHMOffLineCopy = FALSE;
hr = m_pLocalStore->GetFolderInfo(m_idFolder, &fldrinfo); if (SUCCEEDED(hr)) { if ( (fldrinfo.tyFolder == FOLDER_HTTPMAIL) && (fldrinfo.tySpecial != FOLDER_DELETED) && (fldrinfo.tySpecial != FOLDER_MSNPROMO) ) { // Code stolen from CFolderSync::CopyMessages
IMessageFolder * pDeletedItems = NULL; IMessageFolder * pDestLocal = NULL; IServiceProvider * pService = NULL;
hr = g_pStore->OpenSpecialFolder(m_idServer, NULL, FOLDER_DELETED, &pDeletedItems);
if (SUCCEEDED(hr)) { hr = pDeletedItems->QueryInterface(IID_IServiceProvider, (void **)&pService); if (SUCCEEDED(hr)) { hr = pService->QueryService(SID_LocalMessageFolder, IID_IMessageFolder, (void **)&pDestLocal); pService->Release(); }
pDeletedItems->Release(); }
if (SUCCEEDED(hr)) { Assert(pDestLocal != NULL);
afFlags.dwAdd = 0; afFlags.dwRemove = 0; hr = g_pSync->CopyMessages(m_pFldr, pDestLocal, COPY_MESSAGE_MOVE, pList, &afFlags); bHMOffLineCopy = TRUE;
pDestLocal->Release(); }
m_pLocalStore->FreeRecord(&fldrinfo); } }
// If the HM offline copy didn't occur, for whatever reason, revert to the original
// off line delete.
if (!bHMOffLineCopy) hr = g_pSync->DeleteMessages(m_pFldr, dwOptions, pList); } else { Assert(dwOffline == CONN_STATE_CONNECTED);
Assert(pCallback != NULL); if (pCallback == NULL) return(E_INVALIDARG);
hr = m_pServer->DeleteMessages(dwOptions, pList, pCallback); } }
return(hr); }
HRESULT CFolderSync::Initialize(IMessageStore *pStore, IMessageFolder *pLocalFolder, IMessageServer *pServer, OPENFOLDERFLAGS dwFlags, FOLDERTYPE tyFolder, FOLDERID idFolder, FOLDERID idServer) { WNDCLASSEX wc; HRESULT hr; FOLDERINFO info;
Assert(NULL != pStore);
// Save the Folder Type
m_tyFolder = tyFolder;
// Save the FolderId
m_idFolder = idFolder;
m_idServer = idServer;
// Save pStore
m_pLocalStore = pStore; m_pLocalStore->AddRef();
if (pServer != NULL) { hr = m_pLocalStore->GetFolderInfo(m_idServer, &info); if (FAILED(hr)) return(hr);
StrCpyN(m_szAcctId, info.pszAccountId, ARRAYSIZE(m_szAcctId)); m_fImap = (info.tyFolder == FOLDER_IMAP);
m_pLocalStore->FreeRecord(&info);
m_pServer = pServer; m_pServer->AddRef();
hr = g_pConMan->Advise((IConnectionNotify *)this); m_fConManAdvise = SUCCEEDED(hr); }
if (pLocalFolder) { m_pFldr = pLocalFolder; m_pFldr->AddRef(); } else { hr = m_pLocalStore->OpenFolder(m_idFolder, NULL, dwFlags, &m_pFldr); if (FAILED(hr)) return(hr); } wc.cbSize = sizeof(WNDCLASSEX); if (!GetClassInfoEx(g_hInst, c_szFolderSyncWndClass, &wc)) { wc.style = 0; wc.lpfnWndProc = FolderSyncWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = g_hInst; wc.hIcon = NULL; wc.hCursor = NULL; wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = c_szFolderSyncWndClass; wc.hIconSm = NULL; if (RegisterClassEx(&wc) == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) return E_FAIL; }
m_hwnd = CreateWindowEx(WS_EX_TOPMOST, c_szFolderSyncWndClass, c_szFolderSyncWndClass, WS_POPUP, 1, 1, 1, 1, NULL, NULL, g_hInst, (LPVOID)this); if (m_hwnd == NULL) return(E_FAIL);
return(S_OK); }
HRESULT CFolderSync::Close() { HRESULT hr = S_OK; if (m_pServer) hr = m_pServer->Close(MSGSVRF_HANDS_OFF_SERVER);
return hr; }
HRESULT CFolderSync::_Offline(DWORD *pdwOffline) { BOOL fOffline; HRESULT hr;
Assert(pdwOffline != NULL); Assert(m_szAcctId[0] != 0);
fOffline = g_pConMan->IsGlobalOffline(); if (fOffline) { *pdwOffline = CONN_STATE_OFFLINE; } else { //Bug# 39337. We just give the operation to the sync object even if we are not connected.
//The sync object in turn figures out whether it needs to dial or not
/*
hr = g_pConMan->CanConnect(m_szAcctId); *pdwOffline = (hr == S_OK) ? CONN_STATE_CONNECTED : CONN_STATE_NOT_CONNECTED; */ *pdwOffline = CONN_STATE_CONNECTED; }
return(S_OK); }
HRESULT CFolderSync::_OfflineServer(FOLDERID idServer, DWORD *pdwOffline) { HRESULT hr; FOLDERINFO info; BOOL fOffline;
Assert(pdwOffline != NULL);
hr = m_pLocalStore->GetFolderInfo(idServer, &info); if (FAILED(hr)) return(hr);
if (info.tyFolder == FOLDER_LOCAL) { *pdwOffline = CONN_STATE_CONNECTED; } else { fOffline = g_pConMan->IsGlobalOffline(); if (fOffline) { *pdwOffline = CONN_STATE_OFFLINE; } else { /*
hr = g_pConMan->CanConnect(info.pszAccountId); *pdwOffline = (hr == S_OK) ? CONN_STATE_CONNECTED : CONN_STATE_NOT_CONNECTED; */ *pdwOffline = CONN_STATE_CONNECTED; } }
m_pLocalStore->FreeRecord(&info);
return(S_OK); }
HRESULT CFolderSync::QueryService(REFGUID guidService, REFIID riid, LPVOID *ppvObject) { IServiceProvider *pSP; HRESULT hr = E_NOINTERFACE;
if (guidService == SID_LocalMessageFolder) { if (m_pFldr != NULL) hr = m_pFldr->QueryInterface(riid, ppvObject); } else if (m_pServer && m_pServer->QueryInterface(IID_IServiceProvider, (LPVOID *)&pSP)==S_OK) { hr = pSP->QueryService(guidService, riid, ppvObject); pSP->Release(); }
return hr; }
HRESULT CFolderSync::OnBegin(STOREOPERATIONTYPE tyOperation, STOREOPERATIONINFO *pOpInfo, IOperationCancel *pCancel) { Assert(m_pCopy->type == SOT_INVALID); Assert(m_pCopy->fAsync);
m_pCopy->type = tyOperation;
if (!m_pCopy->fBegin) { Assert(m_pCopy->pCallback != NULL); m_pCopy->pCallback->OnBegin(SOT_COPYMOVE_MESSAGE, pOpInfo, (IOperationCancel *)this);
m_pCopy->fBegin = TRUE; }
return(S_OK); }
HRESULT CFolderSync::OnProgress(STOREOPERATIONTYPE tyOperation, DWORD dwCurrent, DWORD dwMax, LPCSTR pszStatus) { return(S_OK); }
HRESULT CFolderSync::OnTimeout(LPINETSERVER pServer, LPDWORD pdwTimeout, IXPTYPE ixpServerType) { Assert(m_pCopy != NULL); return(m_pCopy->pCallback->OnTimeout(pServer, pdwTimeout, ixpServerType)); }
HRESULT CFolderSync::CanConnect(LPCSTR pszAccountId, DWORD dwFlags) { Assert(m_pCopy != NULL); return(m_pCopy->pCallback->CanConnect(pszAccountId, dwFlags)); }
HRESULT CFolderSync::OnLogonPrompt(LPINETSERVER pServer, IXPTYPE ixpServerType) { Assert(m_pCopy != NULL); return(m_pCopy->pCallback->OnLogonPrompt(pServer, ixpServerType)); }
HRESULT CFolderSync::OnComplete(STOREOPERATIONTYPE tyOperation, HRESULT hrComplete, LPSTOREOPERATIONINFO pOpInfo, LPSTOREERROR pErrorInfo) { Assert(m_pCopy != NULL); if (m_pCopy->type != tyOperation || m_pCopy->hr != S_FALSE) return(S_OK);
Assert(hrComplete != S_FALSE);
m_pCopy->hr = hrComplete;
if (NULL != pErrorInfo) { BOOL fResult;
fResult = MemAlloc((void **)&m_pCopy->pErrorInfo, sizeof(STOREERROR)); if (FALSE == fResult) { TraceResult(E_OUTOFMEMORY); } else { // Make a copy of the STOREERROR so we can display rich errors to user
m_pCopy->pErrorInfo->hrResult = pErrorInfo->hrResult; m_pCopy->pErrorInfo->uiServerError = pErrorInfo->uiServerError; m_pCopy->pErrorInfo->hrServerError = pErrorInfo->hrServerError; m_pCopy->pErrorInfo->dwSocketError = pErrorInfo->dwSocketError; m_pCopy->pErrorInfo->pszProblem = PszDupA(pErrorInfo->pszProblem); m_pCopy->pErrorInfo->pszDetails = PszDupA(pErrorInfo->pszDetails); m_pCopy->pErrorInfo->pszAccount = PszDupA(pErrorInfo->pszAccount); m_pCopy->pErrorInfo->pszServer = PszDupA(pErrorInfo->pszServer); m_pCopy->pErrorInfo->pszFolder = PszDupA(pErrorInfo->pszFolder); m_pCopy->pErrorInfo->pszUserName = PszDupA(pErrorInfo->pszUserName); m_pCopy->pErrorInfo->pszProtocol = PszDupA(pErrorInfo->pszProtocol); m_pCopy->pErrorInfo->ixpType = pErrorInfo->ixpType; m_pCopy->pErrorInfo->dwPort = pErrorInfo->dwPort; m_pCopy->pErrorInfo->fSSL = pErrorInfo->fSSL; m_pCopy->pErrorInfo->dwFlags = pErrorInfo->dwFlags; } }
PostMessage(m_hwnd, WM_USER, 0, 0); m_pCopy->type = SOT_INVALID;
return S_OK; }
HRESULT CFolderSync::OnPrompt(HRESULT hrError, LPCTSTR pszText, LPCTSTR pszCaption, UINT uType, INT *piUserResponse) { Assert(m_pCopy != NULL); return(m_pCopy->pCallback->OnPrompt(hrError, pszText, pszCaption, uType, piUserResponse)); }
HRESULT CFolderSync::GetParentWindow(DWORD dwReserved, HWND *phwndParent) { Assert(m_pCopy != NULL); return(m_pCopy->pCallback->GetParentWindow(dwReserved, phwndParent)); }
HRESULT CFolderSync::Cancel(CANCELTYPE tyCancel) { Assert(m_pCopy != NULL); Assert(tyCancel != CT_INVALID);
m_pCopy->tyCancel = tyCancel;
return(S_OK); }
HRESULT CFolderSync::OnConnectionNotify(CONNNOTIFY nCode, LPVOID pvData, CConnectionManager *pConMan) { FOLDERINFO info; HRESULT hr;
Assert(m_pServer != NULL);
if (nCode == CONNNOTIFY_CONNECTED) { } else if (nCode == CONNNOTIFY_WORKOFFLINE && pvData) { m_pServer->Close(MSGSVRF_DROP_CONNECTION); } else if (nCode == CONNNOTIFY_DISCONNECTED) { hr = g_pStore->GetFolderInfo(m_idServer, &info); if (SUCCEEDED(hr)) { CHAR szAccountId[CCHMAX_ACCOUNT_NAME];
if (SUCCEEDED(GetFolderAccountId(&info, szAccountId, ARRAYSIZE(szAccountId)))) { if (!g_pConMan->CanConnect(szAccountId)) m_pServer->Close(MSGSVRF_DROP_CONNECTION); }
g_pStore->FreeRecord(&info); } }
return(S_OK); }
void CFolderSync::_FreeCopyInfo() { Assert(m_pCopy != NULL);
if (m_pCopy->pDest != NULL) m_pCopy->pDest->Release(); if (m_pCopy->pDestLocal != NULL) m_pCopy->pDestLocal->Release(); if (m_pCopy->pList != NULL) MemFree(m_pCopy->pList);
if (m_pCopy->pErrorInfo != NULL) { SafeMemFree(m_pCopy->pErrorInfo->pszProblem); SafeMemFree(m_pCopy->pErrorInfo->pszDetails); SafeMemFree(m_pCopy->pErrorInfo->pszAccount); SafeMemFree(m_pCopy->pErrorInfo->pszServer); SafeMemFree(m_pCopy->pErrorInfo->pszFolder); SafeMemFree(m_pCopy->pErrorInfo->pszUserName); SafeMemFree(m_pCopy->pErrorInfo->pszProtocol); MemFree(m_pCopy->pErrorInfo); }
MemFree(m_pCopy); m_pCopy = NULL; }
HRESULT AllBodiesDownloaded(IMessageFolder *pFldr, LPMESSAGEIDLIST pList) { MESSAGEINFO Message; DWORD iMsg; HRESULT hr;
Assert(pFldr != NULL); Assert(pList != NULL);
hr = S_OK;
for (iMsg = 0; iMsg < pList->cMsgs; iMsg++) { ZeroMemory(&Message, sizeof(MESSAGEINFO)); Message.idMessage = pList->prgidMsg[iMsg]; if (DB_S_FOUND == pFldr->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, NULL)) { if (0 == (Message.dwFlags & ARF_HASBODY)) hr = S_FALSE;
pFldr->FreeRecord(&Message); }
if (hr != S_OK) break; }
return(hr); }
//--------------------------------------------------------------------------
// CFolderSync::CopyMessages
//--------------------------------------------------------------------------
STDMETHODIMP CFolderSync::CopyMessages(IMessageFolder *pDest, COPYMESSAGEFLAGS dwOptions, LPMESSAGEIDLIST pList, LPADJUSTFLAGS pFlags, LPRESULTLIST pResults, IStoreCallback *pCallback) { HWND hwnd; DWORD dwOfflineSrc, dwOfflineDst; HRESULT hr; IMessageFolder *pDestLocal; ADJUSTFLAGS aflags; FOLDERID idDst, idServerDst; IServiceProvider *pService; FOLDERINFO fiSource = {0};
Assert(pDest != NULL); Assert(pList != NULL);
hr = pDest->GetFolderId(&idDst); if (FAILED(hr)) return(hr);
hr = GetFolderServerId(idDst, &idServerDst); if (FAILED(hr)) return(hr);
if (m_pServer == NULL) { dwOfflineSrc = CONN_STATE_CONNECTED; } else { hr = _Offline(&dwOfflineSrc); if (FAILED(hr)) return(hr); }
if (m_tyFolder == FOLDER_IMAP) { if (pFlags != NULL) { aflags = *pFlags; } else { aflags.dwAdd = 0; aflags.dwRemove = 0; } aflags.dwRemove |= ARF_ENDANGERED; pFlags = &aflags; }
if (m_idServer == idServerDst) { if (m_pServer == NULL) { hr = m_pFldr->CopyMessages(pDest, dwOptions, pList, pFlags, pResults, pCallback); } else if (dwOfflineSrc == CONN_STATE_NOT_CONNECTED) { hr = E_NOT_ONLINE; } else if (dwOfflineSrc == CONN_STATE_CONNECTED) { Assert(pCallback != NULL); if (pCallback == NULL) return(E_INVALIDARG);
hr = m_pServer->CopyMessages(pDest, dwOptions, pList, pFlags, pCallback); } else { Assert(dwOfflineSrc == CONN_STATE_OFFLINE);
pDestLocal = NULL;
hr = pDest->QueryInterface(IID_IServiceProvider, (void **)&pService); if (SUCCEEDED(hr)) { hr = pService->QueryService(SID_LocalMessageFolder, IID_IMessageFolder, (void **)&pDestLocal);
pService->Release(); }
if (FAILED(hr)) return(hr);
Assert(pDestLocal != NULL); hr = g_pSync->CopyMessages(m_pFldr, pDestLocal, dwOptions, pList, pFlags);
pDestLocal->Release(); }
return(hr); }
hr = _OfflineServer(idServerDst, &dwOfflineDst); if (FAILED(hr)) return(hr);
if (dwOfflineDst == CONN_STATE_NOT_CONNECTED) return(E_NOT_ONLINE);
Assert(m_pCopy == NULL); if (m_pCopy != NULL) return(E_FAIL);
if (dwOfflineSrc != CONN_STATE_CONNECTED) { hr = AllBodiesDownloaded(m_pFldr, pList); if (FAILED(hr)) { return(hr); } else if (hr == S_FALSE) { pCallback->GetParentWindow(0, &hwnd); AthMessageBoxW(hwnd, MAKEINTRESOURCEW(idsAthena), !!(COPY_MESSAGE_MOVE & dwOptions) ? MAKEINTRESOURCEW(idsCantMoveNotDownloaded) : MAKEINTRESOURCEW(idsCantCopyNotDownloaded), 0, MB_OK | MB_ICONEXCLAMATION);
return(E_NOT_ONLINE); } }
// Bug# 94639
// If the src and dest servers are not the same, and if the source is Hotmail Deleted Items folder
// disallow move.
// For Hotmail accounts Deleted Items folder is managed by the Hotmail servers.
// So the users cannot manually delete or move messages.
// Hence, disallow moving from Deleted Items.
// Instead ask if the user wants to copy the message
if ((m_idServer != idServerDst) && (FOLDER_HTTPMAIL == m_tyFolder)) { IF_FAILEXIT(hr = m_pLocalStore->GetFolderInfo(m_idFolder, &fiSource)); if (FOLDER_DELETED == fiSource.tySpecial) { if (IDNO == AthMessageBoxW(m_hwnd, MAKEINTRESOURCEW(idsAthena), MAKEINTRESOURCEW(idsHttpNoMoveCopy), 0, MB_YESNO)) { // Just return S_OK;
hr = S_OK; goto exit; } else { // Change the move flags to copy.
dwOptions = (dwOptions & ~COPY_MESSAGE_MOVE); } } }
if (!MemAlloc((void **)&m_pCopy, sizeof(COPYINFO))) return(E_OUTOFMEMORY);
ZeroMemory(m_pCopy, sizeof(COPYINFO)); m_pCopy->fSrcOffline = (dwOfflineSrc != CONN_STATE_CONNECTED); m_pCopy->fDestOffline = (dwOfflineDst != CONN_STATE_CONNECTED); m_pCopy->hr = S_OK; m_pCopy->pDest = pDest; pDest->AddRef(); m_pCopy->fMove = !!(COPY_MESSAGE_MOVE & dwOptions); hr = CloneMessageIDList(pList, &m_pCopy->pList); if (FAILED(hr)) { _FreeCopyInfo(); return(hr); }
if (m_pCopy->fDestOffline) { hr = m_pCopy->pDest->QueryInterface(IID_IServiceProvider, (void **)&pService); if (SUCCEEDED(hr)) { hr = pService->QueryService(SID_LocalMessageFolder, IID_IMessageFolder, (void **)&m_pCopy->pDestLocal);
pService->Release(); }
if (FAILED(hr)) { _FreeCopyInfo(); return(E_INVALIDARG); } } if (pFlags != NULL) m_pCopy->AdjustFlags = *pFlags;
m_pCopy->pCallback = pCallback;
hr = _CopyMessageState();
exit: m_pLocalStore->FreeRecord(&fiSource); return(hr); }
HRESULT CFolderSync::CopyOpen() { HRESULT hr; MESSAGEINFO mi = {0}; LPMESSAGEINFO pmi = NULL;
if (m_pCopy->iMsg >= m_pCopy->pList->cMsgs) { m_pCopy->state = COPY_STATE_DONE; return(S_OK); }
Assert(m_pCopy->pStream == NULL);
// initialize message info with the message id
mi.idMessage = m_pCopy->pList->prgidMsg[m_pCopy->iMsg]; // find the row
hr = GetMessageInfo(m_pFldr, m_pCopy->pList->prgidMsg[m_pCopy->iMsg], &mi); if (FAILED(hr)) goto exit; pmi = &mi; if (0 == mi.faStream || !!(mi.dwFlags & ARF_ARTICLE_EXPIRED)) { // if the server is NULL and we don't have a body stream,
// then something is wrong with the message. we skip the
// bad message and move onto the next one.
if (NULL == m_pServer || m_pCopy->fSrcOffline) { m_pCopy->state = COPY_STATE_DELETE2; hr = S_OK; } else if (!m_pCopy->fSrcRequested) { Assert(m_pServer != NULL); hr = m_pServer->GetMessage(m_pCopy->pList->prgidMsg[m_pCopy->iMsg], (IStoreCallback *)this); if (hr == E_PENDING) m_pCopy->fSrcRequested = TRUE; Assert(FAILED(hr)); } } else { hr = m_pFldr->OpenStream(ACCESS_READ, mi.faStream, &m_pCopy->pStream); if (FAILED(hr)) goto exit; m_pCopy->state = COPY_STATE_SAVE; } exit: if (NULL != pmi) m_pFldr->FreeRecord(pmi); return(hr); }
HRESULT CFolderSync::CopySave() { DWORD dwFlags; HRESULT hr; MESSAGEINFO Message; MESSAGEID id; IMimeMessage *pMessage = NULL; Assert(m_pCopy->pStream != NULL); dwFlags = 0; ZeroMemory(&Message, sizeof(MESSAGEINFO)); Message.idMessage = m_pCopy->pList->prgidMsg[m_pCopy->iMsg]; if (DB_S_FOUND == m_pFldr->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, NULL)) { dwFlags = Message.dwFlags; m_pFldr->FreeRecord(&Message); } if (m_pCopy->AdjustFlags.dwRemove != 0) dwFlags &= ~m_pCopy->AdjustFlags.dwRemove; if (m_pCopy->AdjustFlags.dwAdd != 0) dwFlags |= m_pCopy->AdjustFlags.dwAdd; // create a mime message
hr = m_pFldr->OpenMessage(m_pCopy->pList->prgidMsg[m_pCopy->iMsg], OPEN_MESSAGE_SECURE, &pMessage, NOSTORECALLBACK); if (FAILED(hr)) goto exit; if (m_pCopy->fDestOffline) { Assert(m_pCopy->pDestLocal != NULL); hr = g_pSync->CreateMessage(m_pCopy->pDestLocal, &id, SAVE_MESSAGE_GENID, dwFlags, m_pCopy->pStream, pMessage); } else { hr = m_pCopy->pDest->SaveMessage(&id, SAVE_MESSAGE_GENID, dwFlags, m_pCopy->pStream, pMessage, (IStoreCallback *)this); } if (hr == E_PENDING || SUCCEEDED(hr)) m_pCopy->state = COPY_STATE_SAVE2; exit: if (NULL != pMessage) pMessage->Release(); m_pCopy->pStream->Release(); m_pCopy->pStream = NULL; return(hr); }
HRESULT CFolderSync::CopySave2() { if (m_pCopy->fMove) { m_pCopy->state = COPY_STATE_DELETE; } else { m_pCopy->state = COPY_STATE_OPEN; m_pCopy->iMsg++; m_pCopy->fSrcRequested = FALSE;
m_pCopy->pCallback->OnProgress(SOT_COPYMOVE_MESSAGE, m_pCopy->iMsg, m_pCopy->pList->cMsgs, NULL); }
return(S_OK); }
HRESULT CFolderSync::CopyDelete() { MESSAGEIDLIST list; HRESULT hr; MESSAGEID id; ADJUSTFLAGS afFlags;
list.cMsgs = 1; id = m_pCopy->pList->prgidMsg[m_pCopy->iMsg]; list.prgidMsg = &id;
if (m_pServer != NULL) { if (m_pCopy->fSrcOffline) { if (m_fImap) { afFlags.dwAdd = ARF_ENDANGERED; afFlags.dwRemove = 0; hr = g_pSync->SetMessageFlags(m_pFldr, &list, &afFlags); } else { #ifdef DEAD
hr = g_pSync->DeleteMessages(m_pFldr, DELETE_MESSAGE_NOTRASHCAN, &list); #endif // DEAD
hr = S_OK; } } else { hr = m_pServer->DeleteMessages(DELETE_MESSAGE_NOTRASHCAN | DELETE_MESSAGE_NOPROMPT | DELETE_MESSAGE_MAYIGNORENOTRASH, &list, (IStoreCallback *)this); } } else { hr = m_pFldr->DeleteMessages(DELETE_MESSAGE_NOTRASHCAN | DELETE_MESSAGE_NOPROMPT, &list, NULL, NOSTORECALLBACK); }
if (hr == E_PENDING || SUCCEEDED(hr)) m_pCopy->state = COPY_STATE_DELETE2;
return(hr); }
HRESULT CFolderSync::CopyDelete2() { m_pCopy->state = COPY_STATE_OPEN; m_pCopy->iMsg++; m_pCopy->fSrcRequested = FALSE;
m_pCopy->pCallback->OnProgress(SOT_COPYMOVE_MESSAGE, m_pCopy->iMsg, m_pCopy->pList->cMsgs, NULL);
return(S_OK); }
HRESULT CFolderSync::_CopyMessageState() { BOOL fBegin; HRESULT hr; HWND hwnd; IStoreCallback *pCallback;
Assert(m_pCopy != NULL); Assert(m_pCopy->hr != S_FALSE); Assert(m_pCopy->hr != E_PENDING);
if (FAILED(m_pCopy->hr) && m_pCopy->state == COPY_STATE_OPEN && m_pCopy->fSrcRequested) { m_pCopy->fDownloadFail = TRUE; m_pCopy->hr = S_OK; m_pCopy->iMsg++; m_pCopy->fSrcRequested = FALSE; }
if (FAILED(m_pCopy->hr)) { hr = m_pCopy->hr; } else { while (TRUE) { if (m_pCopy->tyCancel != CT_INVALID) { m_pCopy->state = COPY_STATE_DONE; hr = S_FALSE; break; }
hr = (this->*(c_rgpfnCopyMsgs[m_pCopy->state]))();
if (FAILED(hr) || m_pCopy->state == COPY_STATE_DONE) break; } }
if (hr == E_PENDING) { Assert(m_pCopy->type == SOT_INVALID); m_pCopy->fAsync = TRUE; m_pCopy->hr = S_FALSE; } else if (FAILED(hr) || m_pCopy->state == COPY_STATE_DONE) { fBegin = m_pCopy->fBegin; #ifdef DEBUG
if (fBegin) Assert(m_pCopy->fAsync); #endif // DEBUG
pCallback = m_pCopy->pCallback;
if (fBegin) { if (m_pCopy->fDownloadFail && SUCCEEDED(hr)) { if (SUCCEEDED(pCallback->GetParentWindow(0, &hwnd))) { AthMessageBoxW(hwnd, MAKEINTRESOURCEW(idsAthena), m_pCopy->fMove ? MAKEINTRESOURCEW(idsMoveDownloadFail) : MAKEINTRESOURCEW(idsCopyDownloadFail), NULL, MB_ICONEXCLAMATION | MB_OK); } }
pCallback->OnComplete(SOT_COPYMOVE_MESSAGE, hr, NULL, m_pCopy->pErrorInfo); }
_FreeCopyInfo(); }
return(hr); }
LRESULT CALLBACK CFolderSync::FolderSyncWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HRESULT hr; CFolderSync *pThis;
pThis = (CFolderSync *)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (NULL == pThis && msg != WM_CREATE) return DefWindowProc(hwnd, msg, wParam, lParam);
switch(msg) { case WM_CREATE: Assert(pThis == NULL); pThis = (CFolderSync *)((CREATESTRUCT *)lParam)->lpCreateParams; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis); break;
case WM_USER: pThis->_CopyMessageState(); break; }
return(DefWindowProc(hwnd, msg, wParam, lParam)); }
//--------------------------------------------------------------------------
// CFolderSync::ConnectionAddRef
//
// Purpose: users of IMessageFolder need to keep alive the underlying server
// object if they plan of re-using the same server connection as
// another user of the same IMessageTable. eg. a note and a view window
// This allows us to not have to re-auth for operations that could
// be potentially expensive
//--------------------------------------------------------------------------
HRESULT CFolderSync::ConnectionAddRef() { if (m_pServer) m_pServer->ConnectionAddRef(); return S_OK; }
//--------------------------------------------------------------------------
// CFolderSync::ConnectionRelease
//
// Purpose: figure it out
//--------------------------------------------------------------------------
HRESULT CFolderSync::ConnectionRelease() { if (m_pServer) m_pServer->ConnectionRelease(); return S_OK; }
HRESULT CFolderSync::GetAdBarUrl(IStoreCallback *pCallback) { HRESULT hr = E_FAIL;
Assert(pCallback != NULL); if (pCallback == NULL) return(E_INVALIDARG);
if (m_pServer) hr = m_pServer->GetAdBarUrl(pCallback);
return(hr); }
|