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

  1. /*
  2. * s e r v e r q . c p p
  3. *
  4. * Purpose:
  5. * Implements IMessageServer wrapper for queuing operations to
  6. * IMessageServer object.
  7. *
  8. * This object knows how to pack stack data for an IMessageServer method
  9. * call into a queue so that the call can be reissued when the server is
  10. * free. It maintains a small listen window so that is can post async
  11. * messages to itself to allow completion of the next task
  12. *
  13. * Owner:
  14. * brettm.
  15. *
  16. * History:
  17. * June 1998: Created
  18. *
  19. * Copyright (C) Microsoft Corp. 1993, 1994.
  20. */
  21. #include "pch.hxx"
  22. #include "instance.h"
  23. #include "storutil.h"
  24. #include "serverq.h"
  25. static TCHAR c_szServerQListenWnd[] = "OE ServerQWnd";
  26. #define SQW_NEXTTASK (WM_USER + 1)
  27. /*
  28. * Notes:
  29. *
  30. * the queueing is a little odd. For every method, we immediatley throw it in
  31. * the queue. If the server is not busy then we post a message to ourselves to
  32. * dequeue the task. We do this as if we passed-thru when the server is not busy
  33. * then we never get to hook the IStoreCallback to watch for completion, so we
  34. * never know when to queue the next task.
  35. *
  36. */
  37. CServerQ::CServerQ()
  38. {
  39. m_cRef = 1;
  40. m_pServer = NULL;
  41. m_pTaskQueue = NULL;
  42. m_pLastQueueTask = NULL;
  43. m_hwnd = NULL;
  44. m_pCurrentCallback = NULL;
  45. m_pCurrentTask = NULL;
  46. m_cRefConnection = 0;
  47. #ifdef DEBUG
  48. m_DBG_pArgDataLast = 0;
  49. #endif
  50. }
  51. CServerQ::~CServerQ()
  52. {
  53. SafeRelease(m_pServer);
  54. _Flush(TRUE);
  55. SendMessage(m_hwnd, WM_CLOSE, 0, 0);
  56. }
  57. // IUnknown Members
  58. HRESULT CServerQ::QueryInterface(REFIID iid, LPVOID *ppvObject)
  59. {
  60. HRESULT hr=E_NOINTERFACE;
  61. TraceCall("CServerQ::QueryInterface");
  62. if (ppvObject == NULL)
  63. return TraceResult(E_INVALIDARG);
  64. *ppvObject = NULL;
  65. // Find a ptr to the interface
  66. if (IID_IUnknown == iid)
  67. *ppvObject = (IMessageServer *)this;
  68. else if (IID_IMessageServer == iid)
  69. *ppvObject = (IMessageServer *)this;
  70. else if (IID_IServiceProvider == iid)
  71. *ppvObject = (IServiceProvider *)this;
  72. else if (IID_IStoreCallback == iid)
  73. *ppvObject = (IStoreCallback *)this;
  74. if (*ppvObject)
  75. {
  76. hr = S_OK;
  77. AddRef();
  78. }
  79. return hr;
  80. }
  81. ULONG CServerQ::AddRef()
  82. {
  83. return ++m_cRef;
  84. }
  85. ULONG CServerQ::Release()
  86. {
  87. if (--m_cRef == 0)
  88. {
  89. delete this;
  90. return 0;
  91. }
  92. return m_cRef;
  93. }
  94. HRESULT CServerQ::Init(IMessageServer *pServerInner)
  95. {
  96. HRESULT hr=S_OK;
  97. WNDCLASS wc;
  98. TraceCall("CServerQ::Init");
  99. Assert(m_pServer == NULL);
  100. if (!pServerInner)
  101. return TraceResult(E_INVALIDARG);
  102. if (!GetClassInfo(g_hInst, c_szServerQListenWnd, &wc))
  103. {
  104. ZeroMemory(&wc, sizeof(WNDCLASS));
  105. wc.lpfnWndProc = CServerQ::ExtWndProc;
  106. wc.hInstance = g_hInst;
  107. wc.hCursor = NULL;
  108. wc.lpszClassName = c_szServerQListenWnd;
  109. wc.hbrBackground = NULL;
  110. wc.style = 0;
  111. if (!RegisterClass(&wc))
  112. return TraceResult(E_OUTOFMEMORY);
  113. }
  114. m_hwnd=CreateWindow(c_szServerQListenWnd,
  115. NULL, WS_OVERLAPPED,
  116. 0, 0, 0, 0,
  117. NULL, NULL,
  118. g_hInst,
  119. (LPVOID)this);
  120. if (!m_hwnd)
  121. return TraceResult(E_OUTOFMEMORY);
  122. #ifdef DEBUG
  123. // debug timer
  124. SetTimer(m_hwnd, 0, 10000, NULL);
  125. #endif
  126. ReplaceInterface(m_pServer, pServerInner);
  127. return S_OK;
  128. }
  129. // IMessageServer Methods
  130. HRESULT CServerQ::Initialize(IMessageStore *pStore, FOLDERID idStoreRoot, IMessageFolder *pFolder, FOLDERID idFolder)
  131. {
  132. return m_pServer->Initialize(pStore, idStoreRoot, pFolder, idFolder);
  133. }
  134. HRESULT CServerQ::ResetFolder(IMessageFolder *pFolder, FOLDERID idFolder)
  135. {
  136. return m_pServer->ResetFolder(pFolder, idFolder);
  137. }
  138. HRESULT CServerQ::SetIdleCallback(IStoreCallback *pDefaultCallback)
  139. {
  140. return m_pServer->SetIdleCallback(pDefaultCallback);
  141. }
  142. HRESULT CServerQ::SynchronizeFolder(SYNCFOLDERFLAGS dwFlags, DWORD cHeaders, IStoreCallback *pCallback)
  143. {
  144. ARGUMENT_DATA *pArg;
  145. HRESULT hr;
  146. hr = _AddToQueue(SOT_SYNC_FOLDER, pCallback, NULL, NULL, NULL, &pArg);
  147. if (!FAILED(hr))
  148. {
  149. pArg->dwSyncFlags = dwFlags;
  150. pArg->cHeaders = cHeaders;
  151. return E_PENDING;
  152. }
  153. return TraceResult(hr);
  154. }
  155. HRESULT CServerQ::GetMessage(MESSAGEID idMessage, IStoreCallback *pCallback)
  156. {
  157. ARGUMENT_DATA *pArg,
  158. *pTask;
  159. HRESULT hr;
  160. STOREOPERATIONINFO soi;
  161. BOOL fFound=FALSE;
  162. ULONG l;
  163. // if we have tasks in our queue, looks for a get message task with the same id
  164. // if we find one, add this callback to the list and return STORE_S_ALREADYPENDING
  165. // to indicate that the pStream passed in will NOT be written to, and the caller
  166. // should get the message from the cache when his complete is called
  167. // make sure that the current task's next ptr points to the task queue
  168. // also make sure there is no task queue if there are no pending tasks
  169. Assert (m_pCurrentTask == NULL && m_pTaskQueue==NULL ||
  170. m_pCurrentTask->pNext == m_pTaskQueue);
  171. pTask = m_pCurrentTask;
  172. // let's look for a pending request for this message
  173. while (pTask)
  174. {
  175. if (pTask->sot == SOT_GET_MESSAGE && pTask->idMessage == idMessage)
  176. {
  177. if (pCallback)
  178. {
  179. // cruise thro' the callback list. If this IStoreCallback is already
  180. // registered for this message, then don't add it otherwise it will
  181. // get multiple notifications
  182. if (pTask->pCallback == pCallback)
  183. fFound = TRUE;
  184. else
  185. for (l = 0; l < pTask->cOtherCallbacks; l++)
  186. if (pTask->rgpOtherCallback[l] == pCallback)
  187. {
  188. fFound = TRUE;
  189. break;
  190. }
  191. if (!fFound)
  192. {
  193. if (!MemRealloc((LPVOID *)&pTask->rgpOtherCallback,
  194. sizeof(IStoreCallback *) * (pTask->cOtherCallbacks+1)))
  195. {
  196. hr = TraceResult(E_OUTOFMEMORY);
  197. goto exit;
  198. }
  199. pTask->rgpOtherCallback[pTask->cOtherCallbacks++] = pCallback;
  200. pCallback->AddRef();
  201. if (pTask == m_pCurrentTask)
  202. {
  203. // if this task if the current task, then the OnBegin call has already
  204. // been called. We fake the OnBegin to provide message id on get message start
  205. soi.cbSize = sizeof(STOREOPERATIONINFO);
  206. soi.idMessage = idMessage;
  207. pCallback->OnBegin(SOT_GET_MESSAGE, &soi, NULL);
  208. }
  209. }
  210. }
  211. hr = STORE_S_ALREADYPENDING;
  212. goto exit; // found
  213. }
  214. pTask = pTask->pNext;
  215. }
  216. // not already queued, let's add it.
  217. hr = _AddToQueue(SOT_GET_MESSAGE, pCallback, NULL, NULL, NULL, &pArg);
  218. if (!FAILED(hr))
  219. {
  220. pArg->idMessage = idMessage;
  221. return E_PENDING;
  222. }
  223. exit:
  224. return hr;
  225. }
  226. HRESULT CServerQ::PutMessage(FOLDERID idFolder, MESSAGEFLAGS dwFlags, LPFILETIME pftReceived, IStream *pStream, IStoreCallback *pCallback)
  227. {
  228. ARGUMENT_DATA *pArg;
  229. HRESULT hr;
  230. hr = _AddToQueue(SOT_PUT_MESSAGE, pCallback, NULL, NULL, NULL, &pArg);
  231. if (!FAILED(hr))
  232. {
  233. pArg->idFolder = idFolder;
  234. pArg->dwMsgFlags = dwFlags;
  235. if (NULL != pftReceived)
  236. {
  237. pArg->ftReceived = *pftReceived;
  238. pArg->pftReceived = &pArg->ftReceived;
  239. }
  240. else
  241. pArg->pftReceived = NULL;
  242. if (NULL != pStream)
  243. {
  244. pArg->pPutStream = pStream;
  245. pStream->AddRef();
  246. }
  247. return E_PENDING;
  248. }
  249. return TraceResult(hr);
  250. }
  251. HRESULT CServerQ::CopyMessages(IMessageFolder *pDestFldr, COPYMESSAGEFLAGS dwOptions,
  252. LPMESSAGEIDLIST pList, LPADJUSTFLAGS pFlags, IStoreCallback *pCallback)
  253. {
  254. ARGUMENT_DATA *pArg;
  255. HRESULT hr;
  256. hr = _AddToQueue(SOT_COPYMOVE_MESSAGE, pCallback, pList, pFlags, NULL, &pArg);
  257. if (!FAILED(hr))
  258. {
  259. if (pArg->pDestFldr = pDestFldr)
  260. pDestFldr->AddRef();
  261. pArg->dwCopyOptions = dwOptions;
  262. return E_PENDING;
  263. }
  264. return TraceResult(hr);
  265. }
  266. HRESULT CServerQ::DeleteMessages(DELETEMESSAGEFLAGS dwOptions, LPMESSAGEIDLIST pList,
  267. IStoreCallback *pCallback)
  268. {
  269. ARGUMENT_DATA *pArg;
  270. HRESULT hr;
  271. Assert(NULL == pList || pList->cMsgs > 0);
  272. hr = _AddToQueue(SOT_DELETING_MESSAGES, pCallback, pList, NULL, NULL, &pArg);
  273. if (!FAILED(hr))
  274. {
  275. pArg->dwDeleteOptions = dwOptions;
  276. return E_PENDING;
  277. }
  278. return TraceResult(hr);
  279. }
  280. HRESULT CServerQ::SetMessageFlags(LPMESSAGEIDLIST pList, LPADJUSTFLAGS pFlags, SETMESSAGEFLAGSFLAGS dwFlags,
  281. IStoreCallback *pCallback)
  282. {
  283. ARGUMENT_DATA *pArg=0;
  284. HRESULT hr;
  285. Assert(NULL == pList || pList->cMsgs > 0);
  286. hr = _AddToQueue(SOT_SET_MESSAGEFLAGS, pCallback, pList, pFlags, NULL, &pArg);
  287. if (!FAILED(hr))
  288. {
  289. pArg->dwSetFlags = dwFlags;
  290. return E_PENDING;
  291. }
  292. return TraceResult(hr);
  293. }
  294. HRESULT CServerQ::SynchronizeStore(FOLDERID idParent, DWORD dwFlags, IStoreCallback *pCallback)
  295. {
  296. ARGUMENT_DATA *pArg;
  297. HRESULT hr;
  298. hr = _AddToQueue(SOT_SYNCING_STORE, pCallback, NULL, NULL, NULL, &pArg);
  299. if (!FAILED(hr))
  300. {
  301. pArg->idParent = idParent;
  302. pArg->dwFlags = dwFlags;
  303. return E_PENDING;
  304. }
  305. return TraceResult(hr);
  306. }
  307. HRESULT CServerQ::CreateFolder(FOLDERID idParent, SPECIALFOLDER tySpecial, LPCSTR pszName, FLDRFLAGS dwFlags, IStoreCallback *pCallback)
  308. {
  309. ARGUMENT_DATA *pArg=0;
  310. HRESULT hr;
  311. hr = _AddToQueue(SOT_CREATE_FOLDER, pCallback, NULL, NULL, pszName, &pArg);
  312. if (!FAILED(hr))
  313. {
  314. pArg->idParent = idParent;
  315. pArg->tySpecial = tySpecial;
  316. pArg->dwFldrFlags = dwFlags;
  317. return E_PENDING;
  318. }
  319. return TraceResult(hr);
  320. }
  321. HRESULT CServerQ::MoveFolder(FOLDERID idFolder, FOLDERID idParentNew, IStoreCallback *pCallback)
  322. {
  323. ARGUMENT_DATA *pArg;
  324. HRESULT hr;
  325. hr = _AddToQueue(SOT_MOVE_FOLDER, pCallback, NULL, NULL, NULL, &pArg);
  326. if (!FAILED(hr))
  327. {
  328. pArg->idFolder = idFolder;
  329. pArg->idParentNew = idParentNew;
  330. return E_PENDING;
  331. }
  332. return TraceResult(hr);
  333. }
  334. HRESULT CServerQ::RenameFolder(FOLDERID idFolder, LPCSTR pszName, IStoreCallback *pCallback)
  335. {
  336. ARGUMENT_DATA *pArg=0;
  337. HRESULT hr;
  338. hr = _AddToQueue(SOT_RENAME_FOLDER, pCallback, NULL, NULL, pszName, &pArg);
  339. if (!FAILED(hr))
  340. {
  341. pArg->idFolder = idFolder;
  342. return E_PENDING;
  343. }
  344. return TraceResult(hr);
  345. }
  346. HRESULT CServerQ::DeleteFolder(FOLDERID idFolder, DELETEFOLDERFLAGS dwFlags, IStoreCallback *pCallback)
  347. {
  348. ARGUMENT_DATA *pArg;
  349. HRESULT hr;
  350. hr = _AddToQueue(SOT_DELETE_FOLDER, pCallback, NULL, NULL, NULL, &pArg);
  351. if (!FAILED(hr))
  352. {
  353. pArg->idFolder = idFolder;
  354. pArg->dwDelFldrFlags = dwFlags;
  355. return E_PENDING;
  356. }
  357. return TraceResult(hr);
  358. }
  359. HRESULT CServerQ::SubscribeToFolder(FOLDERID idFolder, BOOL fSubscribe, IStoreCallback *pCallback)
  360. {
  361. ARGUMENT_DATA *pArg;
  362. HRESULT hr;
  363. hr = _AddToQueue(SOT_SUBSCRIBE_FOLDER, pCallback, NULL, NULL, NULL, &pArg);
  364. if (!FAILED(hr))
  365. {
  366. pArg->idFolder = idFolder;
  367. pArg->fSubscribe = fSubscribe;
  368. return E_PENDING;
  369. }
  370. return TraceResult(hr);
  371. }
  372. HRESULT CServerQ::GetFolderCounts(FOLDERID idFolder, IStoreCallback *pCallback)
  373. {
  374. ARGUMENT_DATA *pArg;
  375. HRESULT hr;
  376. hr = _AddToQueue(SOT_UPDATE_FOLDER, pCallback, NULL, NULL, NULL, &pArg);
  377. if (!FAILED(hr))
  378. {
  379. pArg->idFolder = idFolder;
  380. return E_PENDING;
  381. }
  382. return TraceResult(hr);
  383. }
  384. HRESULT CServerQ::GetNewGroups(LPSYSTEMTIME pSysTime, IStoreCallback *pCallback)
  385. {
  386. ARGUMENT_DATA *pArg;
  387. HRESULT hr;
  388. Assert(pSysTime != NULL);
  389. hr = _AddToQueue(SOT_GET_NEW_GROUPS, pCallback, NULL, NULL, NULL, &pArg);
  390. if (!FAILED(hr))
  391. {
  392. pArg->sysTime = *pSysTime;
  393. return E_PENDING;
  394. }
  395. return TraceResult(hr);
  396. }
  397. HRESULT CServerQ::GetWatchedInfo(FOLDERID idFolder, IStoreCallback *pCallback)
  398. {
  399. ARGUMENT_DATA *pArg;
  400. HRESULT hr;
  401. hr = _AddToQueue(SOT_GET_WATCH_INFO, pCallback, NULL, NULL, NULL, &pArg);
  402. if (!FAILED(hr))
  403. {
  404. pArg->idFolder = idFolder;
  405. return (E_PENDING);
  406. }
  407. return TraceResult(hr);
  408. }
  409. HRESULT CServerQ::Close(DWORD dwFlags)
  410. {
  411. HRESULT hr=S_OK;
  412. if (m_cRefConnection != 0 && dwFlags & MSGSVRF_HANDS_OFF_SERVER)
  413. {
  414. // some-body else has a connection ref on the server object.
  415. // let's ignore the hands-off close
  416. return STORE_S_IN_USE;
  417. }
  418. if (m_pServer)
  419. hr = m_pServer->Close(dwFlags);
  420. // make sure we flush any pending ops
  421. _Flush(FALSE);
  422. return hr;
  423. }
  424. HRESULT CServerQ::OnBegin(STOREOPERATIONTYPE tyOperation, STOREOPERATIONINFO *pOpInfo, IOperationCancel *pCancel)
  425. {
  426. TraceInfoTag(TAG_SERVERQ, _MSG("CServerQ::OnBegin[sot='%s', pTask->sot='%s']", sotToSz(tyOperation), sotToSz(m_pCurrentTask->sot)));
  427. IxpAssert (m_pCurrentTask);
  428. if (m_pCurrentTask &&
  429. m_pCurrentTask->sot == SOT_GET_MESSAGE)
  430. {
  431. // multiplex
  432. for (ULONG ul=0; ul < m_pCurrentTask->cOtherCallbacks; ul++)
  433. m_pCurrentTask->rgpOtherCallback[ul]->OnBegin(tyOperation, pOpInfo, pCancel);
  434. }
  435. return m_pCurrentCallback ? m_pCurrentCallback->OnBegin(tyOperation, pOpInfo, pCancel) : S_OK;
  436. }
  437. HRESULT CServerQ::OnProgress(STOREOPERATIONTYPE tyOperation, DWORD dwCurrent, DWORD dwMax, LPCSTR pszStatus)
  438. {
  439. IxpAssert (m_pCurrentTask);
  440. if (m_pCurrentTask &&
  441. m_pCurrentTask->sot == SOT_GET_MESSAGE)
  442. {
  443. // multiplex
  444. for (ULONG ul=0; ul < m_pCurrentTask->cOtherCallbacks; ul++)
  445. m_pCurrentTask->rgpOtherCallback[ul]->OnProgress(tyOperation, dwCurrent, dwMax, pszStatus);
  446. }
  447. return m_pCurrentCallback ? m_pCurrentCallback->OnProgress(tyOperation, dwCurrent, dwMax, pszStatus) : S_OK;
  448. }
  449. HRESULT CServerQ::OnTimeout(LPINETSERVER pServer, LPDWORD pdwTimeout, IXPTYPE ixpServerType)
  450. {
  451. return m_pCurrentCallback ? m_pCurrentCallback->OnTimeout(pServer, pdwTimeout, ixpServerType) : S_OK;
  452. }
  453. HRESULT CServerQ::CanConnect(LPCSTR pszAccountId, DWORD dwFlags)
  454. {
  455. return m_pCurrentCallback ? m_pCurrentCallback->CanConnect(pszAccountId, dwFlags) : S_OK;
  456. }
  457. HRESULT CServerQ::OnLogonPrompt(LPINETSERVER pServer, IXPTYPE ixpServerType)
  458. {
  459. return m_pCurrentCallback ? m_pCurrentCallback->OnLogonPrompt(pServer, ixpServerType) : S_OK;
  460. }
  461. HRESULT CServerQ::OnComplete(STOREOPERATIONTYPE tyOperation, HRESULT hrComplete, LPSTOREOPERATIONINFO pOpInfo, LPSTOREERROR pErrorInfo)
  462. {
  463. HRESULT hr;
  464. TraceInfoTag(TAG_SERVERQ,_MSG("CServerQ::OnComplete[sot='%s', pTask->sot='%s']", sotToSz(tyOperation), sotToSz(m_pCurrentTask ? m_pCurrentTask->sot : SOT_INVALID)));
  465. IxpAssert (m_pCurrentTask);
  466. // multiplex
  467. if (m_pCurrentTask &&
  468. m_pCurrentTask->sot == SOT_GET_MESSAGE)
  469. {
  470. for (ULONG ul=0; ul < m_pCurrentTask->cOtherCallbacks; ul++)
  471. m_pCurrentTask->rgpOtherCallback[ul]->OnComplete(tyOperation, hrComplete, pOpInfo, pErrorInfo);
  472. }
  473. if ((hrComplete == HR_E_OFFLINE) || (hrComplete == HR_E_USER_CANCEL_CONNECT))
  474. {
  475. switch (m_pCurrentTask->sot)
  476. {
  477. case SOT_CREATE_FOLDER:
  478. hrComplete = HR_E_OFFLINE_FOLDER_CREATE;
  479. break;
  480. case SOT_MOVE_FOLDER:
  481. hrComplete = HR_E_OFFLINE_FOLDER_MOVE;
  482. break;
  483. case SOT_DELETE_FOLDER:
  484. hrComplete = HR_E_OFFLINE_FOLDER_DELETE;
  485. break;
  486. case SOT_RENAME_FOLDER:
  487. hrComplete = HR_E_OFFLINE_FOLDER_RENAME;
  488. break;
  489. }
  490. }
  491. hr = m_pCurrentCallback ? m_pCurrentCallback->OnComplete(tyOperation, hrComplete, pOpInfo, pErrorInfo) : S_OK;
  492. // if the operation failed due to a connection error (or user-cancel) then flush the queue
  493. // if the sync-folder operation failed, then ALWAYS flush the queue
  494. if (FAILED(hrComplete) &&
  495. ((hrComplete == STORE_E_OPERATION_CANCELED) ||
  496. (pErrorInfo &&
  497. pErrorInfo->dwFlags & SE_FLAG_FLUSHALL)))
  498. {
  499. _Flush(FALSE);
  500. }
  501. if (m_pCurrentTask && m_pCurrentTask->sot == tyOperation)
  502. _StartNextTask(); // operation complete, start the next task
  503. return hr;
  504. }
  505. STDMETHODIMP CServerQ::GetServerMessageFlags(MESSAGEFLAGS *pFlags)
  506. {
  507. return m_pServer->GetServerMessageFlags(pFlags);
  508. }
  509. HRESULT CServerQ::OnPrompt(HRESULT hrError, LPCTSTR pszText, LPCTSTR pszCaption, UINT uType, INT *piUserResponse)
  510. {
  511. return m_pCurrentCallback ? m_pCurrentCallback->OnPrompt(hrError, pszText, pszCaption, uType, piUserResponse) : E_NOTIMPL;
  512. }
  513. HRESULT CServerQ::GetParentWindow(DWORD dwReserved, HWND *phwndParent)
  514. {
  515. return m_pCurrentCallback ? m_pCurrentCallback->GetParentWindow(dwReserved, phwndParent) : E_NOTIMPL;
  516. }
  517. LRESULT CServerQ::ExtWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  518. {
  519. CServerQ *pServer = (CServerQ *)GetWndThisPtr(hwnd);
  520. switch (uMsg)
  521. {
  522. case WM_CREATE:
  523. SetWndThisPtrOnCreate(hwnd, lParam);
  524. break;
  525. #ifdef DEBUG
  526. case WM_TIMER:
  527. if (pServer->m_DBG_pArgDataLast &&
  528. pServer->m_DBG_pArgDataLast == pServer->m_pCurrentTask)
  529. {
  530. // if current-task is the same every 10 seconds, the print a warning message
  531. TraceInfo("WARNING: serverq processing same task for > 10 seconds");
  532. pServer->_DBG_DumpQueue();
  533. // beeping here is very hostile to httpmail. httpmail needs
  534. // lots and lots of time to download mail headers. this gives
  535. // our uses an opportunity to meditate on the many wonders of our new
  536. // protocol.
  537. //MessageBeep((UINT)-1);
  538. }
  539. else
  540. pServer->m_DBG_pArgDataLast = pServer->m_pCurrentTask;
  541. break;
  542. #endif
  543. case SQW_NEXTTASK:
  544. pServer->_OnNextTask();
  545. break;
  546. }
  547. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  548. }
  549. /*
  550. * Function : _AddToQueue
  551. *
  552. * Purpose : Adds an element to the tail of the queue and returns the allocated blob
  553. *
  554. * The only arguments on IMessageServer that need allocation to duplicate are
  555. * message list and adjust flags. I roll these arguments into one function AddToQueue
  556. * so that there is less code-gen for the error condition case, and if AddToQueue succeeds
  557. * there should be no futher failing operations - so noone calling this should need to
  558. * clean up the argdata and/or de-queue the failed addition
  559. *
  560. */
  561. HRESULT CServerQ::_AddToQueue( STOREOPERATIONTYPE sot,
  562. IStoreCallback *pCallback,
  563. LPMESSAGEIDLIST pList,
  564. LPADJUSTFLAGS pFlags,
  565. LPCSTR pszName,
  566. ARGUMENT_DATA **ppNewArgData)
  567. {
  568. HRESULT hr;
  569. ARGUMENT_DATA *pArgData=0;
  570. TraceCall("CServerQ::_AddToQueue");
  571. TraceInfoTag(TAG_SERVERQ,_MSG("CServerQ::_AddToQueue (sot='%s')", sotToSz(sot)));
  572. Assert (ppNewArgData);
  573. if (!MemAlloc((LPVOID *)&pArgData, sizeof(ARGUMENT_DATA)))
  574. {
  575. return TraceResult(E_OUTOFMEMORY);
  576. }
  577. ZeroMemory((LPVOID *)pArgData, sizeof(ARGUMENT_DATA));
  578. if (pList)
  579. {
  580. hr = CloneMessageIDList(pList, &pArgData->pList);
  581. if (FAILED(hr))
  582. {
  583. TraceResult(hr);
  584. goto error;
  585. }
  586. }
  587. if (pFlags)
  588. {
  589. hr = CloneAdjustFlags(pFlags, &pArgData->pFlags);
  590. if (FAILED(hr))
  591. {
  592. TraceResult(hr);
  593. goto error;
  594. }
  595. }
  596. if (pszName)
  597. {
  598. pArgData->pszName = PszDupA(pszName);
  599. if (!pArgData->pszName)
  600. {
  601. hr = TraceResult(E_OUTOFMEMORY);
  602. goto error;
  603. }
  604. }
  605. if (pArgData->pCallback = pCallback)
  606. pCallback->AddRef();
  607. pArgData->sot = sot;
  608. if (m_pLastQueueTask)
  609. m_pLastQueueTask->pNext = pArgData;
  610. m_pLastQueueTask = pArgData;
  611. if (!m_pTaskQueue)
  612. {
  613. m_pTaskQueue = pArgData;
  614. // if there are no pending tasks, then start the next task
  615. if (!m_pCurrentTask)
  616. _StartNextTask();
  617. }
  618. *ppNewArgData = pArgData;
  619. return S_OK;
  620. error:
  621. _FreeArgumentData(pArgData);
  622. return hr;
  623. }
  624. HRESULT CServerQ::_OnNextTask()
  625. {
  626. HRESULT hr;
  627. TraceCall("CServerQ::_OnNextTask");
  628. AssertSz(m_pCurrentTask, "How did we get here without a pending task?");
  629. TraceInfoTag(TAG_SERVERQ,_MSG("CServerQ::_OnNextTask [ sot ='%s']", sotToSz(m_pCurrentTask->sot)));
  630. switch (m_pCurrentTask->sot)
  631. {
  632. case SOT_SYNC_FOLDER:
  633. hr = m_pServer->SynchronizeFolder(m_pCurrentTask->dwSyncFlags,
  634. m_pCurrentTask->cHeaders, (IStoreCallback *)this);
  635. break;
  636. case SOT_GET_MESSAGE:
  637. // TODO: add getmsg chaining here
  638. hr = m_pServer->GetMessage(m_pCurrentTask->idMessage, (IStoreCallback *)this);
  639. break;
  640. case SOT_PUT_MESSAGE:
  641. hr = m_pServer->PutMessage(m_pCurrentTask->idFolder,
  642. m_pCurrentTask->dwMsgFlags,
  643. m_pCurrentTask->pftReceived,
  644. m_pCurrentTask->pPutStream, (IStoreCallback *)this);
  645. break;
  646. case SOT_COPYMOVE_MESSAGE:
  647. hr = m_pServer->CopyMessages(m_pCurrentTask->pDestFldr,
  648. m_pCurrentTask->dwCopyOptions,
  649. m_pCurrentTask->pList, m_pCurrentTask->pFlags, (IStoreCallback *)this);
  650. break;
  651. case SOT_DELETING_MESSAGES:
  652. hr = m_pServer->DeleteMessages(m_pCurrentTask->dwDeleteOptions,
  653. m_pCurrentTask->pList, (IStoreCallback *)this);
  654. break;
  655. case SOT_SET_MESSAGEFLAGS:
  656. hr = m_pServer->SetMessageFlags(m_pCurrentTask->pList, m_pCurrentTask->pFlags,
  657. m_pCurrentTask->dwSetFlags, (IStoreCallback *)this);
  658. break;
  659. case SOT_SYNCING_STORE:
  660. hr = m_pServer->SynchronizeStore(m_pCurrentTask->idParent,
  661. m_pCurrentTask->dwFlags, (IStoreCallback *)this);
  662. break;
  663. case SOT_CREATE_FOLDER:
  664. hr = m_pServer->CreateFolder(m_pCurrentTask->idParent,
  665. m_pCurrentTask->tySpecial,
  666. m_pCurrentTask->pszName, m_pCurrentTask->dwFldrFlags,
  667. (IStoreCallback *)this);
  668. break;
  669. case SOT_MOVE_FOLDER:
  670. hr = m_pServer->MoveFolder(m_pCurrentTask->idFolder,
  671. m_pCurrentTask->idParentNew, (IStoreCallback *)this);
  672. break;
  673. case SOT_RENAME_FOLDER:
  674. hr = m_pServer->RenameFolder(m_pCurrentTask->idFolder,
  675. m_pCurrentTask->pszName, (IStoreCallback *)this);
  676. break;
  677. case SOT_DELETE_FOLDER:
  678. hr = m_pServer->DeleteFolder(m_pCurrentTask->idFolder,
  679. m_pCurrentTask->dwDelFldrFlags, (IStoreCallback *)this);
  680. break;
  681. case SOT_SUBSCRIBE_FOLDER:
  682. hr = m_pServer->SubscribeToFolder(m_pCurrentTask->idFolder,
  683. m_pCurrentTask->fSubscribe, (IStoreCallback *)this);
  684. break;
  685. case SOT_UPDATE_FOLDER:
  686. hr = m_pServer->GetFolderCounts(m_pCurrentTask->idFolder,
  687. (IStoreCallback *)this);
  688. break;
  689. case SOT_GET_NEW_GROUPS:
  690. hr = m_pServer->GetNewGroups(&m_pCurrentTask->sysTime,
  691. (IStoreCallback *)this);
  692. break;
  693. case SOT_GET_WATCH_INFO:
  694. hr = m_pServer->GetWatchedInfo(m_pCurrentTask->idFolder,
  695. (IStoreCallback *)this);
  696. break;
  697. case SOT_GET_ADURL:
  698. hr = m_pServer->GetAdBarUrl((IStoreCallback *)this);
  699. break;
  700. case SOT_GET_HTTP_MINPOLLINGINTERVAL:
  701. hr = m_pServer->GetMinPollingInterval((IStoreCallback*)this);
  702. break;
  703. default:
  704. AssertSz(0, "Bad ArgData type");
  705. }
  706. if (hr != E_PENDING)
  707. {
  708. // if the operation did not return E_PENDING, then it is not completing ASYNC
  709. // if this is true, then we need to take care of the callback reporting here are
  710. // we have already returned E_PENDING when we put this dude in the queue
  711. STOREERROR rError;
  712. STOREERROR *pError=0;
  713. TCHAR szBuf[CCHMAX_STRINGRES];
  714. TraceInfoTag(TAG_SERVERQ,_MSG("CServerQ::Operation failed to go ASYNC - propagating error. sot='%s', hr=0x%x", sotToSz(m_pCurrentTask->sot), hr));
  715. if (hr != S_OK)
  716. {
  717. ZeroMemory((LPVOID)&rError, sizeof(STOREERROR));
  718. rError.hrResult = hr;
  719. LoadString(g_hLocRes, idsGenericError, szBuf, ARRAYSIZE(szBuf));
  720. rError.pszDetails = szBuf;
  721. pError = &rError;
  722. }
  723. // $TODO: need to add richer error reporting here.
  724. if (m_pCurrentCallback)
  725. {
  726. m_pCurrentCallback->OnBegin(m_pCurrentTask->sot, NULL, NULL);
  727. m_pCurrentCallback->OnComplete(m_pCurrentTask->sot, hr, NULL, pError);
  728. }
  729. if (m_pCurrentTask)
  730. _StartNextTask(); // operation complete, start the next task
  731. }
  732. return S_OK;
  733. }
  734. HRESULT CServerQ::_FreeArgumentData(ARGUMENT_DATA *pArgData)
  735. {
  736. ULONG ul;
  737. if (!pArgData)
  738. return S_OK;
  739. switch (pArgData->sot)
  740. {
  741. case SOT_SYNC_FOLDER:
  742. break;
  743. case SOT_GET_MESSAGE:
  744. for (ul = 0; ul < pArgData->cOtherCallbacks; ul++)
  745. ReleaseObj(pArgData->rgpOtherCallback[ul]);
  746. SafeMemFree(pArgData->rgpOtherCallback);
  747. pArgData->cOtherCallbacks = 0;
  748. break;
  749. case SOT_PUT_MESSAGE:
  750. ReleaseObj(pArgData->pPutStream);
  751. break;
  752. case SOT_COPYMOVE_MESSAGE:
  753. ReleaseObj(pArgData->pDestFldr);
  754. SafeMemFree(pArgData->pList);
  755. SafeMemFree(pArgData->pFlags);
  756. break;
  757. case SOT_DELETING_MESSAGES:
  758. SafeMemFree(pArgData->pList);
  759. break;
  760. case SOT_SET_MESSAGEFLAGS:
  761. SafeMemFree(pArgData->pList);
  762. SafeMemFree(pArgData->pFlags);
  763. break;
  764. case SOT_SYNCING_STORE:
  765. break;
  766. case SOT_CREATE_FOLDER:
  767. if (pArgData->pszName)
  768. MemFree((LPSTR)pArgData->pszName);
  769. break;
  770. case SOT_MOVE_FOLDER:
  771. break;
  772. case SOT_RENAME_FOLDER:
  773. if (pArgData->pszName)
  774. MemFree((LPSTR)pArgData->pszName);
  775. break;
  776. case SOT_DELETE_FOLDER:
  777. break;
  778. case SOT_SUBSCRIBE_FOLDER:
  779. break;
  780. case SOT_UPDATE_FOLDER:
  781. break;
  782. case SOT_GET_NEW_GROUPS:
  783. break;
  784. case SOT_GET_WATCH_INFO:
  785. break;
  786. case SOT_GET_ADURL:
  787. case SOT_GET_HTTP_MINPOLLINGINTERVAL:
  788. break;
  789. default:
  790. AssertSz(0, "Bad ArgData type");
  791. }
  792. ReleaseObj(pArgData->pCallback);
  793. MemFree(pArgData);
  794. return S_OK;
  795. }
  796. HRESULT CreateServerQueue(IMessageServer *pServerInner, IMessageServer **ppServer)
  797. {
  798. CServerQ *pQueue=0;
  799. HRESULT hr;
  800. pQueue = new CServerQ();
  801. if (!pQueue)
  802. {
  803. hr = TraceResult(E_OUTOFMEMORY);
  804. goto exit;
  805. }
  806. hr = pQueue->Init(pServerInner);
  807. if (FAILED(hr))
  808. {
  809. TraceResult(hr);
  810. goto exit;
  811. }
  812. *ppServer = (IMessageServer *)pQueue;
  813. pQueue=0;
  814. exit:
  815. ReleaseObj(pQueue);
  816. return hr;
  817. }
  818. HRESULT CServerQ::QueryService(REFGUID guidService, REFIID riid, LPVOID *ppvObject)
  819. {
  820. if (SID_MessageServer == guidService)
  821. {
  822. HRESULT hrResult;
  823. // CServerQ is a IMessageServer, too! Try to satisfy the incoming request ourselves
  824. hrResult = QueryInterface(riid, ppvObject);
  825. if (SUCCEEDED(hrResult))
  826. return hrResult;
  827. // Oh well, we can't provide this interface. Ask our server object.
  828. if (m_pServer != NULL)
  829. return m_pServer->QueryInterface(riid, ppvObject);
  830. }
  831. return E_NOINTERFACE;
  832. }
  833. HRESULT CServerQ::_StartNextTask()
  834. {
  835. HRESULT hr;
  836. TraceCall("CServerQ::_StartNextTask");
  837. // clear the current task and dequeue the next one
  838. // we post a message to ourselves to start the operation
  839. // so that our stack is clean.
  840. _FreeArgumentData(m_pCurrentTask);
  841. m_pCurrentTask = NULL;
  842. m_pCurrentCallback = NULL;
  843. if (!m_pTaskQueue) // no more tasks
  844. {
  845. TraceInfoTag(TAG_SERVERQ,_MSG("CServerQ::_StartNextTask - no tasks left"));
  846. m_pLastQueueTask = NULL;
  847. return S_OK;
  848. }
  849. m_pCurrentTask = m_pTaskQueue;
  850. m_pTaskQueue = m_pTaskQueue->pNext;
  851. m_pCurrentCallback = m_pCurrentTask->pCallback;
  852. PostMessage(m_hwnd, SQW_NEXTTASK, 0, 0);
  853. return S_OK;
  854. }
  855. HRESULT CServerQ::_Flush(BOOL fFlushCurrent)
  856. {
  857. ARGUMENT_DATA *pArgData,
  858. *pArgDataFree;
  859. STOREERROR rError;
  860. STOREOPERATIONINFO soi;
  861. STOREOPERATIONINFO *psoi=NULL;
  862. TraceCall("CServerQ::_Flush");
  863. ZeroMemory((LPVOID)&rError, sizeof(STOREERROR));
  864. rError.hrResult = STORE_E_OPERATION_CANCELED;
  865. // cancel the current operation, if so requested
  866. if (fFlushCurrent &&
  867. m_pCurrentCallback &&
  868. m_pCurrentTask)
  869. {
  870. m_pCurrentCallback->OnComplete(m_pCurrentTask->sot, STORE_E_OPERATION_CANCELED, NULL, &rError);
  871. _FreeArgumentData(m_pCurrentTask);
  872. m_pCurrentTask = NULL;
  873. m_pCurrentCallback = NULL;
  874. }
  875. m_pLastQueueTask = NULL;
  876. // flush any queued ops
  877. pArgData = m_pTaskQueue;
  878. while (pArgData)
  879. {
  880. TraceInfoTag(TAG_SERVERQ,_MSG("CServerQ::Flushing Task: '%s'", sotToSz(pArgData->sot)));
  881. // send an OnComplete to all of the callbacks in the queue to cancel the operation
  882. if (pArgData->sot == SOT_GET_MESSAGE)
  883. {
  884. // if a GetMessage, besure to pass back the message-id on the flush so that
  885. // the view knows which message is getting flushed
  886. soi.cbSize = sizeof(STOREOPERATIONINFO);
  887. soi.idMessage = pArgData->idMessage;
  888. psoi = &soi;
  889. for (ULONG ul=0; ul < pArgData->cOtherCallbacks; ul++)
  890. {
  891. Assert (pArgData->rgpOtherCallback[ul]);
  892. pArgData->rgpOtherCallback[ul]->OnBegin(pArgData->sot, psoi, NULL);
  893. pArgData->rgpOtherCallback[ul]->OnComplete(pArgData->sot, STORE_E_OPERATION_CANCELED, NULL, &rError);
  894. }
  895. }
  896. if (pArgData->pCallback)
  897. {
  898. pArgData->pCallback->OnBegin(pArgData->sot, psoi, NULL);
  899. pArgData->pCallback->OnComplete(pArgData->sot, STORE_E_OPERATION_CANCELED, NULL, &rError);
  900. }
  901. pArgDataFree = pArgData;
  902. pArgData = pArgData->pNext;
  903. _FreeArgumentData(pArgDataFree);
  904. }
  905. m_pTaskQueue = NULL;
  906. return S_OK;
  907. }
  908. HRESULT CServerQ::ConnectionAddRef()
  909. {
  910. m_cRefConnection++;
  911. return S_OK;
  912. }
  913. HRESULT CServerQ::ConnectionRelease()
  914. {
  915. if (m_cRefConnection == 0)
  916. return E_UNEXPECTED;
  917. m_cRefConnection--;
  918. return S_OK;
  919. }
  920. #ifdef DEBUG
  921. HRESULT CServerQ::_DBG_DumpQueue()
  922. {
  923. ARGUMENT_DATA *pTask;
  924. TraceInfo("ServerQ pending tasks:");
  925. pTask = m_pCurrentTask;
  926. while (pTask)
  927. {
  928. TraceInfo(_MSG("\tsot=%s", sotToSz(pTask->sot)));
  929. pTask = pTask->pNext;
  930. }
  931. return S_OK;
  932. }
  933. #endif
  934. HRESULT CServerQ::GetAdBarUrl(IStoreCallback *pCallback)
  935. {
  936. ARGUMENT_DATA *pArg;
  937. HRESULT hr = S_OK;
  938. IF_FAILEXIT(hr = _AddToQueue(SOT_GET_ADURL, pCallback, NULL, NULL, NULL, &pArg));
  939. exit:
  940. return hr;
  941. }
  942. HRESULT CServerQ::GetMinPollingInterval(IStoreCallback *pCallback)
  943. {
  944. ARGUMENT_DATA *pArg;
  945. HRESULT hr = S_OK;
  946. IF_FAILEXIT(hr = _AddToQueue(SOT_GET_HTTP_MINPOLLINGINTERVAL, pCallback, NULL, NULL, NULL, &pArg));
  947. exit:
  948. return hr;
  949. }