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.
930 lines
26 KiB
930 lines
26 KiB
#include "pch.hxx"
|
|
#include "store.h"
|
|
#include "instance.h"
|
|
#include "storutil.h"
|
|
#include <conman.h>
|
|
#include <syncop.h>
|
|
#include <shared.h>
|
|
#include "enumsync.h"
|
|
#include "playback.h"
|
|
#include "sync.h"
|
|
|
|
COfflineSync *g_pSync = NULL;
|
|
|
|
//--------------------------------------------------------------------------
|
|
// COfflineSync::COfflineSync
|
|
//--------------------------------------------------------------------------
|
|
COfflineSync::COfflineSync(void)
|
|
{
|
|
m_cRef = 1;
|
|
m_pDB = NULL;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CFolderSync::~CFolderSync
|
|
//--------------------------------------------------------------------------
|
|
COfflineSync::~COfflineSync(void)
|
|
{
|
|
if (m_pDB != NULL)
|
|
m_pDB->Release();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CFolderSync::QueryInterface
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP COfflineSync::QueryInterface(REFIID riid, LPVOID *ppv)
|
|
{
|
|
if (IID_IUnknown == riid)
|
|
*ppv = (IUnknown *)this;
|
|
{
|
|
*ppv = NULL;
|
|
return(E_NOINTERFACE);
|
|
}
|
|
|
|
// AddRef It
|
|
((IUnknown *)*ppv)->AddRef();
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// COfflineSync::AddRef
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP_(ULONG) COfflineSync::AddRef(void)
|
|
{
|
|
return InterlockedIncrement(&m_cRef);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// COfflineSync::Release
|
|
//--------------------------------------------------------------------------
|
|
STDMETHODIMP_(ULONG) COfflineSync::Release(void)
|
|
{
|
|
LONG cRef = InterlockedDecrement(&m_cRef);
|
|
if (0 == cRef)
|
|
delete this;
|
|
return (ULONG)cRef;
|
|
}
|
|
|
|
HRESULT COfflineSync::_SetMessageFlags(IMessageFolder *pFolder, FOLDERID idServer, FOLDERID idFolder, MESSAGEID idMessage, MESSAGEFLAGS dwFlags, LPADJUSTFLAGS pFlags)
|
|
{
|
|
MESSAGEFLAGS dwAdd, dwRemove;
|
|
SYNCOPINFO info;
|
|
MESSAGEIDLIST list;
|
|
HRESULT hr;
|
|
|
|
Assert(pFolder != NULL);
|
|
Assert(pFlags != NULL);
|
|
Assert(0 != pFlags->dwAdd || 0 != pFlags->dwRemove);
|
|
|
|
// if no new flags are being added or only unset flags are being removed,
|
|
// we don't need to do anything
|
|
if ((dwFlags | pFlags->dwAdd) == dwFlags &&
|
|
(dwFlags & pFlags->dwRemove) == 0)
|
|
return(S_OK);
|
|
|
|
hr = _FindExistingOperation(idServer, idFolder, idMessage,
|
|
SYNC_CREATE_MSG | SYNC_SETPROP_MSG, SYNC_COPY_MSG | SYNC_MOVE_MSG, &info);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
if (info.tyOperation == SYNC_CREATE_MSG)
|
|
{
|
|
// this message has been created offline and will be uploaded to the
|
|
// server, so we can just set the flags in the message and they will
|
|
// get uploaded with the message
|
|
}
|
|
else
|
|
{
|
|
dwAdd = pFlags->dwAdd;
|
|
dwRemove = pFlags->dwRemove;
|
|
|
|
dwFlags = info.dwAdd & dwRemove;
|
|
if (dwFlags != 0)
|
|
{
|
|
FLAGCLEAR(info.dwAdd, dwFlags);
|
|
FLAGCLEAR(dwRemove, dwFlags);
|
|
}
|
|
|
|
dwFlags = info.dwRemove & dwAdd;
|
|
if (dwFlags != 0)
|
|
{
|
|
FLAGCLEAR(info.dwRemove, dwFlags);
|
|
FLAGCLEAR(dwAdd, dwFlags);
|
|
}
|
|
|
|
FLAGSET(info.dwAdd, dwAdd);
|
|
FLAGSET(info.dwRemove, dwRemove);
|
|
|
|
if (info.dwAdd == 0 && info.dwRemove == 0 && info.tyOperation == SYNC_SETPROP_MSG)
|
|
{
|
|
// no flags are being changed so we can get rid of this operation
|
|
hr = m_pDB->DeleteRecord(&info);
|
|
}
|
|
else
|
|
{
|
|
hr = m_pDB->UpdateRecord(&info);
|
|
}
|
|
}
|
|
|
|
m_pDB->FreeRecord(&info);
|
|
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
}
|
|
else
|
|
{
|
|
// no create or set prop operations exist for this message yet,
|
|
// so create a new one
|
|
|
|
ZeroMemory(&info, sizeof(SYNCOPINFO));
|
|
hr = m_pDB->GenerateId((LPDWORD)&info.idOperation);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
info.idServer = idServer;
|
|
info.idFolder = idFolder;
|
|
info.idMessage = idMessage;
|
|
info.tyOperation = SYNC_SETPROP_MSG;
|
|
// info.dwFlags
|
|
info.dwAdd = (~dwFlags & pFlags->dwAdd);
|
|
info.dwRemove = (dwFlags & pFlags->dwRemove);
|
|
|
|
hr = m_pDB->InsertRecord(&info);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
}
|
|
|
|
list.cAllocated = 0;
|
|
list.cMsgs = 1;
|
|
list.prgidMsg = &idMessage;
|
|
hr = pFolder->SetMessageFlags(&list, pFlags, NULL, NULL);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT COfflineSync::SetMessageFlags(IMessageFolder *pFolder, LPMESSAGEIDLIST pList, LPADJUSTFLAGS pFlags)
|
|
{
|
|
DWORD i;
|
|
FOLDERID idFolder, idServer;
|
|
MESSAGEINFO Message;
|
|
HRESULT hr;
|
|
HROWSET hRowset = NULL;
|
|
|
|
Assert(pFolder != NULL);
|
|
Assert(pFlags != NULL);
|
|
Assert(0 != pFlags->dwAdd || 0 != pFlags->dwRemove);
|
|
|
|
hr = pFolder->GetFolderId(&idFolder);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
|
|
hr = GetFolderServerId(idFolder, &idServer);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
|
|
if (NULL == pList)
|
|
{
|
|
hr = pFolder->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
}
|
|
|
|
for (i = 0; ; i++)
|
|
{
|
|
if (pList != NULL)
|
|
{
|
|
if (i >= pList->cMsgs)
|
|
break;
|
|
|
|
Message.idMessage = pList->prgidMsg[i];
|
|
|
|
hr = pFolder->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, NULL);
|
|
if (FAILED(hr))
|
|
break;
|
|
else if (hr != DB_S_FOUND)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
hr = pFolder->QueryRowset(hRowset, 1, (LPVOID *)&Message, NULL);
|
|
if (S_FALSE == hr)
|
|
{
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
else if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
hr = _SetMessageFlags(pFolder, idServer, idFolder, Message.idMessage, Message.dwFlags, pFlags);
|
|
|
|
pFolder->FreeRecord(&Message);
|
|
|
|
if (FAILED(hr))
|
|
break;
|
|
}
|
|
|
|
if (hRowset != NULL)
|
|
pFolder->CloseRowset(&hRowset);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT COfflineSync::DeleteMessages(IMessageFolder *pFolder, DELETEMESSAGEFLAGS dwFlags, LPMESSAGEIDLIST pList)
|
|
{
|
|
DWORD i;
|
|
ADJUSTFLAGS afFlags;
|
|
BOOL fNoOp, fImap;
|
|
FOLDERINFO Server;
|
|
FOLDERID idFolder, idServer;
|
|
MESSAGEID idMessage;
|
|
SYNCOPINFO info;
|
|
MESSAGEIDLIST list;
|
|
MESSAGEINFO Message;
|
|
HRESULT hr;
|
|
HROWSET hRowset = NULL;
|
|
|
|
// TODO: what about trashcan deletes????
|
|
|
|
Assert(pFolder != NULL);
|
|
|
|
hr = pFolder->GetFolderId(&idFolder);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
|
|
hr = GetFolderServer(idFolder, &Server);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
|
|
idServer = Server.idFolder;
|
|
fImap = (Server.tyFolder == FOLDER_IMAP);
|
|
|
|
g_pStore->FreeRecord(&Server);
|
|
|
|
if (fImap)
|
|
{
|
|
if (!!(dwFlags & DELETE_MESSAGE_UNDELETE))
|
|
{
|
|
afFlags.dwAdd = 0;
|
|
afFlags.dwRemove = ARF_ENDANGERED;
|
|
}
|
|
else
|
|
{
|
|
afFlags.dwAdd = ARF_ENDANGERED;
|
|
afFlags.dwRemove = 0;
|
|
}
|
|
|
|
return(SetMessageFlags(pFolder, pList, &afFlags));
|
|
}
|
|
|
|
Assert(0 == (dwFlags & DELETE_MESSAGE_UNDELETE));
|
|
|
|
if (NULL == pList)
|
|
{
|
|
hr = pFolder->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
}
|
|
|
|
list.cAllocated = 0;
|
|
list.cMsgs = 1;
|
|
list.prgidMsg = &idMessage;
|
|
|
|
afFlags.dwAdd = ARF_DELETED_OFFLINE;
|
|
afFlags.dwRemove = 0;
|
|
|
|
for (i = 0; ; i++)
|
|
{
|
|
if (pList != NULL)
|
|
{
|
|
if (i >= pList->cMsgs)
|
|
break;
|
|
|
|
idMessage = pList->prgidMsg[i];
|
|
}
|
|
else
|
|
{
|
|
hr = pFolder->QueryRowset(hRowset, 1, (LPVOID *)&Message, NULL);
|
|
if (S_FALSE == hr)
|
|
{
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
else if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
idMessage = Message.idMessage;
|
|
|
|
pFolder->FreeRecord(&Message);
|
|
}
|
|
|
|
hr = _FindExistingOperation(idServer, idFolder, idMessage,
|
|
SYNC_CREATE_MSG | SYNC_SETPROP_MSG, SYNC_COPY_MSG | SYNC_MOVE_MSG, &info);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
fNoOp = TRUE;
|
|
|
|
if (info.tyOperation == SYNC_CREATE_MSG || info.tyOperation == SYNC_COPY_MSG)
|
|
{
|
|
// we don't need to do this create or copy anymore because the message is being
|
|
// deleted. we don't need to do the delete either because the message
|
|
// has never existed on the server
|
|
|
|
hr = m_pDB->DeleteRecord(&info);
|
|
}
|
|
else if (info.tyOperation == SYNC_SETPROP_MSG)
|
|
{
|
|
// if it is a set prop operation, we don't need to do it anymore because the message
|
|
// is just getting deleted anyway
|
|
|
|
fNoOp = FALSE;
|
|
|
|
hr = m_pDB->DeleteRecord(&info);
|
|
}
|
|
else
|
|
{
|
|
Assert(info.tyOperation == SYNC_MOVE_MSG);
|
|
|
|
// convert it to a delete operation which is the same as moving it then deleting it
|
|
|
|
info.idFolderDest = 0;
|
|
info.idMessageDest = 0;
|
|
info.tyOperation = SYNC_DELETE_MSG;
|
|
info.dwAdd = 0;
|
|
info.dwRemove = 0;
|
|
info.dwFlags = dwFlags;
|
|
|
|
hr = m_pDB->UpdateRecord(&info);
|
|
}
|
|
|
|
m_pDB->FreeRecord(&info);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
else if (fNoOp)
|
|
{
|
|
hr = pFolder->DeleteMessages(dwFlags, &list, NULL, NULL);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// create the delete operation
|
|
|
|
ZeroMemory(&info, sizeof(SYNCOPINFO));
|
|
hr = m_pDB->GenerateId((LPDWORD)&info.idOperation);
|
|
if (FAILED(hr))
|
|
break;
|
|
info.idServer = idServer;
|
|
info.idFolder = idFolder;
|
|
info.idMessage = idMessage;
|
|
info.tyOperation = SYNC_DELETE_MSG;
|
|
info.dwFlags = dwFlags;
|
|
// info.dwAdd
|
|
// info.dwRemove
|
|
|
|
hr = m_pDB->InsertRecord(&info);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
hr = pFolder->SetMessageFlags(&list, &afFlags, NULL, NULL);
|
|
if (FAILED(hr))
|
|
break;
|
|
}
|
|
|
|
if (hRowset != NULL)
|
|
m_pDB->CloseRowset(&hRowset);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT COfflineSync::CreateMessage(IMessageFolder *pFolder,
|
|
LPMESSAGEID pidMessage,
|
|
SAVEMESSAGEFLAGS dwOptions,
|
|
MESSAGEFLAGS dwFlags,
|
|
IStream *pStream,
|
|
IMimeMessage *pMessage)
|
|
{
|
|
MESSAGEID idMessage;
|
|
HRESULT hr;
|
|
SYNCOPINFO info;
|
|
FOLDERID idFolder, idServer;
|
|
|
|
Assert(pFolder != NULL);
|
|
Assert(pMessage != NULL);
|
|
Assert(!!(dwOptions & SAVE_MESSAGE_GENID));
|
|
|
|
hr = pFolder->GetFolderId(&idFolder);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
|
|
hr = GetFolderServerId(idFolder, &idServer);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
|
|
ZeroMemory(&info, sizeof(SYNCOPINFO));
|
|
hr = m_pDB->GenerateId((LPDWORD)&info.idOperation);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
|
|
hr = pFolder->SaveMessage(&idMessage, SAVE_MESSAGE_GENID, dwFlags, pStream, pMessage, NULL);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
|
|
info.idServer = idServer;
|
|
info.idFolder = idFolder;
|
|
info.idMessage = idMessage;
|
|
info.tyOperation = SYNC_CREATE_MSG;
|
|
info.dwFlags = dwOptions;
|
|
// info.dwAdd
|
|
// info.dwRemove
|
|
|
|
hr = m_pDB->InsertRecord(&info);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT COfflineSync::CopyMessages(IMessageFolder *pFolder, IMessageFolder *pFolderDest, COPYMESSAGEFLAGS dwCopyFlags, LPMESSAGEIDLIST pList, LPADJUSTFLAGS pFlags)
|
|
{
|
|
DWORD i;
|
|
BOOL fMove, fImap, fCopyToMove;
|
|
FOLDERINFO Server;
|
|
MESSAGEFLAGS dwFlags;
|
|
ADJUSTFLAGS afFlags;
|
|
FOLDERID idFolder, idServer, idFolderDest;
|
|
MESSAGEID idMessage;
|
|
SYNCOPINFO info, infoT;
|
|
MESSAGEIDLIST list;
|
|
HRESULT hr;
|
|
IMimeMessage *pMsg;
|
|
HROWSET hRowset = NULL;
|
|
MESSAGEINFO Message = { 0 };
|
|
|
|
Assert(pFolder != NULL);
|
|
Assert(pFolderDest != NULL);
|
|
|
|
hr = pFolder->GetFolderId(&idFolder);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
|
|
hr = pFolderDest->GetFolderId(&idFolderDest);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
|
|
hr = GetFolderServer(idFolder, &Server);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
|
|
idServer = Server.idFolder;
|
|
fImap = (Server.tyFolder == FOLDER_IMAP);
|
|
|
|
g_pStore->FreeRecord(&Server);
|
|
|
|
#ifdef DEBUG
|
|
FOLDERID idServerDest;
|
|
|
|
Assert(SUCCEEDED(GetFolderServerId(idFolderDest, &idServerDest)));
|
|
Assert(SUCCEEDED(hr));
|
|
Assert(idServer == idServerDest);
|
|
#endif // DEBUG
|
|
|
|
if (NULL == pList)
|
|
{
|
|
hr = pFolder->CreateRowset(IINDEX_PRIMARY, NOFLAGS, &hRowset);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
}
|
|
|
|
fMove = !!(dwCopyFlags & COPY_MESSAGE_MOVE);
|
|
|
|
if (fMove)
|
|
{
|
|
list.cAllocated = 0;
|
|
list.cMsgs = 1;
|
|
list.prgidMsg = &idMessage;
|
|
|
|
if (fImap)
|
|
{
|
|
afFlags.dwAdd = ARF_ENDANGERED;
|
|
afFlags.dwRemove = 0;
|
|
}
|
|
else
|
|
{
|
|
afFlags.dwAdd = ARF_DELETED_OFFLINE;
|
|
afFlags.dwRemove = 0;
|
|
}
|
|
}
|
|
|
|
for (i = 0; ; i++)
|
|
{
|
|
if (pList != NULL)
|
|
{
|
|
if (i >= pList->cMsgs)
|
|
break;
|
|
|
|
Message.idMessage = pList->prgidMsg[i];
|
|
|
|
hr = pFolder->FindRecord(IINDEX_PRIMARY, COLUMNS_ALL, &Message, NULL);
|
|
if (FAILED(hr))
|
|
break;
|
|
else if (hr != DB_S_FOUND)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
hr = pFolder->QueryRowset(hRowset, 1, (LPVOID *)&Message, NULL);
|
|
if (S_FALSE == hr)
|
|
{
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
else if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
idMessage = Message.idMessage;
|
|
dwFlags = Message.dwFlags;
|
|
|
|
if (pFlags != NULL)
|
|
{
|
|
if (pFlags->dwRemove != 0)
|
|
Message.dwFlags &= ~pFlags->dwRemove;
|
|
if (pFlags->dwAdd != 0)
|
|
Message.dwFlags |= pFlags->dwAdd;
|
|
}
|
|
|
|
pMsg = NULL;
|
|
hr = pFolder->OpenMessage(idMessage, NOFLAGS, &pMsg, NULL);
|
|
if (hr == STORE_E_NOBODY)
|
|
{
|
|
// just create the header in the destination folder
|
|
Assert(pMsg == NULL);
|
|
|
|
hr = pFolderDest->GenerateId((LPDWORD)&Message.idMessage);
|
|
if (SUCCEEDED(hr))
|
|
hr = pFolderDest->InsertRecord(&Message);
|
|
}
|
|
else if (SUCCEEDED(hr))
|
|
{
|
|
// create the whole message in the destination folder
|
|
Assert(pMsg != NULL);
|
|
|
|
hr = pFolderDest->SaveMessage(&Message.idMessage, SAVE_MESSAGE_GENID, Message.dwFlags, 0, pMsg, NULL);
|
|
|
|
pMsg->Release();
|
|
}
|
|
else
|
|
{
|
|
Assert(pMsg == NULL);
|
|
}
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
hr = _FindExistingOperation(idServer, idFolder, idMessage,
|
|
SYNC_CREATE_MSG, SYNC_COPY_MSG | SYNC_MOVE_MSG, &info);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
if (fMove)
|
|
{
|
|
// delete source msg because we're moving a msg which doesn't exist on the server
|
|
// and then move the previous operation to the destination folder
|
|
|
|
hr = pFolder->DeleteMessages(DELETE_MESSAGE_NOTRASHCAN | DELETE_MESSAGE_NOPROMPT, &list, NULL, NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (info.tyOperation == SYNC_CREATE_MSG)
|
|
{
|
|
info.idFolder = idFolderDest;
|
|
info.idMessage = Message.idMessage;
|
|
}
|
|
else
|
|
{
|
|
info.idFolderDest = idFolderDest;
|
|
info.idMessageDest = Message.idMessage;
|
|
}
|
|
|
|
hr = m_pDB->UpdateRecord(&info);
|
|
|
|
// foo
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (info.tyOperation == SYNC_CREATE_MSG)
|
|
{
|
|
// we can't copy this message because it doesn't exist on the server,
|
|
// so we'll add another create operation for the destination message
|
|
|
|
ZeroMemory(&infoT, sizeof(SYNCOPINFO));
|
|
hr = m_pDB->GenerateId((LPDWORD)&infoT.idOperation);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
infoT.idServer = idServer;
|
|
infoT.idFolder = idFolderDest;
|
|
infoT.idMessage = Message.idMessage;
|
|
infoT.tyOperation = SYNC_CREATE_MSG;
|
|
// infoT.dwFlags
|
|
// info.dwAdd
|
|
// info.dwRemove
|
|
|
|
hr = m_pDB->InsertRecord(&infoT);
|
|
}
|
|
}
|
|
else if (info.tyOperation == SYNC_COPY_MSG)
|
|
{
|
|
fCopyToMove = FALSE;
|
|
|
|
// if there is an earlier operation that will result in the source msg
|
|
// being deleted, then we'll find that and remove it (delete) or change
|
|
// it (move), and then we'll do a move instead of a copy
|
|
|
|
hr = _FindExistingOperation(info.idServer, info.idFolder, info.idMessage,
|
|
SYNC_DELETE_MSG | SYNC_MOVE_MSG, 0, &infoT);
|
|
if (hr == S_OK)
|
|
{
|
|
if (infoT.tyOperation == SYNC_DELETE_MSG)
|
|
{
|
|
hr = m_pDB->DeleteRecord(&infoT);
|
|
}
|
|
else
|
|
{
|
|
Assert(infoT.tyOperation == SYNC_MOVE_MSG);
|
|
|
|
infoT.tyOperation = SYNC_COPY_MSG;
|
|
|
|
hr = m_pDB->UpdateRecord(&infoT);
|
|
}
|
|
|
|
fCopyToMove = TRUE;
|
|
|
|
m_pDB->FreeRecord(&infoT);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pDB->GenerateId((LPDWORD)&info.idOperation);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
info.idFolderDest = idFolderDest;
|
|
info.idMessageDest = Message.idMessage;
|
|
if (fCopyToMove)
|
|
info.tyOperation = SYNC_MOVE_MSG;
|
|
|
|
hr = m_pDB->InsertRecord(&info);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Assert(info.tyOperation == SYNC_MOVE_MSG);
|
|
|
|
// instead of doing a move then a copy which wouldn't work so good,
|
|
// we'll have the current copy become a move and the earlier move will become a copy
|
|
|
|
infoT = info;
|
|
hr = m_pDB->GenerateId((LPDWORD)&infoT.idOperation);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
infoT.idFolderDest = idFolderDest;
|
|
infoT.idMessageDest = Message.idMessage;
|
|
|
|
hr = m_pDB->InsertRecord(&infoT);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
info.tyOperation = SYNC_COPY_MSG;
|
|
|
|
hr = m_pDB->UpdateRecord(&info);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
m_pDB->FreeRecord(&info);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
pFolder->FreeRecord(&Message);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
ZeroMemory(&info, sizeof(SYNCOPINFO));
|
|
hr = m_pDB->GenerateId((LPDWORD)&info.idOperation);
|
|
if (FAILED(hr))
|
|
break;
|
|
info.idServer = idServer;
|
|
info.idFolder = idFolder;
|
|
info.idMessage = idMessage;
|
|
info.tyOperation = (fMove && !fImap) ? SYNC_MOVE_MSG : SYNC_COPY_MSG;
|
|
// info.dwFlags
|
|
if (pFlags != NULL)
|
|
{
|
|
info.dwAdd = (~dwFlags & pFlags->dwAdd);
|
|
info.dwRemove = (dwFlags & pFlags->dwRemove);
|
|
}
|
|
info.idFolderDest = idFolderDest;
|
|
info.idMessageDest = Message.idMessage;
|
|
|
|
hr = m_pDB->InsertRecord(&info);
|
|
// TODO: if this fails, we should probably blow away the new msg we just created...
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
if (fMove)
|
|
{
|
|
if (fImap)
|
|
{
|
|
// put this msg in the endangered species list
|
|
hr = _SetMessageFlags(pFolder, idServer, idFolder, idMessage, dwFlags, &afFlags);
|
|
if (FAILED(hr))
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// hide this msg because it is now deleted
|
|
pFolder->SetMessageFlags(&list, &afFlags, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
pFolder->FreeRecord(&Message);
|
|
}
|
|
|
|
pFolder->FreeRecord(&Message);
|
|
|
|
if (hRowset != NULL)
|
|
pFolder->CloseRowset(&hRowset);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT COfflineSync::Initialize()
|
|
{
|
|
HRESULT hr;
|
|
BOOL fReset;
|
|
TABLEINDEX Index;
|
|
CHAR szDirectory[MAX_PATH];
|
|
CHAR szFilePath[MAX_PATH];
|
|
SYNCOPUSERDATA UserData={0};
|
|
|
|
Assert(g_pStore != NULL);
|
|
|
|
hr = g_pStore->GetDirectory(szDirectory, ARRAYSIZE(szDirectory));
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
|
|
hr = MakeFilePath(szDirectory, c_szOfflineFile, c_szEmpty, szFilePath, ARRAYSIZE(szFilePath));
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
|
|
hr = g_pDBSession->OpenDatabase(szFilePath, NOFLAGS, &g_SyncOpTableSchema, NULL, &m_pDB);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
|
|
fReset = FALSE;
|
|
|
|
// Create the idServer / idFolder Index
|
|
if (FAILED(m_pDB->GetIndexInfo(IINDEX_ALL, NULL, &Index)))
|
|
fReset = TRUE;
|
|
|
|
// If still noreset, see of indexes are the same
|
|
else if (S_FALSE == CompareTableIndexes(&Index, &g_OpFolderIdIndex))
|
|
fReset = TRUE;
|
|
|
|
// Change the Index
|
|
if (fReset)
|
|
{
|
|
// Create the idParent / FolderName Index
|
|
hr = m_pDB->ModifyIndex(IINDEX_ALL, NULL, &g_OpFolderIdIndex);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
}
|
|
|
|
hr = m_pDB->GetUserData(&UserData, sizeof(SYNCOPUSERDATA));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (!UserData.fInitialized)
|
|
{
|
|
UserData.fInitialized = TRUE;
|
|
|
|
hr = m_pDB->SetUserData(&UserData, sizeof(SYNCOPUSERDATA));
|
|
}
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT COfflineSync::DoPlayback(HWND hwnd, FOLDERID *pId, DWORD cId, FOLDERID idFolderSel)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cOps;
|
|
COfflinePlayback *pPlayback;
|
|
|
|
Assert(pId != NULL);
|
|
Assert(cId > 0);
|
|
|
|
hr = m_pDB->GetRecordCount(IINDEX_PRIMARY, &cOps);
|
|
if (SUCCEEDED(hr) && cOps > 0)
|
|
{
|
|
pPlayback = new COfflinePlayback;
|
|
if (pPlayback == NULL)
|
|
return(E_OUTOFMEMORY);
|
|
|
|
hr = pPlayback->DoPlayback(hwnd, m_pDB, pId, cId, idFolderSel);
|
|
|
|
pPlayback->Release();
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT COfflineSync::_FindExistingOperation(FOLDERID idServer, FOLDERID idFolder, MESSAGEID idMessage,
|
|
DWORD dwFlagsSrc, DWORD dwFlagsDest, LPSYNCOPINFO pInfo)
|
|
{
|
|
ROWORDINAL iRow;
|
|
HRESULT hr;
|
|
HROWSET hRowset = NULL;
|
|
|
|
Assert(pInfo != NULL);
|
|
Assert(dwFlagsSrc != 0 || dwFlagsDest != 0);
|
|
|
|
ZeroMemory(pInfo, sizeof(SYNCOPINFO));
|
|
pInfo->idServer = idServer;
|
|
hr = m_pDB->FindRecord(IINDEX_ALL, 1, pInfo, &iRow);
|
|
if (hr != DB_S_FOUND)
|
|
return(S_FALSE);
|
|
m_pDB->FreeRecord(pInfo);
|
|
|
|
hr = m_pDB->CreateRowset(IINDEX_ALL, NOFLAGS, &hRowset);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
|
|
hr = m_pDB->SeekRowset(hRowset, SEEK_ROWSET_BEGIN, iRow - 1, NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
while (TRUE)
|
|
{
|
|
hr = m_pDB->QueryRowset(hRowset, 1, (LPVOID *)pInfo, NULL);
|
|
if (S_FALSE == hr)
|
|
{
|
|
break;
|
|
}
|
|
else if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (pInfo->idServer != idServer)
|
|
{
|
|
hr = S_FALSE;
|
|
m_pDB->FreeRecord(pInfo);
|
|
break;
|
|
}
|
|
|
|
if (dwFlagsSrc != 0)
|
|
{
|
|
if (pInfo->idFolder == idFolder && pInfo->idMessage == idMessage)
|
|
{
|
|
if (!!(dwFlagsSrc & pInfo->tyOperation))
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (dwFlagsDest != 0)
|
|
{
|
|
if (pInfo->idFolderDest == idFolder && pInfo->idMessageDest == idMessage)
|
|
{
|
|
if (!!(dwFlagsDest & pInfo->tyOperation))
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_pDB->FreeRecord(pInfo);
|
|
}
|
|
}
|
|
|
|
m_pDB->CloseRowset(&hRowset);
|
|
|
|
return(hr);
|
|
}
|