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.

3704 lines
115 KiB

  1. //-----------------------------------------------------------------------------
  2. //
  3. //
  4. // File: aqadmsvr.cpp
  5. //
  6. // Description: Implements the IAdvQueueAdmin interface for the CAQSvrInst
  7. // object. Also contains implementations of helper functions and classes.
  8. //
  9. // Author: Mike Swafford (MikeSwa)
  10. //
  11. // History:
  12. // 11/30/98 - MikeSwa Created
  13. //
  14. // Copyright (C) 1998 Microsoft Corporation
  15. //
  16. //-----------------------------------------------------------------------------
  17. #include "aqprecmp.h"
  18. #include "aqadmsvr.h"
  19. #include "mailadmq.h"
  20. #include <intrnlqa_i.c>
  21. #define QA_DMT_CONTEXT_SIG 'CDAQ'
  22. //
  23. // Order of recipient address types to check
  24. //
  25. const DWORD g_rgdwQAPIRecipPropIDs[] = {
  26. IMMPID_RP_ADDRESS_SMTP,
  27. IMMPID_RP_ADDRESS_X400,
  28. IMMPID_RP_LEGACY_EX_DN,
  29. IMMPID_RP_ADDRESS_X500,
  30. IMMPID_RP_ADDRESS_OTHER};
  31. const DWORD g_rgdwQAPISenderPropIDs[] = {
  32. IMMPID_MP_SENDER_ADDRESS_SMTP,
  33. IMMPID_MP_SENDER_ADDRESS_X400,
  34. IMMPID_MP_SENDER_ADDRESS_LEGACY_EX_DN,
  35. IMMPID_MP_SENDER_ADDRESS_X500,
  36. IMMPID_MP_SENDER_ADDRESS_OTHER};
  37. const DWORD g_cQAPIAddressTypes = 5;
  38. #define QAPI_SMTP_ADDRESS_TYPE L"SMTP:"
  39. #define QAPI_X400_ADDRESS_TYPE L"X400:"
  40. #define QAPI_EX_ADDRESS_TYPE L"EX:"
  41. #define QAPI_X500_ADDRESS_TYPE L"X500:"
  42. #define QAPI_OTHER_ADDRESS_TYPE L"X-UNKNOWN:"
  43. const WCHAR *g_rgwszQAPIAddressTypes[] = {
  44. QAPI_SMTP_ADDRESS_TYPE,
  45. QAPI_X400_ADDRESS_TYPE,
  46. QAPI_EX_ADDRESS_TYPE,
  47. QAPI_X500_ADDRESS_TYPE,
  48. QAPI_OTHER_ADDRESS_TYPE};
  49. const DWORD g_rgcbQAPIAddressTypes[] = {
  50. sizeof(QAPI_SMTP_ADDRESS_TYPE),
  51. sizeof(QAPI_X400_ADDRESS_TYPE),
  52. sizeof(QAPI_EX_ADDRESS_TYPE),
  53. sizeof(QAPI_X500_ADDRESS_TYPE),
  54. sizeof(QAPI_OTHER_ADDRESS_TYPE)};
  55. //---[ fVerifyQAPIAddressTypes ]-----------------------------------------------
  56. //
  57. //
  58. // Description:
  59. // Validates that the above global structures are in synch
  60. // Parameters:
  61. // -
  62. // Returns:
  63. // TRUE on success
  64. // FALSE on failure
  65. // History:
  66. // 2/19/2001 - MikeSwa Created
  67. //
  68. //-----------------------------------------------------------------------------
  69. inline BOOL fVerifyQAPIAddressTypes()
  70. {
  71. static BOOL fVerificationDone = FALSE;
  72. if (!fVerificationDone)
  73. {
  74. //
  75. // Loop through all recipiences
  76. //
  77. for (DWORD i = 0; i < g_cQAPIAddressTypes; i++)
  78. {
  79. DWORD cbAddressType = (wcslen(g_rgwszQAPIAddressTypes[i])+1)*sizeof(WCHAR);
  80. if (g_rgcbQAPIAddressTypes[i] != cbAddressType)
  81. return FALSE;
  82. }
  83. fVerificationDone = TRUE;
  84. }
  85. return TRUE;
  86. }
  87. //Used to check *client* supplied structures against versions. RPC supplied
  88. //structures are not checked.
  89. inline BOOL fCheckCurrentVersion(DWORD dwVersion)
  90. {
  91. return (((DWORD)CURRENT_QUEUE_ADMIN_VERSION) == dwVersion);
  92. }
  93. //---[ QueueAdminDNTIteratorContext ]------------------------------------------
  94. //
  95. //
  96. // Description:
  97. // Context passed to QueueAdmin DMT iterator functions.
  98. // Hungarian:
  99. // qadntc, pqadntc
  100. //
  101. //-----------------------------------------------------------------------------
  102. class QueueAdminDMTIteratorContext
  103. {
  104. public:
  105. QueueAdminDMTIteratorContext()
  106. {
  107. ZeroMemory(this, sizeof(QueueAdminDMTIteratorContext));
  108. m_dwSignature = QA_DMT_CONTEXT_SIG;
  109. };
  110. DWORD m_dwSignature;
  111. DWORD m_cItemsToReturn;
  112. DWORD m_cItemsFound;
  113. HRESULT m_hrResult;
  114. QUEUELINK_ID *m_rgLinkIDs;
  115. QUEUELINK_ID *m_pCurrentLinkID;
  116. QueueAdminMapFn m_pfn;
  117. CAQAdminMessageFilter *m_paqmf;
  118. IQueueAdminMessageFilter *m_pIQueueAdminMessageFilter;
  119. };
  120. //---[ SanitizeCountAndVolume ]------------------------------------------------
  121. //
  122. //
  123. // Description:
  124. // Make queue count and volume sutable for user consumption. There are
  125. // intended timing windows where the internal versions of these counts
  126. // may drop to zero. Rather than redesign this, we just display zero
  127. // to the admin.
  128. // Parameters:
  129. // IN OUT pcCount Count to check and update
  130. // IN OUT puliVolume Queue volume to check and update
  131. // Returns:
  132. // -
  133. // History:
  134. // 1/28/2000 - MikeSwa Created
  135. //
  136. //-----------------------------------------------------------------------------
  137. VOID SanitizeCountAndVolume(IN OUT DWORD *pcCount,
  138. IN OUT ULARGE_INTEGER *puliVolume)
  139. {
  140. TraceFunctEnterEx(0, "SanitizeCountAndVolume");
  141. _ASSERT(pcCount);
  142. _ASSERT(puliVolume);
  143. //
  144. // If we are negative sanitize to size zero
  145. //
  146. if (*pcCount > 0xFFFFF000)
  147. {
  148. DebugTrace(0, "Sanitizing msg count of %d", *pcCount);
  149. *pcCount = 0;
  150. puliVolume->QuadPart = 0;
  151. }
  152. TraceFunctLeave();
  153. }
  154. //---[ IterateDMTAndGetLinkIDs ]------------------------------------------------
  155. //
  156. //
  157. // Description:
  158. // Iterator function used to walk the DMT and generate the perf counters
  159. // we are interested in.
  160. // Parameters:
  161. // IN pvContext - pointer to QueueAdminDMTIteratorContext
  162. // IN pvData - CDomainEntry for the given domain
  163. // IN fWildcardData - TRUE if data is a wildcard entry (ignored)
  164. // OUT pfContinue - TRUE if iterator should continue to the next entry
  165. // OUT pfDelete - TRUE if entry should be deleted
  166. // Returns:
  167. // -
  168. // History:
  169. // 12/3/98 - MikeSwa Created
  170. //
  171. //-----------------------------------------------------------------------------
  172. VOID IterateDMTAndGetLinkIDs(PVOID pvContext, PVOID pvData,
  173. BOOL fWildcard, BOOL *pfContinue,
  174. BOOL *pfDelete)
  175. {
  176. TraceFunctEnterEx((LPARAM) NULL, "IterateDMTAndGetLinkIDs");
  177. CDomainEntry *pdentry = (CDomainEntry *) pvData;
  178. QueueAdminDMTIteratorContext *paqdntc = (QueueAdminDMTIteratorContext *)pvContext;
  179. CLinkMsgQueue *plmq = NULL;
  180. CDomainEntryLinkIterator delit;
  181. HRESULT hr = S_OK;
  182. _ASSERT(pvContext);
  183. _ASSERT(pvData);
  184. _ASSERT(pfContinue);
  185. _ASSERT(pfDelete);
  186. *pfContinue = TRUE;
  187. *pfDelete = FALSE;
  188. //Iterate of all links for this domain entry
  189. hr = delit.HrInitialize(pdentry);
  190. if (FAILED(hr))
  191. {
  192. ErrorTrace((LPARAM) NULL,
  193. "Unable to enumerate domain entry for link IDs - 0x%08X", hr);
  194. goto Exit;
  195. }
  196. do
  197. {
  198. plmq = delit.plmqGetNextLinkMsgQueue(plmq);
  199. if (!plmq)
  200. break;
  201. //See if we are running out of room to return data
  202. if (paqdntc->m_cItemsToReturn <= paqdntc->m_cItemsFound)
  203. {
  204. paqdntc->m_hrResult = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  205. goto Exit;
  206. }
  207. //Have link fill out link info struct
  208. paqdntc->m_hrResult = plmq->HrGetLinkID(paqdntc->m_pCurrentLinkID);
  209. if (FAILED(paqdntc->m_hrResult))
  210. goto Exit;
  211. //Point to next info in array
  212. paqdntc->m_pCurrentLinkID++;
  213. paqdntc->m_cItemsFound++;
  214. } while (plmq);
  215. Exit:
  216. if (plmq)
  217. plmq->Release();
  218. //If we have encountered a failure... do not continue
  219. if (FAILED(paqdntc->m_hrResult))
  220. *pfContinue = FALSE;
  221. TraceFunctLeave();
  222. }
  223. //---[ IterateDMTAndApplyQueueAdminFunction ]----------------------------------
  224. //
  225. //
  226. // Description:
  227. // Iterator function used to walk the DMT and generate the perf counters
  228. // we are interested in.
  229. // Parameters:
  230. // IN pvContext - pointer to QueueAdminDMTIteratorContext
  231. // IN pvData - CDomainEntry for the given domain
  232. // IN fWildcardData - TRUE if data is a wildcard entry (ignored)
  233. // OUT pfContinue - TRUE if iterator should continue to the next entry
  234. // OUT pfDelete - TRUE if entry should be deleted
  235. // Returns:
  236. // -
  237. // History:
  238. // 12/11/98 - MikeSwa Created
  239. //
  240. //-----------------------------------------------------------------------------
  241. VOID IterateDMTAndApplyQueueAdminFunction(PVOID pvContext, PVOID pvData,
  242. BOOL fWildcard, BOOL *pfContinue,
  243. BOOL *pfDelete)
  244. {
  245. CDomainEntry *pdentry = (CDomainEntry *) pvData;
  246. QueueAdminDMTIteratorContext *paqdntc = (QueueAdminDMTIteratorContext *)pvContext;
  247. CDestMsgQueue *pdmq = NULL;
  248. HRESULT hr = S_OK;
  249. CDomainEntryQueueIterator deqit;
  250. _ASSERT(pvContext);
  251. _ASSERT(pvData);
  252. _ASSERT(pfContinue);
  253. _ASSERT(pfDelete);
  254. _ASSERT(paqdntc->m_paqmf);
  255. _ASSERT(paqdntc->m_pIQueueAdminMessageFilter);
  256. *pfContinue = TRUE;
  257. *pfDelete = FALSE;
  258. //Iterate of all links for this domain entry
  259. hr = deqit.HrInitialize(pdentry);
  260. if (FAILED(hr))
  261. return;
  262. do
  263. {
  264. pdmq = deqit.pdmqGetNextDestMsgQueue(pdmq);
  265. if (!pdmq)
  266. break;
  267. paqdntc->m_hrResult = pdmq->HrApplyQueueAdminFunction(
  268. paqdntc->m_pIQueueAdminMessageFilter);
  269. paqdntc->m_cItemsFound++;
  270. } while (pdmq && SUCCEEDED(paqdntc->m_hrResult));
  271. if (pdmq)
  272. pdmq->Release();
  273. //If we have encountered a failure... do not continue
  274. if (FAILED(paqdntc->m_hrResult))
  275. *pfContinue = FALSE;
  276. }
  277. //---[ QueueAdminApplyActionToMessages ]---------------------------------------
  278. //
  279. //
  280. // Description:
  281. // FifoQ map function that is used to apply actions to messages.
  282. // Parameters:
  283. // IN pmsgref ptr to data on queue
  284. // IN pvContext CAQAdminMessageFilter used
  285. // OUT pfContinue TRUE if we should continue
  286. // OUT pfDelete TRUE if item should be deleted
  287. // Returns:
  288. // S_OK on sucess
  289. // History:
  290. // 12/7/98 - MikeSwa Created
  291. // 2/21/99 - MikeSwa Updated to support new IQueueAdmin* interfaces
  292. //
  293. //-----------------------------------------------------------------------------
  294. HRESULT QueueAdminApplyActionToMessages(IN CMsgRef *pmsgref, IN PVOID pvContext,
  295. OUT BOOL *pfContinue, OUT BOOL *pfDelete)
  296. {
  297. _ASSERT(pmsgref);
  298. _ASSERT(pvContext);
  299. _ASSERT(pfContinue);
  300. _ASSERT(pfDelete);
  301. IQueueAdminMessageFilter *pIQueueAdminMessageFilter =
  302. (IQueueAdminMessageFilter *) pvContext;
  303. HRESULT hr = S_OK;
  304. IUnknown *pIUnknownMsg = NULL;
  305. hr = pmsgref->QueryInterface(IID_IUnknown, (void **) &pIUnknownMsg);
  306. _ASSERT(SUCCEEDED(hr) && "QueryInterface for IUnknown failed");
  307. if (FAILED(hr))
  308. {
  309. *pfContinue = FALSE;
  310. goto Exit;
  311. }
  312. hr = pIQueueAdminMessageFilter->HrProcessMessage(pIUnknownMsg,
  313. pfContinue, pfDelete);
  314. if (FAILED(hr))
  315. {
  316. *pfContinue = FALSE;
  317. goto Exit;
  318. }
  319. Exit:
  320. if (pIUnknownMsg)
  321. pIUnknownMsg->Release();
  322. return hr;
  323. }
  324. //---[ CAQAdminMessageFilter::HrProcessMessage ]-------------------------------
  325. //
  326. //
  327. // Description:
  328. // Processes a single message during an iterator funtion
  329. // Parameters:
  330. // IN pIUnknownMsg IUnknown ptr for message
  331. // OUT pfContinue TRUE if iterator should continue
  332. // OUT pfDelete TRUE if iterator should delete from the queue
  333. // Returns:
  334. // S_OK on success
  335. // History:
  336. // 2/21/99 - MikeSwa Created
  337. // 8/9/00 - t-toddc modifed to support IMailMsgProperties as well
  338. //
  339. //-----------------------------------------------------------------------------
  340. STDMETHODIMP CAQAdminMessageFilter::HrProcessMessage(
  341. IUnknown *pIUnknownMsg,
  342. BOOL *pfContinue,
  343. BOOL *pfDelete)
  344. {
  345. TraceFunctEnterEx((LPARAM) this, "CAQAdminMessageFilter::HrProcessMessage");
  346. HRESULT hr = S_OK;
  347. CMsgRef *pmsgref = NULL;
  348. IMailMsgProperties* pIMailMsgProperties = NULL;
  349. BOOL fMsgTypeIsCMsgRef = FALSE;
  350. BOOL fMatchesFilter = FALSE;
  351. _ASSERT(pfContinue);
  352. _ASSERT(pfDelete);
  353. _ASSERT(pIUnknownMsg);
  354. _ASSERT(m_pIQueueAdminAction);
  355. if (!pfContinue || !pfDelete || !pIUnknownMsg)
  356. {
  357. hr = E_POINTER;
  358. goto Exit;
  359. }
  360. if (!m_pIQueueAdminAction)
  361. {
  362. hr = E_FAIL;
  363. goto Exit;
  364. }
  365. *pfContinue = TRUE;
  366. *pfDelete = FALSE;
  367. if (fFoundEnoughMsgs())
  368. {
  369. *pfContinue = FALSE;
  370. goto Exit;
  371. }
  372. //Check and see if we should skip this message (paging functionality)
  373. if ((AQ_MSG_FILTER_ENUMERATION & m_dwFilterFlags) && fSkipMsg())
  374. goto Exit;
  375. //Get CMsgRef "interface", or IMailMsgProperties interface
  376. hr = pIUnknownMsg->QueryInterface(IID_CMsgRef, (void **) &pmsgref);
  377. if (SUCCEEDED(hr))
  378. {
  379. fMsgTypeIsCMsgRef = TRUE;
  380. }
  381. else
  382. {
  383. //
  384. // If it is not a CMsgRef... then it must be an IMailMsgProperties
  385. //
  386. hr = pIUnknownMsg->QueryInterface(IID_IMailMsgProperties,
  387. (void **) &pIMailMsgProperties);
  388. if (FAILED(hr))
  389. {
  390. _ASSERT(FALSE && "Unable to QI for msgref or IMailMsgProperties");
  391. ErrorTrace((LPARAM) this,
  392. "Unable to QI for msgref or mailmsg 0x%08X", hr);
  393. goto Exit;
  394. }
  395. }
  396. if (fMsgTypeIsCMsgRef)
  397. fMatchesFilter = pmsgref->fMatchesQueueAdminFilter(this);
  398. else
  399. fMatchesFilter = CAsyncAdminMailMsgQueue::fMatchesQueueAdminFilter(
  400. pIMailMsgProperties, this);
  401. if (fMatchesFilter)
  402. {
  403. if (AQ_MSG_FILTER_ACTION & m_dwFilterFlags)
  404. {
  405. //Apply action & say that we found another that matches filter
  406. hr = m_pIQueueAdminAction->HrApplyActionToMessage(pIUnknownMsg,
  407. m_dwMessageAction,
  408. m_pvUserContext,
  409. pfDelete);
  410. if (FAILED(hr))
  411. goto Exit;
  412. }
  413. else if (AQ_MSG_FILTER_ENUMERATION & m_dwFilterFlags)
  414. {
  415. //$$TODO - Handle slightly more complex filters like
  416. // - N largest
  417. // - N oldest
  418. //that may require matching, sorting, and throwing away previous matches.
  419. if (pmfGetMsgInfo())
  420. {
  421. if (fMsgTypeIsCMsgRef)
  422. hr = pmsgref->HrGetQueueAdminMsgInfo(pmfGetMsgInfo(),
  423. m_pIQueueAdminAction);
  424. else
  425. hr = CAsyncAdminMailMsgQueue::HrGetQueueAdminMsgInfo(
  426. pIMailMsgProperties, pmfGetMsgInfo(), m_pvUserContext);
  427. }
  428. }
  429. else
  430. _ASSERT(0 && "Unknown message enumeration");
  431. //Mark as found and see if we should continue
  432. if (SUCCEEDED(hr) && fFoundMsg())
  433. *pfContinue = FALSE;
  434. }
  435. Exit:
  436. if (pmsgref)
  437. pmsgref->Release();
  438. if (pIMailMsgProperties)
  439. pIMailMsgProperties->Release();
  440. //See if backing store for message has been deleted
  441. if (AQUEUE_E_MESSAGE_HANDLED == hr)
  442. {
  443. DebugTrace((LPARAM) this, "Found handled message in queue enumeration");
  444. hr = S_OK; //do not fail out of enumeration for a handled message
  445. }
  446. TraceFunctLeave();
  447. return hr;
  448. }
  449. //---[ CAQAdminMessageFilter::HrSetQueueAdminAction ]--------------------------
  450. //
  451. //
  452. // Description:
  453. // Sets the IQueueAdminAction interface for filter
  454. // Parameters:
  455. // IN pIQueueAdminAction Interface for filter
  456. // Returns:
  457. // S_OK on success
  458. // History:
  459. // 2/21/99 - MikeSwa Created
  460. //
  461. //-----------------------------------------------------------------------------
  462. STDMETHODIMP CAQAdminMessageFilter::HrSetQueueAdminAction(
  463. IQueueAdminAction *pIQueueAdminAction)
  464. {
  465. _ASSERT(pIQueueAdminAction);
  466. if (!pIQueueAdminAction)
  467. return E_POINTER;
  468. if (m_pIQueueAdminAction)
  469. m_pIQueueAdminAction->Release();
  470. m_pIQueueAdminAction = pIQueueAdminAction;
  471. m_pIQueueAdminAction->AddRef();
  472. return S_OK;
  473. }
  474. //---[ CAQAdminMessageFilter::HrSetCurrentUserContext ]-------------------------
  475. //
  476. //
  477. // Description:
  478. // Sets a context that is distinct from the pIQueueAdminAction interface
  479. // and is passed to the IQueueAdminAction interface. This can be used
  480. // by a IQueueAdminAction interface to allow per-session state so
  481. // multiple threads can act on a single IQueueAdminAction.
  482. //
  483. // The actual content of the context is left to the implementation of
  484. // IQueueAdminAction.
  485. // Parameters:
  486. // IN pvContext The context passed in
  487. // Returns:
  488. // S_OK always
  489. // History:
  490. // 4/2/99 - MikeSwa Created
  491. //
  492. //-----------------------------------------------------------------------------
  493. STDMETHODIMP CAQAdminMessageFilter::HrSetCurrentUserContext(
  494. PVOID pvContext)
  495. {
  496. m_pvUserContext = pvContext;
  497. return S_OK;
  498. };
  499. //---[ CAQAdminMessageFilter::HrGetCurrentUserContext ]-------------------------
  500. //
  501. //
  502. // Description:
  503. // Returns the context previously set by HrSetCurrentUserContext
  504. // Parameters:
  505. // OUT ppvContext The context previously set
  506. // Returns:
  507. // S_OK if ppvContext is non-NULL
  508. // E_POINTER if ppvContext is NULL
  509. // History:
  510. // 4/2/99 - MikeSwa Created
  511. //
  512. //-----------------------------------------------------------------------------
  513. STDMETHODIMP CAQAdminMessageFilter::HrGetCurrentUserContext(
  514. PVOID *ppvContext)
  515. {
  516. if (!ppvContext)
  517. return E_POINTER;
  518. *ppvContext = m_pvUserContext;
  519. return S_OK;
  520. };
  521. //---[ CAQAdminMessageFilter::QueryInterface ]--------------------------------
  522. //
  523. //
  524. // Description:
  525. // QueryInterface for CDestMsgQueue that supports:
  526. // - IQueueAdminMessageFilter
  527. // - IUnknown
  528. // Parameters:
  529. //
  530. // Returns:
  531. //
  532. // History:
  533. // 2/21/99 - MikeSwa Created
  534. //
  535. //-----------------------------------------------------------------------------
  536. STDMETHODIMP CAQAdminMessageFilter::QueryInterface(REFIID riid, LPVOID *ppvObj)
  537. {
  538. HRESULT hr = S_OK;
  539. if (!ppvObj)
  540. {
  541. hr = E_POINTER;
  542. goto Exit;
  543. }
  544. if (IID_IUnknown == riid)
  545. {
  546. *ppvObj = static_cast<IQueueAdminMessageFilter *>(this);
  547. }
  548. else if (IID_IQueueAdminMessageFilter == riid)
  549. {
  550. *ppvObj = static_cast<IQueueAdminMessageFilter *>(this);
  551. }
  552. else
  553. {
  554. *ppvObj = NULL;
  555. hr = E_NOINTERFACE;
  556. goto Exit;
  557. }
  558. static_cast<IUnknown *>(*ppvObj)->AddRef();
  559. Exit:
  560. return hr;
  561. }
  562. //---[ HrLinkFromLinkID ]------------------------------------------------------
  563. //
  564. //
  565. // Description:
  566. // Utility function used to get the IQueueAdminLink for a giben QUEUELINK_ID
  567. // Parameters:
  568. // IN pdmq CDomainMappingTable for this virtual server instance
  569. // IN pqlLinkID QUEUELINK_ID for link we are trying to find
  570. // OUT pIQueueAdminLink link interface returned
  571. // Returns:
  572. // S_OK on success
  573. // E_INVALIDARG if pqlLinkID is invalid
  574. // Error codes from HrGetDomainEntry and HrGetLinkMsgQueue on failure
  575. // History:
  576. // 12/4/98 - MikeSwa Created
  577. // 2/23/99 - MikeSwa Updated for IQueueAdmin* interfaces
  578. // 12/11/2000 - MikeSwa Added presubmission link (from t-toddc's work)
  579. //
  580. //-----------------------------------------------------------------------------
  581. HRESULT CAQSvrInst::HrLinkFromLinkID(QUEUELINK_ID *pqlLinkID,
  582. IQueueAdminLink **ppIQueueAdminLink)
  583. {
  584. _ASSERT(pqlLinkID);
  585. _ASSERT(ppIQueueAdminLink);
  586. _ASSERT(QLT_LINK == pqlLinkID->qltType);
  587. _ASSERT(pqlLinkID->szName);
  588. HRESULT hr = S_OK;
  589. LPSTR szDomain = NULL;
  590. DWORD cbDomain = 0;
  591. CDomainEntry *pdentry = NULL;
  592. CLinkMsgQueue *plmq = NULL;
  593. CMailMsgAdminLink *pmmaq = NULL;
  594. CAQScheduleID aqsched(pqlLinkID->uuid, pqlLinkID->dwId);
  595. BOOL flinkmatched = FALSE;
  596. *ppIQueueAdminLink = NULL;
  597. if ((QLT_LINK != pqlLinkID->qltType) || !pqlLinkID->szName)
  598. {
  599. hr = E_INVALIDARG;
  600. goto Exit;
  601. }
  602. szDomain = szUnicodeToAscii(pqlLinkID->szName);
  603. if (!szDomain)
  604. {
  605. hr = E_OUTOFMEMORY;
  606. goto Exit;
  607. }
  608. //Get the domain entry that we are interesting in
  609. cbDomain = lstrlen(szDomain);
  610. hr = m_dmt.HrGetDomainEntry(cbDomain, szDomain,
  611. &pdentry);
  612. if (SUCCEEDED(hr))
  613. {
  614. //Search domain entry to link with corresponding router id/schedule id
  615. hr = pdentry->HrGetLinkMsgQueue(&aqsched, &plmq);
  616. if (FAILED(hr))
  617. goto Exit;
  618. flinkmatched = TRUE; //found a link for this domain
  619. }
  620. else
  621. flinkmatched = FALSE;
  622. //Try special links
  623. //check local link
  624. if (!flinkmatched)
  625. {
  626. if(plmq = m_dmt.plmqGetLocalLink())
  627. {
  628. if (plmq->fMatchesID(pqlLinkID))
  629. {
  630. flinkmatched = TRUE;
  631. hr = S_OK;
  632. }
  633. else
  634. {
  635. flinkmatched = FALSE;
  636. plmq->Release();
  637. plmq = NULL;
  638. }
  639. }
  640. }
  641. //unable to find local link, check currently unreachable link
  642. if (!flinkmatched)
  643. {
  644. if(plmq = m_dmt.plmqGetCurrentlyUnreachable())
  645. {
  646. if (plmq->fMatchesID(pqlLinkID))
  647. {
  648. flinkmatched = TRUE;
  649. hr = S_OK;
  650. }
  651. else
  652. {
  653. flinkmatched = FALSE;
  654. plmq->Release();
  655. plmq = NULL;
  656. }
  657. }
  658. }
  659. //unable to find currently unreachable link, check precat link
  660. if (!flinkmatched)
  661. {
  662. if(pmmaq = m_dmt.pmmaqGetPreCategorized())
  663. {
  664. if (pmmaq->fMatchesID(pqlLinkID))
  665. {
  666. flinkmatched = TRUE;
  667. hr = S_OK;
  668. }
  669. else
  670. {
  671. flinkmatched = FALSE;
  672. pmmaq->Release();
  673. pmmaq = NULL;
  674. }
  675. }
  676. }
  677. //unable to find currently unreachable link, check prerouting link
  678. if (!flinkmatched)
  679. {
  680. if(pmmaq = m_dmt.pmmaqGetPreRouting())
  681. {
  682. if (pmmaq->fMatchesID(pqlLinkID))
  683. {
  684. flinkmatched = TRUE;
  685. hr = S_OK;
  686. }
  687. else
  688. {
  689. flinkmatched = FALSE;
  690. pmmaq->Release();
  691. pmmaq = NULL;
  692. }
  693. }
  694. }
  695. //unable to find currently prerouting link, check presubmission link
  696. if (!flinkmatched)
  697. {
  698. if(pmmaq = m_dmt.pmmaqGetPreSubmission())
  699. {
  700. if (pmmaq->fMatchesID(pqlLinkID))
  701. {
  702. flinkmatched = TRUE;
  703. hr = S_OK;
  704. }
  705. else
  706. {
  707. flinkmatched = FALSE;
  708. pmmaq->Release();
  709. pmmaq = NULL;
  710. }
  711. }
  712. }
  713. //unable to find any matching link
  714. if (!flinkmatched)
  715. goto Exit;
  716. if (plmq)
  717. {
  718. hr = plmq->QueryInterface(IID_IQueueAdminLink, (void **)ppIQueueAdminLink);
  719. }
  720. else if (pmmaq)
  721. {
  722. hr = pmmaq->QueryInterface(IID_IQueueAdminLink, (void **)ppIQueueAdminLink);
  723. }
  724. _ASSERT(SUCCEEDED(hr) && "QI for LMQ->IQueueAdminLink failed!!!");
  725. if (FAILED(hr))
  726. goto Exit;
  727. Exit:
  728. if (pdentry)
  729. pdentry->Release();
  730. if (plmq)
  731. plmq->Release();
  732. if (pmmaq)
  733. pmmaq->Release();
  734. if (szDomain)
  735. FreePv(szDomain);
  736. return hr;
  737. }
  738. //---[ HrQueueFromQueueID ]-----------------------------------------------------
  739. //
  740. //
  741. // Description:
  742. // Queue Admin utility function that is used to look up a IQueueAdminQueue
  743. // given a QUEUELINK_ID.
  744. // Parameters:
  745. // IN pdmq CDomainMappingTable for this virtual server instance
  746. // IN pqlLinkID QUEUELINK_ID for queue we are trying to find
  747. // OUT ppIQueueAdminQueue DestMsgQueue we are searching for
  748. // Returns:
  749. // S_OK on success
  750. // E_INVALIDARG if pqlLinkID is invalid
  751. // Error codes from HrGetDomainEntry and HrGetLinkMsgQueue on failure
  752. // History:
  753. // 12/7/98 - MikeSwa Created
  754. // 2/22/99 - MikeSwa Modified to return IQueueAdminQueue interface
  755. //
  756. //-----------------------------------------------------------------------------
  757. HRESULT CAQSvrInst::HrQueueFromQueueID(QUEUELINK_ID *pqlQueueId,
  758. IQueueAdminQueue **ppIQueueAdminQueue)
  759. {
  760. _ASSERT(pqlQueueId);
  761. _ASSERT(ppIQueueAdminQueue);
  762. _ASSERT(QLT_QUEUE == pqlQueueId->qltType);
  763. _ASSERT(pqlQueueId->szName);
  764. HRESULT hr = S_OK;
  765. HRESULT hrTmp = S_OK;
  766. LPSTR szDomain = NULL;
  767. DWORD cbDomain = 0;
  768. CDomainEntry *pdentry = NULL;
  769. CDestMsgQueue *pdmq = NULL;
  770. CAQMessageType aqmt(pqlQueueId->uuid, pqlQueueId->dwId);
  771. IQueueAdminAction *pIQueueAdminAction = NULL;
  772. *ppIQueueAdminQueue = NULL;
  773. if ((QLT_QUEUE != pqlQueueId->qltType) || !pqlQueueId->szName)
  774. {
  775. hr = E_INVALIDARG;
  776. goto Exit;
  777. }
  778. szDomain = szUnicodeToAscii(pqlQueueId->szName);
  779. if (!szDomain)
  780. {
  781. hr = E_OUTOFMEMORY;
  782. goto Exit;
  783. }
  784. //Get the domain entry that we are interested in
  785. cbDomain = lstrlen(szDomain);
  786. hr = m_dmt.HrGetDomainEntry(cbDomain, szDomain,
  787. &pdentry);
  788. if (FAILED(hr))
  789. {
  790. //
  791. // If it is not in the DMT... try our internal queues
  792. //
  793. hr = HrInternalQueueFromQueueID(pqlQueueId,
  794. ppIQueueAdminQueue);
  795. // either way, failure or success, head to exit.
  796. goto Exit;
  797. }
  798. //Search domain entry to link with corresponding router id/schedule id
  799. _ASSERT(pdentry);
  800. hr = pdentry->HrGetDestMsgQueue(&aqmt, &pdmq);
  801. if (FAILED(hr))
  802. goto Exit;
  803. _ASSERT(pdmq);
  804. hr = pdmq->QueryInterface(IID_IQueueAdminQueue,
  805. (void **) ppIQueueAdminQueue);
  806. if (FAILED(hr))
  807. goto Exit;
  808. Exit:
  809. if (FAILED(hr) && (*ppIQueueAdminQueue))
  810. {
  811. (*ppIQueueAdminQueue)->Release();
  812. *ppIQueueAdminQueue = NULL;
  813. }
  814. if (pIQueueAdminAction)
  815. pIQueueAdminAction->Release();
  816. if (pdentry)
  817. pdentry->Release();
  818. if (pdmq)
  819. pdmq->Release();
  820. if (szDomain)
  821. FreePv(szDomain);
  822. return hr;
  823. }
  824. //---[ HrInternalQueueFromQueueID ]--------------------------------------------
  825. //
  826. //
  827. // Description:
  828. // Queue Admin utility function that is used to look up an internal
  829. // IQueueAdminQueue given a QUEUELINK_ID.
  830. // Parameters:
  831. // IN pqlQueueID QUEUELINK_ID for queue we are trying to find
  832. // OUT ppIQueueAdminQueue Internal queue we are searching for
  833. // Returns:
  834. // S_OK on success
  835. // E_INVALIDARG if pqlLinkID is invalid
  836. // AQUEUE_E_INVALID_DOMAIN if the queue was not found among our internal queues
  837. // History:
  838. //
  839. // 8/9/00 - t-toddc created
  840. // 12/11/2000 - MikeSwa Modified for Hg checkin
  841. //
  842. //-----------------------------------------------------------------------------
  843. HRESULT CAQSvrInst::HrInternalQueueFromQueueID(QUEUELINK_ID *pqlQueueId,
  844. IQueueAdminQueue **ppIQueueAdminQueue)
  845. {
  846. HRESULT hr = S_OK;
  847. IQueueAdminQueue* pIQueueAdminQueue = NULL;
  848. IQueueAdminAction* pIQueueAdminAction = NULL;
  849. DWORD i = 0;
  850. BOOL fMatch = FALSE;
  851. IQueueAdminQueue* ppInternalQueues[] =
  852. { &m_asyncqPreLocalDeliveryQueue,
  853. &m_asyncqPreSubmissionQueue,
  854. &m_asyncqPreCatQueue,
  855. &m_asyncqPreRoutingQueue
  856. };
  857. _ASSERT(pqlQueueId);
  858. _ASSERT(ppIQueueAdminQueue);
  859. //
  860. // Loop over all of our internal queues and see if they match
  861. //
  862. for(i = 0; i < sizeof(ppInternalQueues)/sizeof(IQueueAdminQueue*); i++)
  863. {
  864. hr = ppInternalQueues[i]->QueryInterface(IID_IQueueAdminQueue,
  865. (void **) &pIQueueAdminQueue);
  866. if (FAILED(hr))
  867. goto Cleanup;
  868. _ASSERT(pIQueueAdminQueue);
  869. hr = pIQueueAdminQueue->QueryInterface(IID_IQueueAdminAction,
  870. (void **) &pIQueueAdminAction);
  871. _ASSERT(SUCCEEDED(hr) && "QI for IQueueAdminAction failed on internal queue!!");
  872. if (FAILED(hr))
  873. goto Cleanup;
  874. _ASSERT(pIQueueAdminAction);
  875. fMatch = pIQueueAdminAction->fMatchesID(pqlQueueId);
  876. // now release the IQueueAdminAction interface.
  877. pIQueueAdminAction->Release();
  878. pIQueueAdminAction = NULL;
  879. if (fMatch)
  880. {
  881. // set the output to the matching interface
  882. *ppIQueueAdminQueue = pIQueueAdminQueue;
  883. // set to null so it won't be released on cleanup
  884. pIQueueAdminQueue = NULL;
  885. goto Cleanup;
  886. }
  887. else
  888. {
  889. // release the IQueueAdminQueue - this is not a match
  890. pIQueueAdminQueue->Release();
  891. // set to null so it won't be released again on cleanup
  892. pIQueueAdminQueue = NULL;
  893. }
  894. }
  895. // If we reach this point we did not find the queue
  896. hr = AQUEUE_E_INVALID_DOMAIN;
  897. Cleanup:
  898. if (pIQueueAdminQueue)
  899. pIQueueAdminQueue->Release();
  900. if (pIQueueAdminAction)
  901. pIQueueAdminAction->Release();
  902. return hr;
  903. }
  904. //---[ CAQAdminMessageFilter::~CAQAdminMessageFilter ]-------------------------
  905. //
  906. //
  907. // Description:
  908. // Descructor for CAQAdminMessageFilter
  909. // Parameters:
  910. // -
  911. // Returns:
  912. // -
  913. // History:
  914. // 6/7/99 - MikeSwa Moved from inline function
  915. //
  916. //-----------------------------------------------------------------------------
  917. CAQAdminMessageFilter::~CAQAdminMessageFilter()
  918. {
  919. if (m_pIQueueAdminAction)
  920. m_pIQueueAdminAction->Release();
  921. if (m_szMessageId)
  922. FreePv(m_szMessageId);
  923. if (m_szMessageSender)
  924. FreePv(m_szMessageSender);
  925. if (m_szMessageRecipient)
  926. FreePv(m_szMessageRecipient);
  927. }
  928. //---[ fStripAddressType ]-----------------------------------------------------
  929. //
  930. //
  931. // Description:
  932. // Strips the address type from an address. For example, removes "SMTP:"
  933. // from "SMTP:[email protected]".
  934. // Parameters:
  935. // IN wszAddress Address to strip
  936. // OUT *pwszBareAddress Pointer inside szAddress that skips type prefix.
  937. // This will be szAddress if there is nothing to strip
  938. // OUT *piAddressType Index of address type in global array.
  939. // This will be 0, if there is nothing to strip
  940. //
  941. // Returns:
  942. // TRUE if there was an address type specified
  943. // FALSE if there was no address type specified
  944. // History:
  945. // 3/15/2001 - MikeSwa Created
  946. //
  947. //-----------------------------------------------------------------------------
  948. BOOL fStripAddressType(LPCWSTR wszAddress, OUT LPCWSTR *pwszBareAddress, OUT DWORD *piAddressType)
  949. {
  950. TraceFunctEnterEx((LPARAM) wszAddress, "StripAddressType");
  951. BOOL fAddressTypeSpecified = FALSE;
  952. _ASSERT(pwszBareAddress);
  953. _ASSERT(piAddressType);
  954. *pwszBareAddress = wszAddress;
  955. *piAddressType = 0;
  956. if (!wszAddress)
  957. goto Exit;
  958. //
  959. // See if the filter has specified an address type... strip it off and
  960. // remember it. Skip that last address type because the actual address type
  961. // is stored as part of the recipient property.
  962. //
  963. for (DWORD iCurrentAddressType = 0;
  964. iCurrentAddressType < g_cQAPIAddressTypes-1;
  965. iCurrentAddressType++)
  966. {
  967. if (!_wcsnicmp(wszAddress, g_rgwszQAPIAddressTypes[iCurrentAddressType],
  968. (g_rgcbQAPIAddressTypes[iCurrentAddressType]/sizeof(WCHAR))-1))
  969. {
  970. //
  971. // Set our string pointer to be the first character after the
  972. //
  973. *piAddressType = iCurrentAddressType;
  974. *pwszBareAddress += (g_rgcbQAPIAddressTypes[iCurrentAddressType]/sizeof(WCHAR))-1;
  975. fAddressTypeSpecified = TRUE;
  976. break;
  977. }
  978. }
  979. DebugTrace((LPARAM) wszAddress,
  980. "Address %S has an stripped address of %S",
  981. wszAddress, *pwszBareAddress);
  982. Exit:
  983. TraceFunctLeave();
  984. return fAddressTypeSpecified;
  985. }
  986. //---[ CAQAdminMessageFilter::InitFromMsgFilter ]------------------------------
  987. //
  988. //
  989. // Description:
  990. // Initializes a CAQAdminMessageFilter from a MESSAGE_FILTER structure.
  991. // Parameters:
  992. // IN pmf Ptr to MESSAGE_FILTER to initialize from
  993. // Returns:
  994. // -
  995. // History:
  996. // 12/3/98 - MikeSwa Created
  997. // 3/15/2001 - MikeSwa Modified to strip off address type
  998. //
  999. //-----------------------------------------------------------------------------
  1000. void CAQAdminMessageFilter::InitFromMsgFilter(PMESSAGE_FILTER pmf)
  1001. {
  1002. _ASSERT(pmf);
  1003. LPCWSTR wszAddress = NULL;
  1004. if (pmf->fFlags & MF_MESSAGEID)
  1005. {
  1006. m_dwFilterFlags |= AQ_MSG_FILTER_MESSAGEID;
  1007. m_szMessageId = szUnicodeToAscii(pmf->szMessageId);
  1008. m_dwMsgIdHash = dwQueueAdminHash(m_szMessageId);
  1009. }
  1010. if (pmf->fFlags & MF_SENDER)
  1011. {
  1012. m_dwFilterFlags |= AQ_MSG_FILTER_SENDER;
  1013. m_fSenderAddressTypeSpecified = fStripAddressType(
  1014. pmf->szMessageSender, &wszAddress,
  1015. &m_dwSenderAddressType);
  1016. m_szMessageSender = szUnicodeToAscii(wszAddress);
  1017. }
  1018. if (pmf->fFlags & MF_RECIPIENT)
  1019. {
  1020. m_dwFilterFlags |= AQ_MSG_FILTER_RECIPIENT;
  1021. m_fRecipientAddressTypeSpecified = fStripAddressType(
  1022. pmf->szMessageRecipient, &wszAddress,
  1023. &m_dwRecipientAddressType);
  1024. m_szMessageRecipient = szUnicodeToAscii(wszAddress);
  1025. }
  1026. //It doens not make sense to create a filter with a size of 0
  1027. if ((pmf->fFlags & MF_SIZE) && pmf->dwLargerThanSize)
  1028. {
  1029. m_dwFilterFlags |= AQ_MSG_FILTER_LARGER_THAN;
  1030. m_dwThresholdSize = pmf->dwLargerThanSize;
  1031. }
  1032. if (pmf->fFlags & MF_TIME)
  1033. {
  1034. m_dwFilterFlags |= AQ_MSG_FILTER_OLDER_THAN;
  1035. SystemTimeToFileTime(&pmf->stOlderThan, &m_ftThresholdTime);
  1036. }
  1037. if (MF_FROZEN & pmf->fFlags)
  1038. m_dwFilterFlags |= AQ_MSG_FILTER_FROZEN;
  1039. if (MF_ALL & pmf->fFlags)
  1040. m_dwFilterFlags |= AQ_MSG_FILTER_ALL;
  1041. if (MF_INVERTSENSE & pmf->fFlags)
  1042. m_dwFilterFlags |= AQ_MSG_FILTER_INVERTSENSE;
  1043. if (MF_FAILED & pmf->fFlags)
  1044. m_dwFilterFlags |= AQ_MSG_FILTER_FAILED;
  1045. m_dwFilterFlags |= AQ_MSG_FILTER_ACTION;
  1046. }
  1047. //---[ CAQAdminMessageFilter::InitFromMsgEnumFilter ]--------------------------
  1048. //
  1049. //
  1050. // Description:
  1051. // Initializes a CAQAdminMessageFilter from a MESSAGE_ENUM_FILTER struct.
  1052. // Parameters:
  1053. // IN pmef Ptr to MESSAGE_ENUM_FILTER to initialize from
  1054. // Returns:
  1055. // -
  1056. // History:
  1057. // 12/3/98 - MikeSwa Created
  1058. //
  1059. //-----------------------------------------------------------------------------
  1060. void CAQAdminMessageFilter::InitFromMsgEnumFilter(PMESSAGE_ENUM_FILTER pemf)
  1061. {
  1062. LPCWSTR wszAddress = NULL;
  1063. _ASSERT(pemf);
  1064. //only one of MEF_FIRST_N_MESSAGES, MEF_N_LARGEST_MESSAGES, and
  1065. //MEF_N_OLDEST_MESSAGES make sense
  1066. if (MEF_FIRST_N_MESSAGES & pemf->mefType)
  1067. m_dwFilterFlags |= AQ_MSG_FILTER_FIRST_N_MESSAGES;
  1068. else if (MEF_N_LARGEST_MESSAGES & pemf->mefType)
  1069. m_dwFilterFlags |= AQ_MSG_FILTER_N_LARGEST_MESSAGES;
  1070. else if (MEF_N_OLDEST_MESSAGES & pemf->mefType)
  1071. m_dwFilterFlags |= AQ_MSG_FILTER_N_OLDEST_MESSAGES;
  1072. //Check how many messages we should skip (for "paged" results)
  1073. m_cMessagesToSkip = pemf->cSkipMessages;
  1074. if ((AQ_MSG_FILTER_FIRST_N_MESSAGES |
  1075. AQ_MSG_FILTER_N_LARGEST_MESSAGES |
  1076. AQ_MSG_FILTER_N_OLDEST_MESSAGES) & m_dwFilterFlags)
  1077. {
  1078. m_cMessagesToFind = pemf->cMessages;
  1079. }
  1080. if (MEF_OLDER_THAN & pemf->mefType)
  1081. {
  1082. m_dwFilterFlags |= AQ_MSG_FILTER_OLDER_THAN;
  1083. SystemTimeToFileTime(&pemf->stDate, &m_ftThresholdTime);
  1084. }
  1085. if (MEF_LARGER_THAN & pemf->mefType)
  1086. {
  1087. m_dwFilterFlags |= AQ_MSG_FILTER_LARGER_THAN;
  1088. m_dwThresholdSize = pemf->cbSize;
  1089. }
  1090. if (pemf->mefType & MEF_SENDER)
  1091. {
  1092. m_dwFilterFlags |= AQ_MSG_FILTER_SENDER;
  1093. m_fSenderAddressTypeSpecified = fStripAddressType(
  1094. pemf->szMessageSender, &wszAddress,
  1095. &m_dwSenderAddressType);
  1096. m_szMessageSender = szUnicodeToAscii(wszAddress);
  1097. }
  1098. if (pemf->mefType & MEF_RECIPIENT)
  1099. {
  1100. m_dwFilterFlags |= AQ_MSG_FILTER_RECIPIENT;
  1101. m_fRecipientAddressTypeSpecified = fStripAddressType(
  1102. pemf->szMessageRecipient, &wszAddress,
  1103. &m_dwRecipientAddressType);
  1104. m_szMessageRecipient = szUnicodeToAscii(wszAddress);
  1105. }
  1106. if (MEF_FROZEN & pemf->mefType)
  1107. m_dwFilterFlags |= AQ_MSG_FILTER_FROZEN;
  1108. if (MEF_ALL & pemf->mefType)
  1109. m_dwFilterFlags |= AQ_MSG_FILTER_ALL;
  1110. if (MEF_INVERTSENSE & pemf->mefType)
  1111. m_dwFilterFlags |= AQ_MSG_FILTER_INVERTSENSE;
  1112. if (MEF_FAILED & pemf->mefType)
  1113. m_dwFilterFlags |= AQ_MSG_FILTER_FAILED;
  1114. m_dwFilterFlags |= AQ_MSG_FILTER_ENUMERATION;
  1115. }
  1116. //---[ CAQAdminMessageFilter::SetSearchContext ]--------------------------------
  1117. //
  1118. //
  1119. // Description:
  1120. // Sets the search context which describes how many results are needed,
  1121. // and where to store the results
  1122. // Parameters:
  1123. // IN cMessagesToFind Number of results there is room to store
  1124. // IN rgMsgInfo Array of cMessagesToFind MESSAGE_INFO structs
  1125. // to store data
  1126. // Returns:
  1127. // -
  1128. // History:
  1129. // 12/8/98 - MikeSwa Created
  1130. //
  1131. //-----------------------------------------------------------------------------
  1132. void CAQAdminMessageFilter::SetSearchContext(DWORD cMessagesToFind,
  1133. MESSAGE_INFO *rgMsgInfo)
  1134. {
  1135. if (!m_cMessagesToFind || (m_cMessagesToFind > cMessagesToFind))
  1136. m_cMessagesToFind = cMessagesToFind;
  1137. m_rgMsgInfo = rgMsgInfo;
  1138. m_pCurrentMsgInfo = rgMsgInfo;
  1139. };
  1140. //---[ CAQAdminMessageFilter::SetMessageAction ]-------------------------------
  1141. //
  1142. //
  1143. // Description:
  1144. // Sets the action to apply to messages
  1145. // Parameters:
  1146. // IN maMessageAction
  1147. // Returns:
  1148. // -
  1149. // History:
  1150. // 12/10/98 - MikeSwa Created
  1151. //
  1152. //-----------------------------------------------------------------------------
  1153. void CAQAdminMessageFilter::SetMessageAction(MESSAGE_ACTION maMessageAction)
  1154. {
  1155. m_dwMessageAction = maMessageAction;
  1156. }
  1157. //---[ CAQAdminMessageFilter::fFoundEnoughMsgs ]-------------------------------
  1158. //
  1159. //
  1160. // Description:
  1161. // Determines if we have found enough messages for this filter.
  1162. // Parameters:
  1163. // -
  1164. // Returns:
  1165. // TRUE if we have found enough messages to fill this filter
  1166. // FALSE if we haven't
  1167. // History:
  1168. // 12/8/98 - MikeSwa Created
  1169. //
  1170. //-----------------------------------------------------------------------------
  1171. BOOL CAQAdminMessageFilter::fFoundEnoughMsgs()
  1172. {
  1173. //See if we are unlimited or if we've hit our limit
  1174. if (!m_cMessagesToFind) //no limit
  1175. return FALSE;
  1176. else
  1177. return (m_cMessagesFound >= m_cMessagesToFind);
  1178. };
  1179. //---[ CAQAdminMessageFilter::fFoundMsg ]--------------------------------------
  1180. //
  1181. //
  1182. // Description:
  1183. // Used to by the message enumeration code to record finding a message,
  1184. // so internal pointers and counters can be updated
  1185. // Parameters:
  1186. // -
  1187. // Returns:
  1188. // TRUE if we have found enough messages
  1189. // FALSE if we need to find more messages
  1190. // History:
  1191. // 12/8/98 - MikeSwa Created
  1192. //
  1193. //-----------------------------------------------------------------------------
  1194. BOOL CAQAdminMessageFilter::fFoundMsg()
  1195. {
  1196. m_cMessagesFound++;
  1197. if (m_pCurrentMsgInfo)
  1198. m_pCurrentMsgInfo++;
  1199. return fFoundEnoughMsgs();
  1200. };
  1201. //---[ CAQAdminMessageFilter::fMatchesId ]-------------------------------------
  1202. //
  1203. //
  1204. // Description:
  1205. // Returns TRUE if ID matches
  1206. // Parameters:
  1207. // szMessageId String to check
  1208. // Returns:
  1209. // TRUE if match
  1210. // History:
  1211. // 12/9/98 - MikeSwa Created
  1212. //
  1213. //-----------------------------------------------------------------------------
  1214. BOOL CAQAdminMessageFilter::fMatchesId(LPCSTR szMessageId)
  1215. {
  1216. BOOL fStrCmp = FALSE;
  1217. if (szMessageId && m_szMessageId)
  1218. fStrCmp = (0 == lstrcmpi(szMessageId, m_szMessageId));
  1219. else if (!szMessageId && !m_szMessageId)
  1220. fStrCmp = TRUE;
  1221. if (AQ_MSG_FILTER_INVERTSENSE & m_dwFilterFlags)
  1222. fStrCmp = !fStrCmp;
  1223. return fStrCmp;
  1224. }
  1225. //---[ CAQAdminMessageFilter::fMatchesSender ]---------------------------------
  1226. //
  1227. //
  1228. // Description:
  1229. // Checks if the sender of the message matches the sender of filter
  1230. // Parameters:
  1231. // szMessageSender The 822 sender of the message
  1232. // Returns:
  1233. // TRUE on match
  1234. // History:
  1235. // 12/9/98 - MikeSwa Created
  1236. //
  1237. //-----------------------------------------------------------------------------
  1238. BOOL CAQAdminMessageFilter::fMatchesSender(LPCSTR szMessageSender)
  1239. {
  1240. BOOL fStrCmp = FALSE;
  1241. if (szMessageSender && m_szMessageSender)
  1242. {
  1243. fStrCmp = CAddr::IsRecipientInRFC822AddressList(
  1244. (LPSTR) szMessageSender,
  1245. (LPSTR) m_szMessageSender);
  1246. }
  1247. else if (!szMessageSender && !m_szMessageSender)
  1248. fStrCmp = TRUE;
  1249. if (AQ_MSG_FILTER_INVERTSENSE & m_dwFilterFlags)
  1250. fStrCmp = !fStrCmp;
  1251. return fStrCmp;
  1252. }
  1253. //---[ CAQAdminMessageFilter::fMatchesRecipient ]-------------------------------
  1254. //
  1255. //
  1256. // Description:
  1257. // Used to check if the messages recipients match the filters
  1258. // Parameters:
  1259. // szMessageRecipient Recipient list to check
  1260. // Returns:
  1261. // TRUE if matches
  1262. // History:
  1263. // 12/9/98 - MikeSwa Created
  1264. // 2/17/99 - MikeSwa Updated to use smtpaddr lib
  1265. //
  1266. //-----------------------------------------------------------------------------
  1267. BOOL CAQAdminMessageFilter::fMatchesRecipient(LPCSTR szMessageRecipient)
  1268. {
  1269. BOOL fStrCmp = FALSE;
  1270. if (szMessageRecipient && m_szMessageRecipient)
  1271. {
  1272. fStrCmp = CAddr::IsRecipientInRFC822AddressList(
  1273. (LPSTR) szMessageRecipient,
  1274. (LPSTR) m_szMessageRecipient);
  1275. }
  1276. else if (!szMessageRecipient && !m_szMessageRecipient)
  1277. fStrCmp = TRUE;
  1278. if (AQ_MSG_FILTER_INVERTSENSE & m_dwFilterFlags)
  1279. fStrCmp = !fStrCmp;
  1280. return fStrCmp;
  1281. }
  1282. //---[ CAQAdminMessageFilter::fMatchesP1Recipient ]----------------------------
  1283. //
  1284. //
  1285. // Description:
  1286. //
  1287. // Parameters:
  1288. //
  1289. // Returns:
  1290. //
  1291. // History:
  1292. // 2/17/99 - MikeSwa Created
  1293. //
  1294. //-----------------------------------------------------------------------------
  1295. BOOL CAQAdminMessageFilter::fMatchesP1Recipient(
  1296. IMailMsgProperties *pIMailMsgProperties)
  1297. {
  1298. BOOL fStrCmp = FALSE;
  1299. if (pIMailMsgProperties && m_szMessageRecipient)
  1300. {
  1301. fStrCmp = fQueueAdminIsP1Recip(pIMailMsgProperties);
  1302. }
  1303. else if (!pIMailMsgProperties && !m_szMessageRecipient)
  1304. fStrCmp = TRUE;
  1305. if (AQ_MSG_FILTER_INVERTSENSE & m_dwFilterFlags)
  1306. fStrCmp = !fStrCmp;
  1307. return fStrCmp;
  1308. }
  1309. //---[ CAQAdminMessageFilter::fMatchesSize ]-----------------------------------
  1310. //
  1311. //
  1312. // Description:
  1313. // Used to check if the message size matches the filter
  1314. // Parameters:
  1315. // dwSize Size of message to check
  1316. // Returns:
  1317. // TRUE if matches filter
  1318. // History:
  1319. // 12/9/98 - MikeSwa Created
  1320. //
  1321. //-----------------------------------------------------------------------------
  1322. BOOL CAQAdminMessageFilter::fMatchesSize(DWORD dwSize)
  1323. {
  1324. BOOL fMatch = FALSE;
  1325. if (!(AQ_MSG_FILTER_LARGER_THAN & m_dwFilterFlags))
  1326. fMatch = TRUE;
  1327. else if (dwSize > m_dwThresholdSize)
  1328. fMatch = TRUE;
  1329. if (AQ_MSG_FILTER_INVERTSENSE & m_dwFilterFlags)
  1330. fMatch = !fMatch;
  1331. return fMatch;
  1332. }
  1333. //---[ CAQAdminMessageFilter::fMatchesTime ]-----------------------------------
  1334. //
  1335. //
  1336. // Description:
  1337. // Determines if the recieved time of this message matches the filter.
  1338. // Parameters:
  1339. // pftTime Pointer to filetime structure.
  1340. // Returns:
  1341. // TRUE on success
  1342. // History:
  1343. // 12/9/98 - MikeSwa Created
  1344. //
  1345. //-----------------------------------------------------------------------------
  1346. BOOL CAQAdminMessageFilter::fMatchesTime(FILETIME *pftTime)
  1347. {
  1348. BOOL fMatch = FALSE;
  1349. if (!(AQ_MSG_FILTER_OLDER_THAN & m_dwFilterFlags))
  1350. fMatch = TRUE;
  1351. else if (0 > CompareFileTime(pftTime, &m_ftThresholdTime))
  1352. fMatch = TRUE;
  1353. if (AQ_MSG_FILTER_INVERTSENSE & m_dwFilterFlags)
  1354. fMatch = !fMatch;
  1355. return fMatch;
  1356. }
  1357. //---[ CAQAdminMessageFilter::fMatchesMailMsgSender ]--------------------------
  1358. //
  1359. //
  1360. // Description:
  1361. // Checks to see if this filter matches the given mailmsg
  1362. // Parameters:
  1363. // pIMailMsgProperties Mailmsg pointer to check
  1364. // Returns:
  1365. // TRUE if it matches the filter
  1366. // FALSE otherwise
  1367. // History:
  1368. // 3/16/2001 - MikeSwa Created
  1369. //
  1370. //-----------------------------------------------------------------------------
  1371. BOOL CAQAdminMessageFilter::fMatchesMailMsgSender(IMailMsgProperties *pIMailMsgProperties)
  1372. {
  1373. TraceFunctEnterEx((LPARAM) this, "CAQAdminMessageFilter::fMatchesMailMsgSender");
  1374. HRESULT hr = S_OK;
  1375. BOOL fMatch = FALSE;
  1376. LPSTR szSender = NULL;
  1377. DWORD cbSender = 0;
  1378. DWORD iSenderAddressType = 0;
  1379. _ASSERT(pIMailMsgProperties);
  1380. if (AQ_MSG_FILTER_SENDER & m_dwFilterFlags)
  1381. {
  1382. hr = HrQueueAdminGetStringProp(pIMailMsgProperties, IMMPID_MP_RFC822_FROM_ADDRESS,
  1383. &szSender);
  1384. if (FAILED(hr))
  1385. szSender = NULL;
  1386. fMatch = fMatchesSender(szSender);
  1387. //
  1388. // Always check P1 if P2 does not match
  1389. //
  1390. if (!fMatch)
  1391. {
  1392. if (szSender)
  1393. {
  1394. QueueAdminFree(szSender);
  1395. szSender = NULL;
  1396. }
  1397. hr = HrQueueAdminGetP1Sender(pIMailMsgProperties, &szSender,
  1398. &cbSender, &iSenderAddressType,
  1399. m_dwSenderAddressType,
  1400. m_fSenderAddressTypeSpecified);
  1401. if (FAILED(hr))
  1402. szSender = NULL;
  1403. else
  1404. {
  1405. DebugTrace((LPARAM) this,
  1406. "QAPI: Found P1 sender address of type %i:%s",
  1407. iSenderAddressType,
  1408. szSender);
  1409. }
  1410. fMatch = fMatchesSender(szSender);
  1411. }
  1412. if (!fMatch)
  1413. goto Exit;
  1414. }
  1415. Exit:
  1416. if (szSender)
  1417. QueueAdminFree(szSender);
  1418. TraceFunctLeave();
  1419. return fMatch;
  1420. }
  1421. //---[ CAQAdminMessageFilter::fMatchesMailMsgRecipient ]-----------------------
  1422. //
  1423. //
  1424. // Description:
  1425. // Checks to see if this filter matches the given mailmsg recipient
  1426. // Parameters:
  1427. // pIMailMsgProperties Mailmsg pointer to check
  1428. // Returns:
  1429. // TRUE if it matches the filter
  1430. // FALSE otherwise
  1431. // History:
  1432. // 3/16/2001 - MikeSwa Created
  1433. //
  1434. //-----------------------------------------------------------------------------
  1435. BOOL CAQAdminMessageFilter::fMatchesMailMsgRecipient(IMailMsgProperties *pIMailMsgProperties)
  1436. {
  1437. TraceFunctEnterEx((LPARAM) this, "CAQAdminMessageFilter::fMatchesMailMsgRecipient");
  1438. HRESULT hr = S_OK;
  1439. LPSTR szRecip = NULL;
  1440. BOOL fMatch = FALSE;
  1441. _ASSERT(pIMailMsgProperties);
  1442. if (AQ_MSG_FILTER_RECIPIENT & m_dwFilterFlags)
  1443. {
  1444. //Check To, CC, and BCC recipients (if present)
  1445. hr = HrQueueAdminGetStringProp(pIMailMsgProperties,
  1446. IMMPID_MP_RFC822_TO_ADDRESS, &szRecip);
  1447. if (SUCCEEDED(hr) && szRecip)
  1448. {
  1449. fMatch = fMatchesRecipient(szRecip);
  1450. QueueAdminFree(szRecip);
  1451. szRecip = NULL;
  1452. }
  1453. _ASSERT(szRecip == NULL);
  1454. //Check CC recip props if no match was found
  1455. if (!fMatch)
  1456. {
  1457. hr = HrQueueAdminGetStringProp(pIMailMsgProperties,
  1458. IMMPID_MP_RFC822_CC_ADDRESS, &szRecip);
  1459. if (SUCCEEDED(hr) && szRecip)
  1460. {
  1461. fMatch = fMatchesRecipient(szRecip);
  1462. QueueAdminFree(szRecip);
  1463. szRecip = NULL;
  1464. }
  1465. }
  1466. _ASSERT(szRecip == NULL);
  1467. //Check BCC recip props if no match was found
  1468. if (!fMatch)
  1469. {
  1470. hr = HrQueueAdminGetStringProp(pIMailMsgProperties,
  1471. IMMPID_MP_RFC822_BCC_ADDRESS, &szRecip);
  1472. if (SUCCEEDED(hr) && szRecip)
  1473. {
  1474. fMatch = fMatchesRecipient(szRecip);
  1475. QueueAdminFree(szRecip);
  1476. szRecip = NULL;
  1477. }
  1478. }
  1479. _ASSERT(szRecip == NULL);
  1480. //Check P1 recips if no P2 match
  1481. if (!fMatch)
  1482. fMatch = fMatchesP1Recipient(pIMailMsgProperties);
  1483. }
  1484. _ASSERT(szRecip == NULL);
  1485. TraceFunctLeave();
  1486. return fMatch;
  1487. }
  1488. //---[ CAQSvrInst::ApplyActionToLinks ]----------------------------------------
  1489. //
  1490. //
  1491. // Description:
  1492. // Used to start or stop all outgoing connections on the links.
  1493. // Parameters:
  1494. // laAction - describes what action to take on the links.
  1495. // LA_FREEZE - Stop all outbound connections
  1496. // LA_THAW - Restart after a previous LA_STOP
  1497. // LA_INTERNAL - checks state of links
  1498. // Returns:
  1499. // S_OK on success
  1500. // S_FALSE on LA_INTERNAL and if links are frozen
  1501. // History:
  1502. // 11/30/98 - MikeSwa Created
  1503. //
  1504. //-----------------------------------------------------------------------------
  1505. STDMETHODIMP CAQSvrInst::ApplyActionToLinks(LINK_ACTION laAction)
  1506. {
  1507. TraceFunctEnter("CAQSvrInst::ApplyActionToLinks");
  1508. HRESULT hr = S_OK;
  1509. if (fTryShutdownLock())
  1510. {
  1511. if (m_pConnMgr)
  1512. {
  1513. switch(laAction)
  1514. {
  1515. case LA_FREEZE:
  1516. m_pConnMgr->QueueAdminStopConnections();
  1517. break;
  1518. case LA_THAW:
  1519. m_pConnMgr->QueueAdminStartConnections();
  1520. break;
  1521. case LA_INTERNAL: //use to query state
  1522. if (m_pConnMgr->fConnectionsStoppedByAdmin())
  1523. hr = S_FALSE;
  1524. break;
  1525. default:
  1526. _ASSERT(0 && "Undefined LinkAction");
  1527. hr = E_INVALIDARG;
  1528. }
  1529. }
  1530. ShutdownUnlock();
  1531. }
  1532. TraceFunctLeave();
  1533. return hr;
  1534. }
  1535. //---[ CAQSvrInst::ApplyActionToMessages ]-------------------------------------
  1536. //
  1537. //
  1538. // Description:
  1539. // Applies a specified action to the set of messages described by the
  1540. // queueid and message filter
  1541. // Parameters:
  1542. // IN pqlQueueLinkId Struct that identifies Queue/Link of interest
  1543. // IN pmfMessageFilter Struct that describes the messages of interest
  1544. // IN maMessageAction Action to take on message
  1545. // MA_DELETE Delete and NDR message
  1546. // MA_DELETE_SILENT Delete message without NDRing
  1547. // MA_FREEZE_GLOBAL "Freeze" message and prevent delivery
  1548. // MA_THAW_GLOBAL "Thaw" a previously frozen message
  1549. // Returns:
  1550. // S_OK on success
  1551. // AQUEUE_E_SHUTDOWN if shutdown is in progress
  1552. // History:
  1553. // 11/30/98 - MikeSwa Created
  1554. //
  1555. //-----------------------------------------------------------------------------
  1556. STDMETHODIMP CAQSvrInst::ApplyActionToMessages(QUEUELINK_ID *pqlQueueLinkId,
  1557. MESSAGE_FILTER *pmfMessageFilter,
  1558. MESSAGE_ACTION maMessageAction,
  1559. DWORD *pcMsgs)
  1560. {
  1561. TraceFunctEnter("CAQSvrInst::ApplyActionToMessages");
  1562. HRESULT hr = S_OK;
  1563. IQueueAdminQueue *pIQueueAdminQueue = NULL;
  1564. IQueueAdminLink *pIQueueAdminLink = NULL;
  1565. IQueueAdminAction *pIQueueAdminAction = NULL;
  1566. IQueueAdminMessageFilter *pIQueueAdminMessageFilter = NULL;
  1567. CAQAdminMessageFilter *paqmf = new CAQAdminMessageFilter();
  1568. if (!paqmf)
  1569. {
  1570. hr = E_OUTOFMEMORY;
  1571. goto Exit;
  1572. }
  1573. if (!pmfMessageFilter || !pcMsgs ||
  1574. !fCheckCurrentVersion(pmfMessageFilter->dwVersion))
  1575. {
  1576. hr = E_INVALIDARG;
  1577. goto Exit;
  1578. }
  1579. paqmf->InitFromMsgFilter(pmfMessageFilter);
  1580. paqmf->SetMessageAction(maMessageAction);
  1581. hr = paqmf->QueryInterface(IID_IQueueAdminMessageFilter,
  1582. (void **) &pIQueueAdminMessageFilter);
  1583. _ASSERT(SUCCEEDED(hr) && "QI for IID_IQueueAdminMessageFilter failed!!!");
  1584. if (FAILED(hr))
  1585. goto Exit;
  1586. _ASSERT(pIQueueAdminMessageFilter);
  1587. if (QLT_NONE == pqlQueueLinkId->qltType)
  1588. {
  1589. //This is a global action.. iterate over all queues
  1590. QueueAdminDMTIteratorContext aqdntc;
  1591. aqdntc.m_pfn = QueueAdminApplyActionToMessages;
  1592. aqdntc.m_paqmf = paqmf;
  1593. aqdntc.m_pIQueueAdminMessageFilter = pIQueueAdminMessageFilter;
  1594. hr = m_dmt.HrIterateOverSubDomains(NULL,
  1595. IterateDMTAndApplyQueueAdminFunction, &aqdntc);
  1596. if (FAILED(hr))
  1597. {
  1598. if (HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN) == hr)
  1599. {
  1600. hr = S_OK;
  1601. *pcMsgs = 0;
  1602. }
  1603. goto Exit;
  1604. }
  1605. }
  1606. else if (QLT_LINK == pqlQueueLinkId->qltType)
  1607. {
  1608. //Apply action to link
  1609. hr = HrLinkFromLinkID(pqlQueueLinkId, &pIQueueAdminLink);
  1610. if (FAILED(hr))
  1611. {
  1612. if (HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN) == hr)
  1613. {
  1614. hr = S_OK;
  1615. *pcMsgs = 0;
  1616. }
  1617. goto Exit;
  1618. }
  1619. //Query Interface for IQueueAdminAction
  1620. hr = pIQueueAdminLink->QueryInterface(IID_IQueueAdminAction,
  1621. (void **) &pIQueueAdminAction);
  1622. _ASSERT(SUCCEEDED(hr) && "QI failed for LMQ->IQueueAdminAction");
  1623. if (FAILED(hr))
  1624. goto Exit;
  1625. hr = pIQueueAdminAction->HrApplyQueueAdminFunction(
  1626. pIQueueAdminMessageFilter);
  1627. if (FAILED(hr))
  1628. goto Exit;
  1629. }
  1630. else if (QLT_QUEUE == pqlQueueLinkId->qltType)
  1631. {
  1632. //Apply action to queue
  1633. hr = HrQueueFromQueueID(pqlQueueLinkId, &pIQueueAdminQueue);
  1634. if (FAILED(hr))
  1635. {
  1636. if (HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN) == hr)
  1637. {
  1638. hr = S_OK;
  1639. *pcMsgs = 0;
  1640. }
  1641. goto Exit;
  1642. }
  1643. _ASSERT(pIQueueAdminQueue);
  1644. //Query Interface for IQueueAdminAction
  1645. hr = pIQueueAdminQueue->QueryInterface(IID_IQueueAdminAction,
  1646. (void **) &pIQueueAdminAction);
  1647. _ASSERT(SUCCEEDED(hr) && "QI failed for DMQ->IQueueAdminAction");
  1648. if (FAILED(hr))
  1649. goto Exit;
  1650. hr = pIQueueAdminAction->HrApplyQueueAdminFunction(
  1651. pIQueueAdminMessageFilter);
  1652. if (FAILED(hr))
  1653. goto Exit;
  1654. }
  1655. else
  1656. {
  1657. //Bogus parameter
  1658. hr = E_INVALIDARG;
  1659. goto Exit;
  1660. }
  1661. *pcMsgs = paqmf->cMessagesFound();
  1662. Exit:
  1663. if (paqmf)
  1664. paqmf->Release();
  1665. if (pIQueueAdminMessageFilter)
  1666. pIQueueAdminMessageFilter->Release();
  1667. if (pIQueueAdminAction)
  1668. pIQueueAdminAction->Release();
  1669. if (pIQueueAdminLink)
  1670. pIQueueAdminLink->Release();
  1671. if (pIQueueAdminQueue)
  1672. pIQueueAdminQueue->Release();
  1673. TraceFunctLeave();
  1674. return hr;
  1675. }
  1676. //---[ CAQSvrInst::GetQueueInfo ]----------------------------------------------
  1677. //
  1678. //
  1679. // Description:
  1680. // Returns the relevant info for the specified queue
  1681. // Parameters:
  1682. // IN pqlQueueId Struct that identifies Queue of interest
  1683. // IN OUT pqiQueueInfo Struct to dump info into
  1684. // Returns:
  1685. // S_OK on success
  1686. // AQUEUE_E_SHUTDOWN if shutdown is in progress
  1687. // History:
  1688. // 11/30/98 - MikeSwa Created
  1689. // 2/22/99 - MikeSwa Modified to use IQueueAdminQueue interface
  1690. //
  1691. //-----------------------------------------------------------------------------
  1692. HRESULT CAQSvrInst::GetQueueInfo(QUEUELINK_ID *pqlQueueId,
  1693. QUEUE_INFO *pqiQueueInfo)
  1694. {
  1695. TraceFunctEnter("CAQSvrInst::GetQueueInfo");
  1696. HRESULT hr = S_OK;
  1697. IQueueAdminQueue *pIQueueAdminQueue = NULL;
  1698. DWORD dwMsgType = 0;
  1699. _ASSERT(pqlQueueId);
  1700. _ASSERT(pqiQueueInfo);
  1701. _ASSERT(pqlQueueId->szName);
  1702. if (!pqiQueueInfo || !pqlQueueId ||
  1703. (QLT_QUEUE != pqlQueueId->qltType) || !pqlQueueId->szName ||
  1704. !fCheckCurrentVersion(pqiQueueInfo->dwVersion))
  1705. {
  1706. hr = E_INVALIDARG;
  1707. goto Exit;
  1708. }
  1709. hr = HrQueueFromQueueID(pqlQueueId, &pIQueueAdminQueue);
  1710. if (FAILED(hr))
  1711. goto Exit;
  1712. _ASSERT(pIQueueAdminQueue);
  1713. hr = pIQueueAdminQueue->HrGetQueueInfo(pqiQueueInfo);
  1714. SanitizeCountAndVolume(&(pqiQueueInfo->cMessages),
  1715. &(pqiQueueInfo->cbQueueVolume));
  1716. Exit:
  1717. if (pIQueueAdminQueue)
  1718. pIQueueAdminQueue->Release();
  1719. TraceFunctLeave();
  1720. return hr;
  1721. }
  1722. //---[ CAQSvrInst::GetLinkInfo ]-----------------------------------------------
  1723. //
  1724. //
  1725. // Description:
  1726. // Returns the relevant info for the specified link
  1727. // Parameters:
  1728. // IN pqlLinkId Struct that identifies link of interest
  1729. // IN OUT pqiLinkInfo Struct to dump info into
  1730. // Returns:
  1731. // S_OK on success
  1732. // AQUEUE_E_SHUTDOWN if shutdown is in progress
  1733. // E_INVALIDARG if bogus properties are submitted.
  1734. // History:
  1735. // 11/30/98 - MikeSwa Created
  1736. // 7/1/99 - MikeSwa Added LinkDiagnostic
  1737. //
  1738. //-----------------------------------------------------------------------------
  1739. STDMETHODIMP CAQSvrInst::GetLinkInfo(QUEUELINK_ID *pqlLinkId,
  1740. LINK_INFO *pliLinkInfo,
  1741. HRESULT *phrLinkDiagnostic)
  1742. {
  1743. TraceFunctEnter("CAQSvrInst::GetLinkInfo");
  1744. HRESULT hr = S_OK;
  1745. IQueueAdminLink *pIQueueAdminLink = NULL;
  1746. if (!pliLinkInfo || !pqlLinkId || !phrLinkDiagnostic)
  1747. {
  1748. hr = E_INVALIDARG;
  1749. goto Exit;
  1750. }
  1751. hr = HrLinkFromLinkID(pqlLinkId, &pIQueueAdminLink);
  1752. if (FAILED(hr))
  1753. goto Exit;
  1754. hr = pIQueueAdminLink->HrGetLinkInfo(pliLinkInfo, phrLinkDiagnostic);
  1755. SanitizeCountAndVolume(&(pliLinkInfo->cMessages),
  1756. &(pliLinkInfo->cbLinkVolume));
  1757. Exit:
  1758. if (pIQueueAdminLink)
  1759. pIQueueAdminLink->Release();
  1760. TraceFunctLeave();
  1761. return hr;
  1762. }
  1763. //---[ CAQSvrInst::SetLinkState ]-----------------------------------------------
  1764. //
  1765. //
  1766. // Description:
  1767. // Used to mark a link as stopped/started by admin
  1768. // Parameters:
  1769. // IN pqlLinkId Struct that identifies link of interest
  1770. // IN la describes action for link
  1771. // Returns:
  1772. // S_OK on success
  1773. // E_INVALIDARG if action is not supported
  1774. // AQUEUE_E_SHUTDOWN if shutdown is in progress
  1775. // History:
  1776. // 11/30/98 - MikeSwa Created
  1777. // 2/22/99 - MikeSwa Modified to use IQueueAdminLink
  1778. //
  1779. //-----------------------------------------------------------------------------
  1780. STDMETHODIMP CAQSvrInst::SetLinkState(QUEUELINK_ID *pqlLinkId,
  1781. LINK_ACTION la)
  1782. {
  1783. TraceFunctEnter("CAQSvrInst::SetLinkInfo");
  1784. HRESULT hr = S_OK;
  1785. IQueueAdminLink *pIQueueAdminLink = NULL;
  1786. if (!pqlLinkId)
  1787. {
  1788. hr = E_INVALIDARG;
  1789. goto Exit;
  1790. }
  1791. hr = HrLinkFromLinkID(pqlLinkId, &pIQueueAdminLink);
  1792. if (FAILED(hr))
  1793. goto Exit;
  1794. hr = pIQueueAdminLink->HrApplyActionToLink(la);
  1795. if (FAILED(hr))
  1796. goto Exit;
  1797. //Try kicking the connection manager
  1798. if (fTryShutdownLock())
  1799. {
  1800. if (m_pConnMgr)
  1801. m_pConnMgr->KickConnections();
  1802. ShutdownUnlock();
  1803. }
  1804. Exit:
  1805. if (pIQueueAdminLink)
  1806. pIQueueAdminLink->Release();
  1807. TraceFunctLeave();
  1808. return hr;
  1809. }
  1810. //---[ CAQSvrInst::GetLinkIDs ]------------------------------------------------
  1811. //
  1812. //
  1813. // Description:
  1814. // Returns a list all the link IDs on this virtual server
  1815. // Parameters:
  1816. // IN OUT pcLinks Number of links found (sizeof array on IN)
  1817. // If value is 0, then returns total #
  1818. // IN OUT rgLinks Array of QUEUELINK_ID structs
  1819. // Returns:
  1820. // S_OK on success
  1821. // HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) if array is too small
  1822. // AQUEUE_E_SHUTDOWN if shutdown is in progress
  1823. // E_INVALIDARG for bad combinations of arguments
  1824. // History:
  1825. // 11/30/98 - MikeSwa Created
  1826. //
  1827. //-----------------------------------------------------------------------------
  1828. STDMETHODIMP CAQSvrInst::GetLinkIDs(DWORD *pcLinks,
  1829. QUEUELINK_ID *rgLinks)
  1830. {
  1831. TraceFunctEnter("CAQSvrInst::GetLinkIDs");
  1832. HRESULT hr = S_OK;
  1833. QueueAdminDMTIteratorContext aqdmtc;
  1834. CLinkMsgQueue *plmqLocal = NULL;
  1835. CLinkMsgQueue *plmqCurrentlyUnreachable = NULL;
  1836. CMailMsgAdminLink *pmmaqPreCategorized = NULL;
  1837. CMailMsgAdminLink *pmmaqPreRouting = NULL;
  1838. CMailMsgAdminLink *pmmaqPreSubmission = NULL;
  1839. if (!pcLinks || (*pcLinks && !rgLinks))
  1840. {
  1841. hr = E_INVALIDARG;
  1842. goto Exit;
  1843. }
  1844. if (!*pcLinks)
  1845. {
  1846. //Return total number of links if they request it
  1847. //number of links +3 for precat and prerouting
  1848. //note that this may be one more than the number
  1849. //of links actually returned in a subsequent call
  1850. //since currently unreachable may or may not have
  1851. //queues on it and we do not return it if it does
  1852. //not have queues.
  1853. *pcLinks = m_cCurrentRemoteNextHops+3;
  1854. hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  1855. goto Exit;
  1856. }
  1857. aqdmtc.m_cItemsToReturn = *pcLinks;
  1858. aqdmtc.m_rgLinkIDs = rgLinks;
  1859. aqdmtc.m_pCurrentLinkID = rgLinks;
  1860. aqdmtc.m_cItemsFound = 0;
  1861. //Get local Link
  1862. plmqLocal = m_dmt.plmqGetLocalLink();
  1863. if (plmqLocal)
  1864. {
  1865. hr = plmqLocal->HrGetLinkID(aqdmtc.m_pCurrentLinkID);
  1866. if (SUCCEEDED(hr))
  1867. {
  1868. aqdmtc.m_pCurrentLinkID++;
  1869. aqdmtc.m_cItemsFound++;
  1870. }
  1871. plmqLocal->Release();
  1872. }
  1873. //Get currently unreachable link.
  1874. plmqCurrentlyUnreachable = m_dmt.plmqGetCurrentlyUnreachable();
  1875. if (plmqCurrentlyUnreachable)
  1876. {
  1877. //return this link only if there are queues in it
  1878. if (plmqCurrentlyUnreachable->cGetNumQueues() > 0)
  1879. {
  1880. hr = plmqCurrentlyUnreachable->HrGetLinkID(aqdmtc.m_pCurrentLinkID);
  1881. if (SUCCEEDED(hr))
  1882. {
  1883. aqdmtc.m_pCurrentLinkID++;
  1884. aqdmtc.m_cItemsFound++;
  1885. }
  1886. }
  1887. plmqCurrentlyUnreachable->Release();
  1888. }
  1889. //Get presubmit queue
  1890. pmmaqPreSubmission = m_dmt.pmmaqGetPreSubmission();
  1891. if (pmmaqPreSubmission)
  1892. {
  1893. hr = pmmaqPreSubmission->HrGetLinkID(aqdmtc.m_pCurrentLinkID);
  1894. if (SUCCEEDED(hr))
  1895. {
  1896. aqdmtc.m_pCurrentLinkID++;
  1897. aqdmtc.m_cItemsFound++;
  1898. }
  1899. pmmaqPreSubmission->Release();
  1900. }
  1901. //Get precat queue
  1902. pmmaqPreCategorized = m_dmt.pmmaqGetPreCategorized();
  1903. if (pmmaqPreCategorized)
  1904. {
  1905. hr = pmmaqPreCategorized->HrGetLinkID(aqdmtc.m_pCurrentLinkID);
  1906. if (SUCCEEDED(hr))
  1907. {
  1908. aqdmtc.m_pCurrentLinkID++;
  1909. aqdmtc.m_cItemsFound++;
  1910. }
  1911. pmmaqPreCategorized->Release();
  1912. }
  1913. //Get prerouting queue
  1914. pmmaqPreRouting = m_dmt.pmmaqGetPreRouting();
  1915. if (pmmaqPreRouting)
  1916. {
  1917. hr = pmmaqPreRouting->HrGetLinkID(aqdmtc.m_pCurrentLinkID);
  1918. if (SUCCEEDED(hr))
  1919. {
  1920. aqdmtc.m_pCurrentLinkID++;
  1921. aqdmtc.m_cItemsFound++;
  1922. }
  1923. pmmaqPreRouting->Release();
  1924. }
  1925. //Get links for remote domains.
  1926. hr = m_dmt.HrIterateOverSubDomains(NULL, IterateDMTAndGetLinkIDs,
  1927. &aqdmtc);
  1928. if (FAILED(hr))
  1929. {
  1930. if (HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN) == hr)
  1931. {
  1932. //If the call to get remote domains fails with ERROR_NO_SUCH_DOMAIN
  1933. //we must return only the special links --- local, currently unreachable,
  1934. //precat and prerouting.
  1935. hr = S_OK;
  1936. *pcLinks = aqdmtc.m_cItemsFound;
  1937. }
  1938. goto Exit;
  1939. }
  1940. hr = aqdmtc.m_hrResult;
  1941. if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr)
  1942. *pcLinks = m_cCurrentRemoteNextHops+2; //+2 for precat, prerouting which are not
  1943. else //counted in m_cCurrentRemoteNextHops.
  1944. *pcLinks = aqdmtc.m_cItemsFound;
  1945. Exit:
  1946. //make sure we don't return ERROR_INSUFFICIENT_BUFFER if there are no links
  1947. if ((HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr) && !*pcLinks)
  1948. hr = S_OK;
  1949. TraceFunctLeave();
  1950. return hr;
  1951. }
  1952. //---[ CAQSvrInst::GetQueueIDs ]-----------------------------------------------
  1953. //
  1954. //
  1955. // Description:
  1956. // Gets all the queue (DMQ) IDs associated with a given link
  1957. // Parameters:
  1958. // IN pqlLinkId ID of link to get queues for
  1959. // IN OUT pcQueues Sizeof array/ number of queues found
  1960. // IN OUT rgQueues Array to dump queue info into
  1961. // Returns:
  1962. // S_OK on success
  1963. // HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) if array is too small
  1964. // AQUEUE_E_SHUTDOWN if shutdown is in progress
  1965. // E_INVALIDARG for bad combinations of arguments
  1966. // History:
  1967. // 11/30/98 - MikeSwa Created
  1968. //
  1969. //-----------------------------------------------------------------------------
  1970. STDMETHODIMP CAQSvrInst::GetQueueIDs(QUEUELINK_ID *pqlLinkId,
  1971. DWORD *pcQueues,
  1972. QUEUELINK_ID *rgQueues)
  1973. {
  1974. TraceFunctEnter("CAQSvrInst::GetQueueIDs");
  1975. HRESULT hr = S_OK;
  1976. IQueueAdminLink *pIQueueAdminLink = NULL;
  1977. DWORD cQueues = 0;
  1978. //Verify args
  1979. if (!pqlLinkId || !pcQueues || (*pcQueues && !rgQueues))
  1980. {
  1981. hr = E_INVALIDARG;
  1982. goto Exit;
  1983. }
  1984. //Verify QUEUELINK_ID identifying the link of interest
  1985. if (!pqlLinkId->szName || (pqlLinkId->qltType != QLT_LINK))
  1986. {
  1987. hr = E_INVALIDARG;
  1988. goto Exit;
  1989. }
  1990. hr = HrLinkFromLinkID(pqlLinkId, &pIQueueAdminLink);
  1991. if (FAILED(hr))
  1992. goto Exit;
  1993. _ASSERT(pIQueueAdminLink);
  1994. hr = pIQueueAdminLink->HrGetNumQueues(&cQueues);
  1995. if (FAILED(hr))
  1996. goto Exit;
  1997. if ((cQueues > *pcQueues) || (!*pcQueues))
  1998. {
  1999. *pcQueues = cQueues;
  2000. hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  2001. goto Exit;
  2002. }
  2003. hr = pIQueueAdminLink->HrGetQueueIDs(pcQueues, rgQueues);
  2004. if (FAILED(hr))
  2005. goto Exit;
  2006. Exit:
  2007. //make sure we don't return ERROR_INSUFFICIENT_BUFFER if there are no queues
  2008. if ((HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr) && !*pcQueues)
  2009. hr = S_OK;
  2010. if (pIQueueAdminLink)
  2011. pIQueueAdminLink->Release();
  2012. TraceFunctLeave();
  2013. return hr;
  2014. }
  2015. //---[ CAQSvrInst::GetMessageProperties ]--------------------------------------
  2016. //
  2017. //
  2018. // Description:
  2019. // Gets the message info for messages described by the filter
  2020. // Parameters:
  2021. // IN pqlQueueLinkId Struct that identifies Queue/Link of interest
  2022. // IN pmfMessageEnumFilter Filter that describes messages of interest
  2023. // IN OUT pcMsgs sizeof array / number of messages found
  2024. // IN OUT rgMsgs array of message info structures
  2025. // Returns:
  2026. // S_OK on success
  2027. // HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) if array is too small
  2028. // AQUEUE_E_SHUTDOWN if shutdown is in progress
  2029. // E_INVALIDARG if bogus args are passed in.
  2030. // History:
  2031. // 11/30/98 - MikeSwa Created
  2032. // 2/22/99 - MikeSwa Modified to use IQueueAdmin* interface
  2033. //
  2034. //-----------------------------------------------------------------------------
  2035. STDMETHODIMP CAQSvrInst::GetMessageProperties(QUEUELINK_ID *pqlQueueLinkId,
  2036. MESSAGE_ENUM_FILTER *pmfMessageEnumFilter,
  2037. DWORD *pcMsgs,
  2038. MESSAGE_INFO *rgMsgs)
  2039. {
  2040. TraceFunctEnter("CAQSvrInst::GetMessageProperties");
  2041. HRESULT hr = S_OK;
  2042. IQueueAdminQueue *pIQueueAdminQueue = NULL;
  2043. IQueueAdminAction *pIQueueAdminAction = NULL;
  2044. IQueueAdminMessageFilter *pIQueueAdminMessageFilter = NULL;
  2045. MESSAGE_INFO *pMsgInfo = rgMsgs;
  2046. DWORD i = 0;
  2047. CAQAdminMessageFilter *paqmf = new CAQAdminMessageFilter();
  2048. if (!paqmf)
  2049. {
  2050. hr = E_OUTOFMEMORY;
  2051. goto Exit;
  2052. }
  2053. //Do some parameter checking
  2054. if (!pqlQueueLinkId || !pmfMessageEnumFilter || !pcMsgs ||
  2055. !pqlQueueLinkId->szName ||
  2056. !fCheckCurrentVersion(pmfMessageEnumFilter->dwVersion))
  2057. {
  2058. hr = E_INVALIDARG;
  2059. goto Exit;
  2060. }
  2061. if (*pcMsgs && !rgMsgs)
  2062. {
  2063. //If we are specifying messages, we should have space to return data
  2064. hr = E_INVALIDARG;
  2065. goto Exit;
  2066. }
  2067. _ASSERT(QLT_QUEUE == pqlQueueLinkId->qltType);
  2068. if (QLT_QUEUE != pqlQueueLinkId->qltType)
  2069. {
  2070. hr = E_INVALIDARG;
  2071. goto Exit;
  2072. }
  2073. paqmf->InitFromMsgEnumFilter(pmfMessageEnumFilter);
  2074. paqmf->SetSearchContext(*pcMsgs, rgMsgs);
  2075. hr = HrQueueFromQueueID(pqlQueueLinkId, &pIQueueAdminQueue);
  2076. if (FAILED(hr))
  2077. goto Exit;
  2078. _ASSERT(pIQueueAdminQueue);
  2079. hr = pIQueueAdminQueue->QueryInterface(IID_IQueueAdminAction,
  2080. (void **) &pIQueueAdminAction);
  2081. _ASSERT(SUCCEEDED(hr) && "QI for IID_IQueueAdminAction failed!!!");
  2082. if (FAILED(hr))
  2083. goto Exit;
  2084. hr = paqmf->QueryInterface(IID_IQueueAdminMessageFilter,
  2085. (void **) &pIQueueAdminMessageFilter);
  2086. _ASSERT(SUCCEEDED(hr) && "QI for IID_IQueueAdminMessageFilter failed!!!");
  2087. if (FAILED(hr))
  2088. goto Exit;
  2089. _ASSERT(pIQueueAdminAction);
  2090. hr = pIQueueAdminAction->HrApplyQueueAdminFunction(
  2091. pIQueueAdminMessageFilter);
  2092. if (FAILED(hr))
  2093. goto Exit;
  2094. if (!*pcMsgs && paqmf->cMessagesFound())
  2095. hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  2096. *pcMsgs = paqmf->cMessagesFound();
  2097. Exit:
  2098. if (paqmf)
  2099. paqmf->Release();
  2100. if (pIQueueAdminMessageFilter)
  2101. pIQueueAdminMessageFilter->Release();
  2102. if (pIQueueAdminQueue)
  2103. pIQueueAdminQueue->Release();
  2104. if (pIQueueAdminAction)
  2105. pIQueueAdminAction->Release();
  2106. TraceFunctLeave();
  2107. return hr;
  2108. }
  2109. //---[ CAQSvrInst::QuerySupportedActions ]-------------------------------------
  2110. //
  2111. //
  2112. // Description:
  2113. // Returns the supported actions and filters for a given queue
  2114. // Parameters:
  2115. // IN pqlQueueLinkId The queue/link we are interested in
  2116. // OUT pdwSupportedActions The MESSAGE_ACTION flags supported
  2117. // OUT pdwSupportedFilterFlags The supported filter flags
  2118. // Returns:
  2119. // S_OK on success
  2120. // History:
  2121. // 6/15/99 - MikeSwa Created
  2122. //
  2123. //-----------------------------------------------------------------------------
  2124. STDMETHODIMP CAQSvrInst::QuerySupportedActions(
  2125. QUEUELINK_ID *pqlQueueLinkId,
  2126. DWORD *pdwSupportedActions,
  2127. DWORD *pdwSupportedFilterFlags)
  2128. {
  2129. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::QuerySupportedActions");
  2130. HRESULT hr = S_OK;
  2131. IQueueAdminAction *pIQueueAdminAction = NULL;
  2132. IQueueAdminQueue *pIQueueAdminQueue = NULL;
  2133. IQueueAdminLink *pIQueueAdminLink = NULL;
  2134. _ASSERT(pqlQueueLinkId);
  2135. _ASSERT(pdwSupportedActions);
  2136. _ASSERT(pdwSupportedFilterFlags);
  2137. if (QLT_LINK == pqlQueueLinkId->qltType)
  2138. {
  2139. //Apply action to link
  2140. hr = HrLinkFromLinkID(pqlQueueLinkId, &pIQueueAdminLink);
  2141. if (FAILED(hr))
  2142. goto Exit;
  2143. //Query Interface for IQueueAdminAction
  2144. hr = pIQueueAdminLink->QueryInterface(IID_IQueueAdminAction,
  2145. (void **) &pIQueueAdminAction);
  2146. _ASSERT(SUCCEEDED(hr) && "QI failed for LMQ->IQueueAdminAction");
  2147. if (FAILED(hr))
  2148. goto Exit;
  2149. }
  2150. else if (QLT_QUEUE == pqlQueueLinkId->qltType)
  2151. {
  2152. //Apply action to queue
  2153. hr = HrQueueFromQueueID(pqlQueueLinkId, &pIQueueAdminQueue);
  2154. if (FAILED(hr))
  2155. goto Exit;
  2156. _ASSERT(pIQueueAdminQueue);
  2157. //Query Interface for IQueueAdminAction
  2158. hr = pIQueueAdminQueue->QueryInterface(IID_IQueueAdminAction,
  2159. (void **) &pIQueueAdminAction);
  2160. _ASSERT(SUCCEEDED(hr) && "QI failed for DMQ->IQueueAdminAction");
  2161. if (FAILED(hr))
  2162. goto Exit;
  2163. }
  2164. //
  2165. // If we do not find an action for this ID, then return the default
  2166. // implementation (most likely is a server level search)...
  2167. // otherwise ask our action interface what is supported
  2168. //
  2169. if (!pIQueueAdminAction)
  2170. {
  2171. hr = QueryDefaultSupportedActions(pdwSupportedActions,
  2172. pdwSupportedFilterFlags);
  2173. }
  2174. else
  2175. {
  2176. hr = pIQueueAdminAction->QuerySupportedActions(
  2177. pdwSupportedActions,
  2178. pdwSupportedFilterFlags);
  2179. }
  2180. Exit:
  2181. if (FAILED(hr))
  2182. {
  2183. if (HRESULT_FROM_WIN32(ERROR_NO_SUCH_DOMAIN) == hr)
  2184. hr = S_OK; //eat this error
  2185. *pdwSupportedActions = 0;
  2186. *pdwSupportedFilterFlags = 0;
  2187. }
  2188. if (pIQueueAdminAction)
  2189. pIQueueAdminAction->Release();
  2190. if (pIQueueAdminLink)
  2191. pIQueueAdminLink->Release();
  2192. if (pIQueueAdminQueue)
  2193. pIQueueAdminQueue->Release();
  2194. TraceFunctLeave();
  2195. return hr;
  2196. }
  2197. //---[ QueryDefaultSupportedActions ]------------------------------------------
  2198. //
  2199. //
  2200. // Description:
  2201. // Returns the default supported actions.
  2202. // Parameters:
  2203. // OUT pdwSupportedActions The MESSAGE_ACTION flags supported
  2204. // OUT pdwSupportedFilterFlags The supported filter flags
  2205. // Returns:
  2206. // S_OK always
  2207. // History:
  2208. // 1/27/2000 - MikeSwa Created
  2209. //
  2210. //-----------------------------------------------------------------------------
  2211. HRESULT QueryDefaultSupportedActions(DWORD *pdwSupportedActions,
  2212. DWORD *pdwSupportedFilterFlags)
  2213. {
  2214. //Currently all of a single type of queue supports the same actions and
  2215. //filters. The only special cases are the precat and prerouting queue
  2216. *pdwSupportedActions = MA_DELETE |\
  2217. MA_DELETE_SILENT |\
  2218. MA_FREEZE_GLOBAL |\
  2219. MA_THAW_GLOBAL |\
  2220. MA_COUNT;
  2221. *pdwSupportedFilterFlags = MF_MESSAGEID |\
  2222. MF_SENDER |\
  2223. MF_RECIPIENT |\
  2224. MF_SIZE |\
  2225. MF_TIME |\
  2226. MF_FROZEN |\
  2227. MF_FAILED |\
  2228. MF_ALL |\
  2229. MF_INVERTSENSE;
  2230. return S_OK;
  2231. }
  2232. //---[ CAQSvrInst::HrGetLocalQueueAdminQueue ]---------------------------------
  2233. //
  2234. //
  2235. // Description:
  2236. // Returns an interface for the local queue.
  2237. // Parameters:
  2238. // ppIQueueAdminQueue Interface returned
  2239. // Returns:
  2240. // S_OK on success
  2241. // History:
  2242. // 2/23/99 - MikeSwa Created
  2243. //
  2244. //-----------------------------------------------------------------------------
  2245. HRESULT CAQSvrInst::HrGetLocalQueueAdminQueue(
  2246. IQueueAdminQueue **ppIQueueAdminQueue)
  2247. {
  2248. return m_asyncqPreLocalDeliveryQueue.QueryInterface(IID_IQueueAdminQueue,
  2249. (void **) ppIQueueAdminQueue);
  2250. }
  2251. //---[ HrQueueAdminGetStringProp ]---------------------------------------------
  2252. //
  2253. //
  2254. // Description:
  2255. // Wrapper function to handle getting a string property for queue admin
  2256. // Parameters:
  2257. // IN pIMailMsgProperties Ptr to IMailMsgProperties interface
  2258. // IN dwPropID PropID of interest
  2259. // OUT pszProp String allocated for QueueAdmin
  2260. // OUT pcbProp Size out param (including
  2261. // terminating NULL(s)).
  2262. // Returns:
  2263. // S_OK on success (even if property is not found)
  2264. // E_OUTOFMEMORY if allocation fails.
  2265. // History:
  2266. // 12/8/98 - MikeSwa Created
  2267. // 2/9/99 - MikeSwa Added string size OUT param & changed code to use
  2268. // buffer size returned by GetProperty.
  2269. //
  2270. //-----------------------------------------------------------------------------
  2271. HRESULT HrQueueAdminGetStringProp(IMailMsgProperties *pIMailMsgProperties,
  2272. DWORD dwPropID, LPSTR *pszProp, DWORD *pcbProp)
  2273. {
  2274. TraceFunctEnterEx((LPARAM) pIMailMsgProperties, "HrQueueAdminGetStringProp");
  2275. BYTE pbBuffer[4];
  2276. HRESULT hr = S_OK;
  2277. DWORD cbIntBuffSize = sizeof(pbBuffer);
  2278. LPSTR pszIntBuff = NULL;
  2279. _ASSERT(pszProp);
  2280. // Init the OUT params
  2281. if(pcbProp)
  2282. *pcbProp = 0;
  2283. *pszProp = NULL;
  2284. //Use GetProperty instead of GetStringA, because it returns the size as well
  2285. hr = pIMailMsgProperties->GetProperty(dwPropID, sizeof(pbBuffer),
  2286. &cbIntBuffSize, pbBuffer);
  2287. if (FAILED(hr))
  2288. {
  2289. if (MAILMSG_E_PROPNOTFOUND == hr)
  2290. {
  2291. hr = S_OK;
  2292. goto Exit;
  2293. }
  2294. else if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr)
  2295. {
  2296. //Our stack buffer is not big enough (which we expected)...
  2297. //we will have to do a get property directory into out return buffer
  2298. hr = S_OK;
  2299. }
  2300. else
  2301. {
  2302. goto Exit;
  2303. }
  2304. }
  2305. //Allocate enough space for our string plus an extra terminating \0, so
  2306. //we can munge it into a multivalue prop if needed.
  2307. pszIntBuff = (LPSTR) pvQueueAdminAlloc(cbIntBuffSize+sizeof(CHAR));
  2308. if (!pszIntBuff)
  2309. {
  2310. hr = E_OUTOFMEMORY;
  2311. goto Exit;
  2312. }
  2313. //Now get the property with our property-sized buffer
  2314. hr = pIMailMsgProperties->GetProperty(dwPropID, cbIntBuffSize, &cbIntBuffSize,
  2315. (BYTE *) pszIntBuff);
  2316. if (FAILED(hr))
  2317. goto Exit;
  2318. //Set extra terminating NULL.
  2319. pszIntBuff[cbIntBuffSize/sizeof(CHAR)] = '\0';
  2320. // Set the OUT params
  2321. if(pcbProp)
  2322. *pcbProp = cbIntBuffSize + sizeof(CHAR);
  2323. *pszProp = pszIntBuff;
  2324. pszIntBuff = NULL;
  2325. Exit:
  2326. if (pszIntBuff)
  2327. QueueAdminFree(pszIntBuff);
  2328. TraceFunctLeave();
  2329. return hr;
  2330. }
  2331. //---[ HrQueueAdminGetUnicodeStringProp ]--------------------------------------
  2332. //
  2333. //
  2334. // Description:
  2335. // Wrapper function to handle getting a string property for queue admin
  2336. // Parameters:
  2337. // IN pIMailMsgProperties Ptr to IMailMsgProperties interface
  2338. // IN dwPropID PropID of interest
  2339. // OUT pwszProp UNICODE String allocated for QueueAdmin
  2340. // OUT pcbProp Size out param (including
  2341. // terminating NULL(s)).
  2342. // Returns:
  2343. // S_OK on success (even if property is not found)
  2344. // E_OUTOFMEMORY if allocation fails.
  2345. // History:
  2346. // 12/8/98 - MikeSwa Created
  2347. // 2/9/99 - MikeSwa Added string size OUT param & changed code to use
  2348. // buffer size returned by GetProperty.
  2349. //
  2350. //-----------------------------------------------------------------------------
  2351. HRESULT HrQueueAdminGetUnicodeStringProp(
  2352. IMailMsgProperties *pIMailMsgProperties,
  2353. DWORD dwPropID, LPWSTR *pwszProp, DWORD *pcbProp)
  2354. {
  2355. TraceFunctEnterEx((LPARAM) NULL, "HrQueueAdminGetUnicodeStringProp");
  2356. HRESULT hr = S_OK;
  2357. LPSTR szProp = NULL;
  2358. _ASSERT(pwszProp);
  2359. *pwszProp = NULL;
  2360. hr = HrQueueAdminGetStringProp(pIMailMsgProperties, dwPropID, &szProp,
  2361. pcbProp);
  2362. if (SUCCEEDED(hr) && szProp)
  2363. {
  2364. BOOL fUTF8 = (dwPropID == IMMPID_MP_RFC822_MSG_SUBJECT);
  2365. *pwszProp = wszQueueAdminConvertToUnicode(szProp,
  2366. pcbProp ? *pcbProp : 0,
  2367. fUTF8);
  2368. QueueAdminFree(szProp);
  2369. if (pcbProp)
  2370. *pcbProp *= sizeof(WCHAR)/sizeof(CHAR);
  2371. }
  2372. TraceFunctLeave();
  2373. return hr;
  2374. }
  2375. //---[ cGetNumRecipsFromRFC822 ]-----------------------------------------------
  2376. //
  2377. //
  2378. // Description:
  2379. // Utility function that extracts the number of recipients from a RFC822
  2380. // header. Input values should be as returned by HrQueueAdminGetStringProp
  2381. // Parameters:
  2382. // IN szHeader String of header to parse (can be NULL)
  2383. // IN cbHeader Size of string header to parse
  2384. // Returns:
  2385. // Number of recipients found in header
  2386. // History:
  2387. // 12/8/98 - MikeSwa Created
  2388. // 2/9/99 - MikeSwa Modified to handle all RFC822 address formats
  2389. //
  2390. //-----------------------------------------------------------------------------
  2391. DWORD cQueueAdminGetNumRecipsFromRFC822(LPSTR szHeader, DWORD cbHeader)
  2392. {
  2393. //Call through to handy smtpaddr library
  2394. return CAddr::GetRFC822AddressCount(szHeader);
  2395. }
  2396. //---[ QueueAdminGetRecipListFromP1 ]------------------------------------------
  2397. //
  2398. //
  2399. // Description:
  2400. // Creates a list of recipients from the P1.
  2401. //
  2402. // Parameters:
  2403. // IN pIMailMsgProperties
  2404. // IN OUT pMsgInfo (modified following)
  2405. // cEnvRecipients
  2406. // cbEnvRecipients
  2407. // mszEnvRecipients
  2408. //
  2409. // The mszEnvRecipients field is a multi-string UNICODE buffer containing
  2410. // a NULL-terminated string for each recipient. The buffer itself is
  2411. // terminated by an additional NULL. Each recipient string will be formatted
  2412. // in the proxy address style format of 'addr-type ":" address'. The
  2413. // addr-type should match DS proxy type (i.e. "SMTP" for SMTP). The address
  2414. // should be returned in it's native format.
  2415. //
  2416. // Returns:
  2417. // -
  2418. // History:
  2419. // 2/17/99 - MikeSwa Created
  2420. // 6/10/99 - MikeSwa Modified - P1 recipeints are now always reported
  2421. // as separate fields in the MESSAGE_INFO structure.
  2422. // 2/19/2001 - MikeSwa Modified - Added support for address types other
  2423. // than SMTP.
  2424. //
  2425. //-----------------------------------------------------------------------------
  2426. void QueueAdminGetRecipListFromP1(IMailMsgProperties *pIMailMsgProperties,
  2427. MESSAGE_INFO *pMsgInfo)
  2428. {
  2429. TraceFunctEnterEx((LPARAM) NULL, "QueueAdminGetRecipListFromP1IfNecessary");
  2430. LPWSTR wszRecipBuffer = NULL;
  2431. LPWSTR wszPrevPlace = NULL;
  2432. LPWSTR wszCurrentPlace = NULL;
  2433. LPWSTR wszTmpBuffer = NULL;
  2434. CHAR szPropBuffer[QUEUE_ADMIN_MAX_BUFFER_REQUIRED] = "";
  2435. HRESULT hr = S_OK;
  2436. const WCHAR wszDelimiter[] = L"";
  2437. DWORD cbPropSize = 0;
  2438. DWORD cbSpaceLeft = 0;
  2439. DWORD cbGrowBuffer = 0;
  2440. DWORD cWCharsWritten = 0;
  2441. DWORD cRecips = 0;
  2442. DWORD iCurrentRecip = 0;
  2443. DWORD cbBufferSize = sizeof(WCHAR)*QUEUE_ADMIN_MAX_BUFFER_REQUIRED;
  2444. BOOL fContinueToNextRecip = TRUE;
  2445. DWORD iCurrentAddressType = 0;
  2446. IMailMsgRecipients *pIMailMsgRecipients = NULL;
  2447. const DWORD MAX_RECIP_RETURN_BUFFER = (1024*50);
  2448. DWORD cbPrefixAndDelimiter = 0;
  2449. _ASSERT(pIMailMsgProperties);
  2450. _ASSERT(fVerifyQAPIAddressTypes());
  2451. if (!pMsgInfo || !pIMailMsgProperties)
  2452. return;
  2453. wszRecipBuffer = (LPWSTR) pvQueueAdminAlloc(cbBufferSize);
  2454. //Don't try to write prop if we couldn't allocate
  2455. if (!wszRecipBuffer)
  2456. {
  2457. ErrorTrace((LPARAM) pIMailMsgProperties,
  2458. "Unable to alloc %d size buffer", cbBufferSize);
  2459. goto Exit;
  2460. }
  2461. cbSpaceLeft = cbBufferSize;
  2462. wszCurrentPlace = wszRecipBuffer;
  2463. hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgRecipients,
  2464. (void **) &pIMailMsgRecipients);
  2465. _ASSERT(SUCCEEDED(hr) && "QueryInterface for IMailMsgRecipients failed");
  2466. if (FAILED(hr))
  2467. goto Exit;
  2468. _ASSERT(pIMailMsgRecipients);
  2469. hr = pIMailMsgRecipients->Count(&cRecips);
  2470. if (FAILED(hr))
  2471. goto Exit;
  2472. if (!cRecips)
  2473. goto Exit;
  2474. //Start string as double-terminated
  2475. wcscpy(wszCurrentPlace, wszDelimiter);
  2476. //Loop over recipients and dump them to string
  2477. for (iCurrentRecip = 0; iCurrentRecip < cRecips; iCurrentRecip++)
  2478. {
  2479. //
  2480. // Loop over the possible address types
  2481. //
  2482. fContinueToNextRecip = FALSE;
  2483. for (iCurrentAddressType = 0;
  2484. (iCurrentAddressType < g_cQAPIAddressTypes) && !fContinueToNextRecip;
  2485. iCurrentAddressType++)
  2486. {
  2487. //
  2488. // Unless we specifically know we need to check the next
  2489. // next prop id, we will default to skipping this recipient
  2490. //
  2491. fContinueToNextRecip = TRUE;
  2492. //
  2493. // Compute Extra space required per-recipient
  2494. //
  2495. cbPrefixAndDelimiter =
  2496. g_rgcbQAPIAddressTypes[iCurrentAddressType] +
  2497. sizeof(wszDelimiter) -
  2498. sizeof(WCHAR);
  2499. _ASSERT(cbPrefixAndDelimiter);
  2500. DebugTrace((LPARAM) pIMailMsgProperties,
  2501. "Prefix and delimiter size for %S is %d",
  2502. g_rgwszQAPIAddressTypes[iCurrentAddressType],
  2503. cbPrefixAndDelimiter);
  2504. cbPropSize = sizeof(szPropBuffer);
  2505. hr = pIMailMsgRecipients->GetProperty(iCurrentRecip,
  2506. g_rgdwQAPIRecipPropIDs[iCurrentAddressType],
  2507. sizeof(szPropBuffer), &cbPropSize, (BYTE *) szPropBuffer);
  2508. if (FAILED(hr))
  2509. {
  2510. if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr)
  2511. {
  2512. //
  2513. // If this recip is larger than
  2514. // QUEUE_ADMIN_MAX_BUFFER_REQUIRED
  2515. // Go to the next one
  2516. hr = S_OK;
  2517. DebugTrace((LPARAM) pIMailMsgProperties,
  2518. "Message recipient %d (propid %d) larger than %d",
  2519. iCurrentRecip, g_rgdwQAPIRecipPropIDs[iCurrentAddressType],
  2520. sizeof(szPropBuffer));
  2521. continue;
  2522. }
  2523. else if (MAILMSG_E_PROPNOTFOUND == hr)
  2524. {
  2525. //
  2526. // Try the next address type
  2527. //
  2528. fContinueToNextRecip = FALSE;
  2529. continue;
  2530. }
  2531. ErrorTrace((LPARAM) NULL,
  2532. "pIMailMsgRecipients->GetProperty failed with hr - 0x%08X", hr);
  2533. goto Exit;
  2534. }
  2535. _ASSERT(cbPropSize); //This doesn't make sense.. GetProp should have failed
  2536. if (!cbPropSize)
  2537. continue;
  2538. if ((cbSpaceLeft <= cbPrefixAndDelimiter) ||
  2539. (cbPropSize*sizeof(WCHAR) > cbSpaceLeft - cbPrefixAndDelimiter))
  2540. {
  2541. //We do not have enough space left to process this recip
  2542. //and include the prefix and terminating NULLs
  2543. //
  2544. // Pick a new size... how many recips have we processed vs.
  2545. // how many are there
  2546. //
  2547. cbGrowBuffer = (cbBufferSize*(cRecips-iCurrentRecip))/(iCurrentRecip+1);
  2548. DebugTrace((LPARAM) pIMailMsgProperties,
  2549. "Growbuffer is %d, %d recips of %d - Current Buffers is %d",
  2550. cbGrowBuffer, iCurrentRecip+1, cRecips, cbBufferSize);
  2551. //
  2552. // Sanity check our size... we want to return a reasonable number of
  2553. // recips, but we don't want to kill the machine with the default
  2554. // enumeration filter
  2555. //
  2556. if (cbBufferSize >= MAX_RECIP_RETURN_BUFFER)
  2557. goto Exit;
  2558. if (cbBufferSize+cbGrowBuffer >= MAX_RECIP_RETURN_BUFFER)
  2559. cbGrowBuffer = MAX_RECIP_RETURN_BUFFER-cbBufferSize;
  2560. //
  2561. // Sanity check minimum size
  2562. //
  2563. if (cbGrowBuffer < (sizeof(WCHAR)*QUEUE_ADMIN_MAX_BUFFER_REQUIRED))
  2564. cbGrowBuffer = sizeof(WCHAR)*QUEUE_ADMIN_MAX_BUFFER_REQUIRED;
  2565. cbSpaceLeft += cbGrowBuffer;
  2566. cbBufferSize += cbGrowBuffer;
  2567. DebugTrace((LPARAM) pIMailMsgProperties,
  2568. "Attempting to Grow recip buffer %d to %d", cbGrowBuffer, cbBufferSize);
  2569. wszTmpBuffer = (LPWSTR) pvQueueAdminReAlloc(wszRecipBuffer, cbBufferSize);
  2570. if (!wszTmpBuffer)
  2571. {
  2572. DebugTrace((LPARAM) pIMailMsgProperties,
  2573. "Unable to realloc %p to size %d", wszRecipBuffer, cbBufferSize);
  2574. goto Exit; //bail
  2575. }
  2576. wszCurrentPlace = wszTmpBuffer + (wszCurrentPlace-wszRecipBuffer);
  2577. wszRecipBuffer = wszTmpBuffer;
  2578. }
  2579. //Copy address type prefix
  2580. wcscpy(wszCurrentPlace, g_rgwszQAPIAddressTypes[iCurrentAddressType]);
  2581. wszPrevPlace = wszCurrentPlace;
  2582. wszCurrentPlace += (g_rgcbQAPIAddressTypes[iCurrentAddressType]/sizeof(WCHAR) - 1);
  2583. //We need to convert this to UNICODE in place
  2584. cWCharsWritten = MultiByteToWideChar(CP_ACP,
  2585. 0,
  2586. szPropBuffer,
  2587. -1,
  2588. wszCurrentPlace,
  2589. (cbSpaceLeft - sizeof(wszDelimiter))/sizeof(WCHAR));
  2590. if (!cWCharsWritten)
  2591. {
  2592. hr = HRESULT_FROM_WIN32(GetLastError());
  2593. //If this failed because of the buffer size, then my calculations
  2594. //were off.
  2595. ASSERT (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != hr);
  2596. ErrorTrace((LPARAM) NULL,
  2597. "MultiByteToWideChar failed with hr - 0x%08X", hr);
  2598. wszCurrentPlace = wszPrevPlace;
  2599. wcscpy(wszCurrentPlace, wszDelimiter); //backout prefix
  2600. continue;
  2601. }
  2602. DebugTrace((LPARAM) pIMailMsgProperties,
  2603. "Adding recip %S to buffer", wszCurrentPlace);
  2604. //Write double terminating NULL
  2605. wszCurrentPlace += cWCharsWritten;
  2606. wcscpy(wszCurrentPlace, wszDelimiter);
  2607. //Set current place to the 2nd terminating NULL
  2608. //If there are no more recips... we are already terminated... if
  2609. //there are, they will overwrite the 2nd terminating NULL.
  2610. _ASSERT(L'\0' == *wszCurrentPlace);
  2611. _ASSERT(L'\0' == *(wszCurrentPlace-1));
  2612. cbSpaceLeft -= (DWORD)((wszCurrentPlace-wszPrevPlace)*sizeof(WCHAR));
  2613. }
  2614. }
  2615. Exit:
  2616. if (FAILED(hr) || !cRecips)
  2617. {
  2618. if (wszRecipBuffer)
  2619. QueueAdminFree(wszRecipBuffer);
  2620. }
  2621. else
  2622. {
  2623. if (pMsgInfo)
  2624. {
  2625. _ASSERT(wszPrevPlace >= wszRecipBuffer);
  2626. pMsgInfo->cEnvRecipients = cRecips;
  2627. pMsgInfo->cbEnvRecipients = (DWORD) ((1+wszCurrentPlace-wszRecipBuffer)*sizeof(WCHAR));
  2628. pMsgInfo->mszEnvRecipients = wszRecipBuffer;
  2629. }
  2630. }
  2631. if (pIMailMsgRecipients)
  2632. pIMailMsgRecipients->Release();
  2633. TraceFunctLeave();
  2634. }
  2635. //---[ fQueueAdminIsP1Recip ]--------------------------------------------------
  2636. //
  2637. //
  2638. // Description:
  2639. // Determines if a given recipient is a P1 recipient.
  2640. // Parameters:
  2641. // IN pIMailMsgProperties Msg to check recips for
  2642. // IN szRecip Recipient to check for
  2643. // IN iStartAddressType Address type to start check with
  2644. // Returns:
  2645. // TRUE if the recipient is a P1 recipient for this message
  2646. // FALSE if the recipient is not a P1 recipient for this message
  2647. // History:
  2648. // 2/17/99 - MikeSwa Created
  2649. // 3/16/2001 - MikeSwa Modified - Made a member of CAQAdminMessageFilter
  2650. //
  2651. //-----------------------------------------------------------------------------
  2652. BOOL CAQAdminMessageFilter::fQueueAdminIsP1Recip(
  2653. IMailMsgProperties *pIMailMsgProperties)
  2654. {
  2655. IMailMsgRecipients *pIMailMsgRecipients = NULL;
  2656. HRESULT hr = S_OK;
  2657. DWORD cRecips = 0;
  2658. BOOL fFound = FALSE;
  2659. LPSTR szRecipBuffer = NULL;
  2660. DWORD cbRecipBuffer = 0;
  2661. DWORD cbProp = 0;
  2662. DWORD iCurrentRecip = 0;
  2663. DWORD iCurrentRecipType = 0;
  2664. DWORD iStartAddressType = m_dwRecipientAddressType;
  2665. DWORD iEndAddressType = g_cQAPIAddressTypes;
  2666. LPSTR szRecip = m_szMessageRecipient;
  2667. if (!szRecip || !pIMailMsgProperties)
  2668. goto Exit;
  2669. //
  2670. // IF we have an address type, then only check one address type
  2671. //
  2672. if (m_fRecipientAddressTypeSpecified && (iStartAddressType < g_cQAPIAddressTypes))
  2673. iEndAddressType = iStartAddressType+1;
  2674. //cleanup leading whitespace from recipient
  2675. while (*szRecip && isspace((UCHAR)*szRecip))
  2676. szRecip++;
  2677. if (!*szRecip)
  2678. goto Exit;
  2679. hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgRecipients,
  2680. (void **) &pIMailMsgRecipients);
  2681. _ASSERT(SUCCEEDED(hr) && "QueryInterface for IMailMsgRecipients failed");
  2682. if (FAILED(hr))
  2683. goto Exit;
  2684. _ASSERT(pIMailMsgRecipients);
  2685. hr = pIMailMsgRecipients->Count(&cRecips);
  2686. if (FAILED(hr))
  2687. goto Exit;
  2688. if (!cRecips)
  2689. goto Exit;
  2690. cbRecipBuffer = strlen(szRecip)*sizeof(CHAR) + sizeof(CHAR);
  2691. szRecipBuffer = (LPSTR) pvMalloc(cbRecipBuffer);
  2692. if (!szRecipBuffer)
  2693. {
  2694. hr = E_OUTOFMEMORY;
  2695. goto Exit;
  2696. }
  2697. //Loop over recips and look for a match... this will be slooow
  2698. //(see comment above).
  2699. for (iCurrentRecip = 0; iCurrentRecip < cRecips; iCurrentRecip++)
  2700. {
  2701. //
  2702. // Loop over all the possible recipient types
  2703. //
  2704. for (iCurrentRecipType = iStartAddressType ;
  2705. iCurrentRecipType < iEndAddressType;
  2706. iCurrentRecipType++)
  2707. {
  2708. hr = pIMailMsgRecipients->GetProperty(iCurrentRecip,
  2709. g_rgdwQAPIRecipPropIDs[iCurrentRecipType],
  2710. cbRecipBuffer, &cbProp, (BYTE *) szRecipBuffer);
  2711. if (FAILED(hr))
  2712. continue;
  2713. if (!lstrcmpi(szRecipBuffer, szRecip))
  2714. {
  2715. fFound = TRUE;
  2716. goto Exit;
  2717. }
  2718. //
  2719. // If we found an address of this type... don't bother checking the others
  2720. //
  2721. break;
  2722. }
  2723. }
  2724. Exit:
  2725. if (pIMailMsgRecipients)
  2726. pIMailMsgRecipients->Release();
  2727. if (szRecipBuffer)
  2728. FreePv(szRecipBuffer);
  2729. return fFound;
  2730. }
  2731. //---[ wszQueueAdminConvertToUnicode ]-----------------------------------------
  2732. //
  2733. //
  2734. // Description:
  2735. // Allocates and "upgrades" string to UNICODE. New String is Allocated
  2736. // with pvQueueAdminAlloc, so it can be passed out the queue admin
  2737. // interface.
  2738. // Parameters:
  2739. // szSrc Source string
  2740. // cSrc Strlen of sources string
  2741. // Returns:
  2742. // Pointer to UNICODE version of string (if successful)
  2743. // History:
  2744. // 6/7/99 - MikeSwa Created
  2745. //
  2746. //-----------------------------------------------------------------------------
  2747. LPWSTR wszQueueAdminConvertToUnicode(LPSTR szSrc, DWORD cSrc, BOOL fUTF8)
  2748. {
  2749. LPWSTR wszDest = NULL;
  2750. if (!szSrc)
  2751. return NULL;
  2752. if (!cSrc)
  2753. cSrc = strlen(szSrc);
  2754. else
  2755. {
  2756. //if a count of bytes is provided... it should be NULL terminated
  2757. _ASSERT(strlen(szSrc) <= cSrc);
  2758. }
  2759. wszDest = (LPWSTR) pvQueueAdminAlloc((cSrc+1)*sizeof(WCHAR));
  2760. if (!wszDest)
  2761. return NULL;
  2762. MultiByteToWideChar(fUTF8 ? CP_UTF8 : CP_ACP,
  2763. 0,
  2764. szSrc,
  2765. -1,
  2766. wszDest,
  2767. cSrc+1);
  2768. return wszDest;
  2769. }
  2770. //---[ fBiStrcmpi ]------------------------------------------------------------
  2771. //
  2772. //
  2773. // Description:
  2774. // Compares UNICODE to ASCII
  2775. // Parameters:
  2776. // IN sz ASCII string to compare
  2777. // IN wsz
  2778. // Returns:
  2779. // TRUE if strings match
  2780. // FALSE otherwise
  2781. // History:
  2782. // 6/7/99 - MikeSwa Created
  2783. //
  2784. //-----------------------------------------------------------------------------
  2785. BOOL fBiStrcmpi(LPSTR sz, LPWSTR wsz)
  2786. {
  2787. CHAR ch;
  2788. if (!sz && !wsz)
  2789. return TRUE;
  2790. if (!sz || !wsz)
  2791. return FALSE;
  2792. //Loop through strings.. conver UNICODE chars to ASCII and compare
  2793. while (*sz && *wsz)
  2794. {
  2795. wctomb(&ch, *wsz);
  2796. if (ch != *sz)
  2797. return FALSE;
  2798. sz++;
  2799. wsz++;
  2800. }
  2801. return TRUE; //they matched
  2802. }
  2803. //---[ szUnicodeToAscii ]------------------------------------------------------
  2804. //
  2805. //
  2806. // Description:
  2807. // Convert QueueAdmin parameter to UNICODE. Strings are alloced with
  2808. // Exchmem and are the responsability of the caller to free.
  2809. // Parameters:
  2810. // IN wszSrc Source string to contert
  2811. // Returns:
  2812. // Pointer to ASCII string on success
  2813. // NULL on failure
  2814. // History:
  2815. // 6/7/99 - MikeSwa Created
  2816. // 4/3/2000 - MikeSwa Modified to make loc safe
  2817. //
  2818. //-----------------------------------------------------------------------------
  2819. LPSTR szUnicodeToAscii(LPCWSTR wszSrc)
  2820. {
  2821. TraceFunctEnterEx((LPARAM) NULL, "szUnicodeToAscii");
  2822. LPSTR szDest = NULL;
  2823. DWORD dwErr = ERROR_SUCCESS;
  2824. DWORD cSrc = NULL;
  2825. if (!wszSrc)
  2826. return NULL;
  2827. //
  2828. // Call into WideCharToMultiByte to get length
  2829. //
  2830. cSrc = WideCharToMultiByte(CP_ACP,
  2831. 0,
  2832. wszSrc,
  2833. -1,
  2834. NULL,
  2835. 0,
  2836. NULL,
  2837. NULL);
  2838. cSrc++;
  2839. szDest = (LPSTR) pvMalloc((cSrc+1)*sizeof(CHAR));
  2840. if (!szDest)
  2841. {
  2842. ErrorTrace(0, "Unable to allocate conversion buffer of size %d", cSrc);
  2843. goto Exit;
  2844. }
  2845. //
  2846. // WideCharToMultiByte a second time to do the actual conversion
  2847. //
  2848. if (!WideCharToMultiByte(CP_ACP,
  2849. 0,
  2850. wszSrc,
  2851. -1,
  2852. szDest,
  2853. cSrc+1,
  2854. NULL,
  2855. NULL))
  2856. {
  2857. FreePv(szDest);
  2858. szDest = NULL;
  2859. dwErr = GetLastError();
  2860. ErrorTrace((LPARAM) NULL, "Error convert from UNICODE to ASCII - %lu", dwErr);
  2861. _ASSERT(0 && "Conversion from UNICODE failed");
  2862. }
  2863. else
  2864. {
  2865. DebugTrace(0, "Converted %S to %s", wszSrc, szDest);
  2866. }
  2867. Exit:
  2868. return szDest;
  2869. }
  2870. //---[ HrQADMApplyActionToIMailMessages ]-----------------------------------------
  2871. //
  2872. //
  2873. // Description:
  2874. // Convert QueueAdmin parameter to UNICODE. Strings are alloced with
  2875. // Exchmem and are the responsability of the caller to free.
  2876. // Parameters:
  2877. // IN pIMailMsgProperties mail msg object to apply action on
  2878. // IN pvContext CAQAdminMessageFilter used
  2879. // OUT pfContinue TRUE if we should continue
  2880. // OUT pfDelete TRUE if item should be deleted
  2881. // Returns:
  2882. // S_OK on sucess
  2883. // History:
  2884. // 8/8/00 - t-toddc created
  2885. //
  2886. //-----------------------------------------------------------------------------
  2887. HRESULT HrQADMApplyActionToIMailMessages(IN IMailMsgProperties *pIMailMsgProperties,
  2888. IN PVOID pvContext,
  2889. OUT BOOL *pfContinue,
  2890. OUT BOOL *pfDelete)
  2891. {
  2892. _ASSERT(pIMailMsgProperties);
  2893. _ASSERT(pvContext);
  2894. _ASSERT(pfContinue);
  2895. _ASSERT(pfDelete);
  2896. IQueueAdminMessageFilter *pIQueueAdminMessageFilter =
  2897. (IQueueAdminMessageFilter *) pvContext;
  2898. HRESULT hr = S_OK;
  2899. IUnknown *pIUnknownMsg = NULL;
  2900. hr = pIMailMsgProperties->QueryInterface(IID_IUnknown, (void **) &pIUnknownMsg);
  2901. _ASSERT(SUCCEEDED(hr) && "QueryInterface for IUnknown failed");
  2902. if (FAILED(hr))
  2903. {
  2904. *pfContinue = FALSE;
  2905. goto Cleanup;
  2906. }
  2907. hr = pIQueueAdminMessageFilter->HrProcessMessage(pIUnknownMsg,
  2908. pfContinue,
  2909. pfDelete);
  2910. if (FAILED(hr))
  2911. {
  2912. *pfContinue = FALSE;
  2913. goto Cleanup;
  2914. }
  2915. Cleanup:
  2916. if (pIUnknownMsg)
  2917. pIUnknownMsg->Release();
  2918. return hr;
  2919. }
  2920. //---[ HrGetMsgInfoFromIMailMsgProperty ]--------------------------------------
  2921. //
  2922. // Description:
  2923. // Fills out a queue admin MESSAGE_INFO structure. This function
  2924. // performs all action common to both IMailMsgProperties and CMsgRefs.
  2925. // Parameters:
  2926. // IN pIMailMsgProperties mail msg object to get info from
  2927. // IN OUT pMsgInfo MESSAGE_INFO struct to dump data to
  2928. // Returns:
  2929. // S_OK on success
  2930. // E_OUTOFMEMORY if an allocation failure
  2931. // History:
  2932. // 8/9/00 - t-toddc created
  2933. // 12/11/2000 - MikeSwa Merged code for Hg
  2934. //
  2935. //-----------------------------------------------------------------------------
  2936. HRESULT HrGetMsgInfoFromIMailMsgProperty(IMailMsgProperties* pIMailMsgProperties,
  2937. MESSAGE_INFO* pMsgInfo,
  2938. LINK_INFO_FLAGS flags)
  2939. {
  2940. TraceFunctEnterEx((LPARAM) pIMailMsgProperties, "HrGetMsgInfoFromIMailMsgProperty");
  2941. HRESULT hr = S_OK;
  2942. LPSTR szSender = NULL;
  2943. LPWSTR wszAddressType = NULL;
  2944. DWORD cbSender = 0;
  2945. DWORD iSenderAddressType = 0;
  2946. DWORD cbProp = 0;
  2947. DWORD i = 0;
  2948. _ASSERT(pIMailMsgProperties);
  2949. _ASSERT(pMsgInfo);
  2950. //Get Sender
  2951. hr = HrQueueAdminGetUnicodeStringProp(pIMailMsgProperties,
  2952. IMMPID_MP_RFC822_FROM_ADDRESS,
  2953. &pMsgInfo->szSender);
  2954. if (FAILED(hr))
  2955. goto Exit;
  2956. //If no P2 sender... use the P1
  2957. if (!pMsgInfo->szSender)
  2958. {
  2959. hr = HrQueueAdminGetP1Sender(pIMailMsgProperties, &szSender,
  2960. &cbSender, &iSenderAddressType);
  2961. if (FAILED(hr))
  2962. goto Exit;
  2963. if (szSender)
  2964. {
  2965. //
  2966. // Copy just the UNICODE address into the buffer. One could
  2967. // argue that the address type should be included, but I have
  2968. // decided not to, because:
  2969. // - It is of marginal use. The users of this API can determine
  2970. // what type of address it is.
  2971. // - If a message has been categorized we'll use the SMTP addr
  2972. // - If it comes from another machine (or the store driver has
  2973. // set the IMMPID_MP_RFC822_FROM_ADDRESS property),
  2974. // we will use the RFC822 From.
  2975. // - It changes the behavior from the previous versions.
  2976. // - It will involce adding some complexity/extra allocations
  2977. // to support it.
  2978. //
  2979. pMsgInfo->szSender = wszQueueAdminConvertToUnicode(szSender,
  2980. cbSender, FALSE);
  2981. }
  2982. }
  2983. hr = HrQueueAdminGetUnicodeStringProp(pIMailMsgProperties,
  2984. IMMPID_MP_RFC822_MSG_SUBJECT,
  2985. &pMsgInfo->szSubject);
  2986. if (FAILED(hr))
  2987. goto Exit;
  2988. //See X5:113280 for details. Basically, the P2 recipients are broken for
  2989. //any messages that go over BDAT.... hence we are not displaying them until
  2990. //the underlying SMTP bug is fixed
  2991. pMsgInfo->cRecipients = -1;
  2992. pMsgInfo->cCCRecipients = -1;
  2993. pMsgInfo->cBCCRecipients = -1;
  2994. //Get MsgID
  2995. hr = HrQueueAdminGetUnicodeStringProp(pIMailMsgProperties,
  2996. IMMPID_MP_RFC822_MSG_ID,
  2997. &pMsgInfo->szMessageId);
  2998. if (FAILED(hr))
  2999. goto Exit;
  3000. QueueAdminGetRecipListFromP1(pIMailMsgProperties, pMsgInfo);
  3001. Exit:
  3002. if (szSender)
  3003. QueueAdminFree(szSender);
  3004. TraceFunctLeave();
  3005. return hr;
  3006. }
  3007. //---[ HrQueueAdminGetP1Sender ]-----------------------------------------------
  3008. //
  3009. //
  3010. // Description:
  3011. // Handles getting the P1 sender and type... based on the
  3012. // Parameters:
  3013. // IN pIMailMsgProperties IMailMsgProperties to get sender from
  3014. // OUT pszSender Address of sender (including type) - must be
  3015. // free'd using QueueAdminFree()
  3016. // OUT pcbSender Size of sender (including NULLS)
  3017. // OUT piAddressType Returns the matching address type
  3018. // OPT IN iStartAddressType Address type to start with. Can be used to
  3019. // skip higher priority address types if we are
  3020. // are comparing against a given address. With
  3021. // the default value of 0, we will check all
  3022. // addresses in priority order. This is OPTIONAL
  3023. // OPT IN fRequireAddressTypeMatch Requires that the address type found
  3024. // matches the iStartAddressType
  3025. // Returns:
  3026. // S_OK on success
  3027. // History:
  3028. // 3/1/2001 - MikeSwa Created
  3029. //
  3030. //-----------------------------------------------------------------------------
  3031. HRESULT HrQueueAdminGetP1Sender(IMailMsgProperties *pIMailMsgProperties,
  3032. LPSTR *pszSender,
  3033. DWORD *pcbSender,
  3034. DWORD *piAddressType,
  3035. DWORD iStartAddressType,
  3036. BOOL fRequireAddressTypeMatch)
  3037. {
  3038. TraceFunctEnterEx((LPARAM) pIMailMsgProperties, "HrQueueAdminGetP1Sender");
  3039. HRESULT hr = S_OK;
  3040. DWORD i = iStartAddressType;
  3041. DWORD iStopAddressType = g_cQAPIAddressTypes;
  3042. _ASSERT(pszSender);
  3043. _ASSERT(pcbSender);
  3044. _ASSERT(pIMailMsgProperties);
  3045. _ASSERT(piAddressType);
  3046. if (!pIMailMsgProperties || !pszSender || !pcbSender || !piAddressType)
  3047. {
  3048. hr = E_POINTER;
  3049. goto Exit;
  3050. }
  3051. _ASSERT(iStartAddressType < g_cQAPIAddressTypes);
  3052. *pszSender = NULL;
  3053. *pcbSender = 0;
  3054. *piAddressType = 0;
  3055. if (fRequireAddressTypeMatch && (iStartAddressType < g_cQAPIAddressTypes))
  3056. iStopAddressType = iStartAddressType+1;
  3057. for (i = iStartAddressType; i < iStopAddressType; i++)
  3058. {
  3059. *pszSender = NULL;
  3060. *pcbSender = 0;
  3061. *piAddressType = i;
  3062. hr = HrQueueAdminGetStringProp(pIMailMsgProperties,
  3063. g_rgdwQAPISenderPropIDs[i],
  3064. pszSender, pcbSender);
  3065. //
  3066. // If we have found a match... bail
  3067. // If we have a real failure... bail
  3068. //
  3069. if (SUCCEEDED(hr))
  3070. {
  3071. //
  3072. // HrQueueAdminGetStringProp attempts to hide non-fatal errors
  3073. // from the UI and can succeed if no property is found
  3074. //
  3075. if (*pszSender)
  3076. break;
  3077. }
  3078. else if (MAILMSG_E_PROPNOTFOUND != hr)
  3079. goto Exit;
  3080. }
  3081. Exit:
  3082. TraceFunctLeave();
  3083. return hr;
  3084. }
  3085. //---[ HrQADMGetMsgSize ]------------------------------------------------------
  3086. //
  3087. // Description:
  3088. // obtains the size of an IMailMsgProperties Object
  3089. // Parameters:
  3090. // IN pIMailMsgProperties mail msg object to get info from
  3091. // OUT pcbMsgSize size info
  3092. // Returns:
  3093. // S_OK on success
  3094. // History:
  3095. // 8/10/00 - t-toddc created
  3096. // 12/11/2000 - MikeSwa Merged code for Hg
  3097. //
  3098. //-----------------------------------------------------------------------------
  3099. HRESULT HrQADMGetMsgSize(IMailMsgProperties* pIMailMsgProperties,
  3100. DWORD* pcbMsgSize)
  3101. {
  3102. TraceFunctEnterEx((LPARAM) pIMailMsgProperties, "HrQADMGetMsgSize");
  3103. HRESULT hr = S_OK;
  3104. DWORD cbMsgSize= 0;
  3105. _ASSERT(pIMailMsgProperties);
  3106. _ASSERT(pcbMsgSize);
  3107. //Get the size of the message
  3108. hr = pIMailMsgProperties->GetDWORD(IMMPID_MP_MSG_SIZE_HINT, &cbMsgSize);
  3109. if (FAILED(hr))
  3110. {
  3111. if (MAILMSG_E_PROPNOTFOUND != hr)
  3112. {
  3113. ErrorTrace((LPARAM) pIMailMsgProperties,
  3114. "Failed to get message size hint 0x%08X", hr);
  3115. cbMsgSize = 0;
  3116. goto Exit;
  3117. }
  3118. else
  3119. {
  3120. hr = S_OK;
  3121. cbMsgSize = DEFAULT_MSG_HINT_SIZE;
  3122. DebugTrace((LPARAM) pIMailMsgProperties,
  3123. "Unable to get size.. using default size");
  3124. }
  3125. }
  3126. Exit:
  3127. if (pcbMsgSize)
  3128. *pcbMsgSize = cbMsgSize;
  3129. TraceFunctLeave();
  3130. return hr;
  3131. }
  3132. //---[ UpdateCountersForLinkType ]----------------------------------------------------
  3133. //
  3134. //
  3135. // Description:
  3136. // Updates the VSI perf counters based on the link type passed in.
  3137. // Parameters:
  3138. // paqinst Ptr to server instance object
  3139. // dwLinkType Type of queue
  3140. // Returns:
  3141. // -
  3142. // History:
  3143. // 1/10/2001 - MikeSwa Created
  3144. //
  3145. //-----------------------------------------------------------------------------
  3146. VOID UpdateCountersForLinkType(CAQSvrInst *paqinst, DWORD dwLinkType)
  3147. {
  3148. _ASSERT(paqinst);
  3149. if (!paqinst)
  3150. return;
  3151. if (LI_TYPE_LOCAL_DELIVERY & dwLinkType)
  3152. paqinst->DecPendingLocal();
  3153. else if (LI_TYPE_PENDING_ROUTING & dwLinkType)
  3154. paqinst->DecPendingRouting();
  3155. else if (LI_TYPE_PENDING_CAT & dwLinkType)
  3156. paqinst->DecPendingCat();
  3157. else if (LI_TYPE_PENDING_SUBMIT & dwLinkType)
  3158. paqinst->DecPendingSubmit();
  3159. }
  3160. //---[ QueueAdminFileTimeToSystemTime ]----------------------------------------
  3161. //
  3162. //
  3163. // Description:
  3164. // Converts a filetime to a system time. Checks to see if the FILETIME
  3165. // is zero first, so we don't end up with a date of 1/1/1601
  3166. // Parameters:
  3167. //
  3168. // Returns:
  3169. //
  3170. // History:
  3171. // 1/11/2001 - MikeSwa Created
  3172. //
  3173. //-----------------------------------------------------------------------------
  3174. VOID QueueAdminFileTimeToSystemTime(FILETIME *pft, SYSTEMTIME *pst)
  3175. {
  3176. BOOL fConverted = FALSE;
  3177. _ASSERT(pft);
  3178. _ASSERT(pst);
  3179. if (pft->dwHighDateTime && pft->dwLowDateTime)
  3180. fConverted = FileTimeToSystemTime(pft, pst);
  3181. if (!fConverted)
  3182. ZeroMemory(pst, sizeof(SYSTEMTIME));
  3183. }
  3184. //---[ CAQSvrInst::fIsLocalQueueAdminAction ]----------------------------------
  3185. //
  3186. //
  3187. // Description:
  3188. // Determines if the IQueueAdminAction interface being passed in is local.
  3189. // Used so the same code can provide MESSAGE_INFO for local and remote
  3190. // queues
  3191. // Parameters:
  3192. // pIQueueAdminAction
  3193. // Returns:
  3194. // TRUE if local
  3195. // FALSE otherwise
  3196. // History:
  3197. // 1/11/2001 - MikeSwa Created
  3198. //
  3199. //-----------------------------------------------------------------------------
  3200. BOOL CAQSvrInst::fIsLocalQueueAdminAction(IQueueAdminAction *pIQueueAdminAction)
  3201. {
  3202. HRESULT hr = S_OK;
  3203. BOOL fMatch = FALSE;
  3204. IQueueAdminAction *pIQueueAdminActionLocal = NULL;
  3205. hr = m_asyncqPreLocalDeliveryQueue.QueryInterface(
  3206. IID_IQueueAdminAction,
  3207. (void **) &pIQueueAdminActionLocal);
  3208. _ASSERT(SUCCEEDED(hr) && "QI for IQueueAdminAction failed on internal queue!!");
  3209. if (FAILED(hr))
  3210. goto Exit;
  3211. fMatch = (pIQueueAdminActionLocal == pIQueueAdminAction);
  3212. Exit:
  3213. if (pIQueueAdminActionLocal)
  3214. pIQueueAdminActionLocal->Release();
  3215. return fMatch;
  3216. }