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.

3126 lines
103 KiB

  1. //-----------------------------------------------------------------------------
  2. //
  3. //
  4. // File: msgref.cpp
  5. //
  6. // Description:
  7. // Implementation of CMT Message reference
  8. //
  9. // Author: mikeswa
  10. //
  11. // Copyright (C) 1997 Microsoft Corporation
  12. //
  13. //-----------------------------------------------------------------------------
  14. #include "aqprecmp.h"
  15. #include "dcontext.h"
  16. #include "dsnevent.h"
  17. #include "aqutil.h"
  18. CPool CMsgRef::s_MsgRefPool(MSGREF_SIG);
  19. DWORD CMsgRef::s_cMsgsPendingBounceUsage = 0;
  20. DWORD CMsgRef::s_cCurrentMsgsPendingDelete = 0;
  21. DWORD CMsgRef::s_cTotalMsgsPendingDelete = 0;
  22. DWORD CMsgRef::s_cTotalMsgsDeletedAfterPendingDelete = 0;
  23. DWORD CMsgRef::s_cCurrentMsgsDeletedNotReleased = 0;
  24. #ifndef DEBUG
  25. #define _VERIFY_RECIPS_HANDLED(pIRecipList, iStartRecipIndex, cRecipCount)
  26. #else
  27. #define _VERIFY_RECIPS_HANDLED(pIRecipList, iStartRecipIndex, cRecipCount) \
  28. VerifyRecipsHandledFn(pIRecipList, iStartRecipIndex, cRecipCount);
  29. //---[ VerifyRecipsHandleFn ]--------------------------------------------------
  30. //
  31. //
  32. // Description:
  33. // Verifies that all recipients in a given range have been handled.
  34. // Parameters:
  35. // pIRecipList IMailMsgRecipients Interface for message
  36. // iStartRecipIndex The first recipient to verify
  37. // cRecipCount The count of recipients to verify
  38. // Returns:
  39. // -
  40. // Asserts on failures
  41. // History:
  42. // 10/5/1999 - MikeSwa Created
  43. //
  44. //-----------------------------------------------------------------------------
  45. void VerifyRecipsHandledFn(IMailMsgRecipients *pIRecipList,
  46. DWORD iStartRecipIndex,
  47. DWORD cRecipCount)
  48. {
  49. HRESULT hr = S_OK;
  50. DWORD dwCurrentRecipFlags = 0;
  51. for (DWORD j = iStartRecipIndex; j < cRecipCount + iStartRecipIndex; j++)
  52. {
  53. dwCurrentRecipFlags = 0;
  54. hr = pIRecipList->GetDWORD(j, IMMPID_RP_RECIPIENT_FLAGS,
  55. &dwCurrentRecipFlags);
  56. if (SUCCEEDED(hr))
  57. _ASSERT(RP_HANDLED & dwCurrentRecipFlags);
  58. }
  59. }
  60. #endif //DEBUG
  61. //#ifndef DEBUG
  62. #if 1
  63. #define _VERIFY_QUEUE_PTR(paqinst, szCurrentDomain, pdmq, cQueues, rgpdmqQueues)
  64. #else //DEBUG defined
  65. #define _VERIFY_QUEUE_PTR(paqinst, szCurrentDomain, pdmq, cQueues, rgpdmqQueues) \
  66. VerifyQueuePtrFn(paqinst, szCurrentDomain, pdmq, cQueues, rgpdmqQueues)
  67. //---[ VerifyQueuePtrFn ]-------------------------------------------------------
  68. //
  69. //
  70. // Description:
  71. // Debug function to verify that the queue ptr returned by HrMapDomain is
  72. // valid and does not confict with previous queue pointers.
  73. // Parameters:
  74. // paqinst - ptr to CMQ object
  75. // szCurrentDomain - Domain that ptr is for
  76. // pdmq - Queue ptr to verify
  77. // cQueues - Number of queues so far
  78. // rgpdmqQueues - Array of queues so far
  79. // Returns:
  80. // -
  81. // History:
  82. // 6/1/98 - MikeSwa Created
  83. // 8/14/98 - MikeSwa Modified to handled shutdown
  84. //
  85. //-----------------------------------------------------------------------------
  86. void VerifyQueuePtrFn(CAQSvrInst *paqinst, LPSTR szCurrentDomain, CDestMsgQueue *pdmq,
  87. DWORD cQueues, CDestMsgQueue **rgpdmqQueues)
  88. {
  89. HRESULT hr = S_OK;
  90. CInternalDomainInfo *pDomainInfo = NULL;
  91. DWORD j;
  92. //verify that non-local domains have queue ptrs
  93. hr = paqinst->HrGetInternalDomainInfo(strlen(szCurrentDomain), szCurrentDomain,
  94. &pDomainInfo);
  95. if (AQUEUE_E_SHUTDOWN == hr)
  96. return;
  97. _ASSERT(SUCCEEDED(hr));
  98. if (!(pDomainInfo->m_DomainInfo.dwDomainInfoFlags & DOMAIN_INFO_LOCAL_MAILBOX))
  99. _ASSERT(pdmq && "NULL DesMsgQueue returned for NON-LOCAL domain");
  100. pDomainInfo->Release();
  101. //verify that Recip list is not returning domains out of order
  102. if (pdmq)
  103. {
  104. for (j = 0; j < cQueues; j++)
  105. {
  106. //check if ptr has been used yet
  107. if (rgpdmqQueues[j] == pdmq)
  108. {
  109. _ASSERT(0 && "IMailMsg Domain interface is broken");
  110. }
  111. }
  112. }
  113. }
  114. #endif //DEBUG
  115. //---[ fAllRecipsInRangeHandled ]----------------------------------------------
  116. //
  117. //
  118. // Description:
  119. // Utility function that iterates through recipients in a range (usually
  120. // a single domain) and determines if all the recipients have been handled.
  121. // Parameters:
  122. // pIRecipList IMailMsgRecipients Interface for message
  123. // iStartRecipIndex The first recipient to verify
  124. // cRecipCount The count of recipients to verify
  125. // Returns:
  126. // TRUE if *all* of the recipients in the range are handle
  127. // FALSE if one or more of the recipients in the range are not handled,
  128. // History:
  129. // 10/25/1999 - MikeSwa Created
  130. //
  131. //-----------------------------------------------------------------------------
  132. BOOL fAllRecipsInRangeHandled(IMailMsgRecipients *pIRecipList,
  133. DWORD iStartRecipIndex,
  134. DWORD cRecipCount)
  135. {
  136. HRESULT hr = S_OK;
  137. DWORD dwCurrentRecipFlags = 0;
  138. BOOL fAreHandled = TRUE;
  139. _ASSERT(cRecipCount);
  140. for (DWORD j = iStartRecipIndex; j < cRecipCount + iStartRecipIndex; j++)
  141. {
  142. dwCurrentRecipFlags = 0;
  143. hr = pIRecipList->GetDWORD(j, IMMPID_RP_RECIPIENT_FLAGS,
  144. &dwCurrentRecipFlags);
  145. if (FAILED(hr) || !(RP_HANDLED & dwCurrentRecipFlags))
  146. {
  147. fAreHandled = FALSE;
  148. break;
  149. }
  150. }
  151. if (fAreHandled) {
  152. _VERIFY_RECIPS_HANDLED(pIRecipList, iStartRecipIndex, cRecipCount);
  153. }
  154. return fAreHandled;
  155. }
  156. //---[ CMsgRef::new ]----------------------------------------------------------
  157. //
  158. //
  159. // Description:
  160. // Overide the new operator to allow for the variable size of this class.
  161. // CPool is used for the 90% case allocations, while exchmem is used
  162. // for odd-size allocations.
  163. // Parameters:
  164. // cDomains the number of domains this message is being delivered to.
  165. // Returns:
  166. // -
  167. //-----------------------------------------------------------------------------
  168. void * CMsgRef::operator new(size_t stIgnored, unsigned int cDomains)
  169. {
  170. CPoolMsgRef *pcpmsgref = NULL;
  171. DWORD dwAllocationFlags = MSGREF_CPOOL_SIG;
  172. if (fIsStandardSize(cDomains))
  173. {
  174. dwAllocationFlags |= MSGREF_STANDARD_SIZE;
  175. //if our expected standard size... then use cpool allocator
  176. pcpmsgref = (CPoolMsgRef *) s_MsgRefPool.Alloc();
  177. if (pcpmsgref)
  178. {
  179. DEBUG_DO_IT(InterlockedIncrement((PLONG) &g_cDbgMsgRefsCpoolAllocated));
  180. dwAllocationFlags |= MSGREF_CPOOL_ALLOCATED;
  181. }
  182. else
  183. {
  184. DEBUG_DO_IT(InterlockedIncrement((PLONG) &g_cDbgMsgRefsCpoolFailed));
  185. }
  186. }
  187. if (!pcpmsgref)
  188. {
  189. //
  190. // Sanity check size. If cDomains is ~-1 size() will wrap.
  191. // This will firewall us against corrupt messages.
  192. //
  193. if (size(cDomains) >= cDomains)
  194. {
  195. pcpmsgref = (CPoolMsgRef *) pvMalloc(sizeof(CPoolMsgRef) - sizeof(CMsgRef) + size(cDomains));
  196. if (pcpmsgref)
  197. {
  198. DEBUG_DO_IT(InterlockedIncrement((PLONG) &g_cDbgMsgRefsExchmemAllocated));
  199. }
  200. }
  201. }
  202. if (pcpmsgref)
  203. {
  204. pcpmsgref->m_dwAllocationFlags = dwAllocationFlags;
  205. return ((void *) &(pcpmsgref->m_msgref));
  206. }
  207. else
  208. {
  209. return NULL;
  210. }
  211. }
  212. //---[ CMsgRef::CMsgRef ]------------------------------------------------------
  213. //
  214. //
  215. // Description:
  216. // Constructor for CMsgRef
  217. // Parameters:
  218. // cDomains the number of domains this msg is being delivered to
  219. // pimsg ptr to the imsg object for this message
  220. // Returns:
  221. //
  222. //
  223. //-----------------------------------------------------------------------------
  224. CMsgRef::CMsgRef(DWORD cDomains, IMailMsgQueueMgmt *pIMailMsgQM,
  225. IMailMsgProperties *pIMailMsgProperties,
  226. CAQSvrInst *paqinst, DWORD dwMessageType, GUID guidRouter)
  227. : m_aqmtMessageType(guidRouter, dwMessageType)
  228. {
  229. TraceFunctEnterEx((LPARAM) this, "CMsgRef::CMsgRef");
  230. _ASSERT(pIMailMsgQM);
  231. _ASSERT(pIMailMsgProperties);
  232. _ASSERT(paqinst);
  233. pIMailMsgQM->AddRef();
  234. pIMailMsgProperties->AddRef();
  235. paqinst->AddRef();
  236. m_dwSignature = MSGREF_SIG;
  237. m_dwDataFlags = MSGREF_VERSION;
  238. m_cDomains = cDomains;
  239. m_pIMailMsgQM = pIMailMsgQM;
  240. m_pIMailMsgProperties = pIMailMsgProperties;
  241. m_pIMailMsgRecipients = NULL;
  242. m_paqinst = paqinst;
  243. m_pmgle = NULL;
  244. m_cbMsgSize = 0; //initialize from IMsg
  245. m_dwMsgIdHash = dwQueueAdminHash(NULL);
  246. m_cTimesRetried = 0;
  247. m_cInternalUsageCount = 1; //Initialize to 1 like the mailmsg usage count
  248. //initialize stuff that is past traditional end of object
  249. ZeroMemory(m_rgpdmqDomains, (size(cDomains)+sizeof(CDestMsgQueue *)-sizeof(CMsgRef)));
  250. TraceFunctLeave();
  251. }
  252. //---[ CMsgRef::~CMsgRef ]------------------------------------------------------------
  253. //
  254. //
  255. // Description:
  256. // CMsgRef Destructor
  257. // Parameters:
  258. // -
  259. // Returns:
  260. // -
  261. //
  262. //-----------------------------------------------------------------------------
  263. CMsgRef::~CMsgRef()
  264. {
  265. TraceFunctEnterEx((LPARAM) this, "CMsgRef::~CMsgRef");
  266. HRESULT hr = S_OK;
  267. DWORD i = 0;
  268. _ASSERT(m_paqinst);
  269. _ASSERT(!(MSGREF_USAGE_COUNT_IN_USE & m_dwDataFlags)); //this should never remain set
  270. //There should be corresponding calls to AddUsage/ReleaseUsage
  271. _ASSERT(m_cInternalUsageCount == 1);
  272. //Release MsgGuidListEntry if we have one
  273. if (m_pmgle)
  274. {
  275. m_pmgle->RemoveFromList();
  276. m_pmgle->Release();
  277. }
  278. ReleaseMailMsg(TRUE); //Force commit/delete/release of associated mailmsg
  279. //Update count of messages that have had ::Delete called on them
  280. //but not been released.
  281. if ((MSGREF_MAILMSG_DELETE_PENDING & m_dwDataFlags) &&
  282. (MSGREF_MAILMSG_DELETED & m_dwDataFlags))
  283. {
  284. InterlockedDecrement((PLONG) &s_cCurrentMsgsDeletedNotReleased);
  285. }
  286. //Release references to DestMsgQueues
  287. for (i = 0; i < m_cDomains; i++)
  288. {
  289. if (m_rgpdmqDomains[i])
  290. {
  291. m_rgpdmqDomains[i]->Release();
  292. m_rgpdmqDomains[i] = NULL;
  293. }
  294. }
  295. m_paqinst->Release();
  296. TraceFunctLeave();
  297. }
  298. //---[ CMsgRef::ReleaseMailMsg ]-----------------------------------------------
  299. //
  300. //
  301. // Description:
  302. // Release this objects mailmsg... do the necessary commit/delete
  303. // Parameters:
  304. // fForceRelease TRUE mailmsg must be released (used by desctructor)
  305. // FALSE release mailmsg if shutdown is happening
  306. // Returns:
  307. // -
  308. // History:
  309. // 7/7/99 - MikeSwa Created
  310. // 10/8/99 - MikeSwa Fixed problem when SpinLock fials
  311. //
  312. //-----------------------------------------------------------------------------
  313. void CMsgRef::ReleaseMailMsg(BOOL fForceRelease)
  314. {
  315. TraceFunctEnterEx((LPARAM) this, "CMsgRef::ReleaseMailMsg");
  316. HRESULT hr = S_OK;
  317. if (!fForceRelease && !m_paqinst->fShutdownSignaled())
  318. return; //we'll let the actual release handle this case
  319. //Make sure no one is trying to bounce usage
  320. if (!fTrySpinLock(&m_dwDataFlags, MSGREF_USAGE_COUNT_IN_USE))
  321. {
  322. //Someone is... this should not happen on the final release
  323. DebugTrace((LPARAM) this, "Someone else using mailmsg... bailing");
  324. _ASSERT(!fForceRelease);
  325. return;
  326. }
  327. if (MSGREF_MAILMSG_RELEASED &
  328. dwInterlockedSetBits(&m_dwDataFlags, MSGREF_MAILMSG_RELEASED))
  329. {
  330. //Someone else has already come along and done this
  331. ReleaseSpinLock(&m_dwDataFlags, MSGREF_USAGE_COUNT_IN_USE);
  332. return;
  333. }
  334. if (NULL != m_pIMailMsgQM)
  335. {
  336. if ((m_dwDataFlags & MSGREF_SUPERSEDED) ||
  337. pmbmapGetHandled()->FAllSet(m_cDomains))
  338. {
  339. //The message has been handled (or superseded)... we can delete it
  340. ThreadSafeMailMsgDelete();
  341. }
  342. //Releasing the message will commit it if dirty (and not-deleted)
  343. m_pIMailMsgQM->Release();
  344. m_pIMailMsgQM = NULL;
  345. m_paqinst->DecMsgsInSystem(
  346. m_dwDataFlags & MSGREF_MSG_REMOTE_RETRY,
  347. m_dwDataFlags & MSGREF_MSG_COUNTED_AS_REMOTE,
  348. m_dwDataFlags & MSGREF_MSG_LOCAL_RETRY);
  349. }
  350. if (m_dwDataFlags & MSGREF_MSG_RETRY_ON_DELETE)
  351. {
  352. //Retry msg
  353. DEBUG_DO_IT(InterlockedDecrement((PLONG) &g_cDbgMsgRefsPendingRetryOnDelete));
  354. m_paqinst->HandleAQFailure(AQ_FAILURE_MSGREF_RETRY,
  355. E_FAIL, m_pIMailMsgProperties);
  356. }
  357. if (NULL != m_pIMailMsgProperties)
  358. {
  359. m_pIMailMsgProperties->Release();
  360. m_pIMailMsgProperties = NULL;
  361. }
  362. if (NULL != m_pIMailMsgRecipients)
  363. {
  364. m_pIMailMsgRecipients->Release();
  365. m_pIMailMsgRecipients = NULL;
  366. }
  367. ReleaseSpinLock(&m_dwDataFlags, MSGREF_USAGE_COUNT_IN_USE);
  368. TraceFunctLeave();
  369. }
  370. //---[ CMsgRef::HrInitialize() ]-----------------------------------------------
  371. //
  372. //
  373. // Description:
  374. // Perform initialization for the msgref object. Queries the DMT for
  375. // queues for each of the domains that this message is being sent to.
  376. //
  377. // Can be called multiple times if needed (e.g., if the DMT version
  378. // changes before the messages are queued).
  379. // Parameters:
  380. // IN paqinst Ptr to CMQ object
  381. // IN pIRecipList interface for recipients of msg
  382. // IN pIMessageRouter Router for this message
  383. // IN dwMessageType Msg Type for message
  384. // OUT pcLocalRecips - # of local recipients
  385. // OUT pcRemoteRecips - # of remote recipients
  386. // OUT pcQueues # of queues for this message
  387. // IN OUT rgdmqQueues Array of queue ptrs
  388. // Allocated by calling routine (to ease memory management)
  389. // must be at least cDomains long
  390. //
  391. // Returns:
  392. // S_OK on success
  393. // E_INVALIDARG if the data we need is not preset
  394. // AQUEUE_E_INVALID_MSG_ID if the message id is too long??
  395. //
  396. //-----------------------------------------------------------------------------
  397. HRESULT CMsgRef::HrInitialize(IN IMailMsgRecipients *pIRecipList,
  398. IN IMessageRouter *pIMessageRouter,
  399. IN DWORD dwMessageType,
  400. OUT DWORD *pcLocalRecips,
  401. OUT DWORD *pcRemoteRecips,
  402. OUT DWORD *pcQueues,
  403. IN OUT CDestMsgQueue **rgpdmqQueues)
  404. {
  405. const DWORD IMSG_MAX_DOMAIN_LEN = 512;
  406. TraceFunctEnterEx((LPARAM) this, "CMsgRef::HrInitialize");
  407. HRESULT hr = S_OK;
  408. CDomainMapping dmap; //value returned by DMT
  409. CDestMsgQueue *pdmq = NULL; //ptr to queue returned by DMT
  410. DWORD i; //tmp counter
  411. BOOL fUsed = FALSE;
  412. BOOL fLocked = FALSE; //has the aqinst been locked?
  413. char szCurrentDomain[IMSG_MAX_DOMAIN_LEN +1]; //Current Domain name
  414. DWORD iCurrentLowRecip = 0; //current low recip index
  415. DWORD cCurrentRecipCount = 0; //current recipient count
  416. DWORD cQueues = 0;
  417. DWORD cCachedIMsgHandles = 0;
  418. _ASSERT(m_paqinst);
  419. _ASSERT(pcQueues);
  420. _ASSERT(rgpdmqQueues);
  421. _ASSERT(m_pIMailMsgQM);
  422. _ASSERT(m_pIMailMsgProperties);
  423. _ASSERT(pIMessageRouter);
  424. _ASSERT(pcLocalRecips);
  425. _ASSERT(pcRemoteRecips);
  426. _ASSERT(pIRecipList);
  427. *pcLocalRecips = 0;
  428. *pcRemoteRecips = 0;
  429. pIRecipList->AddRef();
  430. //If being called for a 2nd time... release old info
  431. //even though it might be the same... we don't want to leak
  432. if (m_pIMailMsgRecipients)
  433. m_pIMailMsgRecipients->Release();
  434. m_pIMailMsgRecipients = pIRecipList;
  435. //Reset Message type in case it was updated
  436. m_aqmtMessageType.SetMessageType(dwMessageType);
  437. if (!m_paqinst->fTryShutdownLock())
  438. {
  439. hr = AQUEUE_E_SHUTDOWN;
  440. goto Exit;
  441. }
  442. fLocked = TRUE;
  443. if (!(MSGREF_MSG_INIT & m_dwDataFlags))
  444. {
  445. hr = HrOneTimeInit();
  446. if (FAILED(hr))
  447. goto Exit;
  448. }
  449. for (i = 0; i < m_cDomains; i++)
  450. {
  451. //$$REVIEW: When to we need to check and make sure that it is a
  452. // properly formatted Domain Name... currently X400 will work too!!
  453. hr = pIRecipList->DomainItem(i, IMSG_MAX_DOMAIN_LEN +1, szCurrentDomain,
  454. &iCurrentLowRecip, &cCurrentRecipCount);
  455. if (FAILED(hr))
  456. goto Exit;
  457. //
  458. // This is a quick fix for Milestone 2 so that we can send
  459. // mail to external X400 addresses. Their szCurrentDomain
  460. // will be empty string. Substitute that with " " which is
  461. // added as a local domain
  462. //
  463. if(*szCurrentDomain == '\0') {
  464. //
  465. // jstamerj 1998/07/24 12:24:48:
  466. // We can't handle an empty string, so translate this to " "
  467. //
  468. szCurrentDomain[0] = ' ';
  469. szCurrentDomain[1] = '\0';
  470. }
  471. if (fAllRecipsInRangeHandled(pIRecipList, iCurrentLowRecip, cCurrentRecipCount))
  472. {
  473. //All recipients for this domain have been handled, we do not need to
  474. //queue it up for delivery. Typical reasons for this are:
  475. // - address that have been rejected by cat, but there are valid
  476. // local recips
  477. // - On restart if some recipients for a message still have not been
  478. // delivered
  479. pmbmapGetHandled()->HrMarkBits(m_cDomains, 1, &i, TRUE);
  480. if (FAILED(hr))
  481. ErrorTrace((LPARAM) this, "HrMarkBits returned hr 0x%08X", hr);
  482. pdmq = NULL;
  483. }
  484. else
  485. {
  486. hr = m_paqinst->pdmtGetDMT()->HrMapDomainName(szCurrentDomain, &m_aqmtMessageType,
  487. pIMessageRouter, &dmap, &pdmq);
  488. if (FAILED(hr))
  489. {
  490. if (PHATQ_BAD_DOMAIN_SYNTAX == hr)
  491. {
  492. //The domain name is malformed... we should not attempt delivery
  493. //for this domain, but should deliver rest of message
  494. _VERIFY_RECIPS_HANDLED(pIRecipList, iCurrentLowRecip, cCurrentRecipCount);
  495. ErrorTrace((LPARAM) this,
  496. "Encountered invalid domain %s", szCurrentDomain);
  497. //Set this domain as handled
  498. pmbmapGetHandled()->HrMarkBits(m_cDomains, 1, &i, TRUE);
  499. if (FAILED(hr))
  500. ErrorTrace((LPARAM) this, "HrMarkBits returned hr 0x%08X", hr);
  501. }
  502. else
  503. goto Exit;
  504. }
  505. }
  506. //HrInitialize is being called a 2nd time... release old queues so we don't leak
  507. if (m_rgpdmqDomains[i])
  508. m_rgpdmqDomains[i]->Release();
  509. m_rgpdmqDomains[i] = pdmq;
  510. //keep track of local/remote recip counts
  511. if (pdmq)
  512. *pcRemoteRecips += cCurrentRecipCount;
  513. else
  514. *pcLocalRecips += cCurrentRecipCount;
  515. _ASSERT(cCurrentRecipCount);
  516. _VERIFY_QUEUE_PTR(m_paqinst, szCurrentDomain, pdmq, cQueues, rgpdmqQueues);
  517. rgpdmqQueues[cQueues] = pdmq; //save new ptr
  518. SetRecipIndex(cQueues, iCurrentLowRecip, cCurrentRecipCount + iCurrentLowRecip -1);
  519. cQueues++;
  520. //set bit in bitmap
  521. hr = pmbmapGetDomainBitmap(i)->HrMarkBits(m_cDomains, 1, &i, TRUE);
  522. _ASSERT(SUCCEEDED(hr) && "Bitmap code failed");
  523. }
  524. *pcQueues = cQueues;
  525. Exit:
  526. if (FAILED(hr))
  527. {
  528. //Release references to DestMsgQueues
  529. for (i = 0; i < m_cDomains; i++)
  530. {
  531. if (m_rgpdmqDomains[i])
  532. {
  533. m_rgpdmqDomains[i]->Release();
  534. m_rgpdmqDomains[i] = NULL;
  535. }
  536. }
  537. }
  538. if (fLocked)
  539. {
  540. m_paqinst->ShutdownUnlock();
  541. }
  542. TraceFunctLeave();
  543. return hr;
  544. }
  545. //---[ CMsgRef::HrOneTimeInit ]------------------------------------------------
  546. //
  547. //
  548. // Description:
  549. // Perform 1-time message initialization (HrInitialize can be called
  550. // multiple times... this function encapuslates that things that only
  551. // need to be done once).
  552. //
  553. // Parameters:
  554. // -
  555. // Returns:
  556. // S_OK on success
  557. // History:
  558. // 10/12/98 - MikeSwa Created
  559. //
  560. //-----------------------------------------------------------------------------
  561. HRESULT CMsgRef::HrOneTimeInit()
  562. {
  563. TraceFunctEnterEx((LPARAM) this, "CMsgRef::HrOneTimeInit");
  564. HRESULT hr = S_OK;
  565. DWORD cbProp = 0;
  566. GUID guidID = GUID_NULL;
  567. GUID *pguidID = NULL;
  568. GUID guidSupersedes;
  569. GUID *pguidSupersedes = NULL;
  570. BOOL fFoundMsgID = FALSE;
  571. CHAR szBuffer[300];
  572. _ASSERT(!(MSGREF_MSG_INIT & m_dwDataFlags));
  573. //get data we want from the IMsg
  574. //The info we want to include follows
  575. // -Time IMsg entered the system (creation time)
  576. // -Message Priority
  577. // -Message Size
  578. // -Supersedeable message ID
  579. // -Hash of Message ID
  580. szBuffer[(sizeof(szBuffer)-1)/sizeof(CHAR)] = '\0';
  581. hr = m_pIMailMsgProperties->GetProperty(IMMPID_MP_RFC822_MSG_ID,
  582. sizeof(szBuffer), &cbProp,(BYTE *) szBuffer);
  583. //$$REVIEW - do we care about MsgID's bigger than 300 bytes
  584. if (SUCCEEDED(hr))
  585. fFoundMsgID = TRUE;
  586. //Don't pass on error
  587. hr = S_OK;
  588. //Get Hash of message ID.
  589. if (fFoundMsgID)
  590. m_dwMsgIdHash = dwQueueAdminHash(szBuffer);
  591. if (!m_pmgle)
  592. {
  593. //Although, we know that this function has not successfully
  594. //completed... it is possible that it failed, so we check
  595. //to make sure we don't leak a m_pmgle
  596. hr = m_pIMailMsgProperties->GetStringA(IMMPID_MP_MSG_GUID,
  597. sizeof(szBuffer), szBuffer);
  598. if (SUCCEEDED(hr))
  599. {
  600. //Try to parse GUID from the string
  601. if (fAQParseGuidString(szBuffer, sizeof(szBuffer), &guidID))
  602. {
  603. pguidID = &guidID;
  604. hr = m_pIMailMsgProperties->GetStringA(IMMPID_MP_SUPERSEDES_MSG_GUID,
  605. sizeof(szBuffer), szBuffer);
  606. if (SUCCEEDED(hr))
  607. {
  608. if (fAQParseGuidString(szBuffer, sizeof(szBuffer),
  609. &guidSupersedes))
  610. {
  611. pguidSupersedes = &guidSupersedes;
  612. }
  613. }
  614. }
  615. }
  616. //If this message has a GUID... add it to the superseded this
  617. if (pguidID)
  618. {
  619. _ASSERT(m_paqinst);
  620. m_pmgle = m_paqinst->pmglGetMsgGuidList()->pmgleAddMsgGuid(this,
  621. pguidID, pguidSupersedes);
  622. //We don't care about the return results... if the allocation
  623. //fails, then we just treat it as if it did not have an ID.
  624. }
  625. }
  626. //Get time message was queued
  627. hr = m_pIMailMsgProperties->GetProperty(IMMPID_MP_ARRIVAL_FILETIME,
  628. sizeof(FILETIME), &cbProp, (BYTE *) &m_ftQueueEntry);
  629. if (FAILED(hr))
  630. {
  631. //Message should not make it this far without being stamped with
  632. _ASSERT(MAILMSG_E_PROPNOTFOUND != hr);
  633. goto Exit;
  634. }
  635. //Get Various expire times
  636. hr = m_pIMailMsgProperties->GetProperty(IMMPID_MP_LOCAL_EXPIRE_DELAY,
  637. sizeof(FILETIME), &cbProp, (BYTE *) &m_ftLocalExpireDelay);
  638. if (hr == MAILMSG_E_PROPNOTFOUND)
  639. {
  640. // If this is not set, zero it so it will be calculated on demand
  641. memset(&m_ftLocalExpireDelay, 0, sizeof(FILETIME));
  642. }
  643. else if (FAILED(hr))
  644. {
  645. goto Exit;
  646. }
  647. hr = m_pIMailMsgProperties->GetProperty(IMMPID_MP_LOCAL_EXPIRE_NDR,
  648. sizeof(FILETIME), &cbProp, (BYTE *) &m_ftLocalExpireNDR);
  649. if (hr == MAILMSG_E_PROPNOTFOUND)
  650. {
  651. // If this is not set, zero it so it will be calculated on demand
  652. memset(&m_ftLocalExpireNDR, 0, sizeof(FILETIME));
  653. }
  654. else if (FAILED(hr))
  655. {
  656. goto Exit;
  657. }
  658. hr = m_pIMailMsgProperties->GetProperty(IMMPID_MP_EXPIRE_DELAY,
  659. sizeof(FILETIME), &cbProp, (BYTE *) &m_ftRemoteExpireDelay);
  660. if (hr == MAILMSG_E_PROPNOTFOUND)
  661. {
  662. // If this is not set, zero it so it will be calculated on demand
  663. memset(&m_ftRemoteExpireDelay, 0, sizeof(FILETIME));
  664. }
  665. else if (FAILED(hr))
  666. {
  667. goto Exit;
  668. }
  669. hr = m_pIMailMsgProperties->GetProperty(IMMPID_MP_EXPIRE_NDR,
  670. sizeof(FILETIME), &cbProp, (BYTE *) &m_ftRemoteExpireNDR);
  671. if (hr == MAILMSG_E_PROPNOTFOUND)
  672. {
  673. // If this is not set, zero it so it will be calculated on demand
  674. memset(&m_ftRemoteExpireNDR, 0, sizeof(FILETIME));
  675. }
  676. else if (FAILED(hr))
  677. {
  678. goto Exit;
  679. }
  680. //Get the size of the message
  681. hr = HrQADMGetMsgSize(m_pIMailMsgProperties, &m_cbMsgSize);
  682. if (FAILED(hr))
  683. {
  684. ErrorTrace((LPARAM) this, "Failed to Get Message Size 0x%08X", hr);
  685. hr = S_OK; //This really is not a fatal error
  686. }
  687. //$$TODO: Map to effective routing priority - just use Normal now
  688. m_dwDataFlags |= (eEffPriNormal & MSGREF_PRI_MASK);
  689. m_dwDataFlags |= MSGREF_MSG_INIT;
  690. Exit:
  691. TraceFunctLeave();
  692. return hr;
  693. }
  694. //---[ CMsgRef::HrPrepareDelivery ]--------------------------------------------
  695. //
  696. //
  697. // Description:
  698. // Prepares a msgreference for delivery on the given list of queues.
  699. //
  700. // Caller is NOT responsible for freeing prgdwRecips,
  701. // This should automatically be freed with the message context.
  702. //
  703. // If this function succeeds, then a usage count will have been added
  704. // to this mailmsg. The caller is responsible for calling AckMessage
  705. // (which releases the usage count) or calling ReleaseUsage explicitly.
  706. // Parameters:
  707. // IN BOOL fLocal Prepare delivery for all domains with NULL queues
  708. // IN BOOL fDelayDSN - Check/Set Delay bitmap (only send 1 Delay DSN).
  709. // IN CQuickList *pqlstQueues array of DMQ's
  710. // IN CDestMsgRetryQueue *pmdrq - Retry interface for this delivery attempt
  711. // Will be NULL for local delivery, becasue we will never requeue a local
  712. // message to its original DMQ.
  713. // IN OUT CDeliveryContext *pdcntxt context that must be returned on Ack
  714. // OUT DWORD *pcRecips # of recips to deliver for
  715. // OUT CMsgBitMap **prgdwRecips Array of recipient indexes
  716. // Returns:
  717. // S_OK on success
  718. // AQUEUE_E_MESSAGE_HANDLED if the message has already been handled
  719. // AQUEUE_E_MESSAGE_PENDING if all the the requested recipients are
  720. // either handled or currently pending delivery for another thread
  721. // E_FAIL if any of the M1 requirements are not met
  722. //-----------------------------------------------------------------------------
  723. HRESULT CMsgRef::HrPrepareDelivery(IN BOOL fLocal, IN BOOL fDelayDSN,
  724. IN CQuickList *pqlstQueues,
  725. IN CDestMsgRetryQueue* pdmrq,
  726. OUT CDeliveryContext *pdcntxt,
  727. OUT DWORD *pcRecips, OUT DWORD **prgdwRecips)
  728. {
  729. TraceFunctEnterEx((LPARAM) this, "CMsgRef::HrPrepareDelivery");
  730. //assert IN parameters
  731. _ASSERT(fLocal || pqlstQueues);
  732. //assert OUT parameters
  733. _ASSERT(pdcntxt);
  734. _ASSERT(prgdwRecips);
  735. _ASSERT(pcRecips);
  736. //assume average of 4 recips per domain for return list
  737. const DWORD RECIPIENT_PREALLOCATION_FACTOR = 4; //we may need to tweak this.
  738. HRESULT hr = S_OK;
  739. CMsgBitMap *pmbmapTmp = NULL;
  740. CMsgBitMap *pmbmapCurrent = NULL;
  741. DWORD i,j,k = 0; //loop variables
  742. DWORD *pdwRecipBuffer = NULL; //The buffer for recipient indexes
  743. DWORD iCurrentIndex = 0; //Current index into the buffer
  744. DWORD cdwRecipBuffer = 0; //current size of recipient buffer (in DWORDS)
  745. DWORD iRecipLow = 0; //Recipient index limits
  746. DWORD iRecipHigh = 0;
  747. CDestMsgQueue *pdmq = NULL;
  748. PVOID pvContext = NULL;
  749. DWORD cDest = pqlstQueues ? pqlstQueues->dwGetCount() : 0;
  750. DWORD dwStartDomain = 0;
  751. DWORD dwLastDomain = 0;
  752. BOOL fAddedUsage = FALSE;
  753. BOOL fRecipsPending = FALSE;
  754. _ASSERT((fLocal || cDest) && "We must deliver somewhere");
  755. _ASSERT(m_pIMailMsgRecipients);
  756. //check if msg has been superseded
  757. if (m_dwDataFlags & MSGREF_SUPERSEDED)
  758. {
  759. hr = AQUEUE_E_MESSAGE_HANDLED;
  760. goto Exit;
  761. }
  762. //Pre-allocate recipient buffer. It would be very interesting to determine
  763. //the best way to do this. It might involve tuning the default factor, or
  764. //using CPOOL to allocate the 90% case.
  765. cdwRecipBuffer = cDest * RECIPIENT_PREALLOCATION_FACTOR;
  766. pdwRecipBuffer = (DWORD *) pvMalloc(cdwRecipBuffer * sizeof(DWORD));
  767. if (NULL == pdwRecipBuffer)
  768. {
  769. hr = E_OUTOFMEMORY;
  770. goto Exit;
  771. }
  772. //HOW IT WORKS:
  773. // Here are the steps used to prepare a msgref for delivery
  774. // 1-Determine which domains it needs to be delivered for on this queue
  775. // 2-Filter out any domains that have already been handled
  776. // 3-Mark the domains as pending (and make sure we aren't delivering
  777. // to domains that are pending).
  778. // The above accomplished by using logical operations on bitmap objects
  779. pmbmapTmp = new(m_cDomains) CMsgBitMap(m_cDomains);
  780. if (NULL == pmbmapTmp)
  781. {
  782. hr = E_OUTOFMEMORY;
  783. goto Exit;
  784. }
  785. if (cDest == 0)
  786. {
  787. //make sure local makes it through the loop once
  788. _ASSERT(fLocal);
  789. cDest = 1;
  790. }
  791. //Use group operation to group together like domains
  792. //Both lists of domain mappings must be strictly sorted
  793. for (i = 0; i < m_cDomains; i++)
  794. {
  795. for (j = 0; j < cDest; j++)
  796. {
  797. //$$NOTE: Currently this nested loop is O(n^2)
  798. if (pqlstQueues)
  799. {
  800. pdmq = (CDestMsgQueue *) pqlstQueues->pvGetItem(j, &pvContext);
  801. if(pdmq)
  802. pdmq->AssertSignature();
  803. else
  804. _ASSERT(fLocal);
  805. }
  806. if ((m_rgpdmqDomains[i] == pdmq) ||
  807. (fLocal && !m_rgpdmqDomains[i]))
  808. {
  809. pmbmapCurrent = pmbmapGetDomainBitmap(i);
  810. //The order of the following 5 bitmap operations is important
  811. //to ensure that 2 threads do not get the same recipients
  812. //The ACK performs the following similar operations:
  813. // - Sets handled bits
  814. // - Unsets pending bits
  815. //Check against handled
  816. if (pmbmapGetHandled()->FTest(m_cDomains, pmbmapCurrent))
  817. continue;
  818. //Check against DSN
  819. if (fDelayDSN && pmbmapGetDSN()->FTest(m_cDomains,pmbmapCurrent))
  820. continue;
  821. //Check (and set) against pending
  822. if (!pmbmapGetPending()->FTestAndSet(m_cDomains, pmbmapCurrent))
  823. {
  824. fRecipsPending = TRUE;
  825. continue;
  826. }
  827. // Recheck against handled. If we remove this re-check, then
  828. // there is a possibility that a thread will come along
  829. // check handle, another thread will ack (setting handled
  830. // and unsetting pending), then the first thread comes
  831. // along and sets pending thereby defeating our locking
  832. // mechanism.
  833. if (pmbmapGetHandled()->FTest(m_cDomains, pmbmapCurrent))
  834. {
  835. //We need to unset the pending bit we just set
  836. hr = pmbmapCurrent->HrFilterUnset(m_cDomains, pmbmapGetPending());
  837. DebugTrace((LPARAM) this,
  838. "Backout pending bits in PrepareForDelivery - hr", hr);
  839. //This will succeed unless another thread has munged the
  840. //bitmap
  841. _ASSERT(SUCCEEDED(hr));
  842. hr = S_OK; //caller can do nothing about this
  843. continue;
  844. }
  845. //Check (and set) against DSN
  846. if (fDelayDSN)
  847. {
  848. //This should not be able to happen, since another thread
  849. //must have come along and sent pending to get this far
  850. _VERIFY(pmbmapGetDSN()->FTestAndSet(m_cDomains,pmbmapCurrent));
  851. }
  852. //Everything checks out... we can deliver to this domain
  853. hr = pmbmapTmp->HrGroupOr(m_cDomains, 1, &pmbmapCurrent);
  854. if (FAILED(hr))
  855. goto Exit;
  856. //Create list of recipients.
  857. GetRecipIndex(i, &iRecipLow, &iRecipHigh); //get the limits
  858. //make sure we have enough room to do this
  859. if ((iCurrentIndex + (iRecipHigh - iRecipLow) + 1) >= cdwRecipBuffer)
  860. {
  861. DWORD *pdwTemp = NULL; // Temp ptr for realloc
  862. //reallocate generously (enough for now + our estimate for future)
  863. cdwRecipBuffer += (iRecipHigh - iRecipLow) + 1
  864. + RECIPIENT_PREALLOCATION_FACTOR* (cDest-(j+1));
  865. pdwTemp = (DWORD *) pvRealloc((void *) pdwRecipBuffer,
  866. cdwRecipBuffer*sizeof(DWORD));
  867. if (!pdwTemp)
  868. {
  869. hr = E_OUTOFMEMORY;
  870. goto Exit;
  871. }
  872. // Allocation succeeded, set the ptr.
  873. pdwRecipBuffer = pdwTemp;
  874. }
  875. //Make sure we have the usage count to call SetNextDomain
  876. if (!fAddedUsage)
  877. {
  878. hr = InternalAddUsage();
  879. if (FAILED(hr))
  880. goto Exit;
  881. fAddedUsage = TRUE;
  882. }
  883. //Check to see if this message is marked for deletion
  884. if (fMailMsgMarkedForDeletion())
  885. {
  886. hr = AQUEUE_E_MESSAGE_HANDLED;
  887. goto Exit;
  888. }
  889. if (!iCurrentIndex) //first domain
  890. {
  891. dwStartDomain = i;
  892. }
  893. else
  894. {
  895. hr = m_pIMailMsgRecipients->SetNextDomain(dwLastDomain, i,
  896. FLAG_OVERWRITE_EXISTING_LINKS);
  897. if (FAILED(hr))
  898. {
  899. ErrorTrace((LPARAM) this, "ERROR: SetNextDomain Failed - hr 0x%08X", hr);
  900. goto Exit;
  901. }
  902. }
  903. //save last valid domain
  904. dwLastDomain = i;
  905. DebugTrace((LPARAM) this, "INFO: Sending recipients %d thru %d for domain %d:%d", iRecipLow, iRecipHigh, j, i);
  906. for (k = iRecipLow; k <= iRecipHigh; k++)
  907. {
  908. pdwRecipBuffer[iCurrentIndex] = k;
  909. iCurrentIndex++;
  910. }
  911. _ASSERT(iCurrentIndex <= cdwRecipBuffer);
  912. }
  913. }
  914. }
  915. if (pmbmapTmp->FAllClear(m_cDomains))
  916. {
  917. //there is nothing to do for this message
  918. if (fRecipsPending)
  919. hr = AQUEUE_E_MESSAGE_PENDING;
  920. else
  921. hr = AQUEUE_E_MESSAGE_HANDLED;
  922. goto Exit;
  923. }
  924. _ASSERT(fAddedUsage);
  925. if (dwStartDomain == dwLastDomain)
  926. {
  927. //There is only 1 domain... we never called SetNextDomain.. we need to
  928. //overwrite any previous domain link information
  929. hr = m_pIMailMsgRecipients->SetNextDomain(dwStartDomain, dwStartDomain,
  930. FLAG_SET_FIRST_DOMAIN);
  931. if (FAILED(hr))
  932. {
  933. ErrorTrace((LPARAM) this, "ERROR: SetNextDomain for first domain Failed - hr 0x%08X", hr);
  934. goto Exit;
  935. }
  936. }
  937. //Initialize delivery context
  938. pdcntxt->Init(this, pmbmapTmp, iCurrentIndex, pdwRecipBuffer, dwStartDomain, pdmrq);
  939. //Write other OUT parameters
  940. *pcRecips = iCurrentIndex;
  941. *prgdwRecips = pdwRecipBuffer;
  942. //set pointers to NULL so we don't delete them
  943. pdwRecipBuffer = NULL;
  944. pmbmapTmp = NULL;
  945. Exit:
  946. if (FAILED(hr) && pmbmapTmp && !pmbmapTmp->FAllClear(m_cDomains))
  947. {
  948. //We need to unset the DNS and pending bits we have set
  949. pmbmapTmp->HrFilterUnset(m_cDomains, pmbmapGetPending());
  950. if (fDelayDSN)
  951. pmbmapTmp->HrFilterUnset(m_cDomains, pmbmapGetDSN());
  952. }
  953. if (pmbmapTmp)
  954. delete pmbmapTmp;
  955. if (pdwRecipBuffer)
  956. FreePv(pdwRecipBuffer);
  957. //Make sure we don't leave an extra usage count on failure
  958. if (fAddedUsage && FAILED(hr))
  959. InternalReleaseUsage();
  960. TraceFunctLeave();
  961. return hr;
  962. }
  963. //---[ CMsgRef::HrAckMessage ]-------------------------------------------------
  964. //
  965. //
  966. // Description:
  967. // Acknowledges (un)delivery attempts for a message. Will look at the
  968. // IMsg and determine which queues the msgref needs to be requeued to.
  969. // Parameters:
  970. // IN pdcntxt Delivery context of message
  971. // IN pMsgAck Delivery status of message
  972. // Returns:
  973. // S_OK on success
  974. //
  975. //-----------------------------------------------------------------------------
  976. HRESULT CMsgRef::HrAckMessage(IN CDeliveryContext *pdcntxt,
  977. IN MessageAck *pMsgAck)
  978. {
  979. TraceFunctEnterEx((LPARAM) this, "CMsgRef::HrAckMessage");
  980. _ASSERT(pdcntxt);
  981. _ASSERT(pdcntxt->m_pmbmap);
  982. _ASSERT(pMsgAck);
  983. HRESULT hr = S_OK;
  984. CDSNParams dsnparams;
  985. dsnparams.dwStartDomain = pdcntxt->m_dwStartDomain;
  986. //Do we need to send DSN's?
  987. if (MESSAGE_STATUS_CHECK_RECIPS & pMsgAck->dwMsgStatus)
  988. {
  989. //recipients need to be handled by the DSN code
  990. dsnparams.dwDSNActions = DSN_ACTION_FAILURE;
  991. //we may need to send relay DSN's as well
  992. dsnparams.dwDSNActions |= DSN_ACTION_RELAYED;
  993. SET_DEBUG_DSN_CONTEXT(dsnparams, __LINE__);
  994. dsnparams.pIMailMsgProperties = m_pIMailMsgProperties;
  995. hr = m_paqinst->HrTriggerDSNGenerationEvent(&dsnparams, FALSE);
  996. if (FAILED(hr))
  997. goto Exit;
  998. }
  999. else if (((MESSAGE_STATUS_DROP_DIRECTORY |
  1000. MESSAGE_STATUS_LOCAL_DELIVERY)
  1001. & pMsgAck->dwMsgStatus) &&
  1002. !(MESSAGE_STATUS_RETRY & pMsgAck->dwMsgStatus))
  1003. {
  1004. //This was a successful local (or drop) delivery
  1005. // - NDR all undelivered
  1006. // - Generate any success DSNs if necessary
  1007. dsnparams.dwDSNActions = DSN_ACTION_FAILURE_ALL |
  1008. DSN_ACTION_DELIVERED |
  1009. DSN_ACTION_RELAYED;
  1010. SET_DEBUG_DSN_CONTEXT(dsnparams, __LINE__);
  1011. dsnparams.pIMailMsgProperties = m_pIMailMsgProperties;
  1012. hr = m_paqinst->HrTriggerDSNGenerationEvent(&dsnparams, FALSE);
  1013. if (FAILED(hr))
  1014. goto Exit;
  1015. }
  1016. else if (MESSAGE_STATUS_NDR_ALL & pMsgAck->dwMsgStatus)
  1017. {
  1018. //
  1019. // NDR all undelivered recipients
  1020. //
  1021. //
  1022. // Use specific extended status codes if there is no
  1023. // detailed per-recipient information
  1024. //
  1025. hr = HrPromoteMessageStatusToMailMsg(pdcntxt, pMsgAck);
  1026. if (FAILED(hr))
  1027. {
  1028. //
  1029. // This is a non-fatal error, we should still attempt
  1030. // DSN generation... otherwise the sender will get
  1031. // no indication of the failure.
  1032. //
  1033. ErrorTrace((LPARAM) this,
  1034. "HrPromoteMessageStatusToMailMsg failed with 0x%08X", hr);
  1035. hr = S_OK;
  1036. }
  1037. dsnparams.dwDSNActions = DSN_ACTION_FAILURE_ALL;
  1038. if (MESSAGE_STATUS_DSN_NOT_SUPPORTED & pMsgAck->dwMsgStatus)
  1039. dsnparams.dwDSNActions |= DSN_ACTION_RELAYED;
  1040. dsnparams.pIMailMsgProperties = m_pIMailMsgProperties;
  1041. SET_DEBUG_DSN_CONTEXT(dsnparams, __LINE__);
  1042. hr = m_paqinst->HrTriggerDSNGenerationEvent(&dsnparams, FALSE);
  1043. if (FAILED(hr))
  1044. goto Exit;
  1045. }
  1046. else if (((MESSAGE_STATUS_DROP_DIRECTORY |
  1047. MESSAGE_STATUS_LOCAL_DELIVERY)
  1048. & pMsgAck->dwMsgStatus) &&
  1049. (MESSAGE_STATUS_RETRY & pMsgAck->dwMsgStatus))
  1050. {
  1051. //This was a retryable local (or drop) delivery failure
  1052. // - NDR all hard failures
  1053. // - Generate any success DSNs if necessary
  1054. dsnparams.dwDSNActions = DSN_ACTION_FAILURE |
  1055. DSN_ACTION_DELIVERED |
  1056. DSN_ACTION_RELAYED;
  1057. SET_DEBUG_DSN_CONTEXT(dsnparams, __LINE__);
  1058. dsnparams.pIMailMsgProperties = m_pIMailMsgProperties;
  1059. hr = m_paqinst->HrTriggerDSNGenerationEvent(&dsnparams, FALSE);
  1060. if (FAILED(hr))
  1061. goto Exit;
  1062. }
  1063. //See if we also need to retry this message
  1064. if (MESSAGE_STATUS_RETRY & pMsgAck->dwMsgStatus)
  1065. {
  1066. _ASSERT(!((MESSAGE_STATUS_ALL_DELIVERED | MESSAGE_STATUS_NDR_ALL) & pMsgAck->dwMsgStatus));
  1067. hr = HrPrvRetryMessage(pdcntxt, pMsgAck->dwMsgStatus);
  1068. if (FAILED(hr))
  1069. goto Exit;
  1070. }
  1071. else
  1072. {
  1073. //If retry is not specifically requested, we set the handled bits
  1074. hr = pdcntxt->m_pmbmap->HrFilterSet(m_cDomains, pmbmapGetHandled());
  1075. if (FAILED(hr))
  1076. goto Exit;
  1077. //If this message has a supersedable ID... remove it from list if
  1078. //we are done
  1079. if (m_pmgle && pmbmapGetHandled()->FAllSet(m_cDomains))
  1080. {
  1081. m_pmgle->RemoveFromList();
  1082. }
  1083. //There should be something that was not set in the handled bitmap
  1084. _ASSERT(! pdcntxt->m_pmbmap->FAllClear(m_cDomains));
  1085. }
  1086. //Unset Pending bitmap
  1087. hr = pdcntxt->m_pmbmap->HrFilterUnset(m_cDomains, pmbmapGetPending());
  1088. if (FAILED(hr))
  1089. goto Exit;
  1090. Exit:
  1091. ReleaseAndBounceUsageOnMsgAck(pMsgAck->dwMsgStatus);
  1092. TraceFunctLeave();
  1093. return hr;
  1094. }
  1095. //---[ CMsgRef::HrSendDelayOrNDR ]---------------------------------------------
  1096. //
  1097. //
  1098. // Description:
  1099. // Determines if a message has expired (for Delay or NDR) and generates
  1100. // a DSN if neccessary.
  1101. // Parameters:
  1102. // IN dwDSNOptions Flags describing DSN generation
  1103. // MSGREF_DSN_LOCAL_QUEUE This is for a local queue
  1104. // MSGREF_DSN_SEND_DELAY Allow Delay DSNs
  1105. // MSGREF_DSN_HAS_ROUTING_LOCK This thread holds the routing lock
  1106. // IN pqlstQueues List of DestMsgQueues to DSN
  1107. // IN hrStatus Status to Pass to DSN generation
  1108. // (if Status is AQUEUE_E_NDR_ALL... then message will
  1109. // be NDR'd regardless of timeout).
  1110. // OUT pdwDSNFlags Reports disposition of message
  1111. // MSGREF_DSN_SENT_NDR Message NDR-expired and NDR was sent
  1112. // MSGREF_DSN_SENT_DELAY Message Delay-expired and Delay DSN was sent
  1113. // MSGREF_HANDLED Message has been completely handled
  1114. // MSGREF_HAS_NOT_EXPIRED Message younger than it's exipiration dates
  1115. // Returns:
  1116. // S_OK on success
  1117. // AQUEUE_E_SHUTDOWN if shutdown is in progress
  1118. // History:
  1119. // 7/13/98 - MikeSwa Created
  1120. // 8/14/98 - MikeSwa Modified to add support for local expire
  1121. //
  1122. //-----------------------------------------------------------------------------
  1123. HRESULT CMsgRef::HrSendDelayOrNDR(
  1124. IN DWORD dwDSNOptions,
  1125. IN CQuickList *pqlstQueues,
  1126. IN HRESULT hrStatus,
  1127. OUT DWORD *pdwDSNFlags)
  1128. {
  1129. TraceFunctEnterEx((LPARAM) this, "CMsgRef::HrSendDelayOrNDR");
  1130. HRESULT hr = S_OK;
  1131. BOOL fSendDSN = FALSE;
  1132. BOOL fSendNDR = FALSE;
  1133. DWORD dwTimeContext = 0;
  1134. DWORD dwFlags = 0;
  1135. FILETIME ftExpireTime;
  1136. CDSNParams dsnparams;
  1137. CDeliveryContext dcntxt;
  1138. BOOL fPendingSet = FALSE; //pending bitmap has been set
  1139. BOOL fReleaseUsageNeeded = FALSE; //has an extra usage count been added by Prepare Delivery?
  1140. BOOL fShouldRetryMsg = TRUE;
  1141. //any allocations done in PrepareDelivery will be freed by ~CDeliveryContext
  1142. DWORD cRecips = 0;
  1143. DWORD *rgdwRecips = NULL;
  1144. _ASSERT(pdwDSNFlags);
  1145. _ASSERT(MSGREF_SIG == m_dwSignature);
  1146. *pdwDSNFlags = 0;
  1147. //Shutdown lock should already be aquired... but lets make sure that the
  1148. //service shutdown thread is not waiting for it.
  1149. if (!m_paqinst->fTryShutdownLock())
  1150. {
  1151. hr = AQUEUE_E_SHUTDOWN;
  1152. goto Exit;
  1153. }
  1154. m_paqinst->ShutdownUnlock();
  1155. //
  1156. // We need to open up the file and see what is going on. This will
  1157. // force a RFC822 rendering of the message content and open
  1158. // the content file.
  1159. //
  1160. if (dwDSNOptions & MSGREF_DSN_CHECK_IF_STALE)
  1161. {
  1162. //
  1163. // If we cannot retry the message, it is stale and we shold
  1164. // drop it. This will open the handles for a message, but
  1165. // we will always call BounceUsageCount at the end of this call
  1166. //
  1167. InternalAddUsage();
  1168. fShouldRetryMsg = fShouldRetry();
  1169. InternalReleaseUsage();
  1170. if (!fShouldRetryMsg)
  1171. {
  1172. *pdwDSNFlags = MSGREF_HANDLED;
  1173. goto Exit;
  1174. }
  1175. }
  1176. //if this message has been superseded... remove it from the running
  1177. //Also if the message has already been marked for deletion
  1178. if ((m_dwDataFlags & MSGREF_SUPERSEDED) || fMailMsgMarkedForDeletion())
  1179. {
  1180. *pdwDSNFlags = MSGREF_HANDLED;
  1181. goto Exit;
  1182. }
  1183. //Make sure we check the correct local/remote expire time
  1184. if (MSGREF_DSN_LOCAL_QUEUE & dwDSNOptions)
  1185. memcpy(&ftExpireTime, &m_ftLocalExpireNDR, sizeof(FILETIME));
  1186. else
  1187. memcpy(&ftExpireTime, &m_ftRemoteExpireNDR, sizeof(FILETIME));
  1188. // If this wasn't set we need to calculate it
  1189. if (!((ULARGE_INTEGER*)&ftExpireTime)->QuadPart)
  1190. {
  1191. m_paqinst->CalcExpireTimeNDR(m_ftQueueEntry,
  1192. MSGREF_DSN_LOCAL_QUEUE & dwDSNOptions,
  1193. &ftExpireTime);
  1194. }
  1195. //
  1196. // Default to status passed in
  1197. //
  1198. dsnparams.hrStatus = hrStatus;
  1199. if (fIsFatalError(hrStatus))
  1200. {
  1201. SET_DEBUG_DSN_CONTEXT(dsnparams, __LINE__);
  1202. *pdwDSNFlags |= MSGREF_DSN_SENT_NDR;
  1203. fSendDSN = TRUE;
  1204. fSendNDR = TRUE;
  1205. dsnparams.dwDSNActions |= DSN_ACTION_FAILURE_ALL;
  1206. }
  1207. else if (m_paqinst->fInPast(&ftExpireTime, &dwTimeContext))
  1208. {
  1209. SET_DEBUG_DSN_CONTEXT(dsnparams, __LINE__);
  1210. *pdwDSNFlags |= MSGREF_DSN_SENT_NDR;
  1211. fSendDSN = TRUE;
  1212. fSendNDR = TRUE;
  1213. dsnparams.dwDSNActions |= DSN_ACTION_FAILURE_ALL;
  1214. dsnparams.hrStatus = AQUEUE_E_MSG_EXPIRED;
  1215. }
  1216. else if (MSGREF_DSN_SEND_DELAY & dwDSNOptions)
  1217. {
  1218. //NDR is not needed, but perhaps delay is
  1219. //Make sure we check the correct local/remote expire time
  1220. if (MSGREF_DSN_LOCAL_QUEUE & dwDSNOptions)
  1221. memcpy(&ftExpireTime, &m_ftLocalExpireDelay, sizeof(FILETIME));
  1222. else
  1223. memcpy(&ftExpireTime, &m_ftRemoteExpireDelay, sizeof(FILETIME));
  1224. // If this wasn't set we need to calculate it
  1225. if (!((ULARGE_INTEGER*)&ftExpireTime)->QuadPart)
  1226. {
  1227. m_paqinst->CalcExpireTimeDelay(m_ftQueueEntry,
  1228. MSGREF_DSN_LOCAL_QUEUE & dwDSNOptions,
  1229. &ftExpireTime);
  1230. }
  1231. if (m_paqinst->fInPast(&ftExpireTime, &dwTimeContext))
  1232. {
  1233. SET_DEBUG_DSN_CONTEXT(dsnparams, __LINE__);
  1234. *pdwDSNFlags |= MSGREF_DSN_SENT_DELAY;
  1235. fSendDSN = TRUE;
  1236. dsnparams.dwDSNActions |= DSN_ACTION_DELAYED;
  1237. dsnparams.hrStatus = AQUEUE_E_MSG_EXPIRED;
  1238. }
  1239. }
  1240. if (!fSendDSN)
  1241. {
  1242. //message has not expired for Delay or NDR
  1243. *pdwDSNFlags |= MSGREF_HAS_NOT_EXPIRED;
  1244. goto Exit;
  1245. }
  1246. //Prepare delivery for NDR/DSN's (setting DSN bitmap for delays)
  1247. hr = HrPrepareDelivery(MSGREF_DSN_LOCAL_QUEUE & dwDSNOptions,
  1248. ((*pdwDSNFlags) & MSGREF_DSN_SENT_DELAY) ? TRUE : FALSE,
  1249. pqlstQueues, NULL, &dcntxt,
  1250. &cRecips, &rgdwRecips);
  1251. //make sure we don't delete ourselves when stack context goes away
  1252. dcntxt.m_pmsgref = NULL;
  1253. if (AQUEUE_E_MESSAGE_HANDLED == hr)
  1254. {
  1255. //nothing to do for the message
  1256. //if *not* filtering against Delay...
  1257. //then there are no undelivered recips. Return this info
  1258. if (!((*pdwDSNFlags) & MSGREF_DSN_SENT_DELAY))
  1259. *pdwDSNFlags = MSGREF_HANDLED;
  1260. else
  1261. *pdwDSNFlags = MSGREF_HAS_NOT_EXPIRED; //delay wasn't actually sent
  1262. hr = S_OK;
  1263. goto Exit;
  1264. }
  1265. else if (AQUEUE_E_MESSAGE_PENDING == hr)
  1266. {
  1267. //This message is currently being processed by other threads
  1268. //It has not been handled (so it should not be removed from the queue),
  1269. //but it is not an error condition
  1270. *pdwDSNFlags = 0;
  1271. hr = S_OK;
  1272. goto Exit;
  1273. }
  1274. else if (FAILED(hr))
  1275. {
  1276. *pdwDSNFlags = 0;
  1277. goto Exit;
  1278. }
  1279. fPendingSet = TRUE; //we will need to unset the pending bitmap
  1280. fReleaseUsageNeeded = TRUE;
  1281. dsnparams.dwStartDomain = dcntxt.m_dwStartDomain;
  1282. dsnparams.pIMailMsgProperties = m_pIMailMsgProperties;
  1283. hr = m_paqinst->HrTriggerDSNGenerationEvent(&dsnparams,
  1284. MSGREF_DSN_HAS_ROUTING_LOCK & dwDSNOptions);
  1285. if (FAILED(hr))
  1286. {
  1287. *pdwDSNFlags = 0;
  1288. goto Exit;
  1289. }
  1290. if (S_FALSE == hr) //no DSN generated
  1291. {
  1292. if (fSendNDR) //Expire has passed...remove message from queue
  1293. *pdwDSNFlags = MSGREF_HANDLED;
  1294. else
  1295. *pdwDSNFlags = 0;
  1296. hr = S_OK;
  1297. //continue through function... still remove from queue even if NOTIFY=NEVER
  1298. }
  1299. //if NDR was generated, set handled bits
  1300. if (fSendNDR)
  1301. {
  1302. //Set Handled bitmap
  1303. hr = dcntxt.m_pmbmap->HrFilterSet(m_cDomains, pmbmapGetHandled());
  1304. if (FAILED(hr))
  1305. goto Exit;
  1306. //If this message has a supersedable ID... remove it from list if
  1307. //we are done
  1308. if (m_pmgle && pmbmapGetHandled()->FAllSet(m_cDomains))
  1309. {
  1310. m_pmgle->RemoveFromList();
  1311. }
  1312. //There should be something that was not set in the handled bitmap
  1313. _ASSERT(! dcntxt.m_pmbmap->FAllClear(m_cDomains));
  1314. }
  1315. Exit:
  1316. //Unset Pending bitmap
  1317. if (fPendingSet)
  1318. {
  1319. dcntxt.m_pmbmap->HrFilterUnset(m_cDomains, pmbmapGetPending());
  1320. }
  1321. _ASSERT(m_pIMailMsgQM);
  1322. if (fReleaseUsageNeeded)
  1323. {
  1324. //we called HrPrepareDelivery which added an extra usage count
  1325. InternalReleaseUsage();
  1326. }
  1327. //While we are walking queues... bounce usage count - this will conserve handles
  1328. BounceUsageCount();
  1329. TraceFunctLeave();
  1330. return hr;
  1331. }
  1332. //---[ HrPrvRetryMessage ]-----------------------------------------------------
  1333. //
  1334. //
  1335. // Description:
  1336. // Queues Message for retry
  1337. // Parameters:
  1338. // pdcntxt Delivery context set by HrPrepareDelivery
  1339. // dwMsgStatus Status flags passed back with Ack
  1340. // Returns:
  1341. // S_OK on success
  1342. // History:
  1343. // 7/13/98 - MikeSwa Created (separated from HrAckMessage())
  1344. // 5/25/99 - MikeSwa Modified - Now we requeue only to DMQ that message
  1345. // was originally dequeued from. This is for X5:105384, so
  1346. // that we do not double-count messages that have been
  1347. // ack'd retry
  1348. //
  1349. //-----------------------------------------------------------------------------
  1350. HRESULT CMsgRef::HrPrvRetryMessage(CDeliveryContext *pdcntxt, DWORD dwMsgStatus)
  1351. {
  1352. TraceFunctEnterEx((LPARAM) this, "CMsgRef::HrPrvRetryMessage");
  1353. _ASSERT(pdcntxt);
  1354. HRESULT hr = S_OK;
  1355. DWORD i = 0;
  1356. DWORD j = 0;
  1357. BOOL fLocked = FALSE;
  1358. BOOL fLocal = (MESSAGE_STATUS_LOCAL_DELIVERY & dwMsgStatus) ? TRUE : FALSE;
  1359. DWORD dwNewFlags = fLocal ? MSGREF_MSG_LOCAL_RETRY : MSGREF_MSG_REMOTE_RETRY;
  1360. CDestMsgRetryQueue *pdmrq = NULL;
  1361. //make sure retry flags are set for this message
  1362. DWORD dwOldFlags = dwInterlockedSetBits(&m_dwDataFlags, dwNewFlags);
  1363. DebugTrace((LPARAM) this, "INFO: Message queued for retry");
  1364. InterlockedIncrement((PLONG) &m_cTimesRetried);
  1365. //update global counters if first retry attempt
  1366. if (!(dwNewFlags & dwOldFlags)) //this is the first time for this type of retry
  1367. m_paqinst->IncRetryCount(fLocal);
  1368. if (fLocal)
  1369. goto Exit;
  1370. //
  1371. // Check and see if we should even bother requeuing the message.
  1372. //
  1373. if (!fShouldRetry())
  1374. {
  1375. DebugTrace((LPARAM) this, "Message is no longer valid... dropping");
  1376. //
  1377. // Since we have found a stale message on this queue, we should
  1378. // tell it to scan on its next DSN generation pass
  1379. //
  1380. pdmrq = pdcntxt->pdmrqGetDMRQ();
  1381. pdmrq->CheckForStaleMsgsNextDSNGenerationPass();
  1382. goto Exit;
  1383. }
  1384. //Requeue Message for remote delivery
  1385. if (m_paqinst->fTryShutdownLock())
  1386. {
  1387. //Don't retry messages if shutdown is happening
  1388. fLocked = TRUE;
  1389. pdmrq = pdcntxt->pdmrqGetDMRQ();
  1390. _ASSERT(pdmrq && "Delivery Context not initialized correctly");
  1391. if (pdmrq)
  1392. {
  1393. hr = pdmrq->HrRetryMsg(this);
  1394. if (FAILED(hr))
  1395. goto Exit;
  1396. }
  1397. }
  1398. else
  1399. {
  1400. hr = S_OK; //shutdown is happening, not a real failure case
  1401. }
  1402. Exit:
  1403. if (fLocked)
  1404. m_paqinst->ShutdownUnlock();
  1405. TraceFunctLeave();
  1406. return hr;
  1407. }
  1408. //---[ CMsgRef::pmbmapGetDomainBitmap]-----------------------------------------
  1409. //
  1410. //
  1411. // Description:
  1412. // Protected method to get the Domain Bitmap for a given domain
  1413. // Parameters:
  1414. // iDomain index to the domain wanted
  1415. // Returns:
  1416. // ptr to the domain bitmap for the given domain
  1417. //
  1418. //-----------------------------------------------------------------------------
  1419. CMsgBitMap *CMsgRef::pmbmapGetDomainBitmap(DWORD iDomain)
  1420. {
  1421. TraceFunctEnterEx((LPARAM) this, "CMsgRef::pmbmapGetDomainBitmap");
  1422. CMsgBitMap *pmbmap = NULL;
  1423. _ASSERT(iDomain < m_cDomains);
  1424. //recall structure of memory, responsibility maps start 3 after end of
  1425. //domain mappings
  1426. pmbmap = (CMsgBitMap *) (m_rgpdmqDomains + m_cDomains);
  1427. pmbmap = (CMsgBitMap *) ((DWORD_PTR) pmbmap + (3+iDomain)*CMsgBitMap::size(m_cDomains));
  1428. TraceFunctLeave();
  1429. return pmbmap;
  1430. }
  1431. //---[ CMsgRef::pmbmapGetHandled]------------------------------------------------------------
  1432. //
  1433. //
  1434. // Description:
  1435. // Protected method to get the Handled Bitmap
  1436. // Parameters:
  1437. // -
  1438. // Returns:
  1439. // ptr to the handled bitmap
  1440. //
  1441. //-----------------------------------------------------------------------------
  1442. CMsgBitMap *CMsgRef::pmbmapGetHandled()
  1443. {
  1444. TraceFunctEnterEx((LPARAM) this, "CMsgRef::pmbmapGetHandled");
  1445. CMsgBitMap *pmbmap = NULL;
  1446. //recall structure of memory, responsibility maps start after end of
  1447. //domain mappings
  1448. pmbmap = (CMsgBitMap *) (m_rgpdmqDomains + m_cDomains);
  1449. TraceFunctLeave();
  1450. return pmbmap;
  1451. }
  1452. //---[ CMsgRef::pmbmapGetPending]------------------------------------------------------------
  1453. //
  1454. //
  1455. // Description:
  1456. // Protected method to get the pending Bitmap
  1457. // Parameters:
  1458. // -
  1459. // Returns:
  1460. // ptr to the pending bitmap
  1461. //
  1462. //-----------------------------------------------------------------------------
  1463. CMsgBitMap *CMsgRef::pmbmapGetPending()
  1464. {
  1465. TraceFunctEnterEx((LPARAM) this, "CMsgRef::pmbmapGetPending");
  1466. CMsgBitMap *pmbmap = NULL;
  1467. //recall structure of memory, responsibility maps start 1 after end of
  1468. //domain mappings
  1469. pmbmap = (CMsgBitMap *) (m_rgpdmqDomains + m_cDomains);
  1470. pmbmap = (CMsgBitMap *) ((DWORD_PTR) pmbmap + CMsgBitMap::size(m_cDomains));
  1471. TraceFunctLeave();
  1472. return pmbmap;
  1473. }
  1474. //---[ CMsgRef::pmbmapGetDSN]------------------------------------------------------------
  1475. //
  1476. //
  1477. // Description:
  1478. // Protected method to get the Handled Bitmap
  1479. // Parameters:
  1480. // -
  1481. // Returns:
  1482. // ptr to the DSN bitmap
  1483. //
  1484. //-----------------------------------------------------------------------------
  1485. CMsgBitMap *CMsgRef::pmbmapGetDSN()
  1486. {
  1487. TraceFunctEnterEx((LPARAM) this, "CMsgRef::pmbmapGetDSN");
  1488. CMsgBitMap *pmbmap = NULL;
  1489. //recall structure of memory, responsibility maps start 3 after end of
  1490. //domain mappings
  1491. pmbmap = (CMsgBitMap *) (m_rgpdmqDomains + m_cDomains);
  1492. pmbmap = (CMsgBitMap *) ((DWORD_PTR) pmbmap + 2*CMsgBitMap::size(m_cDomains));
  1493. TraceFunctLeave();
  1494. return pmbmap;
  1495. }
  1496. //---[ CMsgRef::pdwGetRecipIndexStart ]----------------------------------------
  1497. //
  1498. //
  1499. // Description:
  1500. // Returns the start index of the Array of recipient indexes
  1501. // Parameters:
  1502. // -
  1503. // Returns:
  1504. // See above
  1505. //
  1506. //-----------------------------------------------------------------------------
  1507. DWORD *CMsgRef::pdwGetRecipIndexStart()
  1508. {
  1509. DWORD_PTR dwTmp = 0;
  1510. _ASSERT(m_cDomains);
  1511. //Array of Recipient Indexes start after last CMsgBitmap
  1512. dwTmp = (DWORD_PTR) pmbmapGetDomainBitmap(m_cDomains-1); //will assert if arg larger
  1513. dwTmp += CMsgBitMap::size(m_cDomains);
  1514. _ASSERT(((DWORD_PTR) this + size(m_cDomains)) > dwTmp);
  1515. _ASSERT(dwTmp);
  1516. return (DWORD *) dwTmp;
  1517. }
  1518. //---[ CMsgRef::SetRecipIndex ]-----------------------------------------------
  1519. //
  1520. //
  1521. // Description:
  1522. // Set the starting and stoping recipient index for a given domain. Each
  1523. // Domain in the message has an inclusive range of recipients associated
  1524. // with it. When the message is prepared for delivery, these ranges are
  1525. // expanded into a list of recipient indexes.
  1526. // Parameters:
  1527. // IN iDomain Index of Domain to set range for
  1528. // IN iLowRecip Index of lower recipient
  1529. // IN iHighRecip Index of higher recipient
  1530. // Returns:
  1531. // -
  1532. //
  1533. //-----------------------------------------------------------------------------
  1534. void CMsgRef::SetRecipIndex(DWORD iDomain, DWORD iLowRecip, DWORD iHighRecip)
  1535. {
  1536. _ASSERT(iDomain < m_cDomains);
  1537. _ASSERT(iLowRecip <= iHighRecip);
  1538. DWORD *rgdwRecipIndex = pdwGetRecipIndexStart();
  1539. rgdwRecipIndex[2*iDomain] = iLowRecip;
  1540. rgdwRecipIndex[2*iDomain+1] = iHighRecip;
  1541. }
  1542. //---[ CMsgRef::GetRecipIndex ]------------------------------------------------
  1543. //
  1544. //
  1545. // Description:
  1546. // Get the recipient index for a given Domain index. Used when generating
  1547. // a list of recipient indexes to delivery for.
  1548. // Parameters:
  1549. // IN iDomain Index of Domain to get range for
  1550. // OUT piLowRecip Returned lower index of recipient range
  1551. // OUT piHighRecip Returned higher index of recipient range
  1552. // Returns:
  1553. // -
  1554. //
  1555. //-----------------------------------------------------------------------------
  1556. void CMsgRef::GetRecipIndex(DWORD iDomain, DWORD *piLowRecip, DWORD *piHighRecip)
  1557. {
  1558. _ASSERT(iDomain < m_cDomains);
  1559. _ASSERT(piLowRecip && piHighRecip);
  1560. DWORD *rgdwRecipIndex = pdwGetRecipIndexStart();
  1561. *piLowRecip = rgdwRecipIndex[2*iDomain];
  1562. *piHighRecip = rgdwRecipIndex[2*iDomain+1];
  1563. _ASSERT(*piLowRecip <= *piHighRecip);
  1564. }
  1565. //---[ CMsgRef::SupersedeMsg ]-------------------------------------------------
  1566. //
  1567. //
  1568. // Description:
  1569. // Used to flag this message as superseded by a newer msg
  1570. // Parameters:
  1571. // -
  1572. // Returns:
  1573. // -
  1574. // History:
  1575. // 10/11/98 - MikeSwa Created
  1576. //
  1577. //-----------------------------------------------------------------------------
  1578. void CMsgRef::SupersedeMsg()
  1579. {
  1580. dwInterlockedSetBits(&m_dwDataFlags, MSGREF_SUPERSEDED);
  1581. }
  1582. //---[ CMsgRef::fMatchesQueueAdminFilter ]--------------------------------------
  1583. //
  1584. //
  1585. // Description:
  1586. // Checks a message against a queue admin message filter to see if it
  1587. // is a match
  1588. // Parameters:
  1589. // IN paqmf Message Filter to check against
  1590. // Returns:
  1591. // TRUE if it matches
  1592. // FALSE if it does not
  1593. // History:
  1594. // 12/7/98 - MikeSwa Created
  1595. // 2/17/99 - MikeSwa updated with better recipient checking
  1596. //
  1597. //-----------------------------------------------------------------------------
  1598. BOOL CMsgRef::fMatchesQueueAdminFilter(CAQAdminMessageFilter *paqmf)
  1599. {
  1600. TraceFunctEnterEx((LPARAM) this, "CMsgRef::fMatchesQueueAdminFilter");
  1601. _ASSERT(paqmf);
  1602. HRESULT hr = S_OK;
  1603. BOOL fMatch = TRUE;
  1604. BOOL fUsageAdded = FALSE;
  1605. DWORD dwFilterFlags = paqmf->dwGetMsgFilterFlags();
  1606. LPSTR szMsgId = NULL;
  1607. //Check to see if this message is marked for deletion
  1608. if (fMailMsgMarkedForDeletion())
  1609. {
  1610. fMatch = FALSE;
  1611. goto Exit;
  1612. }
  1613. if (!dwFilterFlags)
  1614. {
  1615. fMatch = FALSE;
  1616. goto Exit;
  1617. }
  1618. if (AQ_MSG_FILTER_ALL & dwFilterFlags)
  1619. {
  1620. fMatch = TRUE;
  1621. goto Exit;
  1622. }
  1623. //Check MsgRef props first
  1624. if (AQ_MSG_FILTER_LARGER_THAN & dwFilterFlags)
  1625. {
  1626. fMatch = paqmf->fMatchesSize(m_cbMsgSize);
  1627. if (!fMatch)
  1628. goto Exit;
  1629. }
  1630. if (AQ_MSG_FILTER_OLDER_THAN & dwFilterFlags)
  1631. {
  1632. fMatch = paqmf->fMatchesTime(&m_ftQueueEntry);
  1633. if (!fMatch)
  1634. goto Exit;
  1635. }
  1636. if (AQ_MSG_FILTER_FROZEN & dwFilterFlags)
  1637. {
  1638. fMatch = fIsMsgFrozen();
  1639. if (AQ_MSG_FILTER_INVERTSENSE & dwFilterFlags)
  1640. fMatch = !fMatch;
  1641. if (!fMatch)
  1642. goto Exit;
  1643. }
  1644. if (AQ_MSG_FILTER_FAILED & dwFilterFlags)
  1645. {
  1646. if (m_cTimesRetried)
  1647. fMatch = TRUE;
  1648. else
  1649. fMatch = FALSE;
  1650. if (AQ_MSG_FILTER_INVERTSENSE & dwFilterFlags)
  1651. fMatch = !fMatch;
  1652. if (!fMatch)
  1653. goto Exit;
  1654. }
  1655. //If we haven't failed by this point, we may need to AddUsage and read
  1656. //props from the mailmsg. Double-check to make sure that we need to
  1657. //add usage.
  1658. if (!((AQ_MSG_FILTER_MESSAGEID | AQ_MSG_FILTER_SENDER | AQ_MSG_FILTER_RECIPIENT) &
  1659. dwFilterFlags))
  1660. goto Exit; //don't have to check props
  1661. //If we are only interested in MSGID... check hash first
  1662. if (AQ_MSG_FILTER_MESSAGEID & dwFilterFlags)
  1663. {
  1664. //If we are interested in ID & hash doesn't match then leave
  1665. if (!paqmf->fMatchesIdHash(m_dwMsgIdHash))
  1666. {
  1667. fMatch = FALSE;
  1668. goto Exit;
  1669. }
  1670. }
  1671. hr = InternalAddUsage();
  1672. if (FAILED(hr))
  1673. goto Exit;
  1674. fUsageAdded = TRUE;
  1675. //Check to see if this message is marked for deletion
  1676. if (fMailMsgMarkedForDeletion())
  1677. {
  1678. fMatch = FALSE;
  1679. goto Exit;
  1680. }
  1681. if (AQ_MSG_FILTER_MESSAGEID & dwFilterFlags)
  1682. {
  1683. hr = HrQueueAdminGetStringProp(m_pIMailMsgProperties, IMMPID_MP_RFC822_MSG_ID,
  1684. &szMsgId);
  1685. if (FAILED(hr))
  1686. szMsgId = NULL;
  1687. fMatch = paqmf->fMatchesId(szMsgId);
  1688. if (!fMatch)
  1689. {
  1690. DEBUG_DO_IT(g_cDbgMsgIdHashFailures++);
  1691. goto Exit;
  1692. }
  1693. }
  1694. if (AQ_MSG_FILTER_SENDER & dwFilterFlags)
  1695. {
  1696. fMatch = paqmf->fMatchesMailMsgSender(m_pIMailMsgProperties);
  1697. if (!fMatch)
  1698. goto Exit;
  1699. }
  1700. if (AQ_MSG_FILTER_RECIPIENT & dwFilterFlags)
  1701. {
  1702. fMatch = paqmf->fMatchesMailMsgRecipient(m_pIMailMsgProperties);
  1703. if (!fMatch)
  1704. goto Exit;
  1705. }
  1706. Exit:
  1707. if (szMsgId)
  1708. QueueAdminFree(szMsgId);
  1709. if (fUsageAdded)
  1710. {
  1711. InternalReleaseUsage();
  1712. BounceUsageCount();
  1713. }
  1714. TraceFunctLeave();
  1715. return fMatch;
  1716. }
  1717. //---[ CMsgRef::HrGetQueueAdminMsgInfo ]----------------------------------------
  1718. //
  1719. //
  1720. // Description:
  1721. // Fills out a queue admin MESSAGE_INFO structure. All allocations are
  1722. // done with pvQueueAdminAlloc to be freed by the RPC code
  1723. // Parameters:
  1724. // IN OUT pMsgInfo MESSAGE_INFO struct to dump data to
  1725. // IN pIQueueAdminAction - Action interface that is requesting info
  1726. // used to determine if local or remote info should be
  1727. // reported.
  1728. // Returns:
  1729. // S_OK on success
  1730. // AQUEUE_E_MESSAGE_HANDLED if the underlying message has been deleted
  1731. // E_OUTOFMEMORY if an allocation failu
  1732. // History:
  1733. // 12/7/98 - MikeSwa Created
  1734. //
  1735. //-----------------------------------------------------------------------------
  1736. HRESULT CMsgRef::HrGetQueueAdminMsgInfo(MESSAGE_INFO *pMsgInfo,
  1737. IQueueAdminAction *pIQueueAdminAction)
  1738. {
  1739. HRESULT hr = S_OK;
  1740. BOOL fUsageAdded = FALSE;
  1741. FILETIME ftSubmitted; //Origination time property buffer
  1742. FILETIME ftExpire; // Expiry time property buffer
  1743. DWORD cbProp = 0;
  1744. if (fNormalPri(m_dwDataFlags & MSGREF_PRI_MASK))
  1745. pMsgInfo->fMsgFlags = MP_NORMAL;
  1746. else if (fHighPri(m_dwDataFlags & MSGREF_PRI_MASK))
  1747. pMsgInfo->fMsgFlags = MP_HIGH;
  1748. else
  1749. pMsgInfo->fMsgFlags = MP_LOW;
  1750. if (MSGREF_MSG_FROZEN & m_dwDataFlags)
  1751. pMsgInfo->fMsgFlags |= MP_MSG_FROZEN;
  1752. //Report the number of failures
  1753. pMsgInfo->cFailures = m_cTimesRetried;
  1754. if (pMsgInfo->cFailures)
  1755. pMsgInfo->fMsgFlags |= MP_MSG_RETRY;
  1756. hr = InternalAddUsage();
  1757. if (FAILED(hr))
  1758. goto Exit;
  1759. fUsageAdded = TRUE;
  1760. //Check to see if this message is marked for deletion
  1761. if (fMailMsgMarkedForDeletion())
  1762. {
  1763. hr = AQUEUE_E_MESSAGE_HANDLED;
  1764. goto Exit;
  1765. }
  1766. //
  1767. // Extract properties that are stored only on mailmsg (this is shared
  1768. // with all QAPI code).
  1769. //
  1770. hr = HrGetMsgInfoFromIMailMsgProperty(m_pIMailMsgProperties,
  1771. pMsgInfo);
  1772. if (FAILED(hr))
  1773. goto Exit;
  1774. pMsgInfo->cbMessageSize = m_cbMsgSize;
  1775. //Get submission and expiration times
  1776. QueueAdminFileTimeToSystemTime(&m_ftQueueEntry, &pMsgInfo->stReceived);
  1777. //
  1778. // Report local expire time if we are on the local queue
  1779. //
  1780. if (m_paqinst && m_paqinst->fIsLocalQueueAdminAction(pIQueueAdminAction))
  1781. {
  1782. if (((ULARGE_INTEGER*)&m_ftLocalExpireNDR)->QuadPart)
  1783. {
  1784. memcpy(&ftExpire, &m_ftLocalExpireNDR, sizeof(FILETIME));
  1785. }
  1786. else
  1787. {
  1788. m_paqinst->CalcExpireTimeNDR(m_ftQueueEntry, TRUE, &ftExpire);
  1789. }
  1790. }
  1791. else
  1792. {
  1793. if (((ULARGE_INTEGER*)&m_ftRemoteExpireNDR)->QuadPart)
  1794. {
  1795. memcpy(&ftExpire, &m_ftRemoteExpireNDR, sizeof(FILETIME));
  1796. }
  1797. else
  1798. {
  1799. m_paqinst->CalcExpireTimeNDR(m_ftQueueEntry, FALSE, &ftExpire);
  1800. }
  1801. }
  1802. QueueAdminFileTimeToSystemTime(&ftExpire, &pMsgInfo->stExpiry);
  1803. //Get the time the message entered the org
  1804. hr = m_pIMailMsgProperties->GetProperty(IMMPID_MP_ORIGINAL_ARRIVAL_TIME,
  1805. sizeof(FILETIME), &cbProp, (BYTE *) &ftSubmitted);
  1806. if (FAILED(hr))
  1807. {
  1808. //Time was not written... use entry time.
  1809. hr = S_OK;
  1810. memcpy(&ftSubmitted, &m_ftQueueEntry, sizeof(FILETIME));
  1811. }
  1812. QueueAdminFileTimeToSystemTime(&ftSubmitted, &pMsgInfo->stSubmission);
  1813. Exit:
  1814. if (fUsageAdded)
  1815. {
  1816. InternalReleaseUsage();
  1817. BounceUsageCount();
  1818. }
  1819. return hr;
  1820. }
  1821. //---[ CMsgRef::BounceUsageCount ]---------------------------------------------
  1822. //
  1823. //
  1824. // Description:
  1825. // Bounces usage count off of 0 to close handles.
  1826. // Parameters:
  1827. // -
  1828. // Returns:
  1829. // -
  1830. // History:
  1831. // 12/7/98 - MikeSwa Created
  1832. // 7/6/99 - MikeSwa Modified - made async
  1833. // 10/27/1999 - MikeSwa - spun-pff SyncBounceUsageCount()
  1834. //
  1835. //-----------------------------------------------------------------------------
  1836. void CMsgRef::BounceUsageCount()
  1837. {
  1838. DWORD dwFlags = 0;
  1839. //Bounce usage count - this will conserve handles
  1840. dwFlags = dwInterlockedSetBits(&m_dwDataFlags, MSGREF_ASYNC_BOUNCE_PENDING);
  1841. //If the bit was *not* already set, then it is safe to call release usage
  1842. if (!(MSGREF_ASYNC_BOUNCE_PENDING & dwFlags))
  1843. {
  1844. if (m_paqinst)
  1845. {
  1846. if (g_cMaxIMsgHandlesAsyncThreshold >
  1847. (DWORD) InterlockedIncrement((PLONG)&s_cMsgsPendingBounceUsage))
  1848. {
  1849. AddRef();
  1850. m_paqinst->HrQueueWorkItem(this,
  1851. CMsgRef::fBounceUsageCountCompletion);
  1852. //Completion function is still called on failure
  1853. }
  1854. else
  1855. {
  1856. //There are too many messages pending async commit. We
  1857. //should force a synchronous bounce
  1858. SyncBounceUsageCount();
  1859. }
  1860. }
  1861. }
  1862. }
  1863. //---[ CMsgRef::SyncBounceUsageCount ]-----------------------------------------
  1864. //
  1865. //
  1866. // Description:
  1867. // Forces synchronous bounce usage. Can be used when there are too many
  1868. // messages pending async bounce usage or if you want to bounce usage
  1869. // immediately (to force deletion of a message marked for deletion).
  1870. // Parameters:
  1871. // -
  1872. // Returns:
  1873. // -
  1874. // History:
  1875. // 10/27/1999 - MikeSwa Created
  1876. //
  1877. //-----------------------------------------------------------------------------
  1878. void CMsgRef::SyncBounceUsageCount()
  1879. {
  1880. BOOL fCompletionRet = FALSE;
  1881. AddRef();
  1882. //Call the async bounce usage function directly
  1883. fCompletionRet = fBounceUsageCountCompletion(this,
  1884. ASYNC_WORK_QUEUE_NORMAL);
  1885. _ASSERT(fCompletionRet); //This is hardcoded to return TRUE
  1886. if (!fCompletionRet)
  1887. Release();
  1888. }
  1889. //---[ CMsgRef::fBounceUsageCountCompletion ]----------------------------------
  1890. //
  1891. //
  1892. // Description:
  1893. // Async completion for BounceUsageCount
  1894. // Parameters:
  1895. // IN pvContext CMsgRef to bounce usage count on
  1896. // Returns:
  1897. // TRUE always
  1898. // History:
  1899. // 7/6/99 - MikeSwa Created
  1900. //
  1901. //-----------------------------------------------------------------------------
  1902. BOOL CMsgRef::fBounceUsageCountCompletion(PVOID pvContext, DWORD dwStatus)
  1903. {
  1904. CMsgRef *pmsgref = (CMsgRef *) pvContext;
  1905. _ASSERT(pmsgref);
  1906. _ASSERT(MSGREF_SIG == pmsgref->m_dwSignature);
  1907. //Don't bounce usage while where are trying to shutdown
  1908. if (ASYNC_WORK_QUEUE_SHUTDOWN != dwStatus)
  1909. {
  1910. //NOTE - Make sure 2 threads don't call ReleaseUsage before AddUsage
  1911. //(which might cause the usage count to drop below 0).
  1912. if (fTrySpinLock(&(pmsgref->m_dwDataFlags), MSGREF_USAGE_COUNT_IN_USE))
  1913. {
  1914. //Make sure another thread has not released the mailmsg
  1915. //for shutdown.
  1916. if ( pmsgref->m_pIMailMsgQM &&
  1917. !(MSGREF_MAILMSG_RELEASED & pmsgref->m_dwDataFlags))
  1918. {
  1919. pmsgref->InternalReleaseUsage();
  1920. pmsgref->InternalAddUsage();
  1921. }
  1922. //Unset the lock usage count bit (since this thread set it)
  1923. ReleaseSpinLock(&(pmsgref->m_dwDataFlags), MSGREF_USAGE_COUNT_IN_USE);
  1924. }
  1925. }
  1926. //Unset the bit set in BounceUsageCount
  1927. dwInterlockedUnsetBits(&(pmsgref->m_dwDataFlags), MSGREF_ASYNC_BOUNCE_PENDING);
  1928. InterlockedDecrement((PLONG)&s_cMsgsPendingBounceUsage);
  1929. pmsgref->Release();
  1930. return TRUE; //This function never retries
  1931. }
  1932. //---[ CMsgRef::HrRemoveMessageFromQueue ]-------------------------------------
  1933. //
  1934. //
  1935. // Description:
  1936. // Remove a message from the specified queue
  1937. // Parameters:
  1938. // IN pdmq Queue to remove message from
  1939. // Returns:
  1940. // S_OK on success
  1941. // History:
  1942. // 12/11/98 - MikeSwa Created
  1943. //
  1944. //-----------------------------------------------------------------------------
  1945. HRESULT CMsgRef::HrRemoveMessageFromQueue(CDestMsgQueue *pdmq)
  1946. {
  1947. HRESULT hr = S_OK;
  1948. DWORD i = 0;
  1949. BOOL fLocal = !pdmq; //If no link is specified... it is local
  1950. //Search for matching queue
  1951. for (i = 0; i < m_cDomains; i++)
  1952. {
  1953. //
  1954. // Check and see if this domain is an exact match. This will work
  1955. // for local as well, since we mark the queue as NULL when we move
  1956. // it to the local queue.
  1957. //
  1958. if (pdmq == m_rgpdmqDomains[i])
  1959. {
  1960. //just check pending bits
  1961. if (!pmbmapGetPending()->FTest(m_cDomains, pmbmapGetDomainBitmap(i)))
  1962. {
  1963. //Set Handled bits so no one else will try to deliver
  1964. pmbmapGetHandled()->FTestAndSet(m_cDomains, pmbmapGetDomainBitmap(i));
  1965. }
  1966. //
  1967. // Stop if we aren't searching for local domains (since we can
  1968. // only have one match in that case).
  1969. //
  1970. if (!fLocal)
  1971. break;
  1972. }
  1973. }
  1974. return hr;
  1975. }
  1976. //---[ CMsgRef::HrQueueAdminNDRMessage ]---------------------------------------
  1977. //
  1978. //
  1979. // Description:
  1980. // Forcably NDRs a message for a given queue.
  1981. // Parameters:
  1982. // IN pdmq Queue to NDR message for
  1983. // Returns:
  1984. // S_OK on success
  1985. // History:
  1986. // 12/12/98 - MikeSwa Created
  1987. //
  1988. //-----------------------------------------------------------------------------
  1989. HRESULT CMsgRef::HrQueueAdminNDRMessage(CDestMsgQueue *pdmq)
  1990. {
  1991. HRESULT hr = S_OK;
  1992. CQuickList qlst;
  1993. CQuickList *pqlst = NULL;
  1994. BOOL fLocal = TRUE;
  1995. DWORD iListIndex = 0;
  1996. DWORD dwDSNFlags = 0;
  1997. if (pdmq)
  1998. {
  1999. hr = qlst.HrAppendItem(pdmq, &iListIndex);
  2000. if (FAILED(hr))
  2001. goto Exit;
  2002. pqlst = &qlst;
  2003. fLocal = FALSE;
  2004. }
  2005. //Force NDR from this queue
  2006. hr = HrSendDelayOrNDR(fLocal ? MSGREF_DSN_LOCAL_QUEUE : 0,
  2007. pqlst, AQUEUE_E_QADMIN_NDR,
  2008. &dwDSNFlags);
  2009. if (FAILED(hr))
  2010. {
  2011. //
  2012. // Not really a failure if the message has already been handled
  2013. //
  2014. if (AQUEUE_E_MESSAGE_HANDLED == hr)
  2015. hr = S_OK;
  2016. goto Exit;
  2017. }
  2018. Exit:
  2019. return hr;
  2020. }
  2021. //---[ CMsgRef::GlobalFreezeMessage ]------------------------------------------
  2022. //
  2023. //
  2024. // Description:
  2025. // Freezes a message for all queues that it is on.
  2026. // Parameters:
  2027. // -
  2028. // Returns:
  2029. // -
  2030. // History:
  2031. // 12/12/98 - MikeSwa Created
  2032. //
  2033. //-----------------------------------------------------------------------------
  2034. void CMsgRef::GlobalFreezeMessage()
  2035. {
  2036. dwInterlockedSetBits(&m_dwDataFlags, MSGREF_MSG_FROZEN);
  2037. }
  2038. //---[ CMsgRef::GlobalFreezeMessage ]------------------------------------------
  2039. //
  2040. //
  2041. // Description:
  2042. // Thaws a previously frozen messages.
  2043. // Parameters:
  2044. // -
  2045. // Returns:
  2046. // -
  2047. // History:
  2048. // 12/12/98 - MikeSwa Created
  2049. // 2/17/2000 - MikeSwa Modified to move the kick of the DMQ up a level.
  2050. //
  2051. //-----------------------------------------------------------------------------
  2052. void CMsgRef::GlobalThawMessage()
  2053. {
  2054. dwInterlockedUnsetBits(&m_dwDataFlags, MSGREF_MSG_FROZEN);
  2055. }
  2056. //---[ CMsgRef::RetryOnDelete ]------------------------------------------------
  2057. //
  2058. //
  2059. // Description:
  2060. // Marks a message to retry on delete. Basically, we hit an error
  2061. // that requires retrying the message at a later time (i.e. - cannot
  2062. // allocate a queue page). However, we cannot put the message into the
  2063. // failed retry queue until all other threads are done accessing it.
  2064. //
  2065. // This call will mark the MsgRef so that it will be retried after its
  2066. // final release.
  2067. // Parameters:
  2068. // -
  2069. // Returns:
  2070. // -
  2071. // History:
  2072. // 1/19/99 - MikeSwa Created
  2073. //
  2074. //-----------------------------------------------------------------------------
  2075. void CMsgRef::RetryOnDelete()
  2076. {
  2077. if (!(MSGREF_MSG_RETRY_ON_DELETE &
  2078. dwInterlockedSetBits(&m_dwDataFlags, MSGREF_MSG_RETRY_ON_DELETE)))
  2079. {
  2080. DEBUG_DO_IT(InterlockedIncrement((PLONG) &g_cDbgMsgRefsPendingRetryOnDelete));
  2081. }
  2082. }
  2083. //---[ CMsgRef::QueryInterface ]-----------------------------------------------
  2084. //
  2085. //
  2086. // Description:
  2087. // QueryInterface for CMsgRef that supports:
  2088. // - IUnknown
  2089. // - CMsgRef
  2090. //
  2091. // This is designed primarily to allow Queue Admin functionality to run
  2092. // against a CMsgRef object as well a "true" COM interface like
  2093. // IMailMsgProperties.
  2094. // Parameters:
  2095. //
  2096. // Returns:
  2097. // S_OK on success
  2098. // History:
  2099. // 2/21/99 - MikeSwa Created
  2100. //
  2101. //-----------------------------------------------------------------------------
  2102. STDMETHODIMP CMsgRef::QueryInterface(REFIID riid, LPVOID *ppvObj)
  2103. {
  2104. HRESULT hr = S_OK;
  2105. if (!ppvObj)
  2106. {
  2107. hr = E_POINTER;
  2108. goto Exit;
  2109. }
  2110. if (IID_IUnknown == riid)
  2111. {
  2112. *ppvObj = static_cast<CMsgRef *>(this);
  2113. }
  2114. else if (IID_CMsgRef == riid)
  2115. {
  2116. *ppvObj = static_cast<CMsgRef *>(this);
  2117. }
  2118. else
  2119. {
  2120. *ppvObj = NULL;
  2121. hr = E_NOINTERFACE;
  2122. goto Exit;
  2123. }
  2124. static_cast<IUnknown *>(*ppvObj)->AddRef();
  2125. Exit:
  2126. return hr;
  2127. }
  2128. //---[ ReleaseAndBounceUsageOnMsgAck ]-----------------------------------------
  2129. //
  2130. //
  2131. // Description:
  2132. // Decides if the message should be commited on the message ack.
  2133. // Parameters:
  2134. // DWORD dwMsgStatus Status of msg ack
  2135. // Returns:
  2136. // -
  2137. // History:
  2138. // 7/6/99 - MikeSwa Created
  2139. // 9/9/99 - Mikeswa Updated to be more aggressive about closing handles
  2140. // 10/27/1999 - MikeSwa Added code to delete handled messages
  2141. //
  2142. //-----------------------------------------------------------------------------
  2143. void CMsgRef::ReleaseAndBounceUsageOnMsgAck(DWORD dwMsgStatus)
  2144. {
  2145. BOOL fBounceUsage = FALSE;
  2146. if (MESSAGE_STATUS_RETRY & dwMsgStatus)
  2147. {
  2148. //If this message was Ack'd retry... we should try an bounce the usage
  2149. //count and close the associated handle.
  2150. fBounceUsage = TRUE;
  2151. }
  2152. else if (g_cMaxIMsgHandlesThreshold < m_paqinst->cCountMsgsForHandleThrottling(m_pIMailMsgProperties))
  2153. {
  2154. //If we are over our alloted number of messages in the system, we should
  2155. //close the message even if was ACK'd OK. The only time we don't want
  2156. //to is if all domains have been handled. In this case, we are about
  2157. //to delete the message and usually do not want to commit it first.
  2158. if (!pmbmapGetHandled()->FAllSet(m_cDomains))
  2159. fBounceUsage = TRUE;
  2160. }
  2161. //
  2162. // Release the usage count added by HrPrepareDelivery
  2163. // NOTE: This must come before the call to Bounce... or the
  2164. // call to bounce usage will have no effect. It must also
  2165. // occur after the above call to cCountMsgsForHandleThrottling,
  2166. // because it may request a property from the mailmsg.
  2167. //
  2168. InternalReleaseUsage();
  2169. if (pmbmapGetHandled()->FAllSet(m_cDomains))
  2170. {
  2171. //There is are 2 known cases were the above could leave
  2172. //the message handles open for a while. In both cases, this message
  2173. //must be queued to multiple DMQs on the same link, and be completely
  2174. //delivered. In one case, the message is dequeued during delivery and
  2175. //sits on the retry queue until it is remerged. In another case, the
  2176. //message sits on the delivery queue until a thead pulls it off and
  2177. //discovers that it is already handled.
  2178. //To avoid this, we will mark the message for deletion when the
  2179. //intneral usage count drops to 0.
  2180. MarkMailMsgForDeletion();
  2181. }
  2182. if (fMailMsgMarkedForDeletion())
  2183. {
  2184. //Force synchronous bounce usgage since delete should be much
  2185. //quicker than commit
  2186. SyncBounceUsageCount();
  2187. }
  2188. else if (fBounceUsage)
  2189. {
  2190. BounceUsageCount();
  2191. }
  2192. }
  2193. //---[ CMsgRef::MarkMailMsgForDeletion ]---------------------------------------
  2194. //
  2195. //
  2196. // Description:
  2197. // Sets the bit saying the that we are done with the mailmsg, and it
  2198. // should delete it the next time the usage count drops to 0.
  2199. //
  2200. // To force deletion, the caller should bounce the usage count after
  2201. // calling.
  2202. // Parameters:
  2203. // -
  2204. // Returns:
  2205. // -
  2206. // History:
  2207. // 10/26/1999 - MikeSwa Created
  2208. //
  2209. //-----------------------------------------------------------------------------
  2210. void CMsgRef::MarkMailMsgForDeletion()
  2211. {
  2212. TraceFunctEnterEx((LPARAM) this, "CMsgRef::MarkMailMsgForDeletion");
  2213. DWORD dwOrigFlags = 0;
  2214. DebugTrace((LPARAM) this,
  2215. "Marking message with ID hash of 0x%x for deletion", m_dwMsgIdHash);
  2216. dwOrigFlags = dwInterlockedSetBits(&m_dwDataFlags, MSGREF_MAILMSG_DELETE_PENDING);
  2217. //If we were the first thread to set this, then update the counters
  2218. if (!(dwOrigFlags & MSGREF_MAILMSG_DELETE_PENDING))
  2219. {
  2220. InterlockedIncrement((PLONG) &s_cCurrentMsgsPendingDelete);
  2221. InterlockedIncrement((PLONG) &s_cTotalMsgsPendingDelete);
  2222. }
  2223. _ASSERT(fMailMsgMarkedForDeletion());
  2224. TraceFunctLeave();
  2225. }
  2226. //---[ CMsgRef::ThreadSafeMailMsgDelete ]--------------------------------------
  2227. //
  2228. //
  2229. // Description:
  2230. // Used to make sure that calling thread is the only one that will call
  2231. // Delete() on the MailMsg. Will set the MSGREF_MAILMSG_DELETED and call
  2232. // Delete(). Only called in ReleaseMailMsg() and InternalReleaseUsage().
  2233. //
  2234. // The caller is responsible for making sure that other threads are not
  2235. // reading the mailmsg or have a usage count.
  2236. // Parameters:
  2237. // -
  2238. // Returns:
  2239. // -
  2240. // History:
  2241. // 10/27/1999 - MikeSwa Created
  2242. //
  2243. //-----------------------------------------------------------------------------
  2244. VOID CMsgRef::ThreadSafeMailMsgDelete()
  2245. {
  2246. TraceFunctEnterEx((LPARAM) this, "CMsgRef::ThreadSafeMailMsgDelete");
  2247. DWORD dwOrigFlags = 0;
  2248. HRESULT hr = S_OK;
  2249. //Try to set the MSGREF_MAILMSG_DELETED.
  2250. dwOrigFlags = dwInterlockedSetBits(&m_dwDataFlags, MSGREF_MAILMSG_DELETED);
  2251. //If we are the first thread to set it, then we can Delete()
  2252. if (!(dwOrigFlags & MSGREF_MAILMSG_DELETED))
  2253. {
  2254. hr = m_pIMailMsgQM->Delete(NULL);
  2255. if (FAILED(hr))
  2256. ErrorTrace((LPARAM) this, "Delete failed with hr 0x%08X", hr);
  2257. //If this message was marked as pending delete, then update
  2258. //the appropriate counters
  2259. if (dwOrigFlags & MSGREF_MAILMSG_DELETE_PENDING)
  2260. {
  2261. InterlockedDecrement((PLONG) &s_cCurrentMsgsPendingDelete);
  2262. InterlockedIncrement((PLONG) &s_cTotalMsgsDeletedAfterPendingDelete);
  2263. InterlockedIncrement((PLONG) &s_cCurrentMsgsDeletedNotReleased);
  2264. }
  2265. }
  2266. TraceFunctLeave();
  2267. }
  2268. //---[ CMsgRef::InternalAddUsage ]---------------------------------------------
  2269. //
  2270. //
  2271. // Description:
  2272. // Wraps the mailmsg call to AddUsage. Allows CMsgRef to call Delete()
  2273. // on the underlying mailmsg while there are still references to the
  2274. // CMsgRef.
  2275. //
  2276. // Calling InternalAddUsage does not guarantee that there is backing
  2277. // storage for the MailMsg. Callers must call fMailMsgMarkedForDeletion()
  2278. // *after* calling InternalAddUsage()
  2279. //
  2280. // We do guarantee that if InternalAddUsage() is called and a then
  2281. // subsequent call to fMailMsgMarkedForDeletion() returns TRUE, then
  2282. // the mailmsg will not be deleted until after the corresponding
  2283. // call to InternalReleaseUsage();
  2284. // Parameters:
  2285. // -
  2286. // Returns:
  2287. // S_OK if the message is deleted and we did not call into mailmsg
  2288. // Error/Success code from mailmsg if we called in.
  2289. // History:
  2290. // 10/26/1999 - MikeSwa Created
  2291. //
  2292. //-----------------------------------------------------------------------------
  2293. HRESULT CMsgRef::InternalAddUsage()
  2294. {
  2295. TraceFunctEnterEx((LPARAM) this, "CMsgRef::InternalAddUsage");
  2296. HRESULT hr = S_OK;
  2297. InterlockedIncrement((PLONG) &m_cInternalUsageCount);
  2298. if (!fMailMsgMarkedForDeletion() && m_pIMailMsgQM)
  2299. {
  2300. hr = m_pIMailMsgQM->AddUsage();
  2301. }
  2302. //If the call to AddUsage failed, we need to decrement our own count
  2303. if (FAILED(hr))
  2304. {
  2305. InterlockedDecrement((PLONG) &m_cInternalUsageCount);
  2306. ErrorTrace((LPARAM) this, "AddUsage failed 0x%0X", hr);
  2307. }
  2308. TraceFunctLeave();
  2309. return hr;
  2310. }
  2311. //---[ CMsgRef::InternalReleaseUsage ]-----------------------------------------
  2312. //
  2313. //
  2314. // Description:
  2315. // Wraps the mailmsg call to ReleaseUsage. Allows us to call Delete()
  2316. // on the mailmsg while there are still references to this CMsgRef.
  2317. //
  2318. // If MSGREF_MAILMSG_DELETE_PENDING is set but Delete() has not yet been
  2319. // called, this will call Delete() when the usage count hits 0.
  2320. // Parameters:
  2321. // -
  2322. // Returns:
  2323. // S_OK if the message is deleted and we did not call into mailmsg
  2324. // Error/Success code from mailmsg if we called in.
  2325. // History:
  2326. // 10/26/1999 - MikeSwa Created
  2327. //
  2328. //-----------------------------------------------------------------------------
  2329. HRESULT CMsgRef::InternalReleaseUsage()
  2330. {
  2331. TraceFunctEnterEx((LPARAM) this, "CMsgRef::InternalReleaseUsage");
  2332. HRESULT hr = S_OK;
  2333. DWORD dwOrigFlags = 0;
  2334. //We need to call into mailmsg *before* we decrement the usage count. It is
  2335. //theoretically possible that this would cause an extra commit after we
  2336. //have decided to delete the message, but since we check right before
  2337. //calling the timing window is very small.
  2338. if (m_pIMailMsgQM && !fMailMsgMarkedForDeletion())
  2339. hr = m_pIMailMsgQM->ReleaseUsage();
  2340. _ASSERT(m_cInternalUsageCount); //Should never go negative
  2341. if (FAILED(hr))
  2342. ErrorTrace((LPARAM) this, "ReleaseUsage failed - 0x%0X", hr);
  2343. //If we have dropped the usage count to zero, then we need to check
  2344. //and see if we need to call delete.
  2345. //The "at rest" value of m_cInternalUsageCount is 1, but BounceUsage
  2346. //(which calls this function) can cause it to drop to 0.
  2347. if (0 == InterlockedDecrement((PLONG) &m_cInternalUsageCount))
  2348. {
  2349. //We need to be absolutely thread safe about calling delete. Not only
  2350. //do we need to ensure that a single thread calls Delete(), but we
  2351. //also need to ensure that someone has not called InternalAddUsage()
  2352. //before MSGREF_MAILMSG_DELETE_PENDING was set, but after the
  2353. //above InterlockedDecrement() was called.
  2354. //
  2355. //If we check for the MSGREF_MAILMSG_DELETE_PENDING before the
  2356. //aboved InterlockedDecrement, we can run into a timing window where
  2357. //the final InternalReleaseUsage does not detect that we need to delete
  2358. //the mailmsg.
  2359. //
  2360. //To avoid these issues, we will check the m_cInternalUsageCount
  2361. //again. If it is still zero, then we will proceed, because we know
  2362. //that the count hit zero after MSGREF_MAILMSG_DELETE_PENDING was set.
  2363. //If the count is *not* currently zero, we know that a later thread
  2364. //will release the usage count and hit this code path.
  2365. if (fMailMsgMarkedForDeletion())
  2366. {
  2367. if (!m_cInternalUsageCount)
  2368. ThreadSafeMailMsgDelete();
  2369. }
  2370. }
  2371. TraceFunctLeave();
  2372. return hr;
  2373. }
  2374. //---[ CMsgRef::ShouldRetry ]--------------------------------------------------
  2375. //
  2376. //
  2377. // Description:
  2378. // Determines if we should retry this message if an error occurs. Will
  2379. // return FALSE if we *know* that the backing store has been deleted.
  2380. // Parameters:
  2381. // -
  2382. // Returns:
  2383. // TRUE if we should rety the message
  2384. // FALSE, the backing store for the message is gone... drop it
  2385. // History:
  2386. // 1/6/2000 - MikeSwa Created
  2387. // 4/12/2000 - MikeSwa modified to call CAQSvrInst member
  2388. //
  2389. //-----------------------------------------------------------------------------
  2390. BOOL CMsgRef::fShouldRetry()
  2391. {
  2392. IMailMsgProperties *pIMailMsgProperties = m_pIMailMsgProperties;
  2393. if (pIMailMsgProperties && m_paqinst)
  2394. return m_paqinst->fShouldRetryMessage(pIMailMsgProperties, FALSE);
  2395. else
  2396. return FALSE;
  2397. }
  2398. //---[ CMsgRef::GetStatsForMsg ]-----------------------------------------------
  2399. //
  2400. //
  2401. // Description:
  2402. // Fills in a CAQStats for this message
  2403. // Parameters:
  2404. // IN OUT paqstat Stats to fill in
  2405. // Returns:
  2406. // NULL
  2407. // History:
  2408. // 1/15/2000 - MikeSwa Created
  2409. //
  2410. //-----------------------------------------------------------------------------
  2411. void CMsgRef::GetStatsForMsg(IN OUT CAQStats *paqstat)
  2412. {
  2413. _ASSERT(paqstat);
  2414. paqstat->m_cMsgs = 1;
  2415. paqstat->m_rgcMsgPriorities[PriGetPriority()] = 1;
  2416. paqstat->m_uliVolume.QuadPart = (ULONGLONG) dwGetMsgSize();
  2417. paqstat->m_dwHighestPri = PriGetPriority();
  2418. }
  2419. //---[ CMsgRef::MarkQueueAsLocal ]---------------------------------------------
  2420. //
  2421. //
  2422. // Description:
  2423. // Marks a given DMQ as local. This is used in the gateway delivery
  2424. // path to prevent messages from being lost if a reroute happens after
  2425. // it has been moved to the local delivery queue.
  2426. // Parameters:
  2427. // pdmq Queue to mark as local for this message
  2428. // Returns:
  2429. // -
  2430. // History:
  2431. // 2/17/2000 - MikeSwa Created
  2432. //
  2433. //-----------------------------------------------------------------------------
  2434. void CMsgRef::MarkQueueAsLocal(IN CDestMsgQueue *pdmq)
  2435. {
  2436. DWORD i = 0;
  2437. //
  2438. //Search for matching queue
  2439. //
  2440. for (i = 0; i < m_cDomains; i++)
  2441. {
  2442. if (pdmq == m_rgpdmqDomains[i])
  2443. {
  2444. //
  2445. // Exact match found. Set pointer to NULL and release it.
  2446. // The caller (CLinkMsgQueue) should still have a reference to
  2447. // the queue.
  2448. //
  2449. if (InterlockedCompareExchangePointer((void **) &m_rgpdmqDomains[i], NULL, pdmq) == (void *) pdmq)
  2450. pdmq->Release();
  2451. return;
  2452. }
  2453. }
  2454. _ASSERT(0 && "Requested DMQ not found!!!!");
  2455. }
  2456. //---[ CountMessageInRemoteTotals ]--------------------------------------------
  2457. //
  2458. //
  2459. // Description:
  2460. // Count this message in the totals for remote messages. This means that
  2461. // we need to decrement the count when this message is released.
  2462. //
  2463. // There is no exactly equivalent local count. The counters for local
  2464. // delivery has based on queue length and can have multiple counts per
  2465. // message object (as can some of the remote counts as well).
  2466. // Parameters:
  2467. // -
  2468. // Returns:
  2469. // -
  2470. // History:
  2471. // 2/28/2000 - MikeSwa Created
  2472. //
  2473. //-----------------------------------------------------------------------------
  2474. void CMsgRef::CountMessageInRemoteTotals()
  2475. {
  2476. dwInterlockedSetBits(&m_dwDataFlags, MSGREF_MSG_COUNTED_AS_REMOTE);
  2477. }
  2478. //---[ CMsgRef::HrPromoteMessageStatusToMailMsg ]------------------------------
  2479. //
  2480. //
  2481. // Description:
  2482. // Promotes extended status from MessageAck to mailmsg recipient property
  2483. // if there is not already specific information there.
  2484. // Parameters:
  2485. // pdcntxt Delivery context for this message
  2486. // pMsgAck Ptr to MessageAck structure for this message
  2487. // Returns:
  2488. // S_OK on success
  2489. // History:
  2490. // 3/20/2000 - MikeSwa Created
  2491. //
  2492. //-----------------------------------------------------------------------------
  2493. HRESULT CMsgRef::HrPromoteMessageStatusToMailMsg(CDeliveryContext *pdcntxt,
  2494. MessageAck *pMsgAck)
  2495. {
  2496. TraceFunctEnterEx((LPARAM) this, "CMsgRef::HrPromoteMessageStatusToMailMsg");
  2497. HRESULT hr = S_OK;
  2498. HRESULT hrRecipStatus = S_OK;
  2499. HRESULT hrTmp = S_OK;
  2500. RECIPIENT_FILTER_CONTEXT rpfctxt;
  2501. BOOL fContextInit = FALSE;
  2502. DWORD iCurrentRecip = 0;
  2503. DWORD dwProp = 0; //place holder for recipient status property
  2504. LPSTR szExtendedStatus = NULL;
  2505. DWORD dwRecipMask = RP_DSN_HANDLED | // No DSN generated
  2506. RP_DSN_NOTIFY_NEVER | // NOTIFY!=NEVER
  2507. (RP_DELIVERED ^ RP_HANDLED); // Not delivered
  2508. DWORD dwRecipFlags = 0;
  2509. _ASSERT(pdcntxt);
  2510. _ASSERT(pMsgAck);
  2511. _ASSERT(m_pIMailMsgRecipients);
  2512. //
  2513. // If we have no extended status, then there is nothing for us to do
  2514. //
  2515. if (!(pMsgAck->dwMsgStatus & MESSAGE_STATUS_EXTENDED_STATUS_CODES) ||
  2516. !pMsgAck->cbExtendedStatus ||
  2517. !pMsgAck->szExtendedStatus)
  2518. {
  2519. DebugTrace((LPARAM) this, "No extended status codes in MessageAck");
  2520. goto Exit;
  2521. }
  2522. //
  2523. // If this was actually not a protocol error, SMTP may not have put
  2524. // the full status codes on it. Instead of "250 2.0.0 OK" it may
  2525. // have just put "OK". We should not write this to mailmsg as is, but
  2526. // should instead fix up string so it is what the DSN sink expects.
  2527. //
  2528. hr = HrUpdateExtendedStatus(pMsgAck->cbExtendedStatus,
  2529. pMsgAck->szExtendedStatus,
  2530. &szExtendedStatus);
  2531. if (FAILED(hr))
  2532. {
  2533. ErrorTrace((LPARAM) this,
  2534. "Unable to get extended status from %s - hr 0x%08X",
  2535. pMsgAck->szExtendedStatus, hr);
  2536. hr = S_OK; //non-fatal error... eat it
  2537. goto Exit;
  2538. }
  2539. //
  2540. // Initialize recipient filter context so we can iterate over the
  2541. // recipients
  2542. //
  2543. hr = m_pIMailMsgRecipients->InitializeRecipientFilterContext(&rpfctxt,
  2544. pdcntxt->m_dwStartDomain, dwRecipFlags,
  2545. dwRecipMask);
  2546. if (FAILED(hr))
  2547. {
  2548. ErrorTrace((LPARAM) this,
  2549. "InitializeRecipientFilterContext failed with 0x%08X", hr);
  2550. goto Exit;
  2551. }
  2552. fContextInit = TRUE;
  2553. DebugTrace((LPARAM) this,
  2554. "Init recip filter context with mask 0x%08X and flags 0x%08X and domain %d",
  2555. dwRecipMask, dwRecipFlags, pdcntxt->m_dwStartDomain);
  2556. //
  2557. // Loop over each recipient and update their properties if needed
  2558. //
  2559. for (hr = m_pIMailMsgRecipients->GetNextRecipient(&rpfctxt, &iCurrentRecip);
  2560. SUCCEEDED(hr);
  2561. hr = m_pIMailMsgRecipients->GetNextRecipient(&rpfctxt, &iCurrentRecip))
  2562. {
  2563. DebugTrace((LPARAM) this, "Looking at recipient %d", iCurrentRecip);
  2564. //
  2565. // See if we have an HRESULT... if it *is* there and is a failure
  2566. // then continue on to the next recipient
  2567. //
  2568. hrRecipStatus = S_OK;
  2569. hr = m_pIMailMsgRecipients->GetDWORD(iCurrentRecip,
  2570. IMMPID_RP_ERROR_CODE, (DWORD *) &hrRecipStatus);
  2571. if (SUCCEEDED(hr) && FAILED(hrRecipStatus))
  2572. {
  2573. DebugTrace((LPARAM) this,
  2574. "Recipient %d already has a status of 0x%08X",
  2575. iCurrentRecip, hrRecipStatus);
  2576. continue;
  2577. }
  2578. //
  2579. // Check for existing status property
  2580. //
  2581. hr = m_pIMailMsgRecipients->GetDWORD(iCurrentRecip,
  2582. IMMPID_RP_SMTP_STATUS_STRING, &dwProp);
  2583. if (MAILMSG_E_PROPNOTFOUND != hr)
  2584. {
  2585. DebugTrace((LPARAM) this,
  2586. "Recipient %d has a status string (hr 0x%08X)",
  2587. iCurrentRecip, hr);
  2588. continue;
  2589. }
  2590. //
  2591. // There is no detailed information on the recipient. We should
  2592. // promote the extended status from the message ack to the recipient
  2593. //
  2594. hr = m_pIMailMsgRecipients->PutStringA(iCurrentRecip,
  2595. IMMPID_RP_SMTP_STATUS_STRING, szExtendedStatus);
  2596. if (FAILED(hr))
  2597. {
  2598. ErrorTrace((LPARAM) this,
  2599. "Unable to write %s to recip %d - hr 0x%08X",
  2600. szExtendedStatus, iCurrentRecip, hr);
  2601. goto Exit;
  2602. }
  2603. DebugTrace((LPARAM) this,
  2604. "Wrote extended status %s to recip %d",
  2605. szExtendedStatus, iCurrentRecip);
  2606. }
  2607. if (HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS) == hr)
  2608. hr = S_OK; //we just reached the end of the context
  2609. Exit:
  2610. //
  2611. // Terminate context if init was called
  2612. //
  2613. if (fContextInit)
  2614. {
  2615. hrTmp = m_pIMailMsgRecipients->TerminateRecipientFilterContext(&rpfctxt);
  2616. if (FAILED(hrTmp))
  2617. {
  2618. ErrorTrace((LPARAM) this,
  2619. "TerminateRecipientFilterContext failed 0x%08X", hr);
  2620. }
  2621. }
  2622. TraceFunctLeave();
  2623. return hr;
  2624. }
  2625. //
  2626. // These are strings defined in smtpcli.hxx that are used in smtpout.cxx
  2627. // in calls to m_ResponseContext.m_cabResponse.Append(). These strings
  2628. // are not in the normal protocol format of "xxx x.x.x Error string"
  2629. //
  2630. // g_cNumExtendedStatusStrings Number of hard-coded strings
  2631. // g_rgszSMTPExtendedStatus SMTP extended status
  2632. // g_rgszSMTPUpdatedExtendedStatus Full protocol strings.
  2633. //
  2634. const DWORD g_cNumExtendedStatusStrings = 4;
  2635. const CHAR g_rgszSMTPExtendedStatus[g_cNumExtendedStatusStrings][200] = {
  2636. "Msg Size greater than allowed by Remote Host",
  2637. "Body type not supported by Remote Host",
  2638. "Failed to authenticate with Remote Host",
  2639. "Failed to negotiate secure channel with Remote Host",
  2640. };
  2641. const CHAR g_rgszSMTPUpdatedExtendedStatus[g_cNumExtendedStatusStrings][200] = {
  2642. "450 5.2.3 Msg Size greater than allowed by Remote Host",
  2643. "554 5.6.1 Body type not supported by Remote Host",
  2644. "505 5.7.3 Failed to authenticate with Remote Host",
  2645. "505 5.7.3 Failed to negotiate secure channel with Remote Host",
  2646. };
  2647. //
  2648. // Extended status strings for some known cases where we reach HrUpdateExtendedStatus
  2649. // without having a valid extended status string
  2650. //
  2651. const CHAR g_szSMTPStatus_BadSenderAddress [] = "500 5.1.7 Invalid or missing sender SMTP address";
  2652. const CHAR g_szSMTPStatus_OtherError [] = "500 5.5.6 Unknown SMTP delivery error";
  2653. //---[ CMsgRef::HrUpdateExtendedStatus ]---------------------------------------
  2654. //
  2655. //
  2656. // Description:
  2657. // Sometime SMTP is lazy about returning actual protocol-complient
  2658. // strings for internal errors. If this is not a valid status string,
  2659. // then we should check against the strings that we know SMTP generates
  2660. // and make it a protocol string that can be used in DSN generation.
  2661. //
  2662. // Parameters:
  2663. // IN szCurrentStatus current status
  2664. // OUT pszNewStatus New protcol friendly status string. This
  2665. // is a const that does not need to be freed
  2666. // Returns:
  2667. // S_OK on succcess
  2668. // E_FAIL when we cannot extract a protocol string from the
  2669. // extended status string.
  2670. // History:
  2671. // 3/20/2000 - MikeSwa Created
  2672. //
  2673. //-----------------------------------------------------------------------------
  2674. HRESULT CMsgRef::HrUpdateExtendedStatus(DWORD cbCurrentStatus,
  2675. LPSTR szCurrentStatus,
  2676. LPSTR *pszNewStatus)
  2677. {
  2678. TraceFunctEnterEx((LPARAM) this, "CMsgRef::HrUpdateExtendedStatus");
  2679. HRESULT hr = S_OK;
  2680. DWORD iCurrentExtendedStatus = 0;
  2681. _ASSERT(szCurrentStatus);
  2682. _ASSERT(pszNewStatus);
  2683. *pszNewStatus = szCurrentStatus;
  2684. //
  2685. // Check for known condition where szCurrentStatus is set to an
  2686. // unterminated '5'
  2687. //
  2688. if (szCurrentStatus[0] == '5' && cbCurrentStatus == 1)
  2689. {
  2690. DWORD cbProp = 0;
  2691. CHAR szBuffer[100];
  2692. // Check to see if the sender is invalid
  2693. hr = m_pIMailMsgProperties->GetProperty(IMMPID_MP_SENDER_ADDRESS_SMTP,
  2694. sizeof(szBuffer), &cbProp,(BYTE *) szBuffer);
  2695. if(FAILED(hr) && hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
  2696. {
  2697. // Failed to read sender - set status accordingly
  2698. *pszNewStatus = (CHAR*) g_szSMTPStatus_BadSenderAddress;
  2699. }
  2700. else
  2701. {
  2702. // Succeeded reading sender or failed due to insufficient
  2703. // buffer - which is fine, we only care if the prop exists
  2704. *pszNewStatus = (CHAR*) g_szSMTPStatus_OtherError;
  2705. }
  2706. DebugTrace((LPARAM) this,
  2707. "Updating char '5' to status \"%s\"", *pszNewStatus);
  2708. // This isn't a failure even if we didn't get the prop.
  2709. hr = S_OK;
  2710. goto Exit;
  2711. }
  2712. //
  2713. // If it starts with a 2 4 or 5, then we know it is not one of the
  2714. // non-protocol strings
  2715. //
  2716. if (('2' == *szCurrentStatus) ||
  2717. ('4' == *szCurrentStatus) ||
  2718. ('5' == *szCurrentStatus))
  2719. {
  2720. DebugTrace((LPARAM) this,
  2721. "Status %s is already in protocol format", szCurrentStatus);
  2722. goto Exit;
  2723. }
  2724. //
  2725. // check against all well-known status
  2726. //
  2727. for (iCurrentExtendedStatus = 0;
  2728. iCurrentExtendedStatus < g_cNumExtendedStatusStrings;
  2729. iCurrentExtendedStatus++)
  2730. {
  2731. if (0 == lstrcmpi(szCurrentStatus, g_rgszSMTPExtendedStatus[iCurrentExtendedStatus]))
  2732. {
  2733. *pszNewStatus = (CHAR *) g_rgszSMTPUpdatedExtendedStatus[iCurrentExtendedStatus];
  2734. DebugTrace((LPARAM) this, "Updating to status \"%s\" from status \"%s\"",
  2735. szCurrentStatus, *pszNewStatus);
  2736. //
  2737. // Our strings should match (modulo the new prefix)
  2738. //
  2739. _ASSERT(0 == lstrcmpi(szCurrentStatus, *pszNewStatus + sizeof("xxx x.x.x")));
  2740. goto Exit;
  2741. }
  2742. }
  2743. hr = E_FAIL;
  2744. Exit:
  2745. TraceFunctLeave();
  2746. return hr;
  2747. }