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.
 
 
 
 
 
 

1191 lines
34 KiB

/*
* s e r v e r q . c p p
*
* Purpose:
* Implements IMessageServer wrapper for queuing operations to
* IMessageServer object.
*
* This object knows how to pack stack data for an IMessageServer method
* call into a queue so that the call can be reissued when the server is
* free. It maintains a small listen window so that is can post async
* messages to itself to allow completion of the next task
*
* Owner:
* brettm.
*
* History:
* June 1998: Created
*
* Copyright (C) Microsoft Corp. 1993, 1994.
*/
#include "pch.hxx"
#include "instance.h"
#include "storutil.h"
#include "serverq.h"
static TCHAR c_szServerQListenWnd[] = "OE ServerQWnd";
#define SQW_NEXTTASK (WM_USER + 1)
/*
* Notes:
*
* the queueing is a little odd. For every method, we immediatley throw it in
* the queue. If the server is not busy then we post a message to ourselves to
* dequeue the task. We do this as if we passed-thru when the server is not busy
* then we never get to hook the IStoreCallback to watch for completion, so we
* never know when to queue the next task.
*
*/
CServerQ::CServerQ()
{
m_cRef = 1;
m_pServer = NULL;
m_pTaskQueue = NULL;
m_pLastQueueTask = NULL;
m_hwnd = NULL;
m_pCurrentCallback = NULL;
m_pCurrentTask = NULL;
m_cRefConnection = 0;
#ifdef DEBUG
m_DBG_pArgDataLast = 0;
#endif
}
CServerQ::~CServerQ()
{
SafeRelease(m_pServer);
_Flush(TRUE);
SendMessage(m_hwnd, WM_CLOSE, 0, 0);
}
// IUnknown Members
HRESULT CServerQ::QueryInterface(REFIID iid, LPVOID *ppvObject)
{
HRESULT hr=E_NOINTERFACE;
TraceCall("CServerQ::QueryInterface");
if (ppvObject == NULL)
return TraceResult(E_INVALIDARG);
*ppvObject = NULL;
// Find a ptr to the interface
if (IID_IUnknown == iid)
*ppvObject = (IMessageServer *)this;
else if (IID_IMessageServer == iid)
*ppvObject = (IMessageServer *)this;
else if (IID_IServiceProvider == iid)
*ppvObject = (IServiceProvider *)this;
else if (IID_IStoreCallback == iid)
*ppvObject = (IStoreCallback *)this;
if (*ppvObject)
{
hr = S_OK;
AddRef();
}
return hr;
}
ULONG CServerQ::AddRef()
{
return ++m_cRef;
}
ULONG CServerQ::Release()
{
if (--m_cRef == 0)
{
delete this;
return 0;
}
return m_cRef;
}
HRESULT CServerQ::Init(IMessageServer *pServerInner)
{
HRESULT hr=S_OK;
WNDCLASS wc;
TraceCall("CServerQ::Init");
Assert(m_pServer == NULL);
if (!pServerInner)
return TraceResult(E_INVALIDARG);
if (!GetClassInfo(g_hInst, c_szServerQListenWnd, &wc))
{
ZeroMemory(&wc, sizeof(WNDCLASS));
wc.lpfnWndProc = CServerQ::ExtWndProc;
wc.hInstance = g_hInst;
wc.hCursor = NULL;
wc.lpszClassName = c_szServerQListenWnd;
wc.hbrBackground = NULL;
wc.style = 0;
if (!RegisterClass(&wc))
return TraceResult(E_OUTOFMEMORY);
}
m_hwnd=CreateWindow(c_szServerQListenWnd,
NULL, WS_OVERLAPPED,
0, 0, 0, 0,
NULL, NULL,
g_hInst,
(LPVOID)this);
if (!m_hwnd)
return TraceResult(E_OUTOFMEMORY);
#ifdef DEBUG
// debug timer
SetTimer(m_hwnd, 0, 10000, NULL);
#endif
ReplaceInterface(m_pServer, pServerInner);
return S_OK;
}
// IMessageServer Methods
HRESULT CServerQ::Initialize(IMessageStore *pStore, FOLDERID idStoreRoot, IMessageFolder *pFolder, FOLDERID idFolder)
{
return m_pServer->Initialize(pStore, idStoreRoot, pFolder, idFolder);
}
HRESULT CServerQ::ResetFolder(IMessageFolder *pFolder, FOLDERID idFolder)
{
return m_pServer->ResetFolder(pFolder, idFolder);
}
HRESULT CServerQ::SetIdleCallback(IStoreCallback *pDefaultCallback)
{
return m_pServer->SetIdleCallback(pDefaultCallback);
}
HRESULT CServerQ::SynchronizeFolder(SYNCFOLDERFLAGS dwFlags, DWORD cHeaders, IStoreCallback *pCallback)
{
ARGUMENT_DATA *pArg;
HRESULT hr;
hr = _AddToQueue(SOT_SYNC_FOLDER, pCallback, NULL, NULL, NULL, &pArg);
if (!FAILED(hr))
{
pArg->dwSyncFlags = dwFlags;
pArg->cHeaders = cHeaders;
return E_PENDING;
}
return TraceResult(hr);
}
HRESULT CServerQ::GetMessage(MESSAGEID idMessage, IStoreCallback *pCallback)
{
ARGUMENT_DATA *pArg,
*pTask;
HRESULT hr;
STOREOPERATIONINFO soi;
BOOL fFound=FALSE;
ULONG l;
// if we have tasks in our queue, looks for a get message task with the same id
// if we find one, add this callback to the list and return STORE_S_ALREADYPENDING
// to indicate that the pStream passed in will NOT be written to, and the caller
// should get the message from the cache when his complete is called
// make sure that the current task's next ptr points to the task queue
// also make sure there is no task queue if there are no pending tasks
Assert (m_pCurrentTask == NULL && m_pTaskQueue==NULL ||
m_pCurrentTask->pNext == m_pTaskQueue);
pTask = m_pCurrentTask;
// let's look for a pending request for this message
while (pTask)
{
if (pTask->sot == SOT_GET_MESSAGE && pTask->idMessage == idMessage)
{
if (pCallback)
{
// cruise thro' the callback list. If this IStoreCallback is already
// registered for this message, then don't add it otherwise it will
// get multiple notifications
if (pTask->pCallback == pCallback)
fFound = TRUE;
else
for (l = 0; l < pTask->cOtherCallbacks; l++)
if (pTask->rgpOtherCallback[l] == pCallback)
{
fFound = TRUE;
break;
}
if (!fFound)
{
if (!MemRealloc((LPVOID *)&pTask->rgpOtherCallback,
sizeof(IStoreCallback *) * (pTask->cOtherCallbacks+1)))
{
hr = TraceResult(E_OUTOFMEMORY);
goto exit;
}
pTask->rgpOtherCallback[pTask->cOtherCallbacks++] = pCallback;
pCallback->AddRef();
if (pTask == m_pCurrentTask)
{
// if this task if the current task, then the OnBegin call has already
// been called. We fake the OnBegin to provide message id on get message start
soi.cbSize = sizeof(STOREOPERATIONINFO);
soi.idMessage = idMessage;
pCallback->OnBegin(SOT_GET_MESSAGE, &soi, NULL);
}
}
}
hr = STORE_S_ALREADYPENDING;
goto exit; // found
}
pTask = pTask->pNext;
}
// not already queued, let's add it.
hr = _AddToQueue(SOT_GET_MESSAGE, pCallback, NULL, NULL, NULL, &pArg);
if (!FAILED(hr))
{
pArg->idMessage = idMessage;
return E_PENDING;
}
exit:
return hr;
}
HRESULT CServerQ::PutMessage(FOLDERID idFolder, MESSAGEFLAGS dwFlags, LPFILETIME pftReceived, IStream *pStream, IStoreCallback *pCallback)
{
ARGUMENT_DATA *pArg;
HRESULT hr;
hr = _AddToQueue(SOT_PUT_MESSAGE, pCallback, NULL, NULL, NULL, &pArg);
if (!FAILED(hr))
{
pArg->idFolder = idFolder;
pArg->dwMsgFlags = dwFlags;
if (NULL != pftReceived)
{
pArg->ftReceived = *pftReceived;
pArg->pftReceived = &pArg->ftReceived;
}
else
pArg->pftReceived = NULL;
if (NULL != pStream)
{
pArg->pPutStream = pStream;
pStream->AddRef();
}
return E_PENDING;
}
return TraceResult(hr);
}
HRESULT CServerQ::CopyMessages(IMessageFolder *pDestFldr, COPYMESSAGEFLAGS dwOptions,
LPMESSAGEIDLIST pList, LPADJUSTFLAGS pFlags, IStoreCallback *pCallback)
{
ARGUMENT_DATA *pArg;
HRESULT hr;
hr = _AddToQueue(SOT_COPYMOVE_MESSAGE, pCallback, pList, pFlags, NULL, &pArg);
if (!FAILED(hr))
{
if (pArg->pDestFldr = pDestFldr)
pDestFldr->AddRef();
pArg->dwCopyOptions = dwOptions;
return E_PENDING;
}
return TraceResult(hr);
}
HRESULT CServerQ::DeleteMessages(DELETEMESSAGEFLAGS dwOptions, LPMESSAGEIDLIST pList,
IStoreCallback *pCallback)
{
ARGUMENT_DATA *pArg;
HRESULT hr;
Assert(NULL == pList || pList->cMsgs > 0);
hr = _AddToQueue(SOT_DELETING_MESSAGES, pCallback, pList, NULL, NULL, &pArg);
if (!FAILED(hr))
{
pArg->dwDeleteOptions = dwOptions;
return E_PENDING;
}
return TraceResult(hr);
}
HRESULT CServerQ::SetMessageFlags(LPMESSAGEIDLIST pList, LPADJUSTFLAGS pFlags, SETMESSAGEFLAGSFLAGS dwFlags,
IStoreCallback *pCallback)
{
ARGUMENT_DATA *pArg=0;
HRESULT hr;
Assert(NULL == pList || pList->cMsgs > 0);
hr = _AddToQueue(SOT_SET_MESSAGEFLAGS, pCallback, pList, pFlags, NULL, &pArg);
if (!FAILED(hr))
{
pArg->dwSetFlags = dwFlags;
return E_PENDING;
}
return TraceResult(hr);
}
HRESULT CServerQ::SynchronizeStore(FOLDERID idParent, DWORD dwFlags, IStoreCallback *pCallback)
{
ARGUMENT_DATA *pArg;
HRESULT hr;
hr = _AddToQueue(SOT_SYNCING_STORE, pCallback, NULL, NULL, NULL, &pArg);
if (!FAILED(hr))
{
pArg->idParent = idParent;
pArg->dwFlags = dwFlags;
return E_PENDING;
}
return TraceResult(hr);
}
HRESULT CServerQ::CreateFolder(FOLDERID idParent, SPECIALFOLDER tySpecial, LPCSTR pszName, FLDRFLAGS dwFlags, IStoreCallback *pCallback)
{
ARGUMENT_DATA *pArg=0;
HRESULT hr;
hr = _AddToQueue(SOT_CREATE_FOLDER, pCallback, NULL, NULL, pszName, &pArg);
if (!FAILED(hr))
{
pArg->idParent = idParent;
pArg->tySpecial = tySpecial;
pArg->dwFldrFlags = dwFlags;
return E_PENDING;
}
return TraceResult(hr);
}
HRESULT CServerQ::MoveFolder(FOLDERID idFolder, FOLDERID idParentNew, IStoreCallback *pCallback)
{
ARGUMENT_DATA *pArg;
HRESULT hr;
hr = _AddToQueue(SOT_MOVE_FOLDER, pCallback, NULL, NULL, NULL, &pArg);
if (!FAILED(hr))
{
pArg->idFolder = idFolder;
pArg->idParentNew = idParentNew;
return E_PENDING;
}
return TraceResult(hr);
}
HRESULT CServerQ::RenameFolder(FOLDERID idFolder, LPCSTR pszName, IStoreCallback *pCallback)
{
ARGUMENT_DATA *pArg=0;
HRESULT hr;
hr = _AddToQueue(SOT_RENAME_FOLDER, pCallback, NULL, NULL, pszName, &pArg);
if (!FAILED(hr))
{
pArg->idFolder = idFolder;
return E_PENDING;
}
return TraceResult(hr);
}
HRESULT CServerQ::DeleteFolder(FOLDERID idFolder, DELETEFOLDERFLAGS dwFlags, IStoreCallback *pCallback)
{
ARGUMENT_DATA *pArg;
HRESULT hr;
hr = _AddToQueue(SOT_DELETE_FOLDER, pCallback, NULL, NULL, NULL, &pArg);
if (!FAILED(hr))
{
pArg->idFolder = idFolder;
pArg->dwDelFldrFlags = dwFlags;
return E_PENDING;
}
return TraceResult(hr);
}
HRESULT CServerQ::SubscribeToFolder(FOLDERID idFolder, BOOL fSubscribe, IStoreCallback *pCallback)
{
ARGUMENT_DATA *pArg;
HRESULT hr;
hr = _AddToQueue(SOT_SUBSCRIBE_FOLDER, pCallback, NULL, NULL, NULL, &pArg);
if (!FAILED(hr))
{
pArg->idFolder = idFolder;
pArg->fSubscribe = fSubscribe;
return E_PENDING;
}
return TraceResult(hr);
}
HRESULT CServerQ::GetFolderCounts(FOLDERID idFolder, IStoreCallback *pCallback)
{
ARGUMENT_DATA *pArg;
HRESULT hr;
hr = _AddToQueue(SOT_UPDATE_FOLDER, pCallback, NULL, NULL, NULL, &pArg);
if (!FAILED(hr))
{
pArg->idFolder = idFolder;
return E_PENDING;
}
return TraceResult(hr);
}
HRESULT CServerQ::GetNewGroups(LPSYSTEMTIME pSysTime, IStoreCallback *pCallback)
{
ARGUMENT_DATA *pArg;
HRESULT hr;
Assert(pSysTime != NULL);
hr = _AddToQueue(SOT_GET_NEW_GROUPS, pCallback, NULL, NULL, NULL, &pArg);
if (!FAILED(hr))
{
pArg->sysTime = *pSysTime;
return E_PENDING;
}
return TraceResult(hr);
}
HRESULT CServerQ::GetWatchedInfo(FOLDERID idFolder, IStoreCallback *pCallback)
{
ARGUMENT_DATA *pArg;
HRESULT hr;
hr = _AddToQueue(SOT_GET_WATCH_INFO, pCallback, NULL, NULL, NULL, &pArg);
if (!FAILED(hr))
{
pArg->idFolder = idFolder;
return (E_PENDING);
}
return TraceResult(hr);
}
HRESULT CServerQ::Close(DWORD dwFlags)
{
HRESULT hr=S_OK;
if (m_cRefConnection != 0 && dwFlags & MSGSVRF_HANDS_OFF_SERVER)
{
// some-body else has a connection ref on the server object.
// let's ignore the hands-off close
return STORE_S_IN_USE;
}
if (m_pServer)
hr = m_pServer->Close(dwFlags);
// make sure we flush any pending ops
_Flush(FALSE);
return hr;
}
HRESULT CServerQ::OnBegin(STOREOPERATIONTYPE tyOperation, STOREOPERATIONINFO *pOpInfo, IOperationCancel *pCancel)
{
TraceInfoTag(TAG_SERVERQ, _MSG("CServerQ::OnBegin[sot='%s', pTask->sot='%s']", sotToSz(tyOperation), sotToSz(m_pCurrentTask->sot)));
IxpAssert (m_pCurrentTask);
if (m_pCurrentTask &&
m_pCurrentTask->sot == SOT_GET_MESSAGE)
{
// multiplex
for (ULONG ul=0; ul < m_pCurrentTask->cOtherCallbacks; ul++)
m_pCurrentTask->rgpOtherCallback[ul]->OnBegin(tyOperation, pOpInfo, pCancel);
}
return m_pCurrentCallback ? m_pCurrentCallback->OnBegin(tyOperation, pOpInfo, pCancel) : S_OK;
}
HRESULT CServerQ::OnProgress(STOREOPERATIONTYPE tyOperation, DWORD dwCurrent, DWORD dwMax, LPCSTR pszStatus)
{
IxpAssert (m_pCurrentTask);
if (m_pCurrentTask &&
m_pCurrentTask->sot == SOT_GET_MESSAGE)
{
// multiplex
for (ULONG ul=0; ul < m_pCurrentTask->cOtherCallbacks; ul++)
m_pCurrentTask->rgpOtherCallback[ul]->OnProgress(tyOperation, dwCurrent, dwMax, pszStatus);
}
return m_pCurrentCallback ? m_pCurrentCallback->OnProgress(tyOperation, dwCurrent, dwMax, pszStatus) : S_OK;
}
HRESULT CServerQ::OnTimeout(LPINETSERVER pServer, LPDWORD pdwTimeout, IXPTYPE ixpServerType)
{
return m_pCurrentCallback ? m_pCurrentCallback->OnTimeout(pServer, pdwTimeout, ixpServerType) : S_OK;
}
HRESULT CServerQ::CanConnect(LPCSTR pszAccountId, DWORD dwFlags)
{
return m_pCurrentCallback ? m_pCurrentCallback->CanConnect(pszAccountId, dwFlags) : S_OK;
}
HRESULT CServerQ::OnLogonPrompt(LPINETSERVER pServer, IXPTYPE ixpServerType)
{
return m_pCurrentCallback ? m_pCurrentCallback->OnLogonPrompt(pServer, ixpServerType) : S_OK;
}
HRESULT CServerQ::OnComplete(STOREOPERATIONTYPE tyOperation, HRESULT hrComplete, LPSTOREOPERATIONINFO pOpInfo, LPSTOREERROR pErrorInfo)
{
HRESULT hr;
TraceInfoTag(TAG_SERVERQ,_MSG("CServerQ::OnComplete[sot='%s', pTask->sot='%s']", sotToSz(tyOperation), sotToSz(m_pCurrentTask ? m_pCurrentTask->sot : SOT_INVALID)));
IxpAssert (m_pCurrentTask);
// multiplex
if (m_pCurrentTask &&
m_pCurrentTask->sot == SOT_GET_MESSAGE)
{
for (ULONG ul=0; ul < m_pCurrentTask->cOtherCallbacks; ul++)
m_pCurrentTask->rgpOtherCallback[ul]->OnComplete(tyOperation, hrComplete, pOpInfo, pErrorInfo);
}
if ((hrComplete == HR_E_OFFLINE) || (hrComplete == HR_E_USER_CANCEL_CONNECT))
{
switch (m_pCurrentTask->sot)
{
case SOT_CREATE_FOLDER:
hrComplete = HR_E_OFFLINE_FOLDER_CREATE;
break;
case SOT_MOVE_FOLDER:
hrComplete = HR_E_OFFLINE_FOLDER_MOVE;
break;
case SOT_DELETE_FOLDER:
hrComplete = HR_E_OFFLINE_FOLDER_DELETE;
break;
case SOT_RENAME_FOLDER:
hrComplete = HR_E_OFFLINE_FOLDER_RENAME;
break;
}
}
hr = m_pCurrentCallback ? m_pCurrentCallback->OnComplete(tyOperation, hrComplete, pOpInfo, pErrorInfo) : S_OK;
// if the operation failed due to a connection error (or user-cancel) then flush the queue
// if the sync-folder operation failed, then ALWAYS flush the queue
if (FAILED(hrComplete) &&
((hrComplete == STORE_E_OPERATION_CANCELED) ||
(pErrorInfo &&
pErrorInfo->dwFlags & SE_FLAG_FLUSHALL)))
{
_Flush(FALSE);
}
if (m_pCurrentTask && m_pCurrentTask->sot == tyOperation)
_StartNextTask(); // operation complete, start the next task
return hr;
}
STDMETHODIMP CServerQ::GetServerMessageFlags(MESSAGEFLAGS *pFlags)
{
return m_pServer->GetServerMessageFlags(pFlags);
}
HRESULT CServerQ::OnPrompt(HRESULT hrError, LPCTSTR pszText, LPCTSTR pszCaption, UINT uType, INT *piUserResponse)
{
return m_pCurrentCallback ? m_pCurrentCallback->OnPrompt(hrError, pszText, pszCaption, uType, piUserResponse) : E_NOTIMPL;
}
HRESULT CServerQ::GetParentWindow(DWORD dwReserved, HWND *phwndParent)
{
return m_pCurrentCallback ? m_pCurrentCallback->GetParentWindow(dwReserved, phwndParent) : E_NOTIMPL;
}
LRESULT CServerQ::ExtWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CServerQ *pServer = (CServerQ *)GetWndThisPtr(hwnd);
switch (uMsg)
{
case WM_CREATE:
SetWndThisPtrOnCreate(hwnd, lParam);
break;
#ifdef DEBUG
case WM_TIMER:
if (pServer->m_DBG_pArgDataLast &&
pServer->m_DBG_pArgDataLast == pServer->m_pCurrentTask)
{
// if current-task is the same every 10 seconds, the print a warning message
TraceInfo("WARNING: serverq processing same task for > 10 seconds");
pServer->_DBG_DumpQueue();
// beeping here is very hostile to httpmail. httpmail needs
// lots and lots of time to download mail headers. this gives
// our uses an opportunity to meditate on the many wonders of our new
// protocol.
//MessageBeep((UINT)-1);
}
else
pServer->m_DBG_pArgDataLast = pServer->m_pCurrentTask;
break;
#endif
case SQW_NEXTTASK:
pServer->_OnNextTask();
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
/*
* Function : _AddToQueue
*
* Purpose : Adds an element to the tail of the queue and returns the allocated blob
*
* The only arguments on IMessageServer that need allocation to duplicate are
* message list and adjust flags. I roll these arguments into one function AddToQueue
* so that there is less code-gen for the error condition case, and if AddToQueue succeeds
* there should be no futher failing operations - so noone calling this should need to
* clean up the argdata and/or de-queue the failed addition
*
*/
HRESULT CServerQ::_AddToQueue( STOREOPERATIONTYPE sot,
IStoreCallback *pCallback,
LPMESSAGEIDLIST pList,
LPADJUSTFLAGS pFlags,
LPCSTR pszName,
ARGUMENT_DATA **ppNewArgData)
{
HRESULT hr;
ARGUMENT_DATA *pArgData=0;
TraceCall("CServerQ::_AddToQueue");
TraceInfoTag(TAG_SERVERQ,_MSG("CServerQ::_AddToQueue (sot='%s')", sotToSz(sot)));
Assert (ppNewArgData);
if (!MemAlloc((LPVOID *)&pArgData, sizeof(ARGUMENT_DATA)))
{
return TraceResult(E_OUTOFMEMORY);
}
ZeroMemory((LPVOID *)pArgData, sizeof(ARGUMENT_DATA));
if (pList)
{
hr = CloneMessageIDList(pList, &pArgData->pList);
if (FAILED(hr))
{
TraceResult(hr);
goto error;
}
}
if (pFlags)
{
hr = CloneAdjustFlags(pFlags, &pArgData->pFlags);
if (FAILED(hr))
{
TraceResult(hr);
goto error;
}
}
if (pszName)
{
pArgData->pszName = PszDupA(pszName);
if (!pArgData->pszName)
{
hr = TraceResult(E_OUTOFMEMORY);
goto error;
}
}
if (pArgData->pCallback = pCallback)
pCallback->AddRef();
pArgData->sot = sot;
if (m_pLastQueueTask)
m_pLastQueueTask->pNext = pArgData;
m_pLastQueueTask = pArgData;
if (!m_pTaskQueue)
{
m_pTaskQueue = pArgData;
// if there are no pending tasks, then start the next task
if (!m_pCurrentTask)
_StartNextTask();
}
*ppNewArgData = pArgData;
return S_OK;
error:
_FreeArgumentData(pArgData);
return hr;
}
HRESULT CServerQ::_OnNextTask()
{
HRESULT hr;
TraceCall("CServerQ::_OnNextTask");
AssertSz(m_pCurrentTask, "How did we get here without a pending task?");
TraceInfoTag(TAG_SERVERQ,_MSG("CServerQ::_OnNextTask [ sot ='%s']", sotToSz(m_pCurrentTask->sot)));
switch (m_pCurrentTask->sot)
{
case SOT_SYNC_FOLDER:
hr = m_pServer->SynchronizeFolder(m_pCurrentTask->dwSyncFlags,
m_pCurrentTask->cHeaders, (IStoreCallback *)this);
break;
case SOT_GET_MESSAGE:
// TODO: add getmsg chaining here
hr = m_pServer->GetMessage(m_pCurrentTask->idMessage, (IStoreCallback *)this);
break;
case SOT_PUT_MESSAGE:
hr = m_pServer->PutMessage(m_pCurrentTask->idFolder,
m_pCurrentTask->dwMsgFlags,
m_pCurrentTask->pftReceived,
m_pCurrentTask->pPutStream, (IStoreCallback *)this);
break;
case SOT_COPYMOVE_MESSAGE:
hr = m_pServer->CopyMessages(m_pCurrentTask->pDestFldr,
m_pCurrentTask->dwCopyOptions,
m_pCurrentTask->pList, m_pCurrentTask->pFlags, (IStoreCallback *)this);
break;
case SOT_DELETING_MESSAGES:
hr = m_pServer->DeleteMessages(m_pCurrentTask->dwDeleteOptions,
m_pCurrentTask->pList, (IStoreCallback *)this);
break;
case SOT_SET_MESSAGEFLAGS:
hr = m_pServer->SetMessageFlags(m_pCurrentTask->pList, m_pCurrentTask->pFlags,
m_pCurrentTask->dwSetFlags, (IStoreCallback *)this);
break;
case SOT_SYNCING_STORE:
hr = m_pServer->SynchronizeStore(m_pCurrentTask->idParent,
m_pCurrentTask->dwFlags, (IStoreCallback *)this);
break;
case SOT_CREATE_FOLDER:
hr = m_pServer->CreateFolder(m_pCurrentTask->idParent,
m_pCurrentTask->tySpecial,
m_pCurrentTask->pszName, m_pCurrentTask->dwFldrFlags,
(IStoreCallback *)this);
break;
case SOT_MOVE_FOLDER:
hr = m_pServer->MoveFolder(m_pCurrentTask->idFolder,
m_pCurrentTask->idParentNew, (IStoreCallback *)this);
break;
case SOT_RENAME_FOLDER:
hr = m_pServer->RenameFolder(m_pCurrentTask->idFolder,
m_pCurrentTask->pszName, (IStoreCallback *)this);
break;
case SOT_DELETE_FOLDER:
hr = m_pServer->DeleteFolder(m_pCurrentTask->idFolder,
m_pCurrentTask->dwDelFldrFlags, (IStoreCallback *)this);
break;
case SOT_SUBSCRIBE_FOLDER:
hr = m_pServer->SubscribeToFolder(m_pCurrentTask->idFolder,
m_pCurrentTask->fSubscribe, (IStoreCallback *)this);
break;
case SOT_UPDATE_FOLDER:
hr = m_pServer->GetFolderCounts(m_pCurrentTask->idFolder,
(IStoreCallback *)this);
break;
case SOT_GET_NEW_GROUPS:
hr = m_pServer->GetNewGroups(&m_pCurrentTask->sysTime,
(IStoreCallback *)this);
break;
case SOT_GET_WATCH_INFO:
hr = m_pServer->GetWatchedInfo(m_pCurrentTask->idFolder,
(IStoreCallback *)this);
break;
case SOT_GET_ADURL:
hr = m_pServer->GetAdBarUrl((IStoreCallback *)this);
break;
case SOT_GET_HTTP_MINPOLLINGINTERVAL:
hr = m_pServer->GetMinPollingInterval((IStoreCallback*)this);
break;
default:
AssertSz(0, "Bad ArgData type");
}
if (hr != E_PENDING)
{
// if the operation did not return E_PENDING, then it is not completing ASYNC
// if this is true, then we need to take care of the callback reporting here are
// we have already returned E_PENDING when we put this dude in the queue
STOREERROR rError;
STOREERROR *pError=0;
TCHAR szBuf[CCHMAX_STRINGRES];
TraceInfoTag(TAG_SERVERQ,_MSG("CServerQ::Operation failed to go ASYNC - propagating error. sot='%s', hr=0x%x", sotToSz(m_pCurrentTask->sot), hr));
if (hr != S_OK)
{
ZeroMemory((LPVOID)&rError, sizeof(STOREERROR));
rError.hrResult = hr;
LoadString(g_hLocRes, idsGenericError, szBuf, ARRAYSIZE(szBuf));
rError.pszDetails = szBuf;
pError = &rError;
}
// $TODO: need to add richer error reporting here.
if (m_pCurrentCallback)
{
m_pCurrentCallback->OnBegin(m_pCurrentTask->sot, NULL, NULL);
m_pCurrentCallback->OnComplete(m_pCurrentTask->sot, hr, NULL, pError);
}
if (m_pCurrentTask)
_StartNextTask(); // operation complete, start the next task
}
return S_OK;
}
HRESULT CServerQ::_FreeArgumentData(ARGUMENT_DATA *pArgData)
{
ULONG ul;
if (!pArgData)
return S_OK;
switch (pArgData->sot)
{
case SOT_SYNC_FOLDER:
break;
case SOT_GET_MESSAGE:
for (ul = 0; ul < pArgData->cOtherCallbacks; ul++)
ReleaseObj(pArgData->rgpOtherCallback[ul]);
SafeMemFree(pArgData->rgpOtherCallback);
pArgData->cOtherCallbacks = 0;
break;
case SOT_PUT_MESSAGE:
ReleaseObj(pArgData->pPutStream);
break;
case SOT_COPYMOVE_MESSAGE:
ReleaseObj(pArgData->pDestFldr);
SafeMemFree(pArgData->pList);
SafeMemFree(pArgData->pFlags);
break;
case SOT_DELETING_MESSAGES:
SafeMemFree(pArgData->pList);
break;
case SOT_SET_MESSAGEFLAGS:
SafeMemFree(pArgData->pList);
SafeMemFree(pArgData->pFlags);
break;
case SOT_SYNCING_STORE:
break;
case SOT_CREATE_FOLDER:
if (pArgData->pszName)
MemFree((LPSTR)pArgData->pszName);
break;
case SOT_MOVE_FOLDER:
break;
case SOT_RENAME_FOLDER:
if (pArgData->pszName)
MemFree((LPSTR)pArgData->pszName);
break;
case SOT_DELETE_FOLDER:
break;
case SOT_SUBSCRIBE_FOLDER:
break;
case SOT_UPDATE_FOLDER:
break;
case SOT_GET_NEW_GROUPS:
break;
case SOT_GET_WATCH_INFO:
break;
case SOT_GET_ADURL:
case SOT_GET_HTTP_MINPOLLINGINTERVAL:
break;
default:
AssertSz(0, "Bad ArgData type");
}
ReleaseObj(pArgData->pCallback);
MemFree(pArgData);
return S_OK;
}
HRESULT CreateServerQueue(IMessageServer *pServerInner, IMessageServer **ppServer)
{
CServerQ *pQueue=0;
HRESULT hr;
pQueue = new CServerQ();
if (!pQueue)
{
hr = TraceResult(E_OUTOFMEMORY);
goto exit;
}
hr = pQueue->Init(pServerInner);
if (FAILED(hr))
{
TraceResult(hr);
goto exit;
}
*ppServer = (IMessageServer *)pQueue;
pQueue=0;
exit:
ReleaseObj(pQueue);
return hr;
}
HRESULT CServerQ::QueryService(REFGUID guidService, REFIID riid, LPVOID *ppvObject)
{
if (SID_MessageServer == guidService)
{
HRESULT hrResult;
// CServerQ is a IMessageServer, too! Try to satisfy the incoming request ourselves
hrResult = QueryInterface(riid, ppvObject);
if (SUCCEEDED(hrResult))
return hrResult;
// Oh well, we can't provide this interface. Ask our server object.
if (m_pServer != NULL)
return m_pServer->QueryInterface(riid, ppvObject);
}
return E_NOINTERFACE;
}
HRESULT CServerQ::_StartNextTask()
{
HRESULT hr;
TraceCall("CServerQ::_StartNextTask");
// clear the current task and dequeue the next one
// we post a message to ourselves to start the operation
// so that our stack is clean.
_FreeArgumentData(m_pCurrentTask);
m_pCurrentTask = NULL;
m_pCurrentCallback = NULL;
if (!m_pTaskQueue) // no more tasks
{
TraceInfoTag(TAG_SERVERQ,_MSG("CServerQ::_StartNextTask - no tasks left"));
m_pLastQueueTask = NULL;
return S_OK;
}
m_pCurrentTask = m_pTaskQueue;
m_pTaskQueue = m_pTaskQueue->pNext;
m_pCurrentCallback = m_pCurrentTask->pCallback;
PostMessage(m_hwnd, SQW_NEXTTASK, 0, 0);
return S_OK;
}
HRESULT CServerQ::_Flush(BOOL fFlushCurrent)
{
ARGUMENT_DATA *pArgData,
*pArgDataFree;
STOREERROR rError;
STOREOPERATIONINFO soi;
STOREOPERATIONINFO *psoi=NULL;
TraceCall("CServerQ::_Flush");
ZeroMemory((LPVOID)&rError, sizeof(STOREERROR));
rError.hrResult = STORE_E_OPERATION_CANCELED;
// cancel the current operation, if so requested
if (fFlushCurrent &&
m_pCurrentCallback &&
m_pCurrentTask)
{
m_pCurrentCallback->OnComplete(m_pCurrentTask->sot, STORE_E_OPERATION_CANCELED, NULL, &rError);
_FreeArgumentData(m_pCurrentTask);
m_pCurrentTask = NULL;
m_pCurrentCallback = NULL;
}
m_pLastQueueTask = NULL;
// flush any queued ops
pArgData = m_pTaskQueue;
while (pArgData)
{
TraceInfoTag(TAG_SERVERQ,_MSG("CServerQ::Flushing Task: '%s'", sotToSz(pArgData->sot)));
// send an OnComplete to all of the callbacks in the queue to cancel the operation
if (pArgData->sot == SOT_GET_MESSAGE)
{
// if a GetMessage, besure to pass back the message-id on the flush so that
// the view knows which message is getting flushed
soi.cbSize = sizeof(STOREOPERATIONINFO);
soi.idMessage = pArgData->idMessage;
psoi = &soi;
for (ULONG ul=0; ul < pArgData->cOtherCallbacks; ul++)
{
Assert (pArgData->rgpOtherCallback[ul]);
pArgData->rgpOtherCallback[ul]->OnBegin(pArgData->sot, psoi, NULL);
pArgData->rgpOtherCallback[ul]->OnComplete(pArgData->sot, STORE_E_OPERATION_CANCELED, NULL, &rError);
}
}
if (pArgData->pCallback)
{
pArgData->pCallback->OnBegin(pArgData->sot, psoi, NULL);
pArgData->pCallback->OnComplete(pArgData->sot, STORE_E_OPERATION_CANCELED, NULL, &rError);
}
pArgDataFree = pArgData;
pArgData = pArgData->pNext;
_FreeArgumentData(pArgDataFree);
}
m_pTaskQueue = NULL;
return S_OK;
}
HRESULT CServerQ::ConnectionAddRef()
{
m_cRefConnection++;
return S_OK;
}
HRESULT CServerQ::ConnectionRelease()
{
if (m_cRefConnection == 0)
return E_UNEXPECTED;
m_cRefConnection--;
return S_OK;
}
#ifdef DEBUG
HRESULT CServerQ::_DBG_DumpQueue()
{
ARGUMENT_DATA *pTask;
TraceInfo("ServerQ pending tasks:");
pTask = m_pCurrentTask;
while (pTask)
{
TraceInfo(_MSG("\tsot=%s", sotToSz(pTask->sot)));
pTask = pTask->pNext;
}
return S_OK;
}
#endif
HRESULT CServerQ::GetAdBarUrl(IStoreCallback *pCallback)
{
ARGUMENT_DATA *pArg;
HRESULT hr = S_OK;
IF_FAILEXIT(hr = _AddToQueue(SOT_GET_ADURL, pCallback, NULL, NULL, NULL, &pArg));
exit:
return hr;
}
HRESULT CServerQ::GetMinPollingInterval(IStoreCallback *pCallback)
{
ARGUMENT_DATA *pArg;
HRESULT hr = S_OK;
IF_FAILEXIT(hr = _AddToQueue(SOT_GET_HTTP_MINPOLLINGINTERVAL, pCallback, NULL, NULL, NULL, &pArg));
exit:
return hr;
}