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.

6423 lines
204 KiB

  1. //-----------------------------------------------------------------------------
  2. //
  3. //
  4. // File: aqinst.cpp
  5. //
  6. // Description: Implementation of the Advanced Queueing Server Instance
  7. //
  8. // Author: mikeswa
  9. //
  10. // Copyright (C) 1997 Microsoft Corporation
  11. //
  12. //-----------------------------------------------------------------------------
  13. #include <aqprecmp.h>
  14. #include "dcontext.h"
  15. #include "connmgr.h"
  16. #include <smtpseo.h>
  17. #include <cat.h>
  18. #include "dsnevent.h"
  19. #include "asyncq.inl"
  20. #include "aqutil.h"
  21. #include "smtpconn.h"
  22. #include "aqrpcsvr.h"
  23. #include "aqsize.h"
  24. #include "propstrm.h"
  25. #include "tran_evntlog.h"
  26. #include "asyncadm.inl"
  27. #define PRELOCAL_QUEUE_ID 0x00000001
  28. #define PRECAT_QUEUE_ID 0x00000002
  29. #define PREROUTING_QUEUE_ID 0x00000004
  30. #define PRESUBMIT_QUEUE_ID 0x00000008
  31. // externs defined in blockmgr and cmmprops to control debug code
  32. DWORD g_fFillPropertyPages = 0;
  33. DWORD g_fValidateSignatures = 0;
  34. DWORD g_fForceCrashOnError = 0;
  35. HRESULT MailTransport_Completion_SubmitMessage(HRESULT hrStatus, PVOID pvContext);
  36. HRESULT MailTransport_Completion_PreCategorization(HRESULT hrStatus, PVOID pvContext);
  37. HRESULT MailTransport_Completion_PostCategorization(HRESULT hrStatus, PVOID pvContext);
  38. const CLSID CLSID_ExchangeStoreDriver = {0x7BD80399,0xE37E,0x11d1,{0x9B,0xE2,0x00,0xA0,0xC9,0x5E,0x61,0x43}};
  39. CPool CAQSvrInst::CAQLocalDeliveryNotify::s_pool(AQLD_SIG);
  40. //---[ CAQSvrInst::fShouldRetryMessage ]---------------------------------------
  41. //
  42. //
  43. // Description:
  44. // Attempts to determine if the message has hit a hard failure (like the
  45. // backing store has been deleted). This uses GetBinding to determine
  46. // The error returned by the store driver. if it is FILE_NOT_FOUND,
  47. // then the backing store for the message has been deleted... or is no
  48. // longer valid (i.e. - the store restarting).
  49. // Parameters:
  50. // pIMailMsgProperties
  51. // fShouldBounceUsageIfRetry TRUE - Should bounce usage on retry
  52. // FALSE - Never bounce usage
  53. // If the message is alreade associated with a msgref, this
  54. // should always be FALSE since bouncing the usage count
  55. // is done through the CMsgRef.
  56. // Returns:
  57. // TRUE If we think we should retry the message
  58. // FALSE If new *know* that the message should be dropped. If unsure,
  59. // we will return TRUE.
  60. // History:
  61. // 1/4/2000 - MikeSwa Created
  62. // 4/10/2000 - MikeSwa Modified to better detect store shutdown/failure
  63. //
  64. //-----------------------------------------------------------------------------
  65. BOOL CAQSvrInst::fShouldRetryMessage(IMailMsgProperties *pIMailMsgProperties,
  66. BOOL fShouldBounceUsageIfRetry)
  67. {
  68. TraceFunctEnterEx((LPARAM) pIMailMsgProperties, "fShouldRetryMessage");
  69. BOOL fShouldRetry = TRUE;
  70. BOOL fHasShutdownLock = FALSE;
  71. HRESULT hr = S_OK;
  72. IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL;
  73. IMailMsgValidateContext *pIMailMsgValidateContext = NULL;
  74. _ASSERT(pIMailMsgProperties);
  75. if (!fTryShutdownLock())
  76. goto Exit;
  77. fHasShutdownLock = TRUE;
  78. //
  79. // First check and see if the message context is still OK - if that
  80. // doesn't work, we use the HrValidateMessageConteNt call below
  81. // and force a RFC822 rendering of the message (which can be a
  82. // huge perf hit).
  83. //
  84. // QI for validation interface
  85. hr = pIMailMsgProperties->QueryInterface(
  86. IID_IMailMsgValidateContext,
  87. (LPVOID *)&pIMailMsgValidateContext);
  88. if (FAILED(hr))
  89. {
  90. ErrorTrace((LPARAM) this,
  91. "Unable to QI for IMailMsgValidateContext 0x%08X",hr);
  92. goto Exit;
  93. }
  94. // Validate the message context
  95. hr = pIMailMsgValidateContext->ValidateContext();
  96. DebugTrace((LPARAM) this,
  97. "ValidateContext returned 0x%08X", hr);
  98. if (hr == S_OK) //this message is fine
  99. goto Exit;
  100. else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
  101. {
  102. fShouldRetry = FALSE;
  103. goto Exit;
  104. }
  105. //
  106. // If the above didn't work... try harder by verifying content. This
  107. // will open the handles... so we need to close them
  108. //
  109. hr = HrValidateMessageContent(pIMailMsgProperties);
  110. if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
  111. {
  112. //The mailmsg has been deleted... we can just drop it.
  113. DebugTrace((LPARAM) pIMailMsgProperties,
  114. "WARNING: Backing store for mailmsg has been deleted.");
  115. fShouldRetry = FALSE;
  116. goto Exit;
  117. }
  118. else if (FAILED(hr))
  119. {
  120. ErrorTrace((LPARAM) pIMailMsgProperties,
  121. "GetBinding failed with hr - 0x%08X", hr);
  122. goto Exit;
  123. }
  124. Exit:
  125. //
  126. // Bounce usage count if we have are sticking it back in the queue
  127. // and the caller does not object
  128. //
  129. if (fShouldRetry && fShouldBounceUsageIfRetry)
  130. {
  131. hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgQueueMgmt,
  132. (void **) &pIMailMsgQueueMgmt);
  133. if (SUCCEEDED(hr) && pIMailMsgQueueMgmt)
  134. {
  135. pIMailMsgQueueMgmt->ReleaseUsage();
  136. pIMailMsgQueueMgmt->AddUsage();
  137. pIMailMsgQueueMgmt->Release();
  138. }
  139. }
  140. if (pIMailMsgValidateContext)
  141. pIMailMsgValidateContext->Release();
  142. if (fHasShutdownLock)
  143. ShutdownUnlock();
  144. TraceFunctLeave();
  145. return fShouldRetry;
  146. }
  147. //thin wrapper for CAQSvrInst::fPreCatQueueCompletion member
  148. BOOL fPreCatQueueCompletionWrapper(IMailMsgProperties *pIMailMsgProperties,
  149. PVOID pvContext)
  150. {
  151. // Testing : Cause intermittent failures in this queue
  152. if (fShouldFail(g_cPreCatQueueFailurePercent))
  153. {
  154. ((CAQSvrInst *)pvContext)->ScheduleInternalRetry(LI_TYPE_PENDING_CAT);
  155. return FALSE;
  156. }
  157. if (!((CAQSvrInst *)pvContext)->fPreCatQueueCompletion(pIMailMsgProperties))
  158. return !((CAQSvrInst *)pvContext)->fShouldRetryMessage(pIMailMsgProperties);
  159. else
  160. return TRUE;
  161. }
  162. //thin wrapper for CAQSvrInst::fPreLocalDeliveryCompletion
  163. BOOL fPreLocalDeliveryQueueCompletionWrapper(CMsgRef *pmsgref,
  164. PVOID pvContext)
  165. {
  166. if (!((CAQSvrInst *)pvContext)->fPreLocalDeliveryQueueCompletion(pmsgref))
  167. return !(pmsgref->fShouldRetry());
  168. else
  169. return TRUE;
  170. }
  171. //thin wrapper for CAQSvrInst::fPostDSNQueueCompletion member
  172. BOOL fPostDSNQueueCompletionWrapper(IMailMsgProperties *pIMailMsgProperties,
  173. PVOID pvContext)
  174. {
  175. return (SUCCEEDED(((CAQSvrInst *)pvContext)->HrInternalSubmitMessage(pIMailMsgProperties)));
  176. }
  177. //thin wrapper for CAQSvrInst::fPreRoutingQueueCompletion
  178. BOOL fPreRoutingQueueCompletionWrapper(IMailMsgProperties *pIMailMsgProperties,
  179. PVOID pvContext)
  180. {
  181. // Testing : Cause intermittent failures in this queue
  182. if (fShouldFail(g_cPreRoutingQueueFailurePercent))
  183. {
  184. ((CAQSvrInst *)pvContext)->ScheduleInternalRetry(LI_TYPE_PENDING_ROUTING);
  185. return FALSE;
  186. }
  187. if (!((CAQSvrInst *)pvContext)->fRouteAndQueueMsg(pIMailMsgProperties))
  188. return !((CAQSvrInst *)pvContext)->fShouldRetryMessage(pIMailMsgProperties);
  189. else
  190. return TRUE;
  191. }
  192. //thin wrappers for handling internal asyncq queue failures
  193. BOOL fAsyncQHandleFailedMailMsg(IMailMsgProperties *pIMailMsgProperties,
  194. PVOID pvContext)
  195. {
  196. ((CAQSvrInst *)pvContext)->HandleAQFailure(AQ_FAILURE_INTERNAL_ASYNCQ,
  197. E_OUTOFMEMORY, pIMailMsgProperties);
  198. return TRUE;
  199. }
  200. BOOL fAsyncQHandleFailedMsgRef(CMsgRef *pmsgref, PVOID pvContext)
  201. {
  202. _ASSERT(pmsgref);
  203. if (pmsgref)
  204. pmsgref->RetryOnDelete();
  205. return TRUE;
  206. }
  207. //Thin wrapper(s) for AsyncQueueRetry - kick starting queues
  208. void LocalDeliveryRetry(PVOID pvContext)
  209. {
  210. ((CAQSvrInst *)pvContext)->AsyncQueueRetry(PRELOCAL_QUEUE_ID);
  211. }
  212. void CatRetry(PVOID pvContext)
  213. {
  214. ((CAQSvrInst *)pvContext)->AsyncQueueRetry(PRECAT_QUEUE_ID);
  215. }
  216. void RoutingRetry(PVOID pvContext)
  217. {
  218. ((CAQSvrInst *)pvContext)->AsyncQueueRetry(PREROUTING_QUEUE_ID);
  219. }
  220. void SubmitRetry(PVOID pvContext)
  221. {
  222. ((CAQSvrInst *)pvContext)->AsyncQueueRetry(PRESUBMIT_QUEUE_ID);
  223. }
  224. //---[ CAQSvrInst::fPreSubmissionQueueCompletionWrapper ]----------------------
  225. //
  226. //
  227. // Description:
  228. // Completion function for PreSubmit Queue
  229. // Parameters:
  230. // pIMailMsgPropeties IMailMsg to submit
  231. // pvContext Ptr to CAQSvrInst
  232. // Returns:
  233. // TRUE completed successfully
  234. // FALSE message needs to be retried
  235. // History:
  236. // 10/8/1999 - MikeSwa Created
  237. //
  238. //-----------------------------------------------------------------------------
  239. BOOL CAQSvrInst::fPreSubmissionQueueCompletionWrapper(
  240. IMailMsgProperties *pIMailMsgProperties,
  241. PVOID pvContext)
  242. {
  243. BOOL fRetry = FALSE;
  244. HRESULT hr = S_OK;
  245. CAQSvrInst *paqinst = (CAQSvrInst *)pvContext;
  246. _ASSERT(paqinst);
  247. // Testing : Cause intermittent failures in this queue
  248. if (fShouldFail(g_cPreSubmitQueueFailurePercent))
  249. {
  250. paqinst->ScheduleInternalRetry(LI_TYPE_PENDING_SUBMIT);
  251. return FALSE;
  252. }
  253. InterlockedDecrement((PLONG) &(paqinst->m_cCurrentMsgsPendingSubmit));
  254. hr = (paqinst->HrInternalSubmitMessage(pIMailMsgProperties));
  255. if (FAILED(hr))
  256. {
  257. if (paqinst->fShouldRetryMessage(pIMailMsgProperties))
  258. {
  259. fRetry = TRUE;
  260. InterlockedIncrement((PLONG) &(paqinst->m_cCurrentMsgsPendingSubmit));
  261. //
  262. // We need to kick off a retry as well for the presubmit queue
  263. //
  264. paqinst->ScheduleInternalRetry(LI_TYPE_PENDING_SUBMIT);
  265. }
  266. }
  267. SleepForPerfAnalysis(g_dwSubmitQueueSleepMilliseconds);
  268. return (!fRetry);
  269. }
  270. DEBUG_DO_IT(CAQSvrInst *g_paqinstLastDeleted = NULL;); //used to find the last deleted in CDB
  271. #define TRACE_COUNTERS \
  272. {\
  273. DebugTrace(0xC0DEC0DE, "INFO: %d msgs pending submission event for server 0x%08X", m_cCurrentMsgsPendingSubmitEvent, this); \
  274. DebugTrace(0xC0DEC0DE, "INFO: %d msgs pending pre-cat event for server 0x%08X", m_cCurrentMsgsPendingPreCatEvent, this); \
  275. DebugTrace(0xC0DEC0DE, "INFO: %d msgs pending post-cat event for server 0x%08X", m_cCurrentMsgsPendingPostCatEvent, this); \
  276. DebugTrace(0xC0DEC0DE, "INFO: %d msgs submited (total post cat) for delivery on server 0x%08X", m_cTotalMsgsQueued, this);\
  277. DebugTrace(0xC0DEC0DE, "INFO: %d msgs pending categorization for server 0x%08X", m_cCurrentMsgsPendingCat, this);\
  278. DebugTrace(0xC0DEC0DE, "INFO: %d msgs ack'd on server 0x%08X", m_cMsgsAcked, this);\
  279. DebugTrace(0xC0DEC0DE, "INFO: %d msgs ack'd for retry on server 0x%08X", m_cMsgsAckedRetry, this);\
  280. DebugTrace(0xC0DEC0DE, "INFO: %d msgs delivered local on server 0x%08X", m_cMsgsDeliveredLocal, this);\
  281. }
  282. // {407525AC-62B5-11d2-A694-00C04FA3490A}
  283. static const GUID g_guidDefaultRouter =
  284. { 0x407525ac, 0x62b5, 0x11d2, { 0xa6, 0x94, 0x0, 0xc0, 0x4f, 0xa3, 0x49, 0xa } };
  285. //GUID for local queue
  286. // {34E2DCCC-C91A-11d2-A6B1-00C04FA3490A}
  287. static const GUID g_guidLocalQueue =
  288. { 0x34e2dccc, 0xc91a, 0x11d2, { 0xa6, 0xb1, 0x0, 0xc0, 0x4f, 0xa3, 0x49, 0xa } };
  289. // GUID for presubmission queue
  290. // {D99AAC44-BEE9-4f9f-8D47-FB12E1443B9A}
  291. static const GUID g_guidPreSubmissionQueue =
  292. { 0xd99aac44, 0xbee9, 0x4f9f, { 0x8d, 0x47, 0xfb, 0x12, 0xe1, 0x44, 0x3b, 0x9a } };
  293. // GUID for precat queue
  294. // {B608067E-85DB-4f4e-9FE9-008A4072CCDC}
  295. static const GUID g_guidPreCatQueue =
  296. { 0xb608067e, 0x85db, 0x4f4e, { 0x9f, 0xe9, 0x0, 0x8a, 0x40, 0x72, 0xcc, 0xdc } };
  297. // GUID for prerouting queue
  298. // {F1B4C8FD-2928-427d-AC0D-23AF0DCFC31F}
  299. static const GUID g_guidPreRoutingQueue =
  300. { 0xf1b4c8fd, 0x2928, 0x427d, { 0xac, 0xd, 0x23, 0xaf, 0xd, 0xcf, 0xc3, 0x1f } };
  301. // GUID for post-DSN generation queue
  302. // {D076B629-6030-405f-ADC9-D888703E072E}
  303. static const GUID g_guidPostDSNGenerationQueue =
  304. { 0xd076b629, 0x6030, 0x405f, { 0xad, 0xc9, 0xd8, 0x88, 0x70, 0x3e, 0x7, 0x2e } };
  305. //---[ CAQSvrInst::CAQSvrInst ]------------------------------------------------
  306. //
  307. //
  308. // Description:
  309. // Class constuctor
  310. // Parameters:
  311. // SMTP_SERVER_INSTANCE *pssi - ptr to SMTP server instance object
  312. // pISMTPServer - interface used to handle local deliverys
  313. // Returns:
  314. // -
  315. //
  316. //-----------------------------------------------------------------------------
  317. CAQSvrInst::CAQSvrInst(DWORD dwServerInstance,
  318. ISMTPServer *pISMTPServer)
  319. : m_mglSupersedeIDs(&m_cSupersededMsgs),
  320. #if _MSC_VER >= 1200
  321. #pragma warning(push)
  322. #endif
  323. #pragma warning(disable:4355)
  324. m_asyncqPreLocalDeliveryQueue("LocalAsyncQueue", LOCAL_LINK_NAME,
  325. &g_guidLocalQueue, 0, this),
  326. m_asyncqPreSubmissionQueue("PreSubmissionQueue",
  327. PRESUBMISSION_QUEUE_NAME,
  328. &g_guidPreSubmissionQueue,
  329. 0,
  330. this),
  331. m_asyncqPreCatQueue("PreCatQueue",
  332. PRECAT_QUEUE_NAME,
  333. &g_guidPreCatQueue,
  334. 0,
  335. this),
  336. m_asyncqPreRoutingQueue("PreRoutingQueue",
  337. PREROUTING_QUEUE_NAME,
  338. &g_guidPreRoutingQueue,
  339. 0,
  340. this),
  341. m_asyncqPostDSNQueue("PostDSNGenerationQueue",
  342. POSTDSN_QUEUE_NAME,
  343. &g_guidPostDSNGenerationQueue,
  344. 0,
  345. this),
  346. m_dsnsink((IUnknown *)(IAQServerEvent *)this),
  347. #if _MSC_VER >= 1200
  348. #pragma warning(pop)
  349. #else
  350. #pragma warning(default:4355)
  351. #endif
  352. m_slPrivateData("CAQSvrInst",
  353. SHARE_LOCK_INST_TRACK_DEFAULTS |
  354. SHARE_LOCK_INST_TRACK_SHARED_THREADS |
  355. SHARE_LOCK_INST_TRACK_CONTENTION, 500)
  356. {
  357. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::CAQSvrInst");
  358. _ASSERT(pISMTPServer);
  359. m_dwSignature = CATMSGQ_SIG;
  360. m_dwFlavorSignature = g_dwFlavorSignature;
  361. m_cbClasses = g_cbClasses;
  362. //Init counters
  363. m_cTotalMsgsQueued = 0; //# of messages on dest queues (after fanout)
  364. m_cMsgsAcked = 0; //# of messages that have been acknowledged
  365. m_cMsgsAckedRetry = 0; //# of messages acked with retry all
  366. m_cMsgsDeliveredLocal= 0; //# of messages delivered to local store
  367. m_cCurrentMsgsPendingSubmitEvent = 0; //current # of messages in
  368. //submission event
  369. m_cCurrentMsgsPendingPreCatEvent = 0; // current # of messages in
  370. // precat event
  371. m_cCurrentMsgsPendingPostCatEvent = 0; //current # of messages in
  372. //post-categorization event
  373. m_cCurrentMsgsSubmitted = 0; //# total msgs in system
  374. m_cCurrentMsgsPendingCat = 0; //# Msgs that have not be categorized
  375. m_cCurrentMsgsPendingRouting = 0; //# Msgs that have been cat.
  376. //but have not been completely queued
  377. m_cCurrentMsgsPendingDelivery = 0; //# Msgs pending remote delivery
  378. m_cCurrentMsgsPendingLocal = 0; //# Msgs pending local delivery
  379. m_cCurrentMsgsPendingRetry = 0; //# Msgs with unsuccessful attempts
  380. m_cCurrentQueueMsgInstances = 0; //# of msgs instances pending
  381. //remote deliver (>= #msgs)
  382. m_cCurrentRemoteDestQueues = 0; //# of DestMsgQueues created
  383. m_cCurrentRemoteNextHops = 0; //# of Next Hop links created
  384. m_cCurrentRemoteNextHopsEnabled = 0; //# of links that can have connections
  385. m_cCurrentRemoteNextHopsPendingRetry = 0; //# of links pending retry
  386. m_cCurrentRemoteNextHopsPendingSchedule = 0; //# of links pending schedule
  387. m_cCurrentRemoteNextHopsFrozenByAdmin = 0; //# of links frozen by admin
  388. m_cTotalMsgsSubmitted = 0; //total # of messages submitted to AQ
  389. m_cTotalExternalMsgsSubmitted = 0; //Sumitted via an external interface
  390. m_cMsgsAckedRetryLocal = 0;
  391. m_cCurrentMsgsPendingLocalRetry = 0;
  392. m_cDMTRetries = 0;
  393. m_cTotalMsgsTURNETRNDelivered = 0;
  394. m_cCurrentMsgsPendingDeferredDelivery = 0;
  395. m_cCurrentResourceFailedMsgsPendingRetry = 0;
  396. m_cTotalMsgsBadmailed = 0;
  397. m_cBadmailNoRecipients = 0;
  398. m_cBadmailHopCountExceeded = 0;
  399. m_cBadmailFailureGeneral = 0;
  400. m_cBadmailBadPickupFile = 0;
  401. m_cBadmailEvent = 0;
  402. m_cBadmailNdrOfDsn = 0;
  403. m_cTotalDSNFailures = 0;
  404. m_cCurrentMsgsInLocalDelivery = 0;
  405. m_cTotalResetRoutes = 0;
  406. m_cCurrentPendingResetRoutes = 0;
  407. m_cCurrentMsgsPendingSubmit = 0;
  408. //Counters to keep track of the number of messages in Cat
  409. m_cCatMsgCalled = 0;
  410. m_cCatCompletionCalled = 0;
  411. //DSN Related counters
  412. m_cDelayedDSNs = 0;
  413. m_cNDRs = 0;
  414. m_cDeliveredDSNs = 0;
  415. m_cRelayedDSNs = 0;
  416. m_cExpandedDSNs = 0;
  417. m_cSupersededMsgs = 0; //number of messages superseded
  418. m_dwDelayExpireMinutes = g_dwDelayExpireMinutes;
  419. m_dwNDRExpireMinutes = g_dwNDRExpireMinutes;
  420. m_dwLocalDelayExpireMinutes = g_dwDelayExpireMinutes;
  421. m_dwLocalNDRExpireMinutes = g_dwNDRExpireMinutes;
  422. m_dwInitMask = 0;
  423. m_prstrDefaultDomain = NULL;
  424. m_prstrBadMailDir = NULL;
  425. m_prstrCopyNDRTo = NULL;
  426. m_prstrServerFQDN = NULL;
  427. m_dwDSNLanguageID = 0;
  428. m_dwDSNOptions = DSN_OPTIONS_DEFAULT;
  429. if (pISMTPServer)
  430. pISMTPServer->AddRef();
  431. m_pISMTPServer = pISMTPServer;
  432. // Get the ISMTPServerEx interface
  433. {
  434. HRESULT hr;
  435. m_pISMTPServerEx = NULL;
  436. hr = m_pISMTPServer->QueryInterface(
  437. IID_ISMTPServerEx,
  438. (LPVOID *)&m_pISMTPServerEx);
  439. if (FAILED(hr))
  440. {
  441. ErrorTrace((LPARAM) m_pISMTPServer,
  442. "Unable to QI for ISMTPServerEx 0x%08X",hr);
  443. m_pISMTPServerEx = NULL;
  444. }
  445. m_pISMTPServerAsync = NULL;
  446. hr = m_pISMTPServer->QueryInterface(
  447. IID_ISMTPServerAsync,
  448. (LPVOID *)&m_pISMTPServerAsync);
  449. if (FAILED(hr))
  450. {
  451. ErrorTrace((LPARAM) m_pISMTPServer,
  452. "Unable to QI for ISMTPServerAsync 0x%08X",hr);
  453. m_pISMTPServerAsync = NULL;
  454. }
  455. }
  456. m_hCat = INVALID_HANDLE_VALUE;
  457. m_dwServerInstance = dwServerInstance;
  458. m_pConnMgr = NULL;
  459. m_pIMessageRouterDefault = NULL;
  460. //Retry stuff
  461. m_dwFirstTierRetrySeconds = g_dwFirstTierRetrySeconds;
  462. m_cLocalRetriesPending = 0; //used for moderating local retries
  463. m_cCatRetriesPending = 0; //used for moderating cat retires
  464. m_cRoutingRetriesPending = 0; //used for moderating routing retries
  465. m_cSubmitRetriesPending = 0; //used for moderating submit retries
  466. m_pIRouterReset = NULL;
  467. //Add to global list of virtual servers
  468. m_liVirtualServers.Blink = &g_liVirtualServers;
  469. g_pslGlobals->ExclusiveLock();
  470. m_liVirtualServers.Flink = g_liVirtualServers.Flink;
  471. g_liVirtualServers.Flink->Blink = &m_liVirtualServers;
  472. g_liVirtualServers.Flink = &m_liVirtualServers;
  473. g_pslGlobals->ExclusiveUnlock();
  474. //
  475. // Assume (until proven otherwise) mailmsg returns the handle count
  476. //
  477. m_fMailMsgReportsNumHandles = TRUE;
  478. m_defq.Initialize(this);
  479. TraceFunctLeave();
  480. }
  481. //---[ CAQSvrInst::~CAQSvrInst ]--------------------------------------------
  482. //
  483. //
  484. // Description:
  485. // Class destuctor
  486. // Parameters:
  487. // -
  488. // Returns:
  489. // -
  490. //
  491. //-----------------------------------------------------------------------------
  492. CAQSvrInst::~CAQSvrInst()
  493. {
  494. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::~CAQSvrInst");
  495. //make sure that all cleanup was done
  496. HrDeinitialize(); //can be called multiple times
  497. if (m_pISMTPServer)
  498. m_pISMTPServer->Release();
  499. if (m_pISMTPServerEx)
  500. m_pISMTPServerEx->Release();
  501. if (m_pISMTPServerAsync)
  502. m_pISMTPServerAsync->Release();
  503. if (m_pConnMgr)
  504. {
  505. m_pConnMgr->Release();
  506. m_pConnMgr = NULL;
  507. }
  508. if (m_prstrDefaultDomain)
  509. m_prstrDefaultDomain->Release();
  510. if (m_prstrBadMailDir)
  511. m_prstrBadMailDir->Release();
  512. if (m_prstrCopyNDRTo)
  513. m_prstrCopyNDRTo->Release();
  514. if (m_prstrServerFQDN)
  515. m_prstrServerFQDN->Release();
  516. //Take out of list of global list
  517. g_pslGlobals->ExclusiveLock();
  518. m_liVirtualServers.Flink->Blink = m_liVirtualServers.Blink;
  519. m_liVirtualServers.Blink->Flink = m_liVirtualServers.Flink;
  520. g_pslGlobals->ExclusiveUnlock();
  521. m_liVirtualServers.Flink = NULL;
  522. m_liVirtualServers.Flink = NULL;
  523. MARK_SIG_AS_DELETED(m_dwSignature);
  524. DEBUG_DO_IT(g_paqinstLastDeleted = this;);
  525. TraceFunctLeave();
  526. }
  527. //---[ CAQSvrInst::HrInitialize ]--------------------------------------------
  528. //
  529. //
  530. // Description:
  531. // Initialization of CAQSvrInst virtual server instance object.
  532. // Parameters:
  533. // IN szUserName User name to log on DS with
  534. // IN szDomainName Domain name to log on to DS with
  535. // IN szPassword Password to authenticate to DS with
  536. // IN pServiceStatusFn Server status callback function
  537. // IN pvServiceContext Context to pass back for callback function
  538. // Returns:
  539. // S_OK on success
  540. //
  541. //-----------------------------------------------------------------------------
  542. HRESULT CAQSvrInst::HrInitialize(
  543. IN LPSTR szUserName,
  544. IN LPSTR szDomainName,
  545. IN LPSTR szPassword,
  546. IN PSRVFN pServiceStatusFn,
  547. IN PVOID pvServiceContext)
  548. {
  549. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HrInitialize");
  550. HRESULT hr = S_OK;
  551. IMailTransportSetRouterReset *pISetRouterReset = NULL;
  552. //
  553. // init SEO
  554. //
  555. hr = m_CSMTPSeoMgr.HrInit(m_dwServerInstance);
  556. if (FAILED(hr)) {
  557. goto Exit;
  558. }
  559. m_pIMessageRouterDefault = new CAQDefaultMessageRouter(
  560. (GUID *) &g_guidDefaultRouter, this);
  561. if (!m_pIMessageRouterDefault)
  562. {
  563. hr = E_OUTOFMEMORY;
  564. goto Exit;
  565. }
  566. m_fmq.Initialize(this);
  567. hr = CAQRpcSvrInst::HrInitializeAQServerInstanceRPC(this,
  568. m_dwServerInstance,
  569. m_pISMTPServer);
  570. if (FAILED(hr))
  571. goto Exit;
  572. //Initialize Message Categorization
  573. hr = CatInit(
  574. NULL,
  575. pServiceStatusFn,
  576. pvServiceContext,
  577. m_pISMTPServer,
  578. (IAdvQueueDomainType *) this,
  579. m_dwServerInstance,
  580. &m_hCat);
  581. if (FAILED(hr))
  582. {
  583. m_hCat = INVALID_HANDLE_VALUE;
  584. goto Exit;
  585. }
  586. //Pass RouterReset Interface to ISMTPServer
  587. if (m_pISMTPServer)
  588. {
  589. hr = m_pISMTPServer->QueryInterface(IID_IMailTransportSetRouterReset,
  590. (void **) &pISetRouterReset);
  591. if (SUCCEEDED(hr))
  592. {
  593. m_dwInitMask |= CMQ_INIT_ROUTER_RESET;
  594. _ASSERT(pISetRouterReset);
  595. hr = pISetRouterReset->RegisterResetInterface(m_dwServerInstance,
  596. (IMailTransportRouterReset *) this);
  597. _ASSERT(SUCCEEDED(hr)); //something is wrong if this failed
  598. pISetRouterReset->Release();
  599. pISetRouterReset = NULL;
  600. }
  601. hr = m_pISMTPServer->QueryInterface(IID_IMailTransportRouterReset,
  602. (void **) &m_pIRouterReset);
  603. if (FAILED(hr))
  604. m_pIRouterReset = NULL;
  605. }
  606. hr = m_dmt.HrInitialize(this, &m_asyncqPreLocalDeliveryQueue,
  607. &m_asyncqPreCatQueue, &m_asyncqPreRoutingQueue,
  608. &m_asyncqPreSubmissionQueue);
  609. if (FAILED(hr))
  610. goto Exit;
  611. m_dwInitMask |= CMQ_INIT_DMT;
  612. hr = m_dct.HrInit();
  613. if (FAILED(hr))
  614. goto Exit;
  615. m_dwInitMask |= CMQ_INIT_DCT;
  616. m_pConnMgr = new CConnMgr;
  617. if (NULL == m_pConnMgr)
  618. {
  619. hr = E_OUTOFMEMORY;
  620. goto Exit;
  621. }
  622. hr = m_pConnMgr->HrInitialize(this);
  623. if (FAILED(hr))
  624. goto Exit;
  625. m_dwInitMask |= CMQ_INIT_CONMGR;
  626. hr = m_dsnsink.HrInitialize();
  627. if (FAILED(hr))
  628. goto Exit;
  629. m_dwInitMask |= CMQ_INIT_DSN;
  630. hr = m_asyncqPreCatQueue.HrInitialize(g_cMaxSyncCatQThreads,
  631. g_cItemsPerCatQAsyncThread,
  632. g_cItemsPerCatQSyncThread,
  633. this,
  634. fPreCatQueueCompletionWrapper,
  635. fAsyncQHandleFailedMailMsg,
  636. NULL,
  637. g_cMaxPendingCat);
  638. if (FAILED(hr))
  639. goto Exit;
  640. m_dwInitMask |= CMQ_INIT_PRECATQ;
  641. hr = m_asyncqPreLocalDeliveryQueue.HrInitialize(g_cMaxSyncLocalQThreads,
  642. g_cItemsPerLocalQAsyncThread,
  643. g_cItemsPerLocalQSyncThread,
  644. this,
  645. fPreLocalDeliveryQueueCompletionWrapper,
  646. fAsyncQHandleFailedMsgRef,
  647. HrWalkPreLocalQueueForDSN,
  648. g_cMaxPendingLocal);
  649. if (FAILED(hr))
  650. goto Exit;
  651. m_dwInitMask |= CMQ_INIT_PRELOCQ;
  652. hr = m_asyncqPostDSNQueue.HrInitialize(g_cMaxSyncPostDSNQThreads,
  653. g_cItemsPerPostDSNQAsyncThread,
  654. g_cItemsPerPostDSNQSyncThread,
  655. this,
  656. fPostDSNQueueCompletionWrapper,
  657. fAsyncQHandleFailedMailMsg,
  658. NULL);
  659. if (FAILED(hr))
  660. goto Exit;
  661. m_dwInitMask |= CMQ_INIT_POSTDSNQ;
  662. hr = m_asyncqPreRoutingQueue.HrInitialize(g_cMaxSyncRoutingQThreads,
  663. g_cItemsPerRoutingQAsyncThread,
  664. g_cItemsPerRoutingQSyncThread,
  665. this,
  666. fPreRoutingQueueCompletionWrapper,
  667. fAsyncQHandleFailedMailMsg,
  668. NULL);
  669. if (FAILED(hr))
  670. goto Exit;
  671. m_dwInitMask |= CMQ_INIT_ROUTINGQ;
  672. hr = m_asyncqPreSubmissionQueue.HrInitialize(g_cMaxSyncSubmitQThreads,
  673. g_cItemsPerSubmitQAsyncThread,
  674. g_cItemsPerSubmitQSyncThread,
  675. this,
  676. fPreSubmissionQueueCompletionWrapper,
  677. fAsyncQHandleFailedMailMsg,
  678. NULL);
  679. if (FAILED(hr))
  680. goto Exit;
  681. m_dwInitMask |= CMQ_INIT_SUBMISSIONQ;
  682. hr = m_aqwWorkQueue.HrInitialize(g_cItemsPerWorkQAsyncThread);
  683. if (FAILED(hr))
  684. goto Exit;
  685. m_dwInitMask |= CMQ_INIT_WORKQ;
  686. m_dwInitMask |= CMQ_INIT_OK; //everything was initialized
  687. // create the router object
  688. hr = HrTriggerInitRouter();
  689. Exit:
  690. TraceFunctLeave();
  691. return hr;
  692. }
  693. //---[ CAQSvrInst::HrDeinitialize() ]----------------------------------------
  694. //
  695. //
  696. // Description:
  697. // Signals server shutdown
  698. // Parameters:
  699. // -
  700. // Returns:
  701. // S_OK on success
  702. // Whatever error codes are generated during the shutdown process
  703. //
  704. //-----------------------------------------------------------------------------
  705. HRESULT CAQSvrInst::HrDeinitialize()
  706. {
  707. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HrDeinitialize");
  708. HRESULT hr = S_OK;
  709. HRESULT hrTmp = S_OK;
  710. DWORD i = 0;
  711. IMailTransportSetRouterReset *pISetRouterReset = NULL;
  712. #ifdef DEBUG
  713. DWORD cLocalDeliveryShutdownSeconds = 60;
  714. #endif
  715. //
  716. // Tell categorizer to stop categorizing before we block on the
  717. // shutdown lock.
  718. //
  719. if (INVALID_HANDLE_VALUE != m_hCat)
  720. {
  721. CatPrepareForShutdown(m_hCat);
  722. }
  723. //
  724. // We have hit this assert a few times due to NT stress failures.
  725. // This is only really useful if at least one messsage has been
  726. // sent. The assert is basically useless if we hit it in KD during
  727. // NT stress runs.
  728. //
  729. if (m_cTotalMsgsSubmitted)
  730. m_dbgcnt.StartCountdown();
  731. ServerStopHintFunction();
  732. //Get Exclusive shutdown lock
  733. SignalShutdown();
  734. ServerStopHintFunction();
  735. //Turn off RPC for this instance
  736. hrTmp = CAQRpcSvrInst::HrDeinitializeAQServerInstanceRPC(this, m_dwServerInstance);
  737. if (FAILED(hrTmp))
  738. {
  739. ErrorTrace((LPARAM) this,
  740. "Error shutting down Aqueue RPC hr - 0x%08X", hrTmp);
  741. if (SUCCEEDED(hr))
  742. hr = hrTmp;
  743. }
  744. // wait for all outstanding local deliveries to complete
  745. while (!(m_asyncqPreLocalDeliveryQueue.fNoPendingAsyncCompletions())) {
  746. _ASSERT(--cLocalDeliveryShutdownSeconds > 0);
  747. ServerStopHintFunction();
  748. Sleep(1000);
  749. }
  750. ServerStopHintFunction();
  751. m_fmq.Deinitialize();
  752. ServerStopHintFunction();
  753. m_defq.Deinitialize();
  754. m_dwInitMask &= ~CMQ_INIT_DCT; //no de-initialize function to call
  755. //stop any pending categorization
  756. ServerStopHintFunction();
  757. if (INVALID_HANDLE_VALUE != m_hCat)
  758. {
  759. m_dbgcnt.SuspendCountdown();
  760. hrTmp = CatCancel(m_hCat);
  761. if FAILED(hrTmp)
  762. {
  763. ErrorTrace((LPARAM) this,
  764. "ERROR: Categorization shutdown error hr - 0x%08X", hrTmp);
  765. if (SUCCEEDED(hr))
  766. hr = hrTmp;
  767. }
  768. //shutdow message categorization
  769. CatTerm(m_hCat);
  770. m_dbgcnt.ResetCountdown();
  771. m_hCat = INVALID_HANDLE_VALUE;
  772. }
  773. //Tell ISMTPServer to set RouterReset Interface to NULL
  774. ServerStopHintFunction();
  775. if (m_pISMTPServer)
  776. {
  777. hr = m_pISMTPServer->QueryInterface(IID_IMailTransportSetRouterReset,
  778. (void **) &pISetRouterReset);
  779. if (SUCCEEDED(hr))
  780. {
  781. m_dwInitMask &= ~CMQ_INIT_ROUTER_RESET;
  782. _ASSERT(pISetRouterReset);
  783. hr = pISetRouterReset->RegisterResetInterface(m_dwServerInstance,
  784. NULL);
  785. _ASSERT(SUCCEEDED(hr)); //something is wrong if this failed
  786. pISetRouterReset->Release();
  787. pISetRouterReset = NULL;
  788. }
  789. }
  790. ServerStopHintFunction();
  791. if (m_pIRouterReset)
  792. {
  793. m_pIRouterReset->Release();
  794. m_pIRouterReset = NULL;
  795. }
  796. ServerStopHintFunction();
  797. if (CMQ_INIT_DMT & m_dwInitMask)
  798. {
  799. m_dwInitMask ^= CMQ_INIT_DMT;
  800. hrTmp = m_dmt.HrDeinitialize();
  801. if (FAILED(hrTmp) && SUCCEEDED(hr))
  802. hr = hrTmp;
  803. }
  804. //Deinitializing the connection manager will also release the retry
  805. //sink to make all it's callbacks.
  806. ServerStopHintFunction();
  807. if (NULL != m_pConnMgr)
  808. {
  809. if (CMQ_INIT_CONMGR & m_dwInitMask)
  810. {
  811. _ASSERT(m_pISMTPServer);
  812. m_dwInitMask ^= CMQ_INIT_CONMGR;
  813. hrTmp = m_pConnMgr->HrDeinitialize();
  814. if (FAILED(hrTmp) && SUCCEEDED(hr))
  815. hr = hrTmp;
  816. }
  817. m_pConnMgr->Release();
  818. m_pConnMgr = NULL;
  819. }
  820. //deinitialize pre-local delivery queue
  821. ServerStopHintFunction();
  822. if (CMQ_INIT_PRELOCQ & m_dwInitMask)
  823. {
  824. hrTmp = m_asyncqPreLocalDeliveryQueue.HrDeinitialize(
  825. HrWalkMsgRefQueueForShutdown, this);
  826. if (FAILED(hrTmp) && SUCCEEDED(hr))
  827. hr = hrTmp;
  828. m_dwInitMask ^= CMQ_INIT_PRELOCQ;
  829. }
  830. //deinitialize pre-cat delivery queue
  831. ServerStopHintFunction();
  832. if (CMQ_INIT_PRECATQ & m_dwInitMask)
  833. {
  834. hrTmp = m_asyncqPreCatQueue.HrDeinitialize(
  835. HrWalkMailMsgQueueForShutdown, this);
  836. if (FAILED(hrTmp) && SUCCEEDED(hr))
  837. hr = hrTmp;
  838. m_dwInitMask ^= CMQ_INIT_PRECATQ;
  839. }
  840. //deinitialize post DNS queue
  841. ServerStopHintFunction();
  842. if (CMQ_INIT_POSTDSNQ & m_dwInitMask)
  843. {
  844. hrTmp = m_asyncqPostDSNQueue.HrDeinitialize(
  845. HrWalkMailMsgQueueForShutdown, this);
  846. if (FAILED(hrTmp) && SUCCEEDED(hr))
  847. hr = hrTmp;
  848. m_dwInitMask ^= CMQ_INIT_POSTDSNQ;
  849. }
  850. //deinitialize pre-routing queue
  851. ServerStopHintFunction();
  852. if (CMQ_INIT_ROUTINGQ & m_dwInitMask)
  853. {
  854. hrTmp = m_asyncqPreRoutingQueue.HrDeinitialize(
  855. HrWalkMailMsgQueueForShutdown, this);
  856. if (FAILED(hrTmp) && SUCCEEDED(hr))
  857. hr = hrTmp;
  858. m_dwInitMask ^= CMQ_INIT_ROUTINGQ;
  859. }
  860. //deinitialize pre-submit queue
  861. ServerStopHintFunction();
  862. if (CMQ_INIT_SUBMISSIONQ & m_dwInitMask)
  863. {
  864. hrTmp = m_asyncqPreSubmissionQueue.HrDeinitialize(
  865. HrWalkMailMsgQueueForShutdown, this);
  866. if (FAILED(hrTmp) && SUCCEEDED(hr))
  867. hr = hrTmp;
  868. m_dwInitMask ^= CMQ_INIT_SUBMISSIONQ;
  869. }
  870. ServerStopHintFunction();
  871. m_mglSupersedeIDs.Deinitialize(this);
  872. ServerStopHintFunction();
  873. if (CMQ_INIT_WORKQ & m_dwInitMask)
  874. {
  875. hrTmp = m_aqwWorkQueue.HrDeinitialize(this);
  876. if (FAILED(hrTmp) && SUCCEEDED(hr))
  877. hr = hrTmp;
  878. m_dwInitMask ^= CMQ_INIT_WORKQ;
  879. }
  880. //the following bits don't have specific delinitialize functions
  881. m_dwInitMask &= ~(CMQ_INIT_DSN | CMQ_INIT_OK);
  882. ServerStopHintFunction();
  883. if (m_pIMessageRouterDefault)
  884. {
  885. m_pIMessageRouterDefault->Release();
  886. m_pIMessageRouterDefault = NULL;
  887. }
  888. // let SEO do it's cleanup
  889. m_CSMTPSeoMgr.Deinit();
  890. m_dbgcnt.EndCountdown();
  891. _ASSERT((!m_dwInitMask) || FAILED(hr));
  892. TraceFunctLeave();
  893. return hr;
  894. }
  895. //---[ CAQSvrInst::HrGetIConnectionManager ]---------------------------------
  896. //
  897. //
  898. // Description:
  899. // Returns the IConnectionManager interface for this AdvancedQueuing instance
  900. // Parameters:
  901. // OUT ppIConnectionManger Returned interface
  902. // Returns:
  903. // S_OK on success
  904. //
  905. //-----------------------------------------------------------------------------
  906. HRESULT CAQSvrInst::HrGetIConnectionManager(
  907. OUT IConnectionManager **ppIConnectionManager)
  908. {
  909. HRESULT hr = S_OK;
  910. _ASSERT(ppIConnectionManager);
  911. if (!fTryShutdownLock())
  912. {
  913. hr = AQUEUE_E_SHUTDOWN;
  914. goto Exit;
  915. }
  916. m_pConnMgr->AddRef();
  917. *ppIConnectionManager = m_pConnMgr;
  918. ShutdownUnlock();
  919. Exit:
  920. return hr;
  921. }
  922. //---[ CAQSvrInst::cCountMsgsForHandleThrottling ]-----------------------------
  923. //
  924. //
  925. // Description:
  926. // returns the number of messages in the system that
  927. // is used elsewhere to decide to start/stop handle throttling
  928. //
  929. //
  930. // Parameters:
  931. // pIMailMsgProperties pointer to MailMsg interface to query
  932. //
  933. // Returns:
  934. // DWORD returned is:
  935. //
  936. // - With Windows2000 RTM mailmsg.dll:
  937. // count of all messages in the system
  938. //
  939. // - With Windows2000 SP1 mailmsg.dll:
  940. // count of open property stream handles
  941. // OR
  942. // count of all messages in the system
  943. // if the former can't be obtained
  944. //
  945. // History:
  946. // 1/28/00 aszafer Created
  947. //-----------------------------------------------------------------------------
  948. #ifndef IMMPID_MPV_TOTAL_OPEN_PROPERTY_STREAM_HANDLES
  949. #define IMMPID_MPV_TOTAL_OPEN_PROPERTY_STREAM_HANDLES 0x3004
  950. #endif
  951. DWORD CAQSvrInst::cCountMsgsForHandleThrottling(IN IMailMsgProperties *pIMailMsgProperties)
  952. {
  953. HRESULT hr = MAILMSG_E_PROPNOTFOUND; //Count as failure if we do not call
  954. DWORD dwStreamOpenHandlesCount = 0;
  955. TraceFunctEnterEx((LPARAM) this, "Entering CAQSvrInst::cCountMsgsForHandleThrottling");
  956. //
  957. // We should never call into mailmsg if we know we do not have the correct version.
  958. // This will load the property stream and cause us to potentially access the
  959. // properties in an unsafe way.
  960. //
  961. if (m_fMailMsgReportsNumHandles && pIMailMsgProperties)
  962. {
  963. hr = pIMailMsgProperties->GetDWORD(
  964. IMMPID_MPV_TOTAL_OPEN_PROPERTY_STREAM_HANDLES,
  965. &dwStreamOpenHandlesCount);
  966. if(FAILED(hr))
  967. {
  968. m_fMailMsgReportsNumHandles = FALSE;
  969. //must be RTM version of mailmsg.dll
  970. DebugTrace((LPARAM) this, "GetDWORD(IMMPID*OPEN_PROPERTY_STREAM_HANDLES) failed hr %08lx", hr);
  971. DebugTrace((LPARAM) this, "returning g_cIMsgInSystem + m_cCurrentMsgsPendingSubmit");
  972. }
  973. }
  974. TraceFunctLeaveEx((LPARAM) this);
  975. return SUCCEEDED(hr) ? dwStreamOpenHandlesCount : g_cIMsgInSystem + m_cCurrentMsgsPendingSubmit ;
  976. }
  977. //---[ CAQSvrInst::QueueMsgForLocalDelivery ]----------------------------------
  978. //
  979. //
  980. // Description:
  981. // Queues a single message for local delivery
  982. // Parameters:
  983. // IN pmsgref Message Ref to deliver locally
  984. // Returns:
  985. // -
  986. // History:
  987. // 1/26/99 - MikeSwa Created
  988. //
  989. //-----------------------------------------------------------------------------
  990. void CAQSvrInst::QueueMsgForLocalDelivery(CMsgRef *pmsgref, BOOL fLocalLink)
  991. {
  992. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::QueueMsgForLocalDelivery");
  993. HRESULT hr = S_OK;
  994. CAQStats aqstat;
  995. CLinkMsgQueue *plmq = NULL;
  996. //
  997. // Get the stats from the msgref
  998. //
  999. pmsgref->GetStatsForMsg(&aqstat);
  1000. //
  1001. // Get the local link and update the stats
  1002. //
  1003. plmq = m_dmt.plmqGetLocalLink();
  1004. if (plmq)
  1005. {
  1006. hr = plmq->HrNotify(&aqstat, TRUE);
  1007. if (FAILED(hr))
  1008. {
  1009. ErrorTrace((LPARAM) this,
  1010. "HrNotify failed... local stats innaccurate 0x%08X", hr);
  1011. hr = S_OK;
  1012. }
  1013. }
  1014. InterlockedIncrement((PLONG) &m_cCurrentMsgsPendingLocal);
  1015. hr = m_asyncqPreLocalDeliveryQueue.HrQueueRequest(pmsgref);
  1016. if (FAILED(hr))
  1017. {
  1018. hr = plmq->HrNotify(&aqstat, FALSE);
  1019. pmsgref->RetryOnDelete();
  1020. InterlockedDecrement((PLONG) &m_cCurrentMsgsPendingLocal);
  1021. }
  1022. //
  1023. // Make sure we release the local link if we got it
  1024. //
  1025. if (plmq)
  1026. plmq->Release();
  1027. TraceFunctLeave();
  1028. }
  1029. //---[ CAQSvrInst::fRouteAndQueueMsg ]-----------------------------------------
  1030. //
  1031. //
  1032. // Description:
  1033. // Add a Categorized Message to the CMT to be queue for delivery
  1034. // Parameters:
  1035. // IN pIMailMsgProperties Msg to routing and queue for delivery
  1036. // Returns:
  1037. // TRUE if message has been successfully routed and queued for delivery
  1038. // (or if errors have been handled internally)
  1039. // FALSE if message needs to be requeued for a later retry
  1040. //
  1041. //-----------------------------------------------------------------------------
  1042. BOOL CAQSvrInst::fRouteAndQueueMsg(IN IMailMsgProperties *pIMailMsgProperties)
  1043. {
  1044. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::fRouteAndQueueMsg");
  1045. HRESULT hr = S_OK;
  1046. HRESULT hrTmp = S_OK;
  1047. DWORD cDomains = 0; //number of domains message will be deliver to
  1048. DWORD cQueues = 0; //number of queues for the message
  1049. DWORD i = 0; //loop counter
  1050. DWORD cLocalRecips = 0;
  1051. DWORD cRemoteRecips = 0;
  1052. DWORD dwDMTVersion = 0;
  1053. CMsgRef *pmsgref = NULL;
  1054. CDestMsgQueue **rgpdmq = NULL;
  1055. BOOL fLocked = FALSE;
  1056. BOOL fRoutingLock = FALSE;
  1057. BOOL fLocal = FALSE;
  1058. BOOL fRemote = FALSE;
  1059. BOOL fOnDMQ = FALSE;
  1060. BOOL fDMTLocked = FALSE;
  1061. BOOL fKeepTrying = TRUE;
  1062. BOOL fGotMsgType = FALSE;
  1063. BOOL fReturn = TRUE;
  1064. DWORD dwMessageType = 0;
  1065. IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL;
  1066. IMailMsgRecipients *pIRecipList = NULL;
  1067. IMessageRouter *pIMessageRouter = NULL;
  1068. _ASSERT(CATMSGQ_SIG == m_dwSignature);
  1069. _ASSERT(pIMailMsgProperties);
  1070. if (NULL == pIMailMsgProperties)
  1071. {
  1072. hr = E_INVALIDARG;
  1073. ErrorTrace((LPARAM) this, "NULL pIMailMsgProperties");
  1074. goto Exit;
  1075. }
  1076. hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgQueueMgmt, (PVOID *) &pIMailMsgQueueMgmt);
  1077. if (FAILED(hr))
  1078. {
  1079. ErrorTrace((LPARAM) pIMailMsgProperties,
  1080. "Unable to QI for IID_IMailMsgQueueMgmt");
  1081. goto Exit;
  1082. }
  1083. hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgRecipients, (PVOID *) &pIRecipList);
  1084. if (FAILED(hr))
  1085. {
  1086. ErrorTrace((LPARAM) pIMailMsgProperties,
  1087. "Unable to QI for IID_IMailMsgRecipients");
  1088. goto Exit;
  1089. }
  1090. //get a shared lock to guard against shutdown.
  1091. if (!fTryShutdownLock())
  1092. {
  1093. DebugTrace((LPARAM) pIMailMsgProperties,
  1094. "Shutdown detecting while routing a message... bailing");
  1095. hr = AQUEUE_E_SHUTDOWN;
  1096. goto Exit;
  1097. }
  1098. fLocked = TRUE;
  1099. hr = pIRecipList->DomainCount(&cDomains);
  1100. if (FAILED(hr))
  1101. {
  1102. ErrorTrace((LPARAM) pIMailMsgProperties,
  1103. "Unable to get a domain count from the message - hr 0x08X", hr);
  1104. goto Exit;
  1105. }
  1106. if (!cDomains) //and hence no recipients
  1107. {
  1108. DebugTrace((LPARAM) pIMailMsgProperties,
  1109. "No domains on message - turfing message");
  1110. //This could be a completely valid case (like an empty DL)
  1111. //In this case we just turf the message and act as if everything is fine
  1112. DecMsgsInSystem(); //update our counters
  1113. HandleBadMail(pIMailMsgProperties, TRUE, NULL, AQUEUE_E_NO_RECIPIENTS, FALSE);
  1114. InterlockedIncrement((PLONG) &m_cBadmailNoRecipients);
  1115. //Delete the message
  1116. HrDeleteIMailMsg(pIMailMsgProperties);
  1117. hr = S_OK;
  1118. goto Exit;
  1119. }
  1120. //Check Message to see if there are unresolved recipients to NDR
  1121. hr = HrNDRUnresolvedRecipients(pIMailMsgProperties, pIRecipList);
  1122. if (FAILED(hr))
  1123. {
  1124. HandleAQFailure(AQ_FAILURE_CANNOT_NDR_UNRESOLVED_RECIPS, hr, pIMailMsgProperties);
  1125. ErrorTrace((LPARAM) this, "ERROR: Unable to NDR message - hr 0x%08X", hr);
  1126. //just drop message for now... we cannot let it continue until this succeeds
  1127. hr = S_OK;
  1128. goto Exit;
  1129. }
  1130. if (S_FALSE == hr)
  1131. {
  1132. //There is no work to be done for this message - delete it
  1133. HrDeleteIMailMsg(pIMailMsgProperties);
  1134. DebugTrace((LPARAM) pIMailMsgProperties,
  1135. "INFO: Deleting message after NDRing all unresolved recips");
  1136. hr = S_OK;
  1137. DecMsgsInSystem(); //update our counters
  1138. goto Exit;
  1139. }
  1140. rgpdmq = (CDestMsgQueue **) pvMalloc(cDomains * sizeof(CDestMsgQueue *));
  1141. if (NULL == rgpdmq)
  1142. {
  1143. ErrorTrace((LPARAM) pIMailMsgProperties,
  1144. "Unable to alloc array of %d Queues", cDomains);
  1145. hr = E_OUTOFMEMORY;
  1146. goto Exit;
  1147. }
  1148. RoutingShareLock();
  1149. fRoutingLock = TRUE;
  1150. hr = HrTriggerGetMessageRouter(pIMailMsgProperties, &pIMessageRouter);
  1151. if (FAILED(hr))
  1152. {
  1153. ErrorTrace((LPARAM) pIMailMsgProperties,
  1154. "Unable to get message router - HR 0x%08X", hr);
  1155. goto Exit;
  1156. }
  1157. hr = pIMessageRouter->GetMessageType(pIMailMsgProperties, &dwMessageType);
  1158. if (FAILED(hr))
  1159. {
  1160. ErrorTrace((LPARAM) pIMailMsgProperties,
  1161. "Unable to get message type - HR 0x%08X", hr);
  1162. goto Exit;
  1163. }
  1164. //We own a reference to the message type now
  1165. fGotMsgType = TRUE;
  1166. pmsgref = new((DWORD) cDomains) CMsgRef(cDomains, pIMailMsgQueueMgmt, pIMailMsgProperties,
  1167. this, dwMessageType, pIMessageRouter->GetTransportSinkID());
  1168. if (NULL == pmsgref)
  1169. {
  1170. ErrorTrace((LPARAM) pIMailMsgProperties,
  1171. "Unable to allocate CMsgRef for %d domains", cDomains);
  1172. hr = E_OUTOFMEMORY;
  1173. goto Exit;
  1174. }
  1175. //Loop until we get consistant info on where to queue message (based on
  1176. //DMT version number).
  1177. while (fKeepTrying)
  1178. {
  1179. dwDMTVersion = m_dmt.dwGetDMTVersion();
  1180. hr = pmsgref->HrInitialize(pIRecipList, pIMessageRouter, dwMessageType,
  1181. &cLocalRecips, &cRemoteRecips, &cQueues, rgpdmq);
  1182. if (FAILED(hr))
  1183. {
  1184. if (HRESULT_FROM_WIN32(ERROR_RETRY) == hr)
  1185. {
  1186. //Some sort of config/routing change... we need to retry the
  1187. //message
  1188. fGotMsgType = FALSE;
  1189. hr = HrReGetMessageType(pIMailMsgProperties,
  1190. pIMessageRouter, &dwMessageType);
  1191. if (FAILED(hr))
  1192. {
  1193. ErrorTrace((LPARAM) pIMailMsgProperties,
  1194. "HrReGetMessageType failed with hr - 0x%08X", hr);
  1195. goto Exit;
  1196. }
  1197. fGotMsgType = TRUE;
  1198. }
  1199. else //It was a genuine error... bail
  1200. {
  1201. ErrorTrace((LPARAM) pIMailMsgProperties,
  1202. "CMsgRef::HrInitialze failed with hr 0x%08X", hr);
  1203. goto Exit;
  1204. }
  1205. }
  1206. //Before Enqueuing Messages or firing off local delivery... throttle usage count
  1207. if (g_cMaxIMsgHandlesThreshold < cCountMsgsForHandleThrottling(pIMailMsgProperties))
  1208. {
  1209. DebugTrace((LPARAM) 0xC0DEC0DE, "INFO: Closing IMsg Content - %d messsages in queue", cCountMsgsForHandleThrottling(pIMailMsgProperties));
  1210. //bounce usage count off of zero
  1211. pIMailMsgQueueMgmt->ReleaseUsage();
  1212. pIMailMsgQueueMgmt->AddUsage();
  1213. }
  1214. m_dmt.AquireDMTShareLock();
  1215. if (m_dmt.dwGetDMTVersion() != dwDMTVersion)
  1216. {
  1217. DebugTrace((LPARAM) this,
  1218. "DMT version change: was %d, now %d",
  1219. dwDMTVersion, m_dmt.dwGetDMTVersion());
  1220. //DMT Version changed... that means that our queues may have been
  1221. //removed from the DMT. Time to retry
  1222. m_dmt.ReleaseDMTShareLock();
  1223. _ASSERT(fKeepTrying);
  1224. InterlockedIncrement((PLONG) &m_cDMTRetries);
  1225. fGotMsgType = FALSE;
  1226. hr = HrReGetMessageType(pIMailMsgProperties, pIMessageRouter,
  1227. &dwMessageType);
  1228. if (FAILED(hr))
  1229. {
  1230. ErrorTrace((LPARAM) pIMailMsgProperties,
  1231. "HrReGetMessageType failed with hr - 0x%08X", hr);
  1232. goto Exit;
  1233. }
  1234. fGotMsgType = TRUE;
  1235. continue; //Try again
  1236. }
  1237. fKeepTrying = FALSE;
  1238. fDMTLocked = TRUE;
  1239. //enqueue the message reference for each destination it is going to
  1240. for (i = 0; i < cQueues; i++)
  1241. {
  1242. if (NULL != rgpdmq[i])
  1243. {
  1244. InterlockedIncrement(&m_cTotalMsgsQueued);
  1245. //enqueue message and assign message type to first enqueue
  1246. hr = rgpdmq[i]->HrEnqueueMsg(pmsgref, !fOnDMQ);
  1247. if (FAILED(hr))
  1248. {
  1249. ErrorTrace((LPARAM) pIMailMsgProperties,
  1250. "HrEnqueueMsg failed - 0x%08X", hr);
  1251. InterlockedDecrement(&m_cTotalMsgsQueued);
  1252. goto Exit;
  1253. }
  1254. fOnDMQ = TRUE;
  1255. //
  1256. // Check and see if this queue is explicitly routed remote.
  1257. // It may be a gateway delivery queue.
  1258. //
  1259. if (!fRemote)
  1260. fRemote = rgpdmq[i]->fIsRemote();
  1261. }
  1262. else
  1263. {
  1264. fLocal = TRUE;
  1265. }
  1266. }
  1267. _ASSERT(fDMTLocked);
  1268. m_dmt.ReleaseDMTShareLock();
  1269. fDMTLocked = FALSE;
  1270. if (fLocal) //kick off local delivery
  1271. {
  1272. QueueMsgForLocalDelivery(pmsgref, FALSE);
  1273. }
  1274. }
  1275. Exit:
  1276. if (fDMTLocked)
  1277. {
  1278. m_dmt.ReleaseDMTShareLock();
  1279. fDMTLocked = FALSE;
  1280. }
  1281. // Check and process any special queues in the DMT
  1282. if (fRoutingLock && fLocked)
  1283. m_dmt.ProcessSpecialLinks(m_dwDelayExpireMinutes, TRUE);
  1284. //
  1285. // Must Release IMessageRouter before starting shutdown since the
  1286. // IMessageRouter sink may have a reference to us (via
  1287. // IRouterReset)
  1288. //
  1289. if (NULL != pIMessageRouter)
  1290. {
  1291. if (!fOnDMQ && fGotMsgType) //we have a reference to this message type
  1292. {
  1293. hrTmp = pIMessageRouter->ReleaseMessageType(dwMessageType, 1);
  1294. _ASSERT(SUCCEEDED(hrTmp));
  1295. }
  1296. pIMessageRouter->Release();
  1297. }
  1298. if (fRoutingLock)
  1299. RoutingShareUnlock();
  1300. if (fLocked)
  1301. ShutdownUnlock();
  1302. if (pIMailMsgQueueMgmt)
  1303. pIMailMsgQueueMgmt->Release();
  1304. if (pIRecipList)
  1305. pIRecipList->Release();
  1306. if (NULL != rgpdmq)
  1307. FreePv(rgpdmq);
  1308. if (fRemote && pmsgref)
  1309. {
  1310. InterlockedIncrement((PLONG) &m_cCurrentMsgsPendingDelivery);
  1311. //msgref needs to decrement the remote count when it is released
  1312. pmsgref->CountMessageInRemoteTotals();
  1313. }
  1314. if (NULL != pmsgref)
  1315. {
  1316. if (FAILED(hr) && (fOnDMQ || fLocal))
  1317. {
  1318. //If we have a msgref and it has been queued, we must
  1319. //wait until all other references are released to retry it
  1320. pmsgref->RetryOnDelete();
  1321. hr = S_OK; //don't let caller retry
  1322. }
  1323. pmsgref->Release();
  1324. }
  1325. //if we did not succeed, msgs is still in pre-routing queue
  1326. if (FAILED(hr))
  1327. {
  1328. fReturn = FALSE;
  1329. //kick off retry if necessary
  1330. ScheduleInternalRetry(LI_TYPE_PENDING_ROUTING);
  1331. }
  1332. else
  1333. InterlockedDecrement((PLONG) &m_cCurrentMsgsPendingRouting);
  1334. TRACE_COUNTERS;
  1335. SleepForPerfAnalysis(g_dwRoutingQueueSleepMilliseconds);
  1336. TraceFunctLeave();
  1337. return fReturn;
  1338. }
  1339. //---[ CAQSvrInst::HrAckMsg ]------------------------------------------------
  1340. //
  1341. //
  1342. // Description:
  1343. // Acknowledge the (un)delivery of a message. Will call the msgref AckMsg,
  1344. // which will requeue it to the appropriate queues
  1345. // Parameters:
  1346. // pMsgAck Pointer to Message Ack structure
  1347. // fLocal TRUE if this is an ack for a local delivery
  1348. // Returns:
  1349. // S_OK on success
  1350. // ERROR_INVALID_HANDLE if the context handle was invalid
  1351. //-----------------------------------------------------------------------------
  1352. HRESULT CAQSvrInst::HrAckMsg(IN MessageAck *pMsgAck, BOOL fLocal)
  1353. {
  1354. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HrAckMsg");
  1355. HRESULT hr = S_OK;
  1356. DWORD i = 0; //loop counter
  1357. CDeliveryContext *pdcntxt = NULL;
  1358. _ASSERT(pMsgAck);
  1359. _ASSERT(pMsgAck->pvMsgContext);
  1360. pdcntxt = (CDeliveryContext *) pMsgAck->pvMsgContext;
  1361. if ((NULL == pdcntxt) || !(pdcntxt->FVerifyHandle(pMsgAck->pIMailMsgProperties)))
  1362. {
  1363. hr = ERROR_INVALID_HANDLE;
  1364. goto Exit;
  1365. }
  1366. if (!fLocal)
  1367. {
  1368. InterlockedIncrement(&m_cMsgsAcked);
  1369. if (MESSAGE_STATUS_RETRY & pMsgAck->dwMsgStatus)
  1370. InterlockedIncrement((PLONG) &m_cMsgsAckedRetry);
  1371. }
  1372. else //local
  1373. {
  1374. if (MESSAGE_STATUS_RETRY & pMsgAck->dwMsgStatus)
  1375. InterlockedIncrement((PLONG) &m_cMsgsAckedRetryLocal);
  1376. }
  1377. hr = pdcntxt->HrAckMessage(pMsgAck);
  1378. if (FAILED(hr))
  1379. goto Exit;
  1380. Exit:
  1381. //clean up all the things we have used here
  1382. if (pdcntxt)
  1383. pdcntxt->Recycle();
  1384. TRACE_COUNTERS;
  1385. TraceFunctLeave();
  1386. return hr;
  1387. }
  1388. //---[ CAQSvrInst::HrNotify ]------------------------------------------------------------
  1389. //
  1390. //
  1391. // Description:
  1392. // Passes notification off to Connection Mangaer
  1393. // Parameters:
  1394. //
  1395. // Returns:
  1396. //
  1397. //
  1398. //-----------------------------------------------------------------------------
  1399. HRESULT CAQSvrInst::HrNotify(IN CAQStats *paqstats, BOOL fAdd)
  1400. {
  1401. HRESULT hr = S_OK;
  1402. if (!fTryShutdownLock())
  1403. {
  1404. hr = AQUEUE_E_SHUTDOWN;
  1405. goto Exit;
  1406. }
  1407. hr = m_pConnMgr->HrNotify(paqstats, fAdd);
  1408. ShutdownUnlock();
  1409. Exit:
  1410. return hr;
  1411. }
  1412. //---[ CAQSvrInst::HrGetInternalDomainInfo ]----------------------------------
  1413. //
  1414. //
  1415. // Description:
  1416. // Expose ability to get internal Domain Info to internal components
  1417. // Parameters:
  1418. // IN cbDomainnameLength Length of string to search for
  1419. // IN szDomainName Domain Name to search for
  1420. // OUT ppIntDomainInfo Domain info returned (must be released)
  1421. // Returns:
  1422. // S_OK if match is found
  1423. // AQUEUE_E_INVALID_DOMAIN if no match is found
  1424. //
  1425. //-----------------------------------------------------------------------------
  1426. HRESULT CAQSvrInst::HrGetInternalDomainInfo(
  1427. IN DWORD cbDomainNameLength,
  1428. IN LPSTR szDomainName,
  1429. OUT CInternalDomainInfo **ppDomainInfo)
  1430. {
  1431. HRESULT hr = S_OK;
  1432. if (!fTryShutdownLock())
  1433. {
  1434. hr = AQUEUE_E_SHUTDOWN;
  1435. goto Exit;
  1436. }
  1437. _ASSERT(CMQ_INIT_DCT & m_dwInitMask);
  1438. hr = m_dct.HrGetInternalDomainInfo(cbDomainNameLength, szDomainName, ppDomainInfo);
  1439. ShutdownUnlock();
  1440. Exit:
  1441. return hr;
  1442. }
  1443. //---[ CAQSvrInst::HrGetDefaultDomainInfo ]----------------------------
  1444. //
  1445. //
  1446. // Description:
  1447. // Expose ability to get internal default Domain Info to internal components
  1448. // Parameters:
  1449. // OUT ppIntDomainInfo Domain info returned (must be released)
  1450. // Returns:
  1451. // S_OK if found
  1452. // AQUEUE_E_INVALID_DOMAIN if not found
  1453. //
  1454. //-----------------------------------------------------------------------------
  1455. HRESULT CAQSvrInst::HrGetDefaultDomainInfo(
  1456. OUT CInternalDomainInfo **ppDomainInfo)
  1457. {
  1458. HRESULT hr = S_OK;
  1459. if (!fTryShutdownLock())
  1460. {
  1461. hr = AQUEUE_E_SHUTDOWN;
  1462. goto Exit;
  1463. }
  1464. _ASSERT(CMQ_INIT_DCT & m_dwInitMask);
  1465. hr = m_dct.HrGetDefaultDomainInfo(ppDomainInfo);
  1466. ShutdownUnlock();
  1467. Exit:
  1468. return hr;
  1469. }
  1470. //---[ CAQSvrInst::HrGetDomainEntry ]----------------------------------------
  1471. //
  1472. //
  1473. // Description:
  1474. // Get Domain Entry
  1475. // Parameters:
  1476. // IN cbDomainnameLength Length of string to search for
  1477. // IN szDomainName Domain Name to search for
  1478. // OUT ppdentry Domain Entry for domain (from DMT)
  1479. // Returns:
  1480. // S_OK on success
  1481. // AQUEUE_E_INVALID_DOMAIN if domain is not found
  1482. //
  1483. //-----------------------------------------------------------------------------
  1484. HRESULT CAQSvrInst::HrGetDomainEntry(IN DWORD cbDomainNameLength,
  1485. IN LPSTR szDomainName,
  1486. OUT CDomainEntry **ppdentry)
  1487. {
  1488. HRESULT hr = S_OK;
  1489. BOOL fLocked = FALSE;
  1490. _ASSERT(cbDomainNameLength);
  1491. _ASSERT(szDomainName);
  1492. _ASSERT(ppdentry);
  1493. if (!fTryShutdownLock())
  1494. {
  1495. hr = AQUEUE_E_SHUTDOWN;
  1496. goto Exit;
  1497. }
  1498. fLocked = TRUE;
  1499. hr = m_dmt.HrGetDomainEntry(cbDomainNameLength, szDomainName, ppdentry);
  1500. if (FAILED(hr))
  1501. goto Exit;
  1502. Exit:
  1503. if (fLocked)
  1504. ShutdownUnlock();
  1505. return hr;
  1506. }
  1507. //---[ CAQSvrInst::HrIterateDMTSubDomains ]----------------------------------------
  1508. //
  1509. //
  1510. // Description:
  1511. // Get Domain Entry
  1512. // Parameters:
  1513. // IN cbDomainnameLength Length of string to search for
  1514. // IN szDomainName Domain Name to search for
  1515. // IN pfn Iterator function
  1516. // IN pvcontext Context passed to each call
  1517. // Returns:
  1518. // S_OK on success
  1519. // AQUEUE_E_INVALID_DOMAIN if domain is not found
  1520. //
  1521. //--------------------------------------------------------------------------------
  1522. HRESULT CAQSvrInst::HrIterateDMTSubDomains(IN LPSTR szDomainName,
  1523. IN DWORD cbDomainNameLength,
  1524. IN DOMAIN_ITR_FN pfn,
  1525. IN PVOID pvContext)
  1526. {
  1527. HRESULT hr = S_OK;
  1528. BOOL fLocked = FALSE;
  1529. DOMAIN_STRING strDomain;
  1530. _ASSERT(cbDomainNameLength);
  1531. _ASSERT(szDomainName);
  1532. _ASSERT(pfn);
  1533. _ASSERT(pvContext);
  1534. if (!fTryShutdownLock())
  1535. {
  1536. hr = AQUEUE_E_SHUTDOWN;
  1537. goto Exit;
  1538. }
  1539. fLocked = TRUE;
  1540. strDomain.Length = (USHORT) cbDomainNameLength;
  1541. strDomain.MaximumLength = (USHORT) cbDomainNameLength;
  1542. strDomain.Buffer = szDomainName;
  1543. hr = m_dmt.HrIterateOverSubDomains(&strDomain, pfn,pvContext);
  1544. if (FAILED(hr))
  1545. goto Exit;
  1546. Exit:
  1547. if (fLocked)
  1548. ShutdownUnlock();
  1549. return hr;
  1550. }
  1551. //---[ CAQSvrInst::HrIterateDMTSubDomains ]----------------------------------------
  1552. //
  1553. //
  1554. // Description:
  1555. // Get Domain Entry
  1556. // Parameters:
  1557. // IN cbDomainnameLength Length of string to search for
  1558. // IN szDomainName Domain Name to search for
  1559. // IN pfn Iterator function
  1560. // IN pvcontext Context passed to each call
  1561. // Returns:
  1562. // S_OK on success
  1563. // AQUEUE_E_INVALID_DOMAIN if domain is not found
  1564. //
  1565. //--------------------------------------------------------------------------------
  1566. HRESULT CAQSvrInst::HrIterateDCTSubDomains(IN LPSTR szDomainName,
  1567. IN DWORD cbDomainNameLength,
  1568. IN DOMAIN_ITR_FN pfn,
  1569. IN PVOID pvContext)
  1570. {
  1571. HRESULT hr = S_OK;
  1572. BOOL fLocked = FALSE;
  1573. DOMAIN_STRING strDomain;
  1574. _ASSERT(cbDomainNameLength);
  1575. _ASSERT(szDomainName);
  1576. _ASSERT(pfn);
  1577. _ASSERT(pvContext);
  1578. if (!fTryShutdownLock())
  1579. {
  1580. hr = AQUEUE_E_SHUTDOWN;
  1581. goto Exit;
  1582. }
  1583. fLocked = TRUE;
  1584. strDomain.Length = (USHORT) cbDomainNameLength;
  1585. strDomain.MaximumLength = (USHORT) cbDomainNameLength;
  1586. strDomain.Buffer = szDomainName;
  1587. hr = m_dct.HrIterateOverSubDomains(&strDomain, pfn,pvContext);
  1588. if (FAILED(hr))
  1589. goto Exit;
  1590. Exit:
  1591. if (fLocked)
  1592. ShutdownUnlock();
  1593. return hr;
  1594. }
  1595. //---[ CAQSvrInst::HrTriggerGetMessageRouter ]--------------------------------------
  1596. //
  1597. //
  1598. // Description:
  1599. // Wrapper function that signals the MAIL_TRANSPORT_ON_GET_ROUTER_EVENT
  1600. // Parameters:
  1601. // IN pIMailMsgProperties - IMailMsgProperties to get
  1602. // OUT pIMessageRouter
  1603. //
  1604. // Returns:
  1605. //
  1606. // History:
  1607. // 5/20/98 - MikeSwa Created
  1608. // jstamerj 1998/07/10 18:30:41: Implemented Server event
  1609. //
  1610. //-----------------------------------------------------------------------------
  1611. HRESULT CAQSvrInst::HrTriggerGetMessageRouter(
  1612. IN IMailMsgProperties *pIMailMsgProperties,
  1613. OUT IMessageRouter **ppIMessageRouter)
  1614. {
  1615. TraceFunctEnterEx((LPARAM)this, "CAQSvrInst::HrTriggerGetMessageRouter");
  1616. HRESULT hr = S_OK;
  1617. BOOL fLocked = FALSE;
  1618. _ASSERT(ppIMessageRouter);
  1619. _ASSERT(pIMailMsgProperties);
  1620. _ASSERT(m_pIMessageRouterDefault);
  1621. if (!fTryShutdownLock())
  1622. {
  1623. hr = AQUEUE_E_SHUTDOWN;
  1624. goto Exit;
  1625. }
  1626. fLocked = TRUE;
  1627. if(m_pISMTPServer) {
  1628. EVENTPARAMS_ROUTER EventParams;
  1629. //
  1630. // Initialiez EventParams
  1631. //
  1632. EventParams.dwVirtualServerID = m_dwServerInstance;
  1633. EventParams.pIMailMsgProperties = pIMailMsgProperties;
  1634. EventParams.pIMessageRouter = NULL;
  1635. EventParams.pIRouterReset = m_pIRouterReset;
  1636. EventParams.pIRoutingEngineDefault = this;
  1637. hr = TriggerServerEvent(
  1638. SMTP_MAILTRANSPORT_GET_ROUTER_FOR_MESSAGE_EVENT,
  1639. &EventParams);
  1640. if(SUCCEEDED(hr)) {
  1641. if(EventParams.pIMessageRouter) {
  1642. //
  1643. // The implementor of GetMessageRouter returned
  1644. // IMessageRouter with a refcount of one for us
  1645. //
  1646. *ppIMessageRouter = EventParams.pIMessageRouter;
  1647. } else {
  1648. //
  1649. // The server event succeeded, but no sink supplied an
  1650. // IMessageRouter (including default functionality)
  1651. //
  1652. hr = E_FAIL;
  1653. }
  1654. }
  1655. } else {
  1656. ErrorTrace((LPARAM)this, "Unable to trigger event to GetMessageRouter; using default");
  1657. //
  1658. // Try calling our default (builtin) GetMessageRouter
  1659. //
  1660. hr = GetMessageRouter(
  1661. pIMailMsgProperties, //IN IMsg
  1662. NULL, //IN pIMessageRouter (Current)
  1663. ppIMessageRouter); //OUT ppIMessageRouter (New)
  1664. }
  1665. Exit:
  1666. if (fLocked)
  1667. ShutdownUnlock();
  1668. TraceFunctLeaveEx((LPARAM)this);
  1669. return hr;
  1670. }
  1671. //---[ CAQSvrInst::HrTriggerLogEvent ]-----------------------------------------
  1672. //
  1673. //
  1674. // Description:
  1675. // Wrapper function that signals the SMTP_LOG_EVENT event
  1676. //
  1677. //-----------------------------------------------------------------------------
  1678. HRESULT CAQSvrInst::HrTriggerLogEvent(
  1679. IN DWORD idMessage,
  1680. IN WORD idCategory,
  1681. IN WORD cSubstrings,
  1682. IN LPCSTR *rgszSubstrings,
  1683. IN WORD wType,
  1684. IN DWORD errCode,
  1685. IN WORD iDebugLevel,
  1686. IN LPCSTR szKey,
  1687. IN DWORD dwOptions,
  1688. IN DWORD iMessageString,
  1689. IN HMODULE hModule)
  1690. {
  1691. TraceFunctEnterEx((LPARAM)this, "CAQSvrInst::HrTriggerLogEvent");
  1692. HRESULT hr = S_OK;
  1693. if (m_pISMTPServerEx) {
  1694. hr = m_pISMTPServerEx->TriggerLogEvent(
  1695. idMessage,
  1696. idCategory,
  1697. cSubstrings,
  1698. rgszSubstrings,
  1699. wType,
  1700. errCode,
  1701. iDebugLevel,
  1702. szKey,
  1703. dwOptions,
  1704. iMessageString,
  1705. hModule);
  1706. } else {
  1707. //
  1708. // If we do not have at least W2K SP2... we will not have this
  1709. // interface.
  1710. //
  1711. ErrorTrace((LPARAM) this,
  1712. "Need W2KSP2: Unable to log event %d with erCode %d");
  1713. }
  1714. TraceFunctLeaveEx((LPARAM)this);
  1715. return hr;
  1716. }
  1717. //---[ CAQSvrInst::HrTriggerInitRouter ]---------------------------------------
  1718. //
  1719. //
  1720. // Description:
  1721. // Wrapper function that signals the MAIL_TRANSPORT_ON_GET_ROUTER_EVENT
  1722. // but only has it create a new router object
  1723. // Parameters:
  1724. // none
  1725. //
  1726. // Returns:
  1727. //
  1728. // History:
  1729. // 5/20/98 - MikeSwa Created
  1730. // jstamerj 1998/07/10 18:30:41: Implemented Server event
  1731. //
  1732. //-----------------------------------------------------------------------------
  1733. HRESULT CAQSvrInst::HrTriggerInitRouter() {
  1734. TraceFunctEnter("CAQSvrInst::HrTriggerInitRouter");
  1735. HRESULT hr = S_OK;
  1736. BOOL fLocked = FALSE;
  1737. if (!fTryShutdownLock())
  1738. {
  1739. hr = AQUEUE_E_SHUTDOWN;
  1740. goto Exit;
  1741. }
  1742. fLocked = TRUE;
  1743. if (m_pISMTPServer) {
  1744. EVENTPARAMS_ROUTER EventParams;
  1745. //
  1746. // Initialiez EventParams
  1747. //
  1748. EventParams.dwVirtualServerID = m_dwServerInstance;
  1749. EventParams.pIMailMsgProperties = NULL;
  1750. EventParams.pIMessageRouter = NULL;
  1751. EventParams.pIRouterReset = m_pIRouterReset;
  1752. EventParams.pIRoutingEngineDefault = NULL;
  1753. hr = TriggerServerEvent(
  1754. SMTP_MAILTRANSPORT_GET_ROUTER_FOR_MESSAGE_EVENT,
  1755. &EventParams);
  1756. } else {
  1757. hr = S_OK;
  1758. }
  1759. Exit:
  1760. if (fLocked)
  1761. ShutdownUnlock();
  1762. TraceFunctLeaveEx((LPARAM)this);
  1763. return hr;
  1764. }
  1765. //---[ CAQSvrInst::QueryInterface ]------------------------------------------
  1766. //
  1767. //
  1768. // Description:
  1769. // QueryInterface for IAdvQueue
  1770. // Parameters:
  1771. //
  1772. // Returns:
  1773. // S_OK on success
  1774. //
  1775. // Notes:
  1776. // This implementation makes it possible for any server component to get
  1777. // the IAdvQueueConfig interface.
  1778. //
  1779. // History:
  1780. // 7/29/98 - MikeSwa Modified (added IAdvQueueDomainType)
  1781. //
  1782. //-----------------------------------------------------------------------------
  1783. STDMETHODIMP CAQSvrInst::QueryInterface(REFIID riid, LPVOID * ppvObj)
  1784. {
  1785. HRESULT hr = S_OK;
  1786. if (!ppvObj)
  1787. {
  1788. hr = E_INVALIDARG;
  1789. goto Exit;
  1790. }
  1791. if (IID_IUnknown == riid)
  1792. {
  1793. *ppvObj = static_cast<IAdvQueue *>(this);
  1794. }
  1795. else if (IID_IAdvQueue == riid)
  1796. {
  1797. *ppvObj = static_cast<IAdvQueue *>(this);
  1798. }
  1799. else if (IID_IAdvQueueConfig == riid)
  1800. {
  1801. *ppvObj = static_cast<IAdvQueueConfig *>(this);
  1802. }
  1803. else if (IID_IAdvQueueDomainType == riid)
  1804. {
  1805. *ppvObj = static_cast<IAdvQueueDomainType *>(this);
  1806. }
  1807. else if (IID_IAdvQueueAdmin == riid)
  1808. {
  1809. *ppvObj = static_cast<IAdvQueueAdmin *>(this);
  1810. }
  1811. else if (IID_IMailTransportRouterSetLinkState == riid)
  1812. {
  1813. *ppvObj = static_cast<IMailTransportRouterSetLinkState *>(this);
  1814. }
  1815. else if (IID_IAQServerEvent == riid)
  1816. {
  1817. *ppvObj = static_cast<IAQServerEvent*>(this);
  1818. }
  1819. else
  1820. {
  1821. *ppvObj = NULL;
  1822. hr = E_NOINTERFACE;
  1823. goto Exit;
  1824. }
  1825. static_cast<IUnknown *>(*ppvObj)->AddRef();
  1826. Exit:
  1827. return hr;
  1828. }
  1829. //---[ CAQSvrInst::SubmitMessage ]---------------------------------------------
  1830. //
  1831. //
  1832. // Description:
  1833. // External function to submit messages for delivery
  1834. // Parameters:
  1835. // pIMailMsgProperties Msg to submit for delivery
  1836. // Returns:
  1837. // S_OK always
  1838. // History:
  1839. // 10/7/1999 - MikeSwa Moved from inline function
  1840. //
  1841. //-----------------------------------------------------------------------------
  1842. STDMETHODIMP CAQSvrInst::SubmitMessage(IN IMailMsgProperties *pIMailMsgProperties)
  1843. {
  1844. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::SubmitMessage");
  1845. HRESULT hr = S_OK;
  1846. if (NULL == pIMailMsgProperties)
  1847. {
  1848. ErrorTrace((LPARAM)NULL,
  1849. "SubmitMessage called with NULL pIMailMsgProperties");
  1850. return E_INVALIDARG;
  1851. }
  1852. InterlockedIncrement((PLONG) &m_cTotalExternalMsgsSubmitted);
  1853. InterlockedIncrement((PLONG) &m_cCurrentMsgsPendingSubmit);
  1854. // Before we queue this message, stamp the message submission time
  1855. hr = HrSetSubmissionTimeIfNecessary(pIMailMsgProperties);
  1856. if (FAILED(hr))
  1857. {
  1858. ErrorTrace((LPARAM) this,
  1859. "ERROR: Unable to stamp submission time on message - hr 0x%08x", hr);
  1860. goto Error_Exit;
  1861. }
  1862. hr = m_asyncqPreSubmissionQueue.HrQueueRequest(
  1863. pIMailMsgProperties, FALSE,
  1864. cCountMsgsForHandleThrottling(pIMailMsgProperties));
  1865. if (FAILED(hr))
  1866. {
  1867. goto Error_Exit;
  1868. }
  1869. Exit:
  1870. TraceFunctLeave();
  1871. return S_OK;
  1872. Error_Exit:
  1873. InterlockedDecrement((PLONG) &m_cCurrentMsgsPendingSubmit);
  1874. HandleAQFailure(AQ_FAILURE_INTERNAL_ASYNCQ, hr, pIMailMsgProperties);
  1875. goto Exit;
  1876. }
  1877. //---[ CAQSvrInst::HrInternalSubmitMessage ]-----------------------------------
  1878. //
  1879. //
  1880. // Description:
  1881. // Implements IAdvQueue::SubmitMessage
  1882. // Parameters:
  1883. // pIMailMsgProperties... Messaage to queue
  1884. // Returns:
  1885. // S_OK on success
  1886. //
  1887. //-----------------------------------------------------------------------------
  1888. HRESULT CAQSvrInst::HrInternalSubmitMessage(
  1889. IMailMsgProperties *pIMailMsgProperties)
  1890. {
  1891. TraceFunctEnterEx((LPARAM) pIMailMsgProperties,
  1892. "CAQSvrInst::SubmitMessage");
  1893. _ASSERT(CATMSGQ_SIG == m_dwSignature);
  1894. HRESULT hr = S_OK;
  1895. DWORD dwMsgStatus = MP_STATUS_SUCCESS;
  1896. EVENTPARAMS_SUBMISSION Params;
  1897. FILETIME ftDeferred;
  1898. DWORD cbProp = 0;
  1899. DWORD dwContext = 0;
  1900. _ASSERT(pIMailMsgProperties);
  1901. if (NULL == pIMailMsgProperties)
  1902. {
  1903. ErrorTrace((LPARAM)NULL,
  1904. "SubmitMessage called with NULL pIMailMsgProperties");
  1905. return E_INVALIDARG;
  1906. }
  1907. //Check and see if we need to request a retry for failed msgs
  1908. m_fmq.StartProcessingIfNecessary();
  1909. hr = pIMailMsgProperties->GetProperty(IMMPID_MP_DEFERRED_DELIVERY_FILETIME,
  1910. sizeof(FILETIME), &cbProp,
  1911. (BYTE *) &ftDeferred);
  1912. if (SUCCEEDED(hr) && !fInPast(&ftDeferred, &dwContext))
  1913. {
  1914. //Defer delivery until a later time if deferred delivery time is
  1915. //present, and in the past
  1916. hr = S_OK;
  1917. InterlockedIncrement((PLONG) &m_cCurrentMsgsPendingDeferredDelivery);
  1918. m_defq.Enqueue(pIMailMsgProperties, &ftDeferred);
  1919. goto Exit;
  1920. }
  1921. // Set submission time for this message
  1922. hr = HrSetSubmissionTimeIfNecessary(pIMailMsgProperties);
  1923. if (FAILED(hr))
  1924. {
  1925. ErrorTrace((LPARAM) this,
  1926. "ERROR: Unable to stamp submission time on message - hr 0x%08x",
  1927. hr);
  1928. return hr;
  1929. }
  1930. InterlockedIncrement((PLONG) &m_cTotalMsgsSubmitted);
  1931. //
  1932. // Set the message status (if currently unset)
  1933. //
  1934. hr = pIMailMsgProperties->GetDWORD(
  1935. IMMPID_MP_MESSAGE_STATUS,
  1936. &dwMsgStatus);
  1937. if( (hr == MAILMSG_E_PROPNOTFOUND) ||
  1938. (g_fResetMessageStatus) )
  1939. {
  1940. //
  1941. // Initialize the message status
  1942. //
  1943. hr = pIMailMsgProperties->PutDWORD(
  1944. IMMPID_MP_MESSAGE_STATUS,
  1945. MP_STATUS_SUCCESS);
  1946. dwMsgStatus = MP_STATUS_SUCCESS;
  1947. }
  1948. //
  1949. //$$TODO: Jump from here to whatever state dwMsgStatus indicates
  1950. //
  1951. MSG_TRACK_INFO msgTrackInfo;
  1952. ZeroMemory( &msgTrackInfo, sizeof( msgTrackInfo ) );
  1953. msgTrackInfo.dwEventId = MTE_BEGIN_SUBMIT_MESSAGE;
  1954. m_pISMTPServer->WriteLog( &msgTrackInfo, pIMailMsgProperties, NULL, NULL );
  1955. //
  1956. // AddRef this object here; release in completion
  1957. //
  1958. AddRef();
  1959. Params.pIMailMsgProperties = pIMailMsgProperties;
  1960. Params.pfnCompletion = MailTransport_Completion_SubmitMessage;
  1961. Params.pCCatMsgQueue = this;
  1962. pIMailMsgProperties->AddRef();
  1963. InterlockedIncrement((PLONG) &m_cCurrentMsgsPendingSubmitEvent);
  1964. TRACE_COUNTERS;
  1965. //
  1966. // Call server event if we can and if dwMsgStatus does not
  1967. // indicate the mssage has already been submitted
  1968. //
  1969. if(SUCCEEDED(hr) &&
  1970. (m_pISMTPServer) &&
  1971. (dwMsgStatus < MP_STATUS_SUBMITTED))
  1972. {
  1973. hr = TriggerServerEvent(
  1974. SMTP_MAILTRANSPORT_SUBMISSION_EVENT,
  1975. &Params);
  1976. DebugTrace((LPARAM)pIMailMsgProperties,
  1977. "TriggerServerEvent returned hr %08lx", hr);
  1978. }
  1979. //
  1980. // If TriggerServerEvent returned an error OR m_pISMTPServer is
  1981. // null, or the message was already submitted, call the event
  1982. // completion routine directly
  1983. //
  1984. if((m_pISMTPServer == NULL) ||
  1985. FAILED(hr) ||
  1986. (dwMsgStatus >= MP_STATUS_SUBMITTED))
  1987. {
  1988. DebugTrace((LPARAM)this, "Skipping the submission event");
  1989. // Call the SEO Dispatcher completion routine directly so we
  1990. // don't loose this mail...
  1991. hr = SubmissionEventCompletion(S_OK, &Params);
  1992. }
  1993. //
  1994. // SEO dispatcher will call the completion routine
  1995. // (MailTransport_Completion_SubmitMessage) regardless wether or
  1996. // not all the sinks work synchronously or async. Because of
  1997. // this, this function is now done.
  1998. //
  1999. Exit:
  2000. TraceFunctLeaveEx((LPARAM) pIMailMsgProperties);
  2001. return hr;
  2002. }
  2003. //---[ CAQSvrInst::HandleFailedMessage ]--------------------------------------
  2004. //
  2005. //
  2006. // Description:
  2007. // Handles a failed message from SMTP... usually by NDRing the message or
  2008. // by treating the message as badmail.
  2009. //
  2010. // NOTE: Message or input file will be deleted by this operation.
  2011. // Parameters:
  2012. // pIMailMsgProperties MailMsg that needs to be handles
  2013. // fUseIMailMsgProperties Use the IMailMsg if set, else use the szFilename,
  2014. // szFileName use the filename if no msg
  2015. // dwFailureReasons One of the failure reasons described in aqueue.idl
  2016. // hrFailureCode Additional information that describes a failure
  2017. // code encountered by SMTP.
  2018. // Returns:
  2019. // S_OK on success
  2020. // E_INVALIDARG if pIMailMsgProperties is NULL
  2021. // History:
  2022. // 7/28/98 - MikeSwa Created
  2023. // 10/14/98 - MikeSwa Added filename string for badmail
  2024. //
  2025. //-----------------------------------------------------------------------------
  2026. STDMETHODIMP CAQSvrInst::HandleFailedMessage(
  2027. IN IMailMsgProperties *pIMailMsgProperties,
  2028. IN BOOL fUseIMailMsgProperties,
  2029. IN LPSTR szFileName,
  2030. IN DWORD dwFailureReason,
  2031. IN HRESULT hrFailureCode)
  2032. {
  2033. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HandleFailedMessage");
  2034. HRESULT hr = S_OK;
  2035. HRESULT hrBadMail = hrFailureCode;
  2036. DWORD iCurrentDomain = 0;
  2037. DWORD cDomains = 0;
  2038. IMailMsgRecipients *pIMailMsgRecipients = NULL;
  2039. CDSNParams dsnparams;
  2040. BOOL fNDR = TRUE; //FALSE -> Badmail handling
  2041. SET_DEBUG_DSN_CONTEXT(dsnparams, __LINE__);
  2042. dsnparams.dwStartDomain = 0;
  2043. dsnparams.dwDSNActions = DSN_ACTION_FAILURE_ALL;
  2044. dsnparams.pIMailMsgProperties = pIMailMsgProperties;
  2045. dsnparams.hrStatus = hrFailureCode;
  2046. MSG_TRACK_INFO msgTrackInfo;
  2047. ZeroMemory( &msgTrackInfo, sizeof( msgTrackInfo ) );
  2048. msgTrackInfo.dwEventId = MTE_AQ_FAILED_MESSAGE;
  2049. msgTrackInfo.dwRcptReportStatus = dwFailureReason;
  2050. m_pISMTPServer->WriteLog( &msgTrackInfo, pIMailMsgProperties, NULL, NULL );
  2051. //
  2052. // Switch over the various general failure reasons and handle them as
  2053. // appropriate.
  2054. //
  2055. switch(dwFailureReason)
  2056. {
  2057. case MESSAGE_FAILURE_HOP_COUNT_EXCEEDED:
  2058. //
  2059. // Attempt to NDR
  2060. //
  2061. _ASSERT(pIMailMsgProperties);
  2062. dsnparams.hrStatus = AQUEUE_E_MAX_HOP_COUNT_EXCEEDED;
  2063. fNDR = TRUE;
  2064. break;
  2065. case MESSAGE_FAILURE_GENERAL:
  2066. //
  2067. // Attempt to NDR
  2068. //
  2069. fNDR = TRUE;
  2070. break;
  2071. case MESSAGE_FAILURE_CAT:
  2072. //
  2073. // Attempt to NDR... set DSN context to CAT
  2074. //
  2075. dsnparams.dwDSNActions |= DSN_ACTION_CONTEXT_CAT;
  2076. fNDR = TRUE;
  2077. break;
  2078. case MESSAGE_FAILURE_BAD_PICKUP_DIR_FILE:
  2079. //
  2080. // Badmail, since we do not have the P1 information needed to badmail
  2081. //
  2082. _ASSERT(szFileName);
  2083. hrBadMail = AQUEUE_E_PICKUP_DIR;
  2084. fNDR = FALSE; //This should be handled as badmail
  2085. break;
  2086. default:
  2087. _ASSERT(0 && "Unhandled failed msg case!");
  2088. }
  2089. if (fNDR && pIMailMsgProperties && fUseIMailMsgProperties)
  2090. {
  2091. hr = HrLinkAllDomains(pIMailMsgProperties);
  2092. if (FAILED(hr))
  2093. goto Exit;
  2094. //Fire DSN Generation event
  2095. hr = HrTriggerDSNGenerationEvent(&dsnparams, FALSE);
  2096. if (FAILED(hr))
  2097. {
  2098. HandleBadMail(pIMailMsgProperties, fUseIMailMsgProperties,
  2099. szFileName, hrBadMail, FALSE);
  2100. if (dwFailureReason == MESSAGE_FAILURE_GENERAL) {
  2101. InterlockedIncrement((PLONG) &m_cBadmailFailureGeneral);
  2102. } else {
  2103. _ASSERT(dwFailureReason == MESSAGE_FAILURE_HOP_COUNT_EXCEEDED);
  2104. InterlockedIncrement((PLONG) &m_cBadmailHopCountExceeded);
  2105. }
  2106. hr = S_OK; //handled error internally
  2107. ErrorTrace((LPARAM) this, "ERROR: Unable to NDR failed mail - hr 0x%08X", hr);
  2108. goto Exit;
  2109. }
  2110. }
  2111. else
  2112. {
  2113. //Handle as badmail
  2114. HandleBadMail(pIMailMsgProperties, fUseIMailMsgProperties,
  2115. szFileName, hrBadMail, FALSE);
  2116. InterlockedIncrement((PLONG) &m_cBadmailBadPickupFile);
  2117. _ASSERT(dwFailureReason == MESSAGE_FAILURE_BAD_PICKUP_DIR_FILE);
  2118. }
  2119. if ( fUseIMailMsgProperties && pIMailMsgProperties)
  2120. {
  2121. //Now that we are done... delete mailmsg from system
  2122. hr = HrDeleteIMailMsg(pIMailMsgProperties);
  2123. if (FAILED(hr))
  2124. {
  2125. ErrorTrace((LPARAM) this, "ERROR: Unable to delete message hr0x%08X", hr);
  2126. //message was actually NDR'd/bad mailed correctly
  2127. hr = S_OK;
  2128. }
  2129. }
  2130. Exit:
  2131. if (pIMailMsgRecipients)
  2132. pIMailMsgRecipients->Release();
  2133. TraceFunctLeave();
  2134. return hr;
  2135. }
  2136. //+------------------------------------------------------------
  2137. //
  2138. // Function: MailTransport_Completion_SubmitMessage
  2139. //
  2140. // Synopsis: SEO will call this routine after all sinks for
  2141. // SubmitMessage have been handeled
  2142. //
  2143. // Arguments:
  2144. // pvContext: Context passed into TriggerServerEvent
  2145. //
  2146. // Returns:
  2147. // S_OK: Success
  2148. //
  2149. // History:
  2150. // jstamerj 980609 16:13:40: Created.
  2151. //
  2152. //-------------------------------------------------------------
  2153. HRESULT MailTransport_Completion_SubmitMessage(
  2154. HRESULT hrStatus,
  2155. PVOID pvContext)
  2156. {
  2157. TraceFunctEnter("MailTransport_Completion_SubmitMessage");
  2158. PEVENTPARAMS_SUBMISSION pParams = (PEVENTPARAMS_SUBMISSION) pvContext;
  2159. CAQSvrInst *paqinst = (CAQSvrInst *) pParams->pCCatMsgQueue;
  2160. TraceFunctLeave();
  2161. return paqinst->SubmissionEventCompletion(
  2162. hrStatus,
  2163. pParams);
  2164. }
  2165. //+------------------------------------------------------------
  2166. //
  2167. // Function: CAQSvrInst::SubmissionEventCompletion
  2168. //
  2169. // Synopsis: Completion routine called when the submission event is
  2170. // done.
  2171. //
  2172. // Arguments:
  2173. // hrStatus: Status of server event
  2174. // pParams: Context passed into TriggerServereEvent
  2175. //
  2176. // Returns:
  2177. // Nothing
  2178. //
  2179. // History:
  2180. // jstamerj 980610 12:26:18: Created.
  2181. //
  2182. //-------------------------------------------------------------
  2183. HRESULT CAQSvrInst::SubmissionEventCompletion(
  2184. HRESULT hrStatus,
  2185. PEVENTPARAMS_SUBMISSION pParams)
  2186. {
  2187. TraceFunctEnterEx((LPARAM)pParams->pIMailMsgProperties,
  2188. "CAQSvrInst::SubmissionEventCompletion");
  2189. _ASSERT(pParams);
  2190. HRESULT hr;
  2191. DebugTrace((LPARAM)pParams->pIMailMsgProperties,
  2192. "Status of event completion: %08lx", hrStatus);
  2193. InterlockedDecrement((PLONG) &m_cCurrentMsgsPendingSubmitEvent);
  2194. //
  2195. // Update the message status
  2196. //
  2197. hr = SetNextMsgStatus(MP_STATUS_SUBMITTED, pParams->pIMailMsgProperties);
  2198. if (hr == S_OK) //anything else implies that the message has been handled
  2199. {
  2200. // Only trigger the precat event if message was not turfed.
  2201. TriggerPreCategorizeEvent(pParams->pIMailMsgProperties);
  2202. }
  2203. //
  2204. // Release refernce added in SubmitMessage
  2205. //
  2206. Release();
  2207. pParams->pIMailMsgProperties->Release();
  2208. //
  2209. // pParams is part of a larger allocation that will be released by
  2210. // SEO Dispatcher code
  2211. //
  2212. TraceFunctLeave();
  2213. return S_OK;
  2214. }
  2215. //---[ CAQSvrInst::SubmitMessageToCategorizer ]-------------------------------------------
  2216. //
  2217. //
  2218. // Description:
  2219. // Implements IAdvQueue::SubmitMessageToCategorizer
  2220. // Parameters:
  2221. // pIMailMsgProperties... Messaage to queue
  2222. // Returns:
  2223. // S_OK on success
  2224. //
  2225. //-----------------------------------------------------------------------------
  2226. HRESULT CAQSvrInst::SubmitMessageToCategorizer(
  2227. IMailMsgProperties *pIMailMsgProperties)
  2228. {
  2229. TraceFunctEnterEx((LPARAM) pIMailMsgProperties, "CAQSvrInst::SubmitMessageToCategorizer");
  2230. HRESULT hr = S_OK;
  2231. HRESULT hrTmp = S_OK;
  2232. IMailMsgQueueMgmt *pIMailMsgQueueMgmt = NULL;
  2233. BOOL fLocked = FALSE;
  2234. if (!fTryShutdownLock())
  2235. {
  2236. hr = AQUEUE_E_SHUTDOWN;
  2237. goto Exit;
  2238. }
  2239. MSG_TRACK_INFO msgTrackInfo;
  2240. ZeroMemory( &msgTrackInfo, sizeof( msgTrackInfo ) );
  2241. msgTrackInfo.dwEventId = MTE_SUBMIT_MESSAGE_TO_CAT;
  2242. m_pISMTPServer->WriteLog( &msgTrackInfo, pIMailMsgProperties, NULL, NULL );
  2243. fLocked = TRUE;
  2244. cIncMsgsInSystem();
  2245. InterlockedIncrement((PLONG) &m_cCurrentMsgsPendingCat);
  2246. TRACE_COUNTERS;
  2247. hr = m_asyncqPreCatQueue.HrQueueRequest(pIMailMsgProperties, FALSE,
  2248. cCountMsgsForHandleThrottling(pIMailMsgProperties));
  2249. if (FAILED(hr))
  2250. {
  2251. HandleAQFailure(AQ_FAILURE_PRECAT_RETRY, E_FAIL, pIMailMsgProperties);
  2252. goto Exit;
  2253. }
  2254. Exit:
  2255. if(fLocked)
  2256. {
  2257. ShutdownUnlock();
  2258. }
  2259. TraceFunctLeave();
  2260. return hr;
  2261. }
  2262. //---[ CAQSvrInst::SetNextMsgStatus ]------------------------------------------
  2263. //
  2264. //
  2265. // Description:
  2266. // Used by the event glue code to set the next message status. Will turf
  2267. // or badmail a message if the status indicates that is the requested
  2268. // action.
  2269. // Parameters:
  2270. // IN dwCurrentStatus The current status (according to *current*
  2271. // place in event pipeline). Valid values are
  2272. // MP_STATUS_SUBMITTED
  2273. // MP_STATUS_CATEGORIZED
  2274. // IN pIMailMsgProperties The message
  2275. // OUT pdwNewStatus The new status
  2276. // Returns:
  2277. // S_OK Success
  2278. // S_FALSE Success, but message has been handled.
  2279. // History:
  2280. // 11/17/98 - MikeSwa Created
  2281. //
  2282. //-----------------------------------------------------------------------------
  2283. HRESULT CAQSvrInst::SetNextMsgStatus(
  2284. IN DWORD dwCurrentStatus,
  2285. IN IMailMsgProperties *pIMailMsgProperties)
  2286. {
  2287. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::SetNextMsgStatus");
  2288. HRESULT hr = S_OK;
  2289. DWORD dwActualStatus = 0;
  2290. DWORD dwNewStatus = 0;
  2291. BOOL fHandled = FALSE;
  2292. _ASSERT(pIMailMsgProperties);
  2293. _ASSERT((MP_STATUS_SUBMITTED == dwCurrentStatus) || (MP_STATUS_CATEGORIZED == dwCurrentStatus));
  2294. hr = pIMailMsgProperties->GetDWORD(IMMPID_MP_MESSAGE_STATUS, &dwActualStatus);
  2295. if (FAILED(hr))
  2296. dwActualStatus = dwCurrentStatus;
  2297. if (MP_STATUS_SUCCESS == dwActualStatus)
  2298. dwActualStatus = dwCurrentStatus;
  2299. switch(dwActualStatus)
  2300. {
  2301. case MP_STATUS_BAD_MAIL:
  2302. HandleBadMail(pIMailMsgProperties, TRUE, NULL, E_FAIL, FALSE);
  2303. InterlockedIncrement((PLONG) &m_cBadmailEvent);
  2304. //OK... now continue as if message was aborted
  2305. case MP_STATUS_ABORT_DELIVERY:
  2306. fHandled = TRUE;
  2307. HrDeleteIMailMsg(pIMailMsgProperties);
  2308. break;
  2309. case MP_STATUS_ABANDON_DELIVERY:
  2310. //In this case, we will leave the message in the queue directory
  2311. //until restart & reset the state so it goes through the entire
  2312. //pipeline. The idea is that someone can write a sink to detect
  2313. //a non-supported state (like CAT disabled in an Exchange install)
  2314. //that will abandon delivery of the messages and log an event.
  2315. //The admin can fix the problem and restart smtpsvc. Once
  2316. //the service is restarted... the messages are magically submitted
  2317. //and re-categorized.
  2318. fHandled = TRUE;
  2319. pIMailMsgProperties->PutDWORD(IMMPID_MP_MESSAGE_STATUS,
  2320. MP_STATUS_SUCCESS);
  2321. break;
  2322. case MP_STATUS_CATEGORIZED:
  2323. //Don't change status from categorized to something else
  2324. dwNewStatus = dwActualStatus;
  2325. DebugTrace((LPARAM) this, "Message 0x%x has already been categorized",
  2326. pIMailMsgProperties);
  2327. break;
  2328. default: //simply move on to the next expected status
  2329. dwNewStatus = dwCurrentStatus;
  2330. }
  2331. if (!fHandled)
  2332. {
  2333. pIMailMsgProperties->PutDWORD(IMMPID_MP_MESSAGE_STATUS, dwNewStatus);
  2334. //callers will not be able to do anything about a failure to write status
  2335. hr = S_OK;
  2336. }
  2337. else
  2338. {
  2339. DecMsgsInSystem();
  2340. hr = S_FALSE;
  2341. }
  2342. TraceFunctLeave();
  2343. return hr;
  2344. }
  2345. //---[ CAQSvrInst::fPreCatQueueCompletion ]-----------------------------------
  2346. //
  2347. //
  2348. // Description:
  2349. // Completion routine for Pre-Categorization queue
  2350. // Parameters:
  2351. // pIMailMsgProperties - MailMsg to give to categorization
  2352. // Returns:
  2353. // TRUE if successful
  2354. // FALSE if message needs to be re-requeue
  2355. // History:
  2356. // 7/17/98 - MikeSwa Created
  2357. //
  2358. //-----------------------------------------------------------------------------
  2359. BOOL CAQSvrInst::fPreCatQueueCompletion(IMailMsgProperties *pIMailMsgProperties)
  2360. {
  2361. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::fPreCatQueueCompletion");
  2362. _ASSERT(CATMSGQ_SIG == m_dwSignature);
  2363. HRESULT hr = S_OK;
  2364. HRESULT hrCatCompletion;
  2365. IUnknown *pIUnknown = NULL;
  2366. BOOL fRet = TRUE;
  2367. BOOL fLocked = FALSE;
  2368. if (!fTryShutdownLock())
  2369. {
  2370. hr = S_OK; //we cannot retry on shutdown
  2371. goto Exit;
  2372. }
  2373. fLocked = TRUE;
  2374. hr = pIMailMsgProperties->QueryInterface(IID_IUnknown, (PVOID *) &pIUnknown);
  2375. _ASSERT(SUCCEEDED(hr) && "QueryInterface for IID_IUknown Failed!");
  2376. _ASSERT(pIUnknown);
  2377. InterlockedIncrement((PLONG) &m_cCatMsgCalled);
  2378. m_asyncqPreCatQueue.IncPendingAsyncCompletions();
  2379. hr = CatMsg(m_hCat, pIUnknown,(PFNCAT_COMPLETION)CAQSvrInst::CatCompletion,
  2380. (LPVOID) this);
  2381. if (FAILED(hr))
  2382. {
  2383. if(hr == CAT_E_RETRY)
  2384. {
  2385. //
  2386. // Return false so that this message will be re-queued
  2387. //
  2388. fRet = FALSE;
  2389. InterlockedDecrement((PLONG) &m_cCatMsgCalled);
  2390. m_asyncqPreCatQueue.DecPendingAsyncCompletions();
  2391. //
  2392. // Schedule a time to retry the messages
  2393. //
  2394. ScheduleInternalRetry(LI_TYPE_PENDING_CAT);
  2395. }
  2396. else
  2397. {
  2398. //
  2399. // Return true since this is not a retryable error
  2400. // Call CatCompletion to handle the non-retryable error (log an event, etc)
  2401. //
  2402. hrCatCompletion = CatCompletion(
  2403. hr, // hrCatResult
  2404. (LPVOID) this, // pContext
  2405. pIUnknown, // pIMsg
  2406. NULL); // rgpIMsg
  2407. _ASSERT(SUCCEEDED(hrCatCompletion));
  2408. }
  2409. }
  2410. Exit:
  2411. if (pIUnknown)
  2412. pIUnknown->Release();
  2413. if(fLocked)
  2414. {
  2415. ShutdownUnlock();
  2416. }
  2417. SleepForPerfAnalysis(g_dwCatQueueSleepMilliseconds);
  2418. TraceFunctLeave();
  2419. return fRet;
  2420. }
  2421. //---[ CAQSvrInst::SetConfigInfo ]--------------------------------------------
  2422. //
  2423. //
  2424. // Description:
  2425. // Implements IAdvQueueConfig::SetConfigInfo
  2426. // Parameters:
  2427. // IN pAQConfigInfo Ptr to config info structure
  2428. // Returns:
  2429. // S_OK on success
  2430. //
  2431. //-----------------------------------------------------------------------------
  2432. STDMETHODIMP CAQSvrInst::SetConfigInfo(IN AQConfigInfo *pAQConfigInfo)
  2433. {
  2434. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::SetConfigInfo");
  2435. HRESULT hr = S_OK;
  2436. if (!pAQConfigInfo)
  2437. {
  2438. hr = E_INVALIDARG;
  2439. goto Exit;
  2440. }
  2441. //check version of structure
  2442. if (!pAQConfigInfo->cbVersion)
  2443. {
  2444. hr = E_INVALIDARG;
  2445. goto Exit;
  2446. }
  2447. //we must be setting something
  2448. if (!(pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_ALL))
  2449. {
  2450. hr = E_INVALIDARG;
  2451. goto Exit;
  2452. }
  2453. m_slPrivateData.ExclusiveLock();
  2454. //Retry related config data
  2455. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_CON_RETRY &&
  2456. MEMBER_OK(pAQConfigInfo, dwFirstRetrySeconds))
  2457. {
  2458. m_dwFirstTierRetrySeconds = pAQConfigInfo->dwFirstRetrySeconds;
  2459. }
  2460. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_EXPIRE_DELAY &&
  2461. MEMBER_OK(pAQConfigInfo, dwDelayExpireMinutes))
  2462. {
  2463. m_dwDelayExpireMinutes = pAQConfigInfo->dwDelayExpireMinutes;
  2464. if (m_dwDelayExpireMinutes == 0) {
  2465. //Default to g_dwRetriesBeforeDelay* retry interval
  2466. m_dwDelayExpireMinutes =
  2467. g_dwRetriesBeforeDelay*m_dwFirstTierRetrySeconds/60;
  2468. }
  2469. }
  2470. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_EXPIRE_NDR &&
  2471. MEMBER_OK(pAQConfigInfo, dwNDRExpireMinutes))
  2472. {
  2473. m_dwNDRExpireMinutes = pAQConfigInfo->dwNDRExpireMinutes;
  2474. if (m_dwNDRExpireMinutes == 0) {
  2475. //Default to g_dwDelayIntervalsBeforeNDR* delay expiration
  2476. m_dwNDRExpireMinutes =
  2477. g_dwDelayIntervalsBeforeNDR*m_dwDelayExpireMinutes;
  2478. }
  2479. }
  2480. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_LOCAL_EXPIRE_DELAY &&
  2481. MEMBER_OK(pAQConfigInfo, dwLocalDelayExpireMinutes))
  2482. {
  2483. DWORD dwOldLocalDelayExpire = m_dwLocalDelayExpireMinutes;
  2484. m_dwLocalDelayExpireMinutes = pAQConfigInfo->dwLocalDelayExpireMinutes;
  2485. if (m_dwLocalDelayExpireMinutes == 0) {
  2486. //Default to g_dwRetriesBeforeDelay* retry interval
  2487. m_dwLocalDelayExpireMinutes =
  2488. g_dwRetriesBeforeDelay*m_dwFirstTierRetrySeconds/60;
  2489. }
  2490. }
  2491. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_LOCAL_EXPIRE_NDR &&
  2492. MEMBER_OK(pAQConfigInfo, dwLocalNDRExpireMinutes))
  2493. {
  2494. m_dwLocalNDRExpireMinutes = pAQConfigInfo->dwLocalNDRExpireMinutes;
  2495. if (m_dwLocalNDRExpireMinutes == 0) {
  2496. //Default to g_dwDelayIntervalsBeforeNDR* delay expiration
  2497. m_dwLocalNDRExpireMinutes =
  2498. g_dwDelayIntervalsBeforeNDR*m_dwLocalDelayExpireMinutes;
  2499. }
  2500. }
  2501. //Handle default local domain
  2502. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_DEFAULT_DOMAIN &&
  2503. MEMBER_OK(pAQConfigInfo, szDefaultLocalDomain))
  2504. {
  2505. hr = HrUpdateRefCountedString(&m_prstrDefaultDomain,
  2506. pAQConfigInfo->szDefaultLocalDomain);
  2507. }
  2508. //Handle Server FQDN
  2509. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_SERVER_FQDN &&
  2510. MEMBER_OK(pAQConfigInfo, szServerFQDN))
  2511. {
  2512. hr = HrUpdateRefCountedString(&m_prstrServerFQDN,
  2513. pAQConfigInfo->szServerFQDN);
  2514. }
  2515. //Handle Copy NDR To Address
  2516. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_SEND_DSN_TO &&
  2517. MEMBER_OK(pAQConfigInfo, szSendCopyOfNDRToAddress))
  2518. {
  2519. hr = HrUpdateRefCountedString(&m_prstrCopyNDRTo,
  2520. pAQConfigInfo->szSendCopyOfNDRToAddress);
  2521. }
  2522. //Handle BadMail config
  2523. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_BADMAIL_DIR &&
  2524. MEMBER_OK(pAQConfigInfo, szBadMailDir))
  2525. {
  2526. hr = HrUpdateRefCountedString(&m_prstrBadMailDir,
  2527. pAQConfigInfo->szBadMailDir);
  2528. }
  2529. //Get DSN options
  2530. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_USE_DSN_OPTIONS &&
  2531. MEMBER_OK(pAQConfigInfo, dwDSNOptions))
  2532. {
  2533. m_dwDSNOptions = pAQConfigInfo->dwDSNOptions;
  2534. }
  2535. //Get Default DSN Language
  2536. if (pAQConfigInfo->dwAQConfigInfoFlags & AQ_CONFIG_INFO_USE_DSN_LANGUAGE &&
  2537. MEMBER_OK(pAQConfigInfo, dwDSNLanguageID))
  2538. {
  2539. m_dwDSNLanguageID = pAQConfigInfo->dwDSNLanguageID;
  2540. }
  2541. m_slPrivateData.ExclusiveUnlock();
  2542. m_pConnMgr->UpdateConfigData(pAQConfigInfo);
  2543. if (INVALID_HANDLE_VALUE != m_hCat)
  2544. {
  2545. HRESULT hrTmp = CatChangeConfig(m_hCat, pAQConfigInfo, m_pISMTPServer, (IAdvQueueDomainType *) this);
  2546. if (SUCCEEDED(hr))
  2547. hr = hrTmp;
  2548. }
  2549. Exit:
  2550. TraceFunctLeave();
  2551. return hr;
  2552. }
  2553. //---[ CAQSvrInst::SetDomainInfo ]-------------------------------------------
  2554. //
  2555. //
  2556. // Description:
  2557. // Implements IAdvQueueConfig::SetDomainInfo
  2558. // Parameters:
  2559. // IN pDomainInfo Per domain config info to store
  2560. // Returns:
  2561. // S_OK on success
  2562. //
  2563. //-----------------------------------------------------------------------------
  2564. STDMETHODIMP CAQSvrInst::SetDomainInfo(IN DomainInfo *pDomainInfo)
  2565. {
  2566. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::SetDomainInfo");
  2567. HRESULT hr = S_OK;
  2568. CInternalDomainInfo *pIntDomainInfo = NULL;
  2569. BOOL fLocked = FALSE;
  2570. if (!fTryShutdownLock())
  2571. {
  2572. hr = AQUEUE_E_SHUTDOWN;
  2573. goto Exit;
  2574. }
  2575. fLocked = TRUE;
  2576. pIntDomainInfo = new CInternalDomainInfo(m_dct.dwGetCurrentVersion());
  2577. if (!pIntDomainInfo)
  2578. {
  2579. hr = E_OUTOFMEMORY;
  2580. goto Exit;
  2581. }
  2582. //Create internal Domain Info struct
  2583. hr = pIntDomainInfo->HrInit(pDomainInfo);
  2584. if (FAILED(hr))
  2585. goto Exit;
  2586. hr = m_dct.HrSetInternalDomainInfo(pIntDomainInfo);
  2587. if (FAILED(hr))
  2588. goto Exit;
  2589. DebugTrace((LPARAM) this, "INFO: Setting domain info flags 0x%08X for domain %s",
  2590. pDomainInfo->dwDomainInfoFlags, pDomainInfo->szDomainName);
  2591. Exit:
  2592. if (fLocked)
  2593. ShutdownUnlock();
  2594. if (pIntDomainInfo)
  2595. pIntDomainInfo->Release();
  2596. TraceFunctLeave();
  2597. return hr;
  2598. }
  2599. //---[ CAQSvrInst::GetDomainInfo ]-------------------------------------------
  2600. //
  2601. //
  2602. // Description:
  2603. // Implements IAdvQueue::GetDomainInfo... returns information about a
  2604. // requested domain. To keep from leaking memory, all calls must be paired
  2605. // with a call to ReleaseDomainInfo. Will handle wildcard matches
  2606. // Parameters:
  2607. // IN cbDomainNameLength Length of domain name string
  2608. // IN szDomainName Domain Name to look for
  2609. // IN OUT pDomainInfo Ptr to Domain info structure to fill
  2610. // OUT ppvDomainContext Ptr to Domain context used to release mem
  2611. // Returns:
  2612. // S_OK on success
  2613. // History:
  2614. // 7/29/98 - MikeSwa Modified (fixed leak of domain info struct)
  2615. //-----------------------------------------------------------------------------
  2616. STDMETHODIMP CAQSvrInst::GetDomainInfo(
  2617. IN DWORD cbDomainNameLength,
  2618. IN CHAR szDomainName[],
  2619. IN OUT DomainInfo *pDomainInfo,
  2620. OUT DWORD **ppvDomainContext)
  2621. {
  2622. HRESULT hr = S_OK;
  2623. CInternalDomainInfo *pIntDomainInfo = NULL;
  2624. BOOL fLocked = FALSE;
  2625. if (!cbDomainNameLength || !szDomainName || !pDomainInfo || !ppvDomainContext)
  2626. {
  2627. hr = E_INVALIDARG;
  2628. goto Exit;
  2629. }
  2630. _ASSERT(pDomainInfo->cbVersion >= sizeof(DomainInfo));
  2631. *ppvDomainContext = NULL;
  2632. if (!fTryShutdownLock())
  2633. {
  2634. hr = AQUEUE_E_SHUTDOWN;
  2635. goto Exit;
  2636. }
  2637. fLocked = TRUE;
  2638. hr = m_dct.HrGetInternalDomainInfo(cbDomainNameLength, szDomainName, &pIntDomainInfo);
  2639. if (FAILED(hr))
  2640. goto Exit;
  2641. _ASSERT(pIntDomainInfo);
  2642. //copy domain info struct
  2643. memcpy(pDomainInfo, &(pIntDomainInfo->m_DomainInfo), sizeof(DomainInfo));
  2644. *ppvDomainContext = (DWORD *) pIntDomainInfo;
  2645. goto Exit;
  2646. Exit:
  2647. if (fLocked)
  2648. ShutdownUnlock();
  2649. return hr;
  2650. }
  2651. //---[ CAQSvrInst::ReleaseDomainInfo ]---------------------------------------
  2652. //
  2653. //
  2654. // Description:
  2655. // Implements IAdvQueueConfig ReleaseDomainInfo... releases data
  2656. // associated with the DomainInfo struct returned by GetDomainInfo.
  2657. // Parameters:
  2658. // IN pvDomainContext Context passed
  2659. // Returns:
  2660. // S_OK on success
  2661. // E_INVALIDARG if pvDomainContext is NULL
  2662. //-----------------------------------------------------------------------------
  2663. STDMETHODIMP CAQSvrInst::ReleaseDomainInfo(IN DWORD *pvDomainContext)
  2664. {
  2665. HRESULT hr = S_OK;
  2666. CInternalDomainInfo *pIntDomainInfo = (CInternalDomainInfo *) pvDomainContext;
  2667. if (!pIntDomainInfo)
  2668. {
  2669. hr = E_INVALIDARG;
  2670. goto Exit;
  2671. }
  2672. pIntDomainInfo->Release();
  2673. Exit:
  2674. return hr;
  2675. }
  2676. //---[ CAQSvrInst::GetPerfCounters ]---------------------------------------
  2677. //
  2678. //
  2679. // Description:
  2680. // Method to retrieve AQ perf counters.
  2681. // Parameters:
  2682. // OUT pAQPerfCounters Struct to return counters in.
  2683. // OUT pCatPerfCouneters Struct to return counters in. (optinal)
  2684. // Returns:
  2685. // S_OK on success
  2686. // E_INVALIDARG if pAQPerfCounters is NULL
  2687. //-----------------------------------------------------------------------------
  2688. STDMETHODIMP CAQSvrInst::GetPerfCounters(
  2689. OUT AQPerfCounters *pAQPerfCounters,
  2690. OUT PCATPERFBLOCK pCatPerfCounters)
  2691. {
  2692. HRESULT hr = S_OK;
  2693. if (!pAQPerfCounters)
  2694. return( E_INVALIDARG );
  2695. _ASSERT((sizeof(AQPerfCounters) == pAQPerfCounters->cbVersion) && "aqueue/smtpsvc dll version mismatch");
  2696. if (sizeof(AQPerfCounters) != pAQPerfCounters->cbVersion)
  2697. return( E_INVALIDARG );
  2698. pAQPerfCounters->cMsgsDeliveredLocal = m_cMsgsDeliveredLocal;
  2699. pAQPerfCounters->cCurrentMsgsPendingCat = m_cCurrentMsgsPendingCat;
  2700. pAQPerfCounters->cCurrentMsgsPendingRemoteDelivery = m_cCurrentMsgsPendingDelivery;
  2701. pAQPerfCounters->cCurrentMsgsPendingLocalDelivery = m_cCurrentMsgsPendingLocal;
  2702. pAQPerfCounters->cCurrentQueueMsgInstances = m_cCurrentQueueMsgInstances;
  2703. pAQPerfCounters->cTotalMsgRemoteSendRetries = m_cMsgsAckedRetry;
  2704. pAQPerfCounters->cTotalMsgLocalRetries = m_cMsgsAckedRetryLocal;
  2705. pAQPerfCounters->cCurrentMsgsPendingLocalRetry = m_cCurrentMsgsPendingLocalRetry;
  2706. //DSN counters
  2707. pAQPerfCounters->cNDRsGenerated = m_cNDRs;
  2708. pAQPerfCounters->cDelayedDSNsGenerated = m_cDelayedDSNs;
  2709. pAQPerfCounters->cDeliveredDSNsGenerated = m_cDeliveredDSNs;
  2710. pAQPerfCounters->cRelayedDSNsGenerated = m_cRelayedDSNs;
  2711. pAQPerfCounters->cExpandedDSNsGenerated = m_cExpandedDSNs;
  2712. pAQPerfCounters->cTotalMsgsTURNETRN = m_cTotalMsgsTURNETRNDelivered;
  2713. //Queue/Link related counters
  2714. pAQPerfCounters->cCurrentRemoteDestQueues = m_cCurrentRemoteDestQueues;
  2715. pAQPerfCounters->cCurrentRemoteNextHopLinks = m_cCurrentRemoteNextHops;
  2716. pAQPerfCounters->cTotalMsgsBadmailNoRecipients = m_cBadmailNoRecipients;
  2717. pAQPerfCounters->cTotalMsgsBadmailHopCountExceeded = m_cBadmailHopCountExceeded;
  2718. pAQPerfCounters->cTotalMsgsBadmailFailureGeneral = m_cBadmailFailureGeneral;
  2719. pAQPerfCounters->cTotalMsgsBadmailBadPickupFile = m_cBadmailBadPickupFile;
  2720. pAQPerfCounters->cTotalMsgsBadmailEvent = m_cBadmailEvent;
  2721. pAQPerfCounters->cTotalMsgsBadmailNdrOfDsn = m_cBadmailNdrOfDsn;
  2722. pAQPerfCounters->cCurrentMsgsPendingRouting = m_cCurrentMsgsPendingRouting;
  2723. pAQPerfCounters->cTotalDSNFailures = m_cTotalDSNFailures;
  2724. pAQPerfCounters->cCurrentMsgsInLocalDelivery = m_cCurrentMsgsInLocalDelivery;
  2725. //
  2726. // The m_cTotalMsgsSubmitted counter counts the number of times
  2727. // HrInternalSubmit msg has been called. This does not include the
  2728. // presubmission queue, so me need to manually add this count in.
  2729. //
  2730. pAQPerfCounters->cTotalMsgsSubmitted = m_cTotalMsgsSubmitted +
  2731. m_cCurrentMsgsPendingSubmit;
  2732. if (fTryShutdownLock()) {
  2733. pAQPerfCounters->cCurrentMsgsPendingUnreachableLink =
  2734. m_dmt.GetCurrentlyUnreachableTotalMsgCount();
  2735. ShutdownUnlock();
  2736. }
  2737. //For now, these counters will be calculated by walking the DMT (same
  2738. //function that determines msgs pending retry).
  2739. pAQPerfCounters->cCurrentRemoteNextHopLinksEnabled = 0;
  2740. pAQPerfCounters->cCurrentRemoteNextHopLinksPendingRetry = 0;
  2741. pAQPerfCounters->cCurrentRemoteNextHopLinksPendingScheduling = 0;
  2742. pAQPerfCounters->cCurrentRemoteNextHopLinksPendingTURNETRN = 0;
  2743. pAQPerfCounters->cCurrentRemoteNextHopLinksFrozenByAdmin = 0;
  2744. //Get Retry remote retry and DSN counters
  2745. pAQPerfCounters->cCurrentMsgsPendingRemoteRetry = 0;
  2746. if (fTryShutdownLock())
  2747. {
  2748. hr = m_dmt.HrIterateOverSubDomains(NULL, CalcDMTPerfCountersIteratorFn,
  2749. pAQPerfCounters);
  2750. //will not generate transient errors (we expect success or an empty table
  2751. _ASSERT(SUCCEEDED(hr) || (DOMHASH_E_NO_SUCH_DOMAIN == hr));
  2752. if((m_hCat != INVALID_HANDLE_VALUE) && (pCatPerfCounters))
  2753. hr = CatGetPerfCounters(m_hCat, pCatPerfCounters);
  2754. _ASSERT(SUCCEEDED(hr));
  2755. ShutdownUnlock();
  2756. }
  2757. //save values in CAQSvrInst, so we can dump them in the debugger
  2758. m_cCurrentRemoteNextHopsEnabled = pAQPerfCounters->cCurrentRemoteNextHopLinksEnabled;
  2759. m_cCurrentRemoteNextHopsPendingRetry = pAQPerfCounters->cCurrentRemoteNextHopLinksPendingRetry;
  2760. m_cCurrentRemoteNextHopsPendingSchedule = pAQPerfCounters->cCurrentRemoteNextHopLinksPendingScheduling;
  2761. m_cCurrentRemoteNextHopsFrozenByAdmin = pAQPerfCounters->cCurrentRemoteNextHopLinksFrozenByAdmin;
  2762. return( S_OK );
  2763. }
  2764. //---[ CAQSvrInst::ResetPerfCounters ]---------------------------------------
  2765. //
  2766. //
  2767. // Description:
  2768. // Method to reset AQ perf counters to 0.
  2769. // Parameters:
  2770. // None
  2771. // Returns:
  2772. // S_OK on success
  2773. //-----------------------------------------------------------------------------
  2774. STDMETHODIMP CAQSvrInst::ResetPerfCounters()
  2775. {
  2776. m_cTotalMsgsQueued = 0;
  2777. m_cTotalMsgsSubmitted = 0;
  2778. m_cMsgsAcked = 0;
  2779. m_cMsgsAckedRetry = 0;
  2780. m_cMsgsDeliveredLocal = 0;
  2781. m_cMsgsAckedRetryLocal = 0;
  2782. m_cTotalMsgsTURNETRNDelivered = 0;
  2783. //clear DSN counters
  2784. m_cDelayedDSNs = 0;
  2785. m_cNDRs = 0;
  2786. m_cDeliveredDSNs = 0;
  2787. m_cRelayedDSNs = 0;
  2788. m_cExpandedDSNs = 0;
  2789. return( S_OK );
  2790. }
  2791. //---[ CAQSvrInst::StartConfigUpdate() ]---------------------------------------
  2792. //
  2793. //
  2794. // Description:
  2795. // Implements IAQConfig::StartConfigUpdate() which is used to signal that
  2796. // all of the domain information is about to be updated.
  2797. // Parameters:
  2798. // -
  2799. // Returns:
  2800. // S_OK on success
  2801. // AQUEUE_E_SHUTDOWN on shutdown
  2802. // History:
  2803. // 9/29/98 - MikeSwa Created
  2804. //
  2805. //-----------------------------------------------------------------------------
  2806. STDMETHODIMP CAQSvrInst::StartConfigUpdate()
  2807. {
  2808. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::StartConfigUpdate");
  2809. HRESULT hr = S_OK;
  2810. if (!fTryShutdownLock())
  2811. {
  2812. hr = AQUEUE_E_SHUTDOWN;
  2813. }
  2814. else
  2815. {
  2816. m_dct.StartConfigUpdate();
  2817. ShutdownUnlock();
  2818. }
  2819. TraceFunctLeave();
  2820. return hr;
  2821. }
  2822. //---[ CAQSvrInst::FinishConfigUpdate ]----------------------------------------
  2823. //
  2824. //
  2825. // Description:
  2826. // Implements IAQConfig::FinishConfigUpdate() which is used to signal that
  2827. // signal that all of the domain information has been updated. This will
  2828. // cause us to walk through the DomainConfigTable and remove any domain
  2829. // config info that has not been updated (ie - a domain that has been
  2830. // deleted).
  2831. // Parameters:
  2832. // -
  2833. // Returns:
  2834. // S_OK on success
  2835. // AQUEUE_E_SHUTDOWN if called while shutdown is in progress.
  2836. // History:
  2837. // 9/29/98 - MikeSwa Created
  2838. //
  2839. //-----------------------------------------------------------------------------
  2840. STDMETHODIMP CAQSvrInst::FinishConfigUpdate()
  2841. {
  2842. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::FinishConfigUpdate");
  2843. HRESULT hr = S_OK;
  2844. if (!fTryShutdownLock())
  2845. {
  2846. hr = AQUEUE_E_SHUTDOWN;
  2847. }
  2848. else
  2849. {
  2850. m_dct.FinishConfigUpdate();
  2851. //Reroute everything
  2852. //$$REVIEW - should we re-route on metabase changes?
  2853. ResetRoutes(RESET_NEXT_HOPS);
  2854. //Important configuration data may have changed... kick connmgr
  2855. m_pConnMgr->KickConnections();
  2856. ShutdownUnlock();
  2857. }
  2858. TraceFunctLeave();
  2859. return hr;
  2860. }
  2861. //+------------------------------------------------------------
  2862. //
  2863. // Function: CAQSvrInst::TriggerPostCategorizeEvent
  2864. //
  2865. // Synopsis: Triggers post categorization event
  2866. //
  2867. // Arguments:
  2868. // pIMsg: MailMsg for event or NULL
  2869. // rgpIMsg: NULL or a null terminated array of mailmsg pointers
  2870. //
  2871. // NOTE: pIMsg or rgpIMsg must be NULL, but neither can be null
  2872. // (exclusive OR)
  2873. //
  2874. // Returns:
  2875. // -
  2876. //
  2877. // History:
  2878. // jstamerj 980616 20:43:08: Created.
  2879. // 8/25/98 - MikeSwa Modified - removed return code
  2880. //
  2881. //-------------------------------------------------------------
  2882. void CAQSvrInst::TriggerPostCategorizeEvent(
  2883. IUnknown *pIMsg,
  2884. IUnknown **rgpIMsg)
  2885. {
  2886. TraceFunctEnterEx((LPARAM)this,
  2887. "CAQSvrInst::TriggerPostCategorizeEvent");
  2888. HRESULT hr = S_OK;
  2889. HRESULT hrTmp = S_OK;
  2890. IMailMsgProperties *pIMailMsgProperties = NULL;
  2891. if(pIMsg)
  2892. {
  2893. hr = TriggerPostCategorizeEventOneMsg(pIMsg);
  2894. if (FAILED(hr))
  2895. {
  2896. hrTmp = pIMsg->QueryInterface(IID_IMailMsgProperties,
  2897. (void **) &pIMailMsgProperties);
  2898. _ASSERT(SUCCEEDED(hrTmp) && "Could not QI for IMailMsgProperties");
  2899. if (FAILED(hrTmp))
  2900. LogAQEvent(AQ_FAILURE_POSTCAT_EVENT, NULL, NULL, NULL);
  2901. else
  2902. {
  2903. HandleAQFailure(AQ_FAILURE_POSTCAT_EVENT, hr,
  2904. pIMailMsgProperties);
  2905. pIMailMsgProperties->Release();
  2906. pIMailMsgProperties = NULL;
  2907. }
  2908. DecMsgsInSystem(FALSE, FALSE);
  2909. hr = S_OK;
  2910. }
  2911. }
  2912. else
  2913. {
  2914. _ASSERT(rgpIMsg);
  2915. IUnknown **ppIMsgCurrent = rgpIMsg;
  2916. DecMsgsInSystem(FALSE, FALSE);
  2917. while(SUCCEEDED(hr) && (*ppIMsgCurrent))
  2918. {
  2919. hr = TriggerPostCategorizeEventOneMsg(
  2920. *ppIMsgCurrent);
  2921. ppIMsgCurrent++;
  2922. if (FAILED(hr))
  2923. {
  2924. hrTmp = (*ppIMsgCurrent)->QueryInterface(IID_IMailMsgProperties,
  2925. (void **) &pIMailMsgProperties);
  2926. _ASSERT(SUCCEEDED(hrTmp) && "Could not QI for IMailMsgProperties");
  2927. if (FAILED(hrTmp))
  2928. LogAQEvent(AQ_FAILURE_POSTCAT_EVENT, NULL, NULL, NULL);
  2929. else
  2930. {
  2931. HandleAQFailure(AQ_FAILURE_POSTCAT_EVENT, hr,
  2932. pIMailMsgProperties);
  2933. pIMailMsgProperties->Release();
  2934. pIMailMsgProperties = NULL;
  2935. }
  2936. hr = S_OK;
  2937. }
  2938. else
  2939. {
  2940. cIncMsgsInSystem();
  2941. }
  2942. }
  2943. }
  2944. TraceFunctLeaveEx((LPARAM)this);
  2945. }
  2946. //+------------------------------------------------------------
  2947. //
  2948. // Function: CAQSvrInst::TriggerPostCategorizeEventOneMsg
  2949. //
  2950. // Synopsis: Triggers ONE server event for one mailmsg
  2951. //
  2952. // Arguments:
  2953. // pIMsg - mailmsg
  2954. //
  2955. // Returns:
  2956. // S_OK: Success
  2957. //
  2958. // History:
  2959. // jstamerj 980616 21:26:30: Created.
  2960. //
  2961. //-------------------------------------------------------------
  2962. HRESULT CAQSvrInst::TriggerPostCategorizeEventOneMsg(
  2963. IUnknown *pIMsg)
  2964. {
  2965. TraceFunctEnterEx((LPARAM)this, "CAQSvrInst::TriggerPostCategorizeEventOneMsg");
  2966. HRESULT hr;
  2967. //
  2968. // trigger one event
  2969. // this is an async event
  2970. //
  2971. EVENTPARAMS_POSTCATEGORIZE Params;
  2972. // Setup pParams
  2973. hr = pIMsg->QueryInterface(IID_IMailMsgProperties,
  2974. (PVOID *)&(Params.pIMailMsgProperties));
  2975. if(FAILED(hr)) {
  2976. ErrorTrace((LPARAM)this, "QI failed with error %08lx",
  2977. hr);
  2978. TraceFunctLeaveEx((LPARAM)this);
  2979. return hr;
  2980. }
  2981. Params.pfnCompletion = MailTransport_Completion_PostCategorization;
  2982. Params.pCCatMsgQueue = (PVOID) this;
  2983. //
  2984. // Addref here, release in completion
  2985. //
  2986. AddRef();
  2987. //
  2988. // keep a count of messages in the post-cat event
  2989. //
  2990. InterlockedIncrement((LPLONG) &m_cCurrentMsgsPendingPostCatEvent);
  2991. if(m_pISMTPServer) {
  2992. hr = TriggerServerEvent(
  2993. SMTP_MAILTRANSPORT_POSTCATEGORIZE_EVENT,
  2994. &Params);
  2995. DebugTrace((LPARAM)this, "TriggerServerEvent returned hr %08lx", hr);
  2996. }
  2997. if((m_pISMTPServer == NULL) || (FAILED(hr))) {
  2998. ErrorTrace((LPARAM)this,
  2999. "Unable to dispatch server event; calling completion routine directly");
  3000. //
  3001. // Call completion routine directly
  3002. //
  3003. TraceFunctLeaveEx((LPARAM)this);
  3004. return PostCategorizationEventCompletion(S_OK, &Params);
  3005. }
  3006. TraceFunctLeaveEx((LPARAM)this);
  3007. return S_OK;
  3008. }
  3009. //+------------------------------------------------------------
  3010. //
  3011. // Function: MailTransport_Completion_PostCategorization
  3012. //
  3013. // Synopsis: SEO will call this routine after all sinks for
  3014. // OnPostCategoriztion have been handeled
  3015. //
  3016. // Arguments:
  3017. // pvContext: Context passed into TriggerServerEvent
  3018. //
  3019. // Returns:
  3020. // S_OK: Success
  3021. //
  3022. // History:
  3023. // jstamerj 980609 16:13:40: Created.
  3024. //
  3025. //-------------------------------------------------------------
  3026. HRESULT MailTransport_Completion_PostCategorization(
  3027. HRESULT hrStatus,
  3028. PVOID pvContext)
  3029. {
  3030. TraceFunctEnter("MailTransport_Completion_PostCategorization");
  3031. PEVENTPARAMS_POSTCATEGORIZE pParams = (PEVENTPARAMS_POSTCATEGORIZE) pvContext;
  3032. CAQSvrInst *paqinst = (CAQSvrInst *) pParams->pCCatMsgQueue;
  3033. TraceFunctLeave();
  3034. return paqinst->PostCategorizationEventCompletion(
  3035. hrStatus,
  3036. pParams);
  3037. }
  3038. //+------------------------------------------------------------
  3039. //
  3040. // Function: CAQSvrInst::PostCategorizationEventCompletion
  3041. //
  3042. // Synopsis: Called on the completion side of OnPostCategorization
  3043. //
  3044. // Arguments:
  3045. // hrStatus: status of server event
  3046. // pParams: context structure passed into TriggerServerEvent
  3047. //
  3048. // Returns:
  3049. // S_OK: Success
  3050. //
  3051. // History:
  3052. // jstamerj 980616 21:33:05: Created.
  3053. //
  3054. //-------------------------------------------------------------
  3055. HRESULT CAQSvrInst::PostCategorizationEventCompletion(
  3056. HRESULT hrStatus,
  3057. PEVENTPARAMS_POSTCATEGORIZE pParams)
  3058. {
  3059. TraceFunctEnterEx((LPARAM)this,
  3060. "CAQSvrInst::PostCategorizationEventCompletion");
  3061. DebugTrace((LPARAM)this, "hrStatus is %08lx", hrStatus);
  3062. HRESULT hr;
  3063. //
  3064. // Decrease count of msgs in post-cat event
  3065. //
  3066. InterlockedDecrement((LPLONG) &m_cCurrentMsgsPendingPostCatEvent);
  3067. hr = SetNextMsgStatus(MP_STATUS_CATEGORIZED, pParams->pIMailMsgProperties);
  3068. //See if this message has been "handled"
  3069. if (S_FALSE == hr)
  3070. {
  3071. //Message has been "handled"... do not try to route it
  3072. hr = S_OK;
  3073. }
  3074. else
  3075. {
  3076. //Increment counters and put msg into the pre-routing queue
  3077. InterlockedIncrement((PLONG) &m_cCurrentMsgsPendingRouting);
  3078. hr = m_asyncqPreRoutingQueue.HrQueueRequest(pParams->pIMailMsgProperties,
  3079. FALSE, cCountMsgsForHandleThrottling(pParams->pIMailMsgProperties));
  3080. if (FAILED(hr))
  3081. {
  3082. HandleAQFailure(AQ_FAILURE_PREROUTING_FAILED, hr, pParams->pIMailMsgProperties);
  3083. ErrorTrace((LPARAM)this, "fRouteAndQueueMsg failed with hr %08lx", hr);
  3084. DecMsgsInSystem(FALSE, FALSE);
  3085. //don't passback shutdown errors in completions routines
  3086. if (AQUEUE_E_SHUTDOWN == hr)
  3087. hr = S_OK;
  3088. }
  3089. }
  3090. //
  3091. // Release mailmsg reference added in TriggerPostCategorizerEventOneMsg
  3092. //
  3093. pParams->pIMailMsgProperties->Release();
  3094. //
  3095. // Release reference to this object added in
  3096. // TriggerPostCategorizerEventOneMsg
  3097. //
  3098. Release();
  3099. TraceFunctLeaveEx((LPARAM)this);
  3100. return S_OK; //we should always handle failures internally here
  3101. }
  3102. //---[ CatCompletion ]---------------------------------------------------------
  3103. //
  3104. //
  3105. // Description:
  3106. // Message Categoriztion Completion function
  3107. // Parameters:
  3108. // hrCatResult HRESULT of categorization attempt
  3109. // pContext Context as passed into MsgCat
  3110. // pIMsg Single categorized IMsg (if not bifurcated)
  3111. // rgpIMsg NULL terminated array of IMsg's (bifurcated)
  3112. // Returns:
  3113. // S_OK on success
  3114. //
  3115. //-----------------------------------------------------------------------------
  3116. HRESULT CAQSvrInst::CatCompletion(HRESULT hrCatResult, PVOID pContext,
  3117. IUnknown *pIMsg,
  3118. IUnknown **rgpIMsg)
  3119. {
  3120. TraceFunctEnterEx((LPARAM) pIMsg, "CatCompletion");
  3121. HRESULT hr = S_OK;
  3122. CAQSvrInst *paqinst = (CAQSvrInst *) pContext;
  3123. IMailMsgProperties *pIMailMsg = NULL;
  3124. IMailMsgQueueMgmt *pIMailMsgQM = NULL;
  3125. _ASSERT(paqinst);
  3126. _ASSERT(CATMSGQ_SIG == paqinst->m_dwSignature);
  3127. //Increment count of times CatCompletion called
  3128. InterlockedIncrement((PLONG) &(paqinst->m_cCatCompletionCalled));
  3129. paqinst->m_asyncqPreCatQueue.DecPendingAsyncCompletions();
  3130. //make sure Cat is returning an HRESULT
  3131. _ASSERT(!hrCatResult || (hrCatResult & 0xFFFF0000));
  3132. if (SUCCEEDED(hrCatResult))
  3133. {
  3134. //
  3135. // Kick off post categorize event
  3136. //
  3137. InterlockedDecrement((PLONG) &(paqinst->m_cCurrentMsgsPendingCat));
  3138. paqinst->TriggerPostCategorizeEvent(pIMsg, rgpIMsg);
  3139. }
  3140. else if (FAILED(hrCatResult) &&
  3141. (CAT_E_RETRY == hrCatResult))
  3142. {
  3143. //MsgCat has some re-tryable error...
  3144. //stick it back in the queue and retry later
  3145. DebugTrace((LPARAM) paqinst, "INFO: MsgCat had tmp failure - hr 0x%08X", hr);
  3146. //
  3147. // Adjust counters... they we be adjusted correctly per msg in
  3148. // HandleCatRetryOneMessage
  3149. //
  3150. InterlockedDecrement((PLONG) &(paqinst->m_cCurrentMsgsPendingCat));
  3151. paqinst->DecMsgsInSystem(FALSE, FALSE);
  3152. if(pIMsg)
  3153. {
  3154. paqinst->HandleCatRetryOneMessage(pIMsg);
  3155. }
  3156. else
  3157. {
  3158. _ASSERT(rgpIMsg);
  3159. IUnknown **ppIMsgCurrent = rgpIMsg;
  3160. while(*ppIMsgCurrent)
  3161. {
  3162. paqinst->HandleCatRetryOneMessage(*ppIMsgCurrent);
  3163. ppIMsgCurrent++;
  3164. }
  3165. }
  3166. }
  3167. else
  3168. {
  3169. _ASSERT(pIMsg && rgpIMsg == NULL && "Message bifurcated inspite of non-retryable cat error");
  3170. paqinst->HandleCatFailure(pIMsg, hrCatResult);
  3171. } // Non retryable error
  3172. TraceFunctLeaveEx((LPARAM)paqinst);
  3173. return S_OK; //all errors should be handled internally
  3174. }
  3175. //---[ CAQSvrInst::HandleCatRetryOneMessage ]----------------------------------
  3176. //
  3177. //
  3178. // Description:
  3179. // Handles cat retry for a single message
  3180. // Parameters:
  3181. // pIUnknown IUnknown for the message to retry
  3182. // Returns:
  3183. // -
  3184. // History:
  3185. // 4/13/2000 - MikeSwa Created
  3186. //
  3187. //-----------------------------------------------------------------------------
  3188. void CAQSvrInst::HandleCatRetryOneMessage(IUnknown *pIUnknown)
  3189. {
  3190. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HandleCatRetryOneMessage");
  3191. IMailMsgProperties *pIMailMsgProperties = NULL;
  3192. HRESULT hr = S_OK;
  3193. hr = pIUnknown->QueryInterface(IID_IMailMsgProperties,
  3194. (void **) &pIMailMsgProperties);
  3195. _ASSERT(SUCCEEDED(hr) && "QueryInterface for IID_IMailMsgProperties FAILED");
  3196. if (FAILED(hr))
  3197. goto Exit;
  3198. //
  3199. // Check and see if the message is still valid
  3200. //
  3201. if (!fShouldRetryMessage(pIMailMsgProperties))
  3202. goto Exit;
  3203. //
  3204. // Queue it to the pre-cat queue
  3205. //
  3206. hr = m_asyncqPreCatQueue.HrQueueRequest(pIMailMsgProperties,
  3207. TRUE, cCountMsgsForHandleThrottling(pIMailMsgProperties));
  3208. if (FAILED(hr))
  3209. {
  3210. HandleAQFailure(AQ_FAILURE_PRECAT_RETRY, hr, pIMailMsgProperties);
  3211. goto Exit;
  3212. }
  3213. //
  3214. // Adjust counters as appropriate
  3215. //
  3216. InterlockedIncrement((PLONG) &m_cCurrentMsgsPendingCat);
  3217. cIncMsgsInSystem();
  3218. //
  3219. // Kick off cat retry if needed
  3220. //
  3221. ScheduleInternalRetry(LI_TYPE_PENDING_CAT);
  3222. Exit:
  3223. if (pIMailMsgProperties)
  3224. pIMailMsgProperties->Release();
  3225. TraceFunctLeave();
  3226. }
  3227. //---[ CAQSvrInst::HandleCatFailure ]------------------------------------------
  3228. //
  3229. //
  3230. // Description:
  3231. // Handles the details of post cat DSN generation. Will put the
  3232. // message in the failed queue if DSN generation fails
  3233. // Parameters:
  3234. // pIUnknown IUnkown for mailmsg
  3235. // hrCatResult Error code returned by cat
  3236. // Returns:
  3237. // -
  3238. // History:
  3239. // 11/11/1999 - MikeSwa Created
  3240. //
  3241. //-----------------------------------------------------------------------------
  3242. void CAQSvrInst::HandleCatFailure(IUnknown *pIUnknown, HRESULT hrCatResult)
  3243. {
  3244. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HandleCatFailure");
  3245. HRESULT hr = S_OK;
  3246. IMailMsgProperties *pIMailMsgProperties = NULL;
  3247. BOOL fHasShutdownLock = FALSE;
  3248. ErrorTrace((LPARAM) this,
  3249. "ERROR: MsgCat failed, will try to NDR message - hr 0x%08X",
  3250. hrCatResult);
  3251. InterlockedDecrement((PLONG) &m_cCurrentMsgsPendingCat);
  3252. DecMsgsInSystem(FALSE, FALSE);
  3253. const char *rgszStrings[1] = { NULL };
  3254. if(!pIUnknown)
  3255. goto Exit;
  3256. //If we are shutting down, this error could be caused by a shutdown being
  3257. //signaled. If this is the case, we do not want to log an error or
  3258. //generate an NDR.
  3259. if (!fTryShutdownLock())
  3260. goto Exit;
  3261. fHasShutdownLock = TRUE;
  3262. HrTriggerLogEvent(
  3263. AQUEUE_CAT_FAILED, // Message ID
  3264. TRAN_CAT_QUEUE_ENGINE, // Category
  3265. 1, // Word count of substring
  3266. rgszStrings, // Substring
  3267. EVENTLOG_WARNING_TYPE, // Type of the message
  3268. hrCatResult, // error code
  3269. LOGEVENT_LEVEL_MEDIUM, // Logging level
  3270. "phatq", // key to this event
  3271. LOGEVENT_FLAG_PERIODIC, // Logging option
  3272. 0, // index of format message string in rgszStrings
  3273. GetModuleHandle(AQ_MODULE_NAME) // module handle to format a message
  3274. );
  3275. hr = pIUnknown->QueryInterface(IID_IMailMsgProperties,
  3276. (void **) &pIMailMsgProperties);
  3277. _ASSERT(SUCCEEDED(hr) && "QueryInterface for IID_IMailMsgProperties FAILED");
  3278. if (FAILED(hr))
  3279. goto Exit;
  3280. // we ignore errors on this since it is only to help debug
  3281. // cat failures
  3282. pIMailMsgProperties->PutDWORD(IMMPID_MP_HR_CAT_STATUS,
  3283. hrCatResult);
  3284. hr = HandleFailedMessage(pIMailMsgProperties,
  3285. TRUE,
  3286. NULL,
  3287. MESSAGE_FAILURE_CAT,
  3288. hrCatResult);
  3289. Exit:
  3290. if (pIMailMsgProperties)
  3291. pIMailMsgProperties->Release();
  3292. if (fHasShutdownLock)
  3293. ShutdownUnlock();
  3294. TraceFunctLeave();
  3295. }
  3296. //+------------------------------------------------------------
  3297. //
  3298. // Function: CAQSvrInst::ResetRoutes
  3299. //
  3300. // Synopsis: This is a sink callback function; sinks will call this
  3301. // function when they wish to reset next hop routes or message types.
  3302. //
  3303. // Arguments:
  3304. // dwResetType: Must be either RESET_NEXT_HOPS or RESET_MESSAGE_TYPES
  3305. //
  3306. // Returns:
  3307. // S_OK: Success
  3308. // E_INVALIDARG: bogus dwResetType
  3309. //
  3310. // History:
  3311. // jstamerj 1998/07/10 19:27:45: Created.
  3312. // 3/9/99 - MikeSwa Added async reset
  3313. //
  3314. //-------------------------------------------------------------
  3315. STDMETHODIMP CAQSvrInst::ResetRoutes(
  3316. IN DWORD dwResetType)
  3317. {
  3318. TraceFunctEnterEx((LPARAM)this, "CAQSvrInst::ResetRoutes");
  3319. HRESULT hr = S_OK;
  3320. InterlockedIncrement((PLONG) &m_cTotalResetRoutes);
  3321. if(dwResetType == RESET_NEXT_HOPS) {
  3322. DebugTrace((LPARAM)this, "ResetNextHops called");
  3323. if (1 == InterlockedIncrement((PLONG) &m_cCurrentPendingResetRoutes))
  3324. {
  3325. DebugTrace((LPARAM) this, "Adding ResetRoutes operation to work queue");
  3326. AddRef(); //released in completion function
  3327. hr = HrQueueWorkItem(this, CAQSvrInst::fResetRoutesNextHopCompletion);
  3328. //Failure will still call completion function, so we should not release
  3329. }
  3330. else
  3331. {
  3332. DebugTrace((LPARAM) this, "Other ResetRoutes pending... only one pending allowed");
  3333. InterlockedDecrement((PLONG) &m_cCurrentPendingResetRoutes);
  3334. }
  3335. } else if(dwResetType == RESET_MESSAGE_TYPES) {
  3336. DebugTrace((LPARAM)this, "ResetMessageTypes called");
  3337. //$$TODO: Reset message types
  3338. } else {
  3339. ErrorTrace((LPARAM)this, "ResetRoutes called with bogus dwResetType %08lx",
  3340. dwResetType);
  3341. hr = E_INVALIDARG;
  3342. }
  3343. return hr;
  3344. }
  3345. //---[ CAQSvrInst::LogResetRouteEvent ]----------------------------------------
  3346. //
  3347. //
  3348. // Description:
  3349. // Log statistics on resetroute
  3350. // Parameters:
  3351. // dwObtainLock time spend on obtaining exclusive lock
  3352. // dwWaitLock time spend on waiting for the lock
  3353. // dwQueue queue length at the moment
  3354. // History:
  3355. // 11/10/2000 haozhang created
  3356. //
  3357. //-----------------------------------------------------------------------------
  3358. void CAQSvrInst::LogResetRouteEvent( DWORD dwObtainLock,
  3359. DWORD dwWaitLock,
  3360. DWORD dwQueue)
  3361. {
  3362. LPSTR lpstr[3];
  3363. char subStrings[3][13];
  3364. sprintf (subStrings[0],"%d",dwObtainLock);
  3365. sprintf (subStrings[1],"%d",dwWaitLock);
  3366. sprintf (subStrings[2],"%d",dwQueue);
  3367. lpstr[0] = subStrings[0];
  3368. lpstr[1] = subStrings[1];
  3369. lpstr[2] = subStrings[2];
  3370. HrTriggerLogEvent(
  3371. AQUEUE_RESETROUTE_DIAGNOSTIC, // Message ID
  3372. TRAN_CAT_QUEUE_ENGINE, // Category ID
  3373. 3, // Word count of substring
  3374. (LPCSTR *) lpstr, // Substring
  3375. EVENTLOG_INFORMATION_TYPE, // Type of the message
  3376. 0, // No error code
  3377. LOGEVENT_LEVEL_MEDIUM, // Debug level
  3378. NULL, // Key to identify this event
  3379. LOGEVENT_FLAG_ALWAYS
  3380. );
  3381. }
  3382. //---[ CAQSvrInst::fResetRoutesNextHopCompletion ]-----------------------------
  3383. //
  3384. //
  3385. // Description:
  3386. // Completion function that handles async reset routes
  3387. // Parameters:
  3388. // pvThis Ptr to CAQSvrInst
  3389. // dwStatus Status returned by
  3390. // Returns:
  3391. // TRUE always
  3392. // History:
  3393. // 3/9/99 - MikeSwa Created
  3394. //
  3395. //-----------------------------------------------------------------------------
  3396. BOOL CAQSvrInst::fResetRoutesNextHopCompletion(PVOID pvThis, DWORD dwStatus)
  3397. {
  3398. TraceFunctEnterEx((LPARAM) pvThis, "CAQSvrInst::fResetRoutesNextHopCompletion");
  3399. CAQSvrInst *paqinst = (CAQSvrInst *) pvThis;
  3400. DWORD cCurrentPendingResetRoutes = 0;
  3401. DWORD dwPreLock;
  3402. DWORD dwObtainLock;
  3403. DWORD dwReleaseLock;
  3404. DWORD dwQueue;
  3405. HRESULT hr = S_OK;
  3406. _ASSERT(paqinst);
  3407. if (ASYNC_WORK_QUEUE_NORMAL == dwStatus)
  3408. {
  3409. if (paqinst && paqinst->fTryShutdownLock())
  3410. {
  3411. DebugTrace((LPARAM) paqinst, "Rerouting domains");
  3412. dwPreLock = GetTickCount();
  3413. paqinst->m_slPrivateData.ExclusiveLock();
  3414. dwObtainLock = GetTickCount();
  3415. dwQueue = paqinst->m_cCurrentRemoteDestQueues;
  3416. //Drop pending reset routes count here. We should do it after
  3417. //we grab the lock to prevent too many threads from
  3418. //trying to grab it exclusively. We also need to do it
  3419. //before we actual update any routing info in case a ResetRoutes
  3420. //is requested midway through this update.
  3421. cCurrentPendingResetRoutes = InterlockedDecrement((PLONG)
  3422. &(paqinst->m_cCurrentPendingResetRoutes));
  3423. //Make sure the count hasn't gone negative
  3424. _ASSERT(cCurrentPendingResetRoutes < 0xFFFFFF00);
  3425. //With the lock held, call HrBeginRerouteDomains. This function
  3426. //will flag a reroute in progress and will move all domains to
  3427. //the currently unreachable queue.
  3428. hr = paqinst->m_dmt.HrBeginRerouteDomains();
  3429. paqinst->m_slPrivateData.ExclusiveUnlock();
  3430. dwReleaseLock = GetTickCount();
  3431. // If the first part failed, we don't do the second part
  3432. if(SUCCEEDED(hr))
  3433. {
  3434. //Now, having released the lock, call HrCompleteRerouteDomains.
  3435. //This function will reroute the contents of the currently
  3436. //unreachable queue and will then unflag the reroute in progress.
  3437. paqinst->m_dmt.HrCompleteRerouteDomains();
  3438. }
  3439. //If things have been re-routing to a special link... we should
  3440. //process them as well.
  3441. paqinst->m_dmt.ProcessSpecialLinks(paqinst->m_dwDelayExpireMinutes,
  3442. FALSE);
  3443. paqinst->ShutdownUnlock();
  3444. //
  3445. // Log Event of ResetRoute
  3446. //
  3447. paqinst->LogResetRouteEvent(
  3448. dwObtainLock - dwPreLock, // time to obtain the exclusive lock
  3449. dwReleaseLock - dwObtainLock, // time waiting on the lock
  3450. dwQueue // number of queues
  3451. );
  3452. }
  3453. }
  3454. else
  3455. {
  3456. if (paqinst)
  3457. {
  3458. cCurrentPendingResetRoutes = InterlockedDecrement((PLONG)
  3459. &(paqinst->m_cCurrentPendingResetRoutes));
  3460. //Make sure the count hasn't gone negative
  3461. _ASSERT(cCurrentPendingResetRoutes < 0xFFFFFF00);
  3462. }
  3463. if (ASYNC_WORK_QUEUE_FAILURE & dwStatus)
  3464. ErrorTrace((LPARAM) paqinst, "ResetRoutes completion failure");
  3465. }
  3466. if (paqinst)
  3467. paqinst->Release();
  3468. TraceFunctLeave();
  3469. return TRUE;
  3470. }
  3471. //---[ CAQSvrInst::GetDomainInfoFlags ]--------------------------------------
  3472. //
  3473. //
  3474. // Description:
  3475. // Determines if a domain is local (has DOMAIN_INFO_LOCAL_MAILBOX set)
  3476. // Parameters:
  3477. // IN szDomainName Name of domain to check for
  3478. // OUT pdwDomainInfoFlags DomainInfo flags for this domain
  3479. // Returns:
  3480. // S_OK on success
  3481. // E_INVALIDARG if szDomainName or pdwDomainInfoFlags is NULL
  3482. // History:
  3483. // 7/29/98 - MikeSwa Created
  3484. //
  3485. //-----------------------------------------------------------------------------
  3486. STDMETHODIMP CAQSvrInst::GetDomainInfoFlags(
  3487. IN LPSTR szDomainName,
  3488. OUT DWORD *pdwDomainInfoFlags)
  3489. {
  3490. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::GetDomainInfoFlags");
  3491. BOOL fLocked = FALSE;
  3492. HRESULT hr = S_OK;
  3493. DWORD cbDomainName = 0;
  3494. CInternalDomainInfo *pIntDomainInfo = NULL;
  3495. ISMTPServerGetAuxDomainInfoFlags *pISMTPServerGetAuxDomainInfoFlags = NULL;
  3496. DWORD dwSinkDomainFlags = 0;
  3497. _ASSERT(pdwDomainInfoFlags && "Invalid Param");
  3498. _ASSERT(szDomainName && "Invalid Param");
  3499. if (!pdwDomainInfoFlags || !szDomainName)
  3500. {
  3501. hr = E_INVALIDARG;
  3502. goto Exit;
  3503. }
  3504. if (!fTryShutdownLock())
  3505. {
  3506. hr = AQUEUE_E_SHUTDOWN;
  3507. goto Exit;
  3508. }
  3509. fLocked = TRUE;
  3510. cbDomainName = lstrlen(szDomainName);
  3511. hr = m_dct.HrGetInternalDomainInfo(cbDomainName, szDomainName,
  3512. &pIntDomainInfo);
  3513. if (FAILED(hr))
  3514. goto Exit;
  3515. _ASSERT(pIntDomainInfo);
  3516. // Aux Domain info not found, use the config we got from our own tables
  3517. *pdwDomainInfoFlags = pIntDomainInfo->m_DomainInfo.dwDomainInfoFlags;
  3518. // We should have gotten back domain config even if it is only
  3519. // the default config - now we need to see if we can get more
  3520. // specific data from an event sink
  3521. if (!cbDomainName || pIntDomainInfo->m_DomainInfo.szDomainName[0] == '*')
  3522. {
  3523. // QI for ISMTPServerGetAuxDomainInfoFlags interface
  3524. hr = m_pISMTPServer->QueryInterface(
  3525. IID_ISMTPServerGetAuxDomainInfoFlags,
  3526. (LPVOID *)&pISMTPServerGetAuxDomainInfoFlags);
  3527. if (FAILED(hr)) {
  3528. ErrorTrace((LPARAM) this,
  3529. "Unable to QI for ISMTPServerGetAuxDomainInfoFlags 0x%08X",hr);
  3530. // Drop this error, this isn't fatal
  3531. hr = S_OK;
  3532. goto Exit;
  3533. }
  3534. // Check for domain info
  3535. hr = pISMTPServerGetAuxDomainInfoFlags->HrTriggerGetAuxDomainInfoFlagsEvent(
  3536. szDomainName,
  3537. &dwSinkDomainFlags);
  3538. if (FAILED(hr)) {
  3539. ErrorTrace((LPARAM) this,
  3540. "Failed calling HrTriggerGetAuxDomainInfoFlags 0x%08X",hr);
  3541. // Drop this error, this isn't fatal
  3542. hr = S_OK;
  3543. goto Exit;
  3544. }
  3545. if (dwSinkDomainFlags & DOMAIN_INFO_INVALID) {
  3546. // Domain info not found from event sink
  3547. hr = S_OK;
  3548. goto Exit;
  3549. }
  3550. // Ok, we got Aux Domain info, use it
  3551. *pdwDomainInfoFlags = dwSinkDomainFlags;
  3552. }
  3553. Exit:
  3554. if (pISMTPServerGetAuxDomainInfoFlags)
  3555. pISMTPServerGetAuxDomainInfoFlags->Release();
  3556. if (pIntDomainInfo)
  3557. pIntDomainInfo->Release();
  3558. if (fLocked)
  3559. ShutdownUnlock();
  3560. TraceFunctLeave();
  3561. return hr;
  3562. }
  3563. //+------------------------------------------------------------
  3564. //
  3565. // Function: CAQSvrInst::GetMessageRouter
  3566. //
  3567. // Synopsis: Default functionality of GetMessageRouter
  3568. // If there is no current IMessageRouter, provide the
  3569. // default IMessageRouter
  3570. //
  3571. // Arguments:
  3572. // pIMailMsgProperties: MailMsg that needs a router
  3573. // pICurrentRouter: current sink provided router
  3574. // ppIMessageRouter: out param for new IMessageRouter
  3575. //
  3576. // Returns:
  3577. // S_OK: Success, provided IMessageRouter
  3578. // E_NOTIMPL: Didn't provide an IMessageRouter
  3579. //
  3580. // History:
  3581. // jstamerj 1998/07/10 19:33:41: Created.
  3582. //
  3583. //-------------------------------------------------------------
  3584. STDMETHODIMP CAQSvrInst::GetMessageRouter(
  3585. IN IMailMsgProperties *pIMailMsgProperties,
  3586. IN IMessageRouter *pICurrentMessageRouter,
  3587. OUT IMessageRouter **ppIMessageRouter)
  3588. {
  3589. _ASSERT(ppIMessageRouter);
  3590. TraceFunctEnterEx((LPARAM)this, "CAQSvrInst::GetMessageRouter");
  3591. if((pICurrentMessageRouter == NULL) &&
  3592. (m_pIMessageRouterDefault)) {
  3593. //
  3594. // Return our default IMessageRouter and AddRef for the caller
  3595. //
  3596. *ppIMessageRouter = m_pIMessageRouterDefault;
  3597. m_pIMessageRouterDefault->AddRef();
  3598. DebugTrace((LPARAM)this, "Supplying default IMessageRouter");
  3599. TraceFunctLeaveEx((LPARAM)this);
  3600. return S_OK;
  3601. } else {
  3602. TraceFunctLeaveEx((LPARAM)this);
  3603. return E_NOTIMPL;
  3604. }
  3605. }
  3606. //---[ CAQSvrInst::HrTriggerDSNGenerationEvent ]-----------------------------
  3607. //
  3608. //
  3609. // Description:
  3610. // Triggers DSN Generation event
  3611. // Parameters:
  3612. // pdsnparams A CDSNParams that will be used to trigger event
  3613. // fHasRoutingLock TRUE if routing lock is current held by this thread
  3614. // Returns:
  3615. // S_OK on success (and DSN was generated)
  3616. // S_FALSE on success, but no DSN generated
  3617. // AQUEUE_E_NOT_INITIALIZED if not initialized correctly
  3618. // History:
  3619. // 7/11/98 - MikeSwa Created
  3620. //
  3621. //-----------------------------------------------------------------------------
  3622. HRESULT CAQSvrInst::HrTriggerDSNGenerationEvent(CDSNParams *pdsnparams,
  3623. BOOL fHasRoutingLock)
  3624. {
  3625. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HrTriggerDSNGenerationEvent");
  3626. HRESULT hr = S_OK;
  3627. DWORD cCurrent = 0;
  3628. CRefCountedString *prstrDefaultDomain = NULL;
  3629. LPSTR szDefaultDomain = NULL;
  3630. CRefCountedString *prstrCopyNDRTo = NULL;
  3631. LPSTR szCopyNDRTo = NULL;
  3632. CRefCountedString *prstrFQDN = NULL;
  3633. LPSTR szFQDN = NULL;
  3634. DWORD cCurrentDSNsGenerated = 0;
  3635. DWORD cbCurrentSize = 0;
  3636. FILETIME *pftExpireTime = NULL;
  3637. FILETIME ftExpireTime;
  3638. if (!(m_dwInitMask & CMQ_INIT_DSN) || !m_pISMTPServer)
  3639. {
  3640. hr = AQUEUE_E_NOT_INITIALIZED;
  3641. goto Exit;
  3642. }
  3643. //Get config string from ref-counted objects
  3644. if (!fHasRoutingLock)
  3645. m_slPrivateData.ShareLock();
  3646. else
  3647. m_slPrivateData.AssertIsLocked();
  3648. if (m_prstrDefaultDomain)
  3649. {
  3650. prstrDefaultDomain = m_prstrDefaultDomain;
  3651. prstrDefaultDomain->AddRef();
  3652. szDefaultDomain = prstrDefaultDomain->szStr();
  3653. }
  3654. else
  3655. {
  3656. //we need to have something as our default domain
  3657. szDefaultDomain = "localhost";
  3658. }
  3659. if (m_prstrCopyNDRTo)
  3660. {
  3661. prstrCopyNDRTo = m_prstrCopyNDRTo;
  3662. prstrCopyNDRTo->AddRef();
  3663. szCopyNDRTo = prstrCopyNDRTo->szStr();
  3664. }
  3665. if (m_prstrServerFQDN)
  3666. {
  3667. prstrFQDN = m_prstrServerFQDN;
  3668. prstrFQDN->AddRef();
  3669. szFQDN = prstrFQDN->szStr();
  3670. }
  3671. if (!fHasRoutingLock)
  3672. m_slPrivateData.ShareUnlock();
  3673. //
  3674. // Get the expire time
  3675. //
  3676. hr = pdsnparams->pIMailMsgProperties->GetProperty(
  3677. IMMPID_MP_EXPIRE_NDR,
  3678. sizeof(FILETIME),
  3679. &cbCurrentSize,
  3680. (BYTE *) &ftExpireTime);
  3681. if (SUCCEEDED(hr))
  3682. {
  3683. _ASSERT(sizeof(FILETIME) == cbCurrentSize);
  3684. pftExpireTime = &ftExpireTime;
  3685. }
  3686. else if (MAILMSG_E_PROPNOTFOUND == hr)
  3687. {
  3688. //
  3689. // Calculate the expire time based on the arrival time
  3690. //
  3691. hr = pdsnparams->pIMailMsgProperties->GetProperty(
  3692. IMMPID_MP_ARRIVAL_FILETIME,
  3693. sizeof(FILETIME),
  3694. &cbCurrentSize,
  3695. (BYTE *) &ftExpireTime);
  3696. if(SUCCEEDED(hr))
  3697. {
  3698. CalcExpireTimeNDR(ftExpireTime, FALSE, &ftExpireTime);
  3699. pftExpireTime = &ftExpireTime;
  3700. }
  3701. else if(hr == MAILMSG_E_PROPNOTFOUND)
  3702. hr = S_OK;
  3703. else
  3704. goto Exit;
  3705. }
  3706. else
  3707. goto Exit;
  3708. //
  3709. // Set the CDSNParam's paqinst pointer so that
  3710. // CDSNParams::HrSubmitDSN call call back into this CAQSvrInst
  3711. // object.
  3712. //
  3713. pdsnparams->paqinst = this;
  3714. hr = m_dsnsink.GenerateDSN(
  3715. this,
  3716. m_dwServerInstance,
  3717. m_pISMTPServer,
  3718. pdsnparams->pIMailMsgProperties,
  3719. pdsnparams->dwStartDomain,
  3720. pdsnparams->dwDSNActions,
  3721. pdsnparams->dwRFC821Status,
  3722. pdsnparams->hrStatus,
  3723. szDefaultDomain,
  3724. szFQDN,
  3725. (CHAR *) DEFAULT_MTA_TYPE,
  3726. pdsnparams->szDebugContext,
  3727. m_dwDSNLanguageID,
  3728. m_dwDSNOptions,
  3729. szCopyNDRTo,
  3730. pftExpireTime,
  3731. pdsnparams,
  3732. g_dwMaxDSNSize);
  3733. if (SUCCEEDED(hr))
  3734. {
  3735. if(pdsnparams->dwDSNTypesGenerated)
  3736. {
  3737. // A DSN was generated
  3738. hr = S_OK;
  3739. }
  3740. else
  3741. {
  3742. // No DSN was generated
  3743. hr = S_FALSE;
  3744. }
  3745. }
  3746. else if (AQUEUE_E_NDR_OF_DSN == hr)
  3747. {
  3748. hr = S_FALSE; //report as no DSN generated
  3749. //original message is badmail
  3750. HandleBadMail(pdsnparams->pIMailMsgProperties, TRUE, NULL,
  3751. AQUEUE_E_NDR_OF_DSN, fHasRoutingLock);
  3752. InterlockedIncrement((PLONG) &m_cBadmailNdrOfDsn);
  3753. }
  3754. else
  3755. {
  3756. //bail out on failure
  3757. InterlockedIncrement((PLONG) &m_cTotalDSNFailures);
  3758. //
  3759. // Check to see if the message has been deleted... store driver
  3760. // has been gone away.
  3761. //
  3762. if (!fShouldRetryMessage(pdsnparams->pIMailMsgProperties, FALSE))
  3763. {
  3764. DebugTrace((LPARAM) this, "Msg no longer valid... abandoning");
  3765. hr = S_FALSE;
  3766. }
  3767. goto Exit;
  3768. }
  3769. Exit:
  3770. if (prstrDefaultDomain)
  3771. prstrDefaultDomain->Release();
  3772. if (prstrCopyNDRTo)
  3773. prstrCopyNDRTo->Release();
  3774. if (prstrFQDN)
  3775. prstrFQDN->Release();
  3776. TraceFunctLeave();
  3777. return hr;
  3778. }
  3779. //---[ CAQSvrInst::HrNDRUnresolvedRecipients ]-------------------------------
  3780. //
  3781. //
  3782. // Description:
  3783. // NDR any unresolved recipients for a given IMailMsgProperties. Also
  3784. // generates a expanded DSNs
  3785. // Parameters:
  3786. // IN pIMailMsgProperties IMailMsgProperties to generate NDR for
  3787. // IN pIMailMsgRecipients Recipients interface for message
  3788. // Returns:
  3789. // S_OK on success and message should continue through transport
  3790. // S_FALSE on success, but message should not be queued for delivery
  3791. // History:
  3792. // 7/21/98 - MikeSwa Created
  3793. // 10/14/98 - MikeSwa Modified to use common utility functions
  3794. //
  3795. //-----------------------------------------------------------------------------
  3796. HRESULT CAQSvrInst::HrNDRUnresolvedRecipients(
  3797. IMailMsgProperties *pIMailMsgProperties,
  3798. IMailMsgRecipients *pIMailMsgRecipients)
  3799. {
  3800. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HrNDRUnresolvedRecipients");
  3801. HRESULT hr = S_OK;
  3802. HRESULT hrCat = S_OK; //cat HRESULT
  3803. DWORD cbProp = 0;
  3804. DWORD iCurrentDomain = 0;
  3805. DWORD cRecips = 0;
  3806. _ASSERT(pIMailMsgProperties);
  3807. hr = pIMailMsgProperties->GetProperty(IMMPID_MP_HR_CAT_STATUS, sizeof(HRESULT),
  3808. &cbProp, (BYTE *) &hrCat);
  3809. if (FAILED(hr))
  3810. {
  3811. if (MAILMSG_E_PROPNOTFOUND == hr) //no result... don't generate DSN
  3812. hr = S_OK; //not really an error
  3813. goto Exit;
  3814. }
  3815. if (CAT_W_SOME_UNDELIVERABLE_MSGS == hrCat)
  3816. {
  3817. //There was an error resolving recipients
  3818. //We need to NDR all recipients with hard errors (like RP_UNRESOLVED)
  3819. //and expand any recipient marked expanded.
  3820. CDSNParams dsnparams;
  3821. dsnparams.dwStartDomain = 0;
  3822. dsnparams.dwDSNActions = DSN_ACTION_FAILURE | DSN_ACTION_EXPANDED;
  3823. dsnparams.pIMailMsgProperties = pIMailMsgProperties;
  3824. dsnparams.hrStatus = CAT_W_SOME_UNDELIVERABLE_MSGS;
  3825. hr = HrLinkAllDomains(pIMailMsgProperties);
  3826. if (FAILED(hr))
  3827. goto Exit;
  3828. //Fire DSN Generation event
  3829. SET_DEBUG_DSN_CONTEXT(dsnparams, __LINE__);
  3830. hr = HrTriggerDSNGenerationEvent(&dsnparams, FALSE);
  3831. if (FAILED(hr))
  3832. goto Exit;
  3833. //Check to see how many recipients have been NDRd
  3834. hr = pIMailMsgRecipients->Count(&cRecips);
  3835. if (FAILED(hr))
  3836. {
  3837. ErrorTrace((LPARAM) this,
  3838. "ERROR: IMailMsgRecipients::Count() FAILED - hr 0x%08X", hr);
  3839. goto Exit;
  3840. }
  3841. //If all recipients have been handled... return S_FALSE
  3842. if (dsnparams.cRecips == cRecips)
  3843. {
  3844. hr = S_FALSE;
  3845. }
  3846. else
  3847. {
  3848. hr = S_OK;
  3849. }
  3850. }
  3851. else
  3852. {
  3853. hr = S_OK;
  3854. }
  3855. Exit:
  3856. TraceFunctLeave();
  3857. return hr;
  3858. }
  3859. //---[ CAQSvrInst::fPreLocalDeliveryQueueCompletion ]------------------------
  3860. //
  3861. //
  3862. // Description:
  3863. // Completion function for PerLocal delivery queue
  3864. // Parameters:
  3865. // pmsgref - Msgref to attempt delivery for
  3866. // Returns:
  3867. // TRUE If Delivery attempt was handled (delivered or NDR'd)
  3868. // FALSE If MsgRef needs to be requeued
  3869. // History:
  3870. // 7/17/98 - MikeSwa Created
  3871. //
  3872. //-----------------------------------------------------------------------------
  3873. BOOL CAQSvrInst::fPreLocalDeliveryQueueCompletion(CMsgRef *pmsgref)
  3874. {
  3875. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::fPreLocalDeliveryQueueCompletion");
  3876. HRESULT hr = S_OK;
  3877. BOOL fMsgHandled = TRUE;
  3878. BOOL fLocked = FALSE; //TRUE if locked for shutdown
  3879. DWORD cRecips = 0;
  3880. DWORD *rgdwRecips= 0;
  3881. CAQStats aqstat;
  3882. CLinkMsgQueue *plmq = NULL;
  3883. BOOL fReleaseLDNotify = FALSE;
  3884. CAQLocalDeliveryNotify *pLDNotify = NULL;
  3885. BOOL fUpdateCounters = TRUE;
  3886. if (NULL == m_pISMTPServer) {
  3887. ErrorTrace((LPARAM) this,
  3888. "ERROR: Local Delivery not configured properly");
  3889. goto Exit;
  3890. }
  3891. if (NULL == pmsgref) {
  3892. ErrorTrace((LPARAM) this,
  3893. "ERROR: Local Delivery not configured properly, msgref=NULL");
  3894. goto Exit;
  3895. }
  3896. if (!fTryShutdownLock()) {
  3897. goto Exit;
  3898. }
  3899. pLDNotify = new CAQLocalDeliveryNotify(this, pmsgref);
  3900. if (!pLDNotify) {
  3901. ErrorTrace((LPARAM) this,
  3902. "ERROR: new CAQLocalDeliveryNotify failed");
  3903. fMsgHandled = FALSE;
  3904. goto Exit;
  3905. }
  3906. fReleaseLDNotify = TRUE;
  3907. fLocked = TRUE;
  3908. if (pmsgref->fIsMsgFrozen())
  3909. {
  3910. //Message is frozen... requeue message
  3911. fMsgHandled = FALSE;
  3912. goto Exit;
  3913. }
  3914. hr = m_dmt.HrPrepareForLocalDelivery(pmsgref,
  3915. FALSE,
  3916. pLDNotify->pdcntxtGetDeliveryContext(),
  3917. &cRecips,
  3918. &rgdwRecips);
  3919. if (FAILED(hr))
  3920. {
  3921. if ((AQUEUE_E_MESSAGE_HANDLED != hr) && (AQUEUE_E_MESSAGE_PENDING != hr))
  3922. {
  3923. //message will be retried when last reference is released.
  3924. pmsgref->RetryOnDelete();
  3925. ErrorTrace((LPARAM) this, "ERROR: HrPrepareLocalDelivery FAILED - hr 0x%08X", hr);
  3926. }
  3927. fMsgHandled = TRUE;
  3928. hr = S_OK;
  3929. goto Exit;
  3930. }
  3931. //Increase Ref count for message ref (as if it was actually queued)
  3932. pmsgref->AddRef();
  3933. //Send off for local delivery
  3934. InterlockedIncrement((PLONG) &m_cCurrentMsgsInLocalDelivery);
  3935. MSG_TRACK_INFO msgTrackInfo;
  3936. ZeroMemory( &msgTrackInfo, sizeof( msgTrackInfo ) );
  3937. msgTrackInfo.dwEventId = MTE_LOCAL_DELIVERY;
  3938. msgTrackInfo.cRcpts = cRecips;
  3939. m_pISMTPServer->WriteLog(&msgTrackInfo,
  3940. pLDNotify->pimsgGetIMsg(),
  3941. NULL,
  3942. NULL);
  3943. // from here on we will end up calling LDCompletion, and they will
  3944. // release it
  3945. fReleaseLDNotify = FALSE;
  3946. m_asyncqPreLocalDeliveryQueue.IncPendingAsyncCompletions();
  3947. // we need to hold a reference on pLDNotify to be able to call
  3948. // into fNotCalledCompletion
  3949. pLDNotify->AddRef();
  3950. fUpdateCounters = FALSE;
  3951. if (m_pISMTPServerAsync) {
  3952. hr = m_pISMTPServerAsync->TriggerLocalDeliveryAsync(
  3953. pLDNotify->pimsgGetIMsg(),
  3954. cRecips,
  3955. rgdwRecips,
  3956. pLDNotify);
  3957. } else {
  3958. hr = m_pISMTPServer->TriggerLocalDelivery(
  3959. pLDNotify->pimsgGetIMsg(),
  3960. cRecips,
  3961. rgdwRecips);
  3962. }
  3963. // if we get back any error code besides MAILMSG_S_PENDING then
  3964. // the dispatcher completed sync. We need to see if the
  3965. // completion function was called, and if not then we need to call
  3966. // it ourselves.
  3967. if (hr != MAILMSG_S_PENDING && pLDNotify->fNotCalledCompletion()) {
  3968. LDCompletion(hr, this, pmsgref, pLDNotify);
  3969. }
  3970. // after this point we can't call pLDNotify
  3971. pLDNotify->Release();
  3972. Exit:
  3973. if (fUpdateCounters && fMsgHandled) {
  3974. UpdateLDCounters(pmsgref);
  3975. }
  3976. if (fReleaseLDNotify) {
  3977. pLDNotify->Release();
  3978. }
  3979. if (fLocked)
  3980. ShutdownUnlock();
  3981. TraceFunctLeave();
  3982. return fMsgHandled;
  3983. }
  3984. //---[ CAQSvrInst::LDCompletion ]----------------------------------------------
  3985. //
  3986. //
  3987. // Description:
  3988. // Completion function local delivery
  3989. // Parameters:
  3990. // hrLDResult - local delivery status
  3991. // pContext - this
  3992. // pmsgref - msgref that we were delivering.
  3993. // History:
  3994. // 10/30/2000 - AWetmore Created
  3995. //
  3996. //-----------------------------------------------------------------------------
  3997. void CAQSvrInst::LDCompletion(HRESULT hr,
  3998. PVOID pContext,
  3999. CMsgRef *pmsgref,
  4000. CAQLocalDeliveryNotify *pLDNotify)
  4001. {
  4002. TraceFunctEnter("CAQSvrInst::LDCompletion");
  4003. BOOL fMsgHandled = TRUE;
  4004. MessageAck *pMsgAck = pLDNotify->pmsgackGetMsgAck();
  4005. CAQSvrInst *pThis = (CAQSvrInst *) pContext;
  4006. InterlockedDecrement((PLONG) &(pThis->m_cCurrentMsgsInLocalDelivery));
  4007. pThis->m_asyncqPreLocalDeliveryQueue.DecPendingAsyncCompletions();
  4008. if (FAILED(hr)) {
  4009. //We will need to handle in one of 2 ways:
  4010. // - set fMsgHandled to FALSE (on STOREDRV_E_RETRY)
  4011. // - NDR the message (on other errors)
  4012. if (STOREDRV_E_RETRY == hr) {
  4013. //try... try again
  4014. DebugTrace((LPARAM) pmsgref, "INFO: Msg queued for local retry");
  4015. fMsgHandled = FALSE;
  4016. pMsgAck->dwMsgStatus = MESSAGE_STATUS_RETRY;
  4017. pThis->ScheduleInternalRetry(LI_TYPE_LOCAL_DELIVERY);
  4018. } else {
  4019. ErrorTrace((LPARAM) pmsgref, "ERROR: Local delivery failed. - hr 0x%08X", hr);
  4020. pMsgAck->dwMsgStatus = MESSAGE_STATUS_NDR_ALL;
  4021. }
  4022. } else {
  4023. InterlockedIncrement(&(pThis->m_cMsgsDeliveredLocal));
  4024. }
  4025. pMsgAck->pIMailMsgProperties = pLDNotify->pimsgGetIMsg();
  4026. pMsgAck->pvMsgContext = (DWORD *) pLDNotify->pdcntxtGetDeliveryContext();
  4027. pMsgAck->dwMsgStatus |= MESSAGE_STATUS_LOCAL_DELIVERY;
  4028. //
  4029. // Make sure we should retry the message. We want to do this before we
  4030. // ACK the message so that we do not reopen the P1 stream if we *are*
  4031. // retrying it.
  4032. //
  4033. if (!fMsgHandled)
  4034. fMsgHandled = !pmsgref->fShouldRetry();
  4035. hr = pThis->HrAckMsg(pMsgAck, TRUE);
  4036. if (FAILED(hr)) {
  4037. ErrorTrace((LPARAM) pThis, "ERROR: Local MsgAck failed - hr 0x%08X", hr);
  4038. goto Exit;
  4039. }
  4040. Exit:
  4041. if (fMsgHandled) //we aren't retrying message
  4042. {
  4043. pThis->UpdateLDCounters(pmsgref);
  4044. } else {
  4045. // retry this message
  4046. pThis->HandleLocalRetry(pLDNotify->pmsgrefGetMsgRef());
  4047. }
  4048. // clean up after ourselves
  4049. pLDNotify->Release();
  4050. SleepForPerfAnalysis(g_dwLocalQueueSleepMilliseconds);
  4051. TraceFunctLeave();
  4052. }
  4053. //---[ CAQSvrInst::UpdateLDCounters ]------------------------------------------
  4054. //
  4055. //
  4056. // Description:
  4057. // Update the local delivery queue counters
  4058. // Parameters:
  4059. // pmsgref - msgref that we were delivering.
  4060. // History:
  4061. // 05/11/2001 - AWetmore Created
  4062. //
  4063. //-----------------------------------------------------------------------------
  4064. void CAQSvrInst::UpdateLDCounters(CMsgRef *pmsgref) {
  4065. TraceFunctEnter("CAQSvrInst::UpdateLDCounters");
  4066. CAQStats aqstat;
  4067. CLinkMsgQueue *plmq = NULL;
  4068. HRESULT hr;
  4069. InterlockedDecrement((PLONG) &(m_cCurrentMsgsPendingLocal));
  4070. //
  4071. // Update stats for the local link
  4072. //
  4073. pmsgref->GetStatsForMsg(&aqstat);
  4074. plmq = m_dmt.plmqGetLocalLink();
  4075. if (plmq)
  4076. {
  4077. hr = plmq->HrNotify(&aqstat, FALSE);
  4078. if (FAILED(hr))
  4079. {
  4080. ErrorTrace((LPARAM) this,
  4081. "HrNotify failed... local stats innaccurate 0x%08X", hr);
  4082. hr = S_OK;
  4083. }
  4084. plmq->Release();
  4085. plmq = NULL;
  4086. }
  4087. TraceFunctLeave();
  4088. }
  4089. //---[ CAQSvrInst::HandleLocalRetry ]------------------------------------------
  4090. //
  4091. //
  4092. // Description:
  4093. // Handles LD retry for a single message
  4094. // Parameters:
  4095. // pIUnknown IUnknown for the message to retry
  4096. // Returns:
  4097. // -
  4098. // History:
  4099. // 10/30/2000 - AWetmore Created
  4100. //
  4101. //-----------------------------------------------------------------------------
  4102. void CAQSvrInst::HandleLocalRetry(CMsgRef *pmsgref)
  4103. {
  4104. TraceFunctEnter("CAQSvrInst::HandleLocalRetry");
  4105. IMailMsgProperties *pIMailMsgProperties = pmsgref->pimsgGetIMsg();
  4106. HRESULT hr = S_OK;
  4107. if (pIMailMsgProperties == NULL) {
  4108. _ASSERT(pIMailMsgProperties && "pimsgGetIMsg() failed!!");
  4109. hr = E_POINTER;
  4110. goto Exit;
  4111. }
  4112. //
  4113. // Check and see if the message is still valid
  4114. //
  4115. if (!pmsgref->fShouldRetry())
  4116. goto Exit;
  4117. //
  4118. // Queue it to the local delivery queue
  4119. //
  4120. hr = m_asyncqPreLocalDeliveryQueue.HrQueueRequest(pmsgref, TRUE);
  4121. if (FAILED(hr))
  4122. {
  4123. ErrorTrace((LPARAM) this,
  4124. "Enqueue to local delivery queue failed, 0x%08X", hr);
  4125. pmsgref->RetryOnDelete();
  4126. goto Exit;
  4127. }
  4128. Exit:
  4129. if (pIMailMsgProperties)
  4130. pIMailMsgProperties->Release();
  4131. TraceFunctLeave();
  4132. }
  4133. //---[ CAQSvrInst::HrSetSubmissionTimeIfNecessary ]----------------------------
  4134. //
  4135. //
  4136. // Description:
  4137. // Sets the submission time on the message if it is not already set.
  4138. //
  4139. // Parameters:
  4140. // IN pIMailMsgProperties message to stamp
  4141. // Returns:
  4142. // S_OK on success
  4143. // History:
  4144. // 8/13/98 - MikeSwa Created
  4145. // 10/9/98 - MikeSwa - Changed behavior to that any pre-existing
  4146. // properties will be maintained.
  4147. // 5/16/2001 - dbraun changed to only set submission time
  4148. // (was SetMessageExpiry)
  4149. //
  4150. //-----------------------------------------------------------------------------
  4151. HRESULT CAQSvrInst::HrSetSubmissionTimeIfNecessary(IMailMsgProperties *pIMailMsgProperties)
  4152. {
  4153. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HrSetSubmissionTimeIfNecessary");
  4154. HRESULT hr = S_OK;
  4155. DWORD dwTimeContext = 0;
  4156. DWORD cbProp = 0;
  4157. FILETIME ftSubmitTime;
  4158. _ASSERT(pIMailMsgProperties);
  4159. //Set arrival time
  4160. hr = pIMailMsgProperties->GetProperty(IMMPID_MP_ARRIVAL_FILETIME,
  4161. sizeof(FILETIME), &cbProp, (BYTE *) &ftSubmitTime);
  4162. if (MAILMSG_E_PROPNOTFOUND == hr)
  4163. {
  4164. //Prop not set... we can set it
  4165. m_qtTime.GetExpireTime(0, &ftSubmitTime, &dwTimeContext);
  4166. hr = pIMailMsgProperties->PutProperty(IMMPID_MP_ARRIVAL_FILETIME,
  4167. sizeof(FILETIME), (BYTE *) &ftSubmitTime);
  4168. if (FAILED(hr))
  4169. {
  4170. ErrorTrace((LPARAM) this, "ERROR: Unable to write arrival time to msg");
  4171. goto Exit;
  4172. }
  4173. }
  4174. else if (FAILED(hr))
  4175. {
  4176. goto Exit;
  4177. }
  4178. Exit:
  4179. TraceFunctLeave();
  4180. return hr;
  4181. }
  4182. //---[ CAQSvrInst::CalcExpireTimeNDR ]-----------------------------------------
  4183. //
  4184. //
  4185. // Description:
  4186. // Calculates the message's NDR expire time
  4187. //
  4188. // Parameters:
  4189. // IN ftSubmission time the message was submitted
  4190. // IN fLocal Bool TRUE if we want local time,
  4191. // otherwise returns remote
  4192. // OUT pftExpire Used to return expire time
  4193. // Returns:
  4194. // -
  4195. // History:
  4196. // 5/16/2001 - dbraun created
  4197. //
  4198. //-----------------------------------------------------------------------------
  4199. void CAQSvrInst::CalcExpireTimeNDR(FILETIME ftSubmission, BOOL fLocal, FILETIME *pftExpire)
  4200. {
  4201. if (fLocal)
  4202. m_qtTime.GetExpireTime(ftSubmission, m_dwLocalNDRExpireMinutes, pftExpire);
  4203. else
  4204. m_qtTime.GetExpireTime(ftSubmission, m_dwNDRExpireMinutes, pftExpire);
  4205. }
  4206. //---[ CAQSvrInst::CalcExpireTimeDelay ]---------------------------------------
  4207. //
  4208. //
  4209. // Description:
  4210. // Calculates the message's delay expire time
  4211. //
  4212. // Parameters:
  4213. // IN ftSubmission time the message was submitted
  4214. // IN fLocal Bool TRUE if we want local time,
  4215. // otherwise returns remote
  4216. // OUT pftExpire Used to return expire time
  4217. // Returns:
  4218. // -
  4219. // History:
  4220. // 5/16/2001 - dbraun created
  4221. //
  4222. //-----------------------------------------------------------------------------
  4223. void CAQSvrInst::CalcExpireTimeDelay(FILETIME ftSubmission, BOOL fLocal, FILETIME *pftExpire)
  4224. {
  4225. if (fLocal)
  4226. m_qtTime.GetExpireTime(ftSubmission, m_dwLocalDelayExpireMinutes, pftExpire);
  4227. else
  4228. m_qtTime.GetExpireTime(ftSubmission, m_dwDelayExpireMinutes, pftExpire);
  4229. }
  4230. //---[ CAQSvrInst::AsyncQueueRetry ]-----------------------------------------
  4231. //
  4232. //
  4233. // Description:
  4234. // Restarts an async queue after a failure.
  4235. // Parameters:
  4236. // dwQueueID Tells which queue to kick
  4237. // PRELOCAL_QUEUE_ID Retries pre-local queue
  4238. // PRECAT_QUEUE_ID Retries pre-cat queue
  4239. // PREROUTING_QUEUE_ID Retries pre-routing queue
  4240. // PRESUBMIT_QUEUE_ID Retries pre-submit queue
  4241. // Returns:
  4242. // -
  4243. // History:
  4244. // 8/17/98 - MikeSwa Created
  4245. // 3/3/2000 - MikeSwa Modified to add presubmit queue
  4246. //
  4247. //-----------------------------------------------------------------------------
  4248. void CAQSvrInst::AsyncQueueRetry(DWORD dwQueueID)
  4249. {
  4250. _ASSERT(CATMSGQ_SIG == m_dwSignature);
  4251. if (fTryShutdownLock())
  4252. {
  4253. if (PRELOCAL_QUEUE_ID == dwQueueID)
  4254. {
  4255. InterlockedDecrement((PLONG) &m_cLocalRetriesPending);
  4256. m_asyncqPreLocalDeliveryQueue.StartRetry();
  4257. }
  4258. else if (PRECAT_QUEUE_ID == dwQueueID)
  4259. {
  4260. InterlockedDecrement((PLONG) &m_cCatRetriesPending);
  4261. m_asyncqPreCatQueue.StartRetry();
  4262. }
  4263. else if (PREROUTING_QUEUE_ID == dwQueueID)
  4264. {
  4265. InterlockedDecrement((PLONG) &m_cRoutingRetriesPending);
  4266. m_asyncqPreRoutingQueue.StartRetry();
  4267. }
  4268. else if (PRESUBMIT_QUEUE_ID == dwQueueID)
  4269. {
  4270. InterlockedDecrement((PLONG) &m_cSubmitRetriesPending);
  4271. m_asyncqPreSubmissionQueue.StartRetry();
  4272. }
  4273. else
  4274. {
  4275. _ASSERT(0 && "Invalid Queue ID");
  4276. }
  4277. ShutdownUnlock();
  4278. }
  4279. }
  4280. //---[ HrCreateBadMailPropertyFile ]------------------------------------------
  4281. //
  4282. //
  4283. // Description:
  4284. // Creates a property stream for the given message. The property
  4285. // stream file is given name the .BDP extension.
  4286. // Parameters:
  4287. // szDestFileBase The filename of the actual badmail file
  4288. // pIMailMsgProperties The original message that is being badmailed
  4289. // (may be NULL if it is a pickup dir file)
  4290. // Returns:
  4291. // S_OK on success
  4292. // E_POINTER if szDestFileBase is NULL
  4293. // History:
  4294. // 8/17/99 - MikeSwa Created
  4295. //
  4296. //-----------------------------------------------------------------------------
  4297. HRESULT HrCreateBadMailPropertyFile(LPSTR szDestFileBase,
  4298. IMailMsgProperties *pIMailMsgProperties)
  4299. {
  4300. TraceFunctEnterEx((LPARAM) NULL, "HrCreateBadMailPropertyFile");
  4301. HRESULT hr = S_OK;
  4302. CHAR szOldExt[] = "123";
  4303. CHAR szNewExt[] = "BDP";
  4304. LPSTR szBadMailFileNameExt = NULL;
  4305. DWORD cbBadMailFileName = 0;
  4306. BOOL fShouldRestoreExtension = FALSE;
  4307. CFilePropertyStream fstrm;
  4308. IMailMsgBind *pIMailMsgBind = NULL;
  4309. IMailMsgPropertyStream *pIMailMsgPropertyStream = NULL;
  4310. _ASSERT(szDestFileBase);
  4311. if (!szDestFileBase)
  4312. {
  4313. hr = E_POINTER;
  4314. ErrorTrace((LPARAM) NULL, "Error NULL badmail filename passed in");
  4315. goto Exit;
  4316. }
  4317. if (!pIMailMsgProperties) //no-op
  4318. goto Exit;
  4319. hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgBind,
  4320. (void **) &pIMailMsgBind);
  4321. if (FAILED(hr))
  4322. {
  4323. ErrorTrace((LPARAM) NULL,
  4324. "Unable to QI for IMailMsgBind - 0x%08X", hr);
  4325. goto Exit;
  4326. }
  4327. _ASSERT(pIMailMsgBind);
  4328. //Create the filename & file
  4329. cbBadMailFileName = strlen(szDestFileBase);
  4330. _ASSERT(cbBadMailFileName > 4); //must at least have . extenstion
  4331. szBadMailFileNameExt = szDestFileBase + cbBadMailFileName-3;
  4332. //szBadMailFileNameExt now points to the first character of the 3 char ext
  4333. _ASSERT('.' == *(szBadMailFileNameExt-1));
  4334. _ASSERT(sizeof(szNewExt) == sizeof(szOldExt));
  4335. memcpy(szOldExt, szBadMailFileNameExt, sizeof(szOldExt));
  4336. memcpy(szBadMailFileNameExt, szNewExt, sizeof(szNewExt));
  4337. fShouldRestoreExtension = TRUE;
  4338. hr = fstrm.HrInitialize(szDestFileBase);
  4339. if (FAILED(hr))
  4340. {
  4341. ErrorTrace((LPARAM) NULL,
  4342. "Unable to create badmail property stream - 0x%08X", hr);
  4343. goto Exit;
  4344. }
  4345. hr = fstrm.QueryInterface(IID_IMailMsgPropertyStream,
  4346. (void **) &pIMailMsgPropertyStream);
  4347. _ASSERT(SUCCEEDED(hr)); //we control this totally
  4348. if (FAILED(hr))
  4349. {
  4350. ErrorTrace((LPARAM) NULL,
  4351. "Unable to QI for IID_IMailMsgPropertyStream - 0x%08X", hr);
  4352. goto Exit;
  4353. }
  4354. hr = pIMailMsgBind->GetProperties(pIMailMsgPropertyStream,
  4355. MAILMSG_GETPROPS_COMPLETE, NULL);
  4356. if (FAILED(hr))
  4357. {
  4358. ErrorTrace((LPARAM) NULL, "GetProperties failed with 0x%08X", hr);
  4359. goto Exit;
  4360. }
  4361. Exit:
  4362. if (fShouldRestoreExtension)
  4363. {
  4364. _ASSERT(szBadMailFileNameExt);
  4365. memcpy(szBadMailFileNameExt, szOldExt, sizeof(szOldExt));
  4366. }
  4367. if (pIMailMsgBind)
  4368. pIMailMsgBind->Release();
  4369. if (pIMailMsgPropertyStream)
  4370. pIMailMsgPropertyStream->Release();
  4371. TraceFunctLeave();
  4372. return hr;
  4373. }
  4374. //---[ HrCreateBadMailReasonFile ]---------------------------------------------
  4375. //
  4376. //
  4377. // Description:
  4378. // Creates a file in the badmail directory that expains why the given
  4379. // message was badmailed, and dump the sender and recipient as well. Uses
  4380. // the extension BMR (BadMailReason) to differentiate from the content.
  4381. // Parameters:
  4382. // szDestFileBase The filename of the actual badmail file
  4383. // hrReason The reason the badmail is being created
  4384. // pIMailMsgProperties The original message that is being badmailed
  4385. // (may be NULL if it is a pickup dir file)
  4386. // Returns:
  4387. // S_OK on success
  4388. // E_POINTER if szDestFileBase is NULL
  4389. // History:
  4390. // 8/16/99 - MikeSwa Created
  4391. //
  4392. //-----------------------------------------------------------------------------
  4393. HRESULT HrCreateBadMailReasonFile(IN LPSTR szDestFileBase,
  4394. IN HRESULT hrReason,
  4395. IN IMailMsgProperties *pIMailMsgProperties)
  4396. {
  4397. TraceFunctEnterEx((LPARAM) NULL, "HrCreateBadMailReasonFile");
  4398. HRESULT hr = S_OK;
  4399. HRESULT hrErrorLogged = hrReason;
  4400. WCHAR wszBadmailReason[1000] = L""; //Localized hrReason string
  4401. WCHAR wszReasonBuffer[2000] = L"";
  4402. WCHAR wszErrorCode[] = L"0x12345678 ";
  4403. WCHAR wszErrorCodeMessage[200] = L"";
  4404. CHAR szPropBuffer[1000] = "";
  4405. DWORD dwErr = 0;
  4406. BOOL fWriteBadmailReason = FALSE;
  4407. BOOL fShouldRestoreExtension = FALSE;
  4408. DWORD cReasonBuffer = 0;
  4409. LPSTR szBadMailFileNameExt = NULL;
  4410. CHAR szOldExt[] = "123";
  4411. CHAR szNewExt[] = "BDR";
  4412. DWORD cbBadMailFileName = 0;
  4413. HANDLE hBadMailFile = NULL;
  4414. DWORD cbBytesWritten = 0;
  4415. DWORD dwFacility = 0;
  4416. LPWSTR rgwszArgList[32];
  4417. const WCHAR wcszBlankLine[] = L"\r\n";
  4418. IMailMsgRecipients *pIMailMsgRecipients = NULL;
  4419. DWORD cRecips = 0;
  4420. DWORD iCurrentRecip = 0;
  4421. _ASSERT(szDestFileBase);
  4422. if (!szDestFileBase)
  4423. {
  4424. ErrorTrace((LPARAM) NULL, "Invalid destination file for badmail");
  4425. hr = E_POINTER;
  4426. goto Exit;
  4427. }
  4428. if (!g_hAQInstance)
  4429. {
  4430. _ASSERT(g_hAQInstance && "This should always be set in DLL main");
  4431. ErrorTrace((LPARAM) NULL, "Error, g_hAQInstance is NULL");
  4432. hr = E_FAIL;
  4433. goto Exit;
  4434. }
  4435. //Create the filename & file
  4436. cbBadMailFileName = strlen(szDestFileBase);
  4437. _ASSERT(cbBadMailFileName > 4); //must at least have . extenstion
  4438. szBadMailFileNameExt = szDestFileBase + cbBadMailFileName-3;
  4439. //szBadMailFileNameExt now points to the first character of the 3 char ext
  4440. _ASSERT('.' == *(szBadMailFileNameExt-1));
  4441. _ASSERT(sizeof(szNewExt) == sizeof(szOldExt));
  4442. memcpy(szOldExt, szBadMailFileNameExt, sizeof(szOldExt));
  4443. memcpy(szBadMailFileNameExt, szNewExt, sizeof(szNewExt));
  4444. fShouldRestoreExtension = TRUE;
  4445. hBadMailFile = CreateFile(szDestFileBase,
  4446. GENERIC_WRITE,
  4447. 0,
  4448. NULL,
  4449. CREATE_ALWAYS,
  4450. FILE_FLAG_SEQUENTIAL_SCAN,
  4451. NULL);
  4452. if (INVALID_HANDLE_VALUE ==hBadMailFile )
  4453. {
  4454. hr = HRESULT_FROM_WIN32(GetLastError());
  4455. ErrorTrace((LPARAM) NULL,
  4456. "Unable to create badmail reason file - err 0x%08X - file %s",
  4457. hr, szDestFileBase);
  4458. goto Exit;
  4459. }
  4460. //Figure out the reason for the failure
  4461. if (SUCCEEDED(hrErrorLogged))
  4462. {
  4463. //someone is being lazy about setting the error reason
  4464. _ASSERT(0 && "No badmail reason given");
  4465. ErrorTrace((LPARAM) NULL, "Non-failing badmail HRESULT given 0x%08X",
  4466. hrErrorLogged);
  4467. //Substitute a generic error so we don't have something obnoxious like
  4468. //"The operation completed succesfully" appear in the badmail file
  4469. hrErrorLogged = E_FAIL;
  4470. }
  4471. //Write the error code in "0x00000000" format
  4472. wsprintfW(wszErrorCode, L"0x%08X", hrErrorLogged);
  4473. dwFacility = ((0x0FFF0000 & hrErrorLogged) >> 16);
  4474. //If it is not ours... then "un-HRESULT" it
  4475. if (dwFacility != FACILITY_ITF)
  4476. hrErrorLogged &= 0x0000FFFF;
  4477. dwErr = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
  4478. FORMAT_MESSAGE_FROM_HMODULE |
  4479. FORMAT_MESSAGE_IGNORE_INSERTS,
  4480. g_hAQInstance,
  4481. hrErrorLogged,
  4482. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  4483. wszBadmailReason,
  4484. sizeof(wszBadmailReason)/sizeof(WCHAR), NULL);
  4485. if (!dwErr)
  4486. {
  4487. //We should fall back on a numeric error that we were given
  4488. ErrorTrace((LPARAM) NULL,
  4489. "Error: unable to format badmail message 0x%08X, error is %d",
  4490. hrErrorLogged, GetLastError());
  4491. wcscpy(wszBadmailReason, wszErrorCode);
  4492. }
  4493. else
  4494. {
  4495. //Get rid of trailing newline
  4496. cReasonBuffer = wcslen(wszBadmailReason);
  4497. cReasonBuffer--;
  4498. while(iswspace(wszBadmailReason[cReasonBuffer]))
  4499. {
  4500. wszBadmailReason[cReasonBuffer] = '\0';
  4501. cReasonBuffer--;
  4502. }
  4503. cReasonBuffer = 0;
  4504. }
  4505. ErrorTrace((LPARAM) NULL,
  4506. "Generating badmail because: %S", wszBadmailReason);
  4507. rgwszArgList[0] = wszBadmailReason;
  4508. rgwszArgList[1] = NULL;
  4509. dwErr = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
  4510. FORMAT_MESSAGE_FROM_HMODULE |
  4511. FORMAT_MESSAGE_ARGUMENT_ARRAY,
  4512. g_hAQInstance,
  4513. PHATQ_BADMAIL_REASON,
  4514. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  4515. wszReasonBuffer,
  4516. sizeof(wszReasonBuffer)/sizeof(WCHAR),
  4517. (va_list *) rgwszArgList);
  4518. if (!dwErr)
  4519. {
  4520. ErrorTrace((LPARAM) NULL,
  4521. "Error: unable to format PHATQ_BADMAIL_REASON, error is %d",
  4522. GetLastError());
  4523. hr = HRESULT_FROM_WIN32(GetLastError());
  4524. wcscpy(wszReasonBuffer, wszBadmailReason);
  4525. }
  4526. cReasonBuffer = wcslen(wszReasonBuffer);
  4527. if (!WriteFile(hBadMailFile, (PVOID) wszReasonBuffer,
  4528. cReasonBuffer*sizeof(WCHAR), &cbBytesWritten, NULL))
  4529. {
  4530. hr = HRESULT_FROM_WIN32(GetLastError());
  4531. ErrorTrace((LPARAM) NULL,
  4532. "Error writing to badmail reason file - erro 0x%08X - file %s",
  4533. hr, szDestFileBase);
  4534. goto Exit;
  4535. }
  4536. //Write the actual error code in 0x00000000 form so tools can parse it out
  4537. rgwszArgList[0] = wszErrorCode;
  4538. rgwszArgList[1] = NULL;
  4539. wcscpy(wszReasonBuffer, wcszBlankLine);
  4540. dwErr = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
  4541. FORMAT_MESSAGE_FROM_HMODULE |
  4542. FORMAT_MESSAGE_ARGUMENT_ARRAY,
  4543. g_hAQInstance,
  4544. PHATQ_BADMAIL_ERROR_CODE,
  4545. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  4546. wszReasonBuffer+(sizeof(wcszBlankLine)-1)/sizeof(WCHAR),
  4547. (sizeof(wszReasonBuffer)-sizeof(wcszBlankLine))/sizeof(WCHAR),
  4548. (va_list *) rgwszArgList);
  4549. wcscat(wszReasonBuffer, wcszBlankLine);
  4550. cReasonBuffer = wcslen(wszReasonBuffer);
  4551. if (!WriteFile(hBadMailFile, (PVOID) wszReasonBuffer,
  4552. cReasonBuffer*sizeof(WCHAR), &cbBytesWritten, NULL))
  4553. {
  4554. hr = HRESULT_FROM_WIN32(GetLastError());
  4555. ErrorTrace((LPARAM) NULL,
  4556. "Error writing to badmail reason file - erro 0x%08X - file %s",
  4557. hr, szDestFileBase);
  4558. goto Exit;
  4559. }
  4560. //All the rest requries access to an actual message... if we don't
  4561. //have one, bail
  4562. if (!pIMailMsgProperties)
  4563. goto Exit;
  4564. //Write Sender of message
  4565. hr = pIMailMsgProperties->GetStringA(IMMPID_MP_SENDER_ADDRESS_SMTP,
  4566. sizeof(szPropBuffer), szPropBuffer);
  4567. if (FAILED(hr))
  4568. {
  4569. ErrorTrace((LPARAM) NULL,
  4570. "ERROR: Unable to get sender of IMailMsg 0x%08X",
  4571. pIMailMsgProperties);
  4572. hr = S_OK; //just don't display sender
  4573. }
  4574. else
  4575. {
  4576. rgwszArgList[0] = (LPWSTR) szPropBuffer;
  4577. rgwszArgList[1] = NULL;
  4578. wcscpy(wszReasonBuffer, wcszBlankLine);
  4579. dwErr = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
  4580. FORMAT_MESSAGE_FROM_HMODULE |
  4581. FORMAT_MESSAGE_ARGUMENT_ARRAY,
  4582. g_hAQInstance,
  4583. PHATQ_BADMAIL_SENDER,
  4584. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  4585. wszReasonBuffer+(sizeof(wcszBlankLine)-1)/sizeof(WCHAR),
  4586. (sizeof(wszReasonBuffer)-sizeof(wcszBlankLine))/sizeof(WCHAR),
  4587. (va_list *) rgwszArgList);
  4588. wcscat(wszReasonBuffer, wcszBlankLine);
  4589. cReasonBuffer = wcslen(wszReasonBuffer);
  4590. if (!WriteFile(hBadMailFile, (PVOID) wszReasonBuffer,
  4591. cReasonBuffer*sizeof(WCHAR), &cbBytesWritten, NULL))
  4592. {
  4593. hr = HRESULT_FROM_WIN32(GetLastError());
  4594. ErrorTrace((LPARAM) NULL,
  4595. "Error writing to badmail reason file - erro 0x%08X - file %s",
  4596. hr, szDestFileBase);
  4597. goto Exit;
  4598. }
  4599. }
  4600. //Write Message recipients
  4601. hr = pIMailMsgProperties->QueryInterface(IID_IMailMsgRecipients,
  4602. (PVOID *) &pIMailMsgRecipients);
  4603. if (FAILED(hr))
  4604. {
  4605. ErrorTrace((LPARAM) NULL,
  4606. "Unable to query interface for recip interface - 0x%08X", hr);
  4607. goto Exit;
  4608. }
  4609. hr = pIMailMsgRecipients->Count(&cRecips);
  4610. if (FAILED(hr))
  4611. {
  4612. ErrorTrace((LPARAM) NULL,
  4613. "Unable to get recipient count - 0x%08X", hr);
  4614. goto Exit;
  4615. }
  4616. //If we don't have any recipients, bail
  4617. if (!cRecips)
  4618. goto Exit;
  4619. //Write the localized text
  4620. wcscpy(wszReasonBuffer, wcszBlankLine);
  4621. dwErr = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
  4622. FORMAT_MESSAGE_FROM_HMODULE |
  4623. FORMAT_MESSAGE_IGNORE_INSERTS,
  4624. g_hAQInstance,
  4625. PHATQ_BADMAIL_RECIPIENTS,
  4626. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  4627. wszReasonBuffer+(sizeof(wcszBlankLine)-1)/sizeof(WCHAR),
  4628. (sizeof(wszReasonBuffer)-sizeof(wcszBlankLine))/sizeof(WCHAR),
  4629. NULL);
  4630. cReasonBuffer = wcslen(wszReasonBuffer);
  4631. if (!WriteFile(hBadMailFile, (PVOID) wszReasonBuffer,
  4632. cReasonBuffer*sizeof(WCHAR), &cbBytesWritten, NULL))
  4633. {
  4634. hr = HRESULT_FROM_WIN32(GetLastError());
  4635. ErrorTrace((LPARAM) NULL,
  4636. "Error writing to badmail reason file - erro 0x%08X - file %s",
  4637. hr, szDestFileBase);
  4638. goto Exit;
  4639. }
  4640. //Loop over SMTP recips and dump them in the file
  4641. for (iCurrentRecip = 0; iCurrentRecip < cRecips; iCurrentRecip++)
  4642. {
  4643. hr = pIMailMsgRecipients->GetStringA(iCurrentRecip,
  4644. IMMPID_RP_ADDRESS_SMTP, sizeof(szPropBuffer), szPropBuffer);
  4645. if (FAILED(hr))
  4646. {
  4647. ErrorTrace((LPARAM) NULL,
  4648. "Unable to get SMTP address for recip %d - 0x%08X",
  4649. iCurrentRecip, hr);
  4650. hr = S_OK;
  4651. continue;
  4652. }
  4653. cReasonBuffer = wsprintfW(wszReasonBuffer, L"\t%S%s",
  4654. szPropBuffer, wcszBlankLine);
  4655. if (!WriteFile(hBadMailFile, (PVOID) wszReasonBuffer,
  4656. cReasonBuffer*sizeof(WCHAR), &cbBytesWritten, NULL))
  4657. {
  4658. hr = HRESULT_FROM_WIN32(GetLastError());
  4659. ErrorTrace((LPARAM) NULL,
  4660. "Error writing to badmail reason file - error 0x%08X - file %s",
  4661. hr, szDestFileBase);
  4662. goto Exit;
  4663. }
  4664. }
  4665. Exit:
  4666. if (fShouldRestoreExtension)
  4667. {
  4668. _ASSERT(szBadMailFileNameExt);
  4669. memcpy(szBadMailFileNameExt, szOldExt, sizeof(szOldExt));
  4670. }
  4671. if (pIMailMsgRecipients)
  4672. pIMailMsgRecipients->Release();
  4673. if (hBadMailFile != INVALID_HANDLE_VALUE)
  4674. _VERIFY(CloseHandle(hBadMailFile));
  4675. TraceFunctLeave();
  4676. return hr;
  4677. }
  4678. //---[ CAQSvrInst::HandleBadMail ]---------------------------------------------
  4679. //
  4680. //
  4681. // Description:
  4682. // Handles mail that needs to be placed in the badmail directory (or
  4683. // equivalent).
  4684. // Parameters:
  4685. // IN pIMailMsgProperties that needs to be badmail'd
  4686. // IN fUseIMailMsgPropeties -- use IMAilMsgProps if set else use szFilename
  4687. // IN szFileName Name of badmail file (if no msg can be supplied)
  4688. // IN hrReason - HRESULT (defined in aqerr) that describes reason
  4689. // Eventually, we may log this information to the badmail
  4690. // file (or recipient)
  4691. // IN fHasRoutingLock - TRUE if this thread holds routing lock
  4692. // Returns:
  4693. // -
  4694. // History:
  4695. // 10/8/98 - MikeSwa Created
  4696. //
  4697. //-----------------------------------------------------------------------------
  4698. void CAQSvrInst::HandleBadMail(IN IMailMsgProperties *pIMailMsgProperties,
  4699. IN BOOL fUseIMailMsgProperties,
  4700. IN LPSTR szOriginalFileName,
  4701. IN HRESULT hrReason,
  4702. IN BOOL fHasRoutingLock)
  4703. {
  4704. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::HandleBadMail");
  4705. HRESULT hr = S_OK;
  4706. LPSTR szFullPathName = NULL;
  4707. LPSTR szFileName = NULL;
  4708. BOOL fDataLocked = FALSE;
  4709. BOOL fDone = TRUE;
  4710. HANDLE hFile = INVALID_HANDLE_VALUE;
  4711. PFIO_CONTEXT pFIOContext = NULL;
  4712. FILETIME ftCurrent;
  4713. CRefCountedString *prstrBadMailDir = NULL;
  4714. MSG_TRACK_INFO msgTrackInfo;
  4715. ZeroMemory( &msgTrackInfo, sizeof( msgTrackInfo ) );
  4716. msgTrackInfo.dwEventId = MTE_BADMAIL;
  4717. m_pISMTPServer->WriteLog( &msgTrackInfo, pIMailMsgProperties, NULL, NULL );
  4718. InterlockedIncrement((PLONG) &m_cTotalMsgsBadmailed);
  4719. if (!fHasRoutingLock)
  4720. {
  4721. m_slPrivateData.ShareLock();
  4722. fDataLocked = TRUE;
  4723. }
  4724. else
  4725. {
  4726. m_slPrivateData.AssertIsLocked();
  4727. }
  4728. if (m_prstrBadMailDir)
  4729. {
  4730. prstrBadMailDir = m_prstrBadMailDir;
  4731. prstrBadMailDir->AddRef();
  4732. }
  4733. else
  4734. {
  4735. LogAQEvent(AQUEUE_E_NO_BADMAIL_DIR, NULL, pIMailMsgProperties, NULL);
  4736. goto Exit;
  4737. }
  4738. if (fDataLocked)
  4739. {
  4740. m_slPrivateData.ShareUnlock();
  4741. fDataLocked = FALSE;
  4742. }
  4743. szFullPathName = (LPSTR) pvMalloc(sizeof(CHAR) *
  4744. (UNIQUEUE_FILENAME_BUFFER_SIZE +
  4745. prstrBadMailDir->cbStrlen()));
  4746. if (!szFullPathName)
  4747. {
  4748. LogAQEvent(AQUEUE_E_BADMAIL, NULL, pIMailMsgProperties, NULL);
  4749. goto Exit;
  4750. }
  4751. memcpy(szFullPathName, prstrBadMailDir->szStr(),
  4752. prstrBadMailDir->cbStrlen());
  4753. if (szFullPathName[prstrBadMailDir->cbStrlen()-1] != '\\')
  4754. {
  4755. _ASSERT(0 && "Malformed badmail config");
  4756. LogAQEvent(AQUEUE_E_NO_BADMAIL_DIR, NULL, pIMailMsgProperties, NULL);
  4757. goto Exit;
  4758. }
  4759. szFileName = szFullPathName + prstrBadMailDir->cbStrlen();
  4760. //If we have a msg use it
  4761. if (pIMailMsgProperties && fUseIMailMsgProperties)
  4762. {
  4763. //Loop while trying to generate a unique file name
  4764. do
  4765. {
  4766. fDone = TRUE;
  4767. GetExpireTime(0, &ftCurrent, NULL);
  4768. GetUniqueFileName(&ftCurrent, szFileName, "BAD");
  4769. //Create file and write MsgContent to it
  4770. hFile = CreateFile( szFullPathName,
  4771. GENERIC_WRITE,
  4772. 0,
  4773. NULL,
  4774. CREATE_NEW,
  4775. FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_OVERLAPPED,
  4776. NULL);
  4777. if (INVALID_HANDLE_VALUE == hFile)
  4778. {
  4779. if (ERROR_ALREADY_EXISTS == GetLastError())
  4780. {
  4781. //Try a new file name
  4782. fDone = FALSE;
  4783. continue;
  4784. }
  4785. //Other we are hosed... log an event
  4786. LogAQEvent(AQUEUE_E_BADMAIL, NULL, pIMailMsgProperties, NULL);
  4787. goto Exit;
  4788. }
  4789. _ASSERT(hFile); //should return INVALID_HANDLE_VALUE on failure
  4790. } while (!fDone);
  4791. if (hFile != INVALID_HANDLE_VALUE)
  4792. pFIOContext = AssociateFile(hFile);
  4793. if (!pFIOContext ||
  4794. FAILED(pIMailMsgProperties->CopyContentToFile(pFIOContext, NULL)))
  4795. {
  4796. //Copy failed log event
  4797. LogAQEvent(AQUEUE_E_BADMAIL, NULL, pIMailMsgProperties, NULL);
  4798. }
  4799. }
  4800. else if (szOriginalFileName)
  4801. {
  4802. //Otherwise (no msg)... just do a movefile
  4803. _ASSERT(szFullPathName[prstrBadMailDir->cbStrlen()-1] == '\\');
  4804. szFullPathName[prstrBadMailDir->cbStrlen()-1] = '\0';
  4805. if (!MoveFileEx(szOriginalFileName, szFullPathName,
  4806. MOVEFILE_COPY_ALLOWED))
  4807. {
  4808. //MoveFile failed... try renaming with a unique file name
  4809. szFullPathName[prstrBadMailDir->cbStrlen()-1] = '\\';
  4810. GetExpireTime(0, &ftCurrent, NULL);
  4811. GetUniqueFileName(&ftCurrent, szFileName, "BAD");
  4812. if (rename(szOriginalFileName, szFullPathName))
  4813. LogAQEvent(AQUEUE_E_BADMAIL, NULL, NULL, szOriginalFileName);
  4814. }
  4815. }
  4816. hr = HrCreateBadMailReasonFile(szFullPathName, hrReason, pIMailMsgProperties);
  4817. if (FAILED(hr))
  4818. {
  4819. ErrorTrace((LPARAM) this,
  4820. "Unable to make badmail reason file - hr 0x%08X", hr);
  4821. }
  4822. hr = HrCreateBadMailPropertyFile(szFullPathName, pIMailMsgProperties);
  4823. if (FAILED(hr))
  4824. {
  4825. ErrorTrace((LPARAM) this,
  4826. "Unable to make badmail property file - hr 0x%08X", hr);
  4827. }
  4828. Exit:
  4829. if (fDataLocked)
  4830. m_slPrivateData.ShareUnlock();
  4831. if (prstrBadMailDir)
  4832. prstrBadMailDir->Release();
  4833. if (szFullPathName)
  4834. FreePv(szFullPathName);
  4835. if (NULL != pFIOContext)
  4836. ReleaseContext(pFIOContext);
  4837. TraceFunctLeave();
  4838. }
  4839. //---[ HandleAQFailure ]--------------------------------------------------------
  4840. //
  4841. //
  4842. // Description:
  4843. // Function to handle AQ failures that would result in loss of data
  4844. // or messages if unhandled. Meant to be a substitute for
  4845. // _ASSERT(SUCCEEDED(hr)).
  4846. //
  4847. // Note: Msgs are still Turfed for M2
  4848. // Parameters:
  4849. // eaqfFailureSituation Enum that describes the failure situation
  4850. // as well as what the context is.
  4851. // hr HRSULT that triggered failure condition
  4852. // pIMailMsgProperties MailMsgProperties
  4853. // Returns:
  4854. // -
  4855. // History:
  4856. // 8/25/98 - MikeSwa Created
  4857. // 10/8/98 - MikeSwa Moved to CAQSvrInst
  4858. //
  4859. //-----------------------------------------------------------------------------
  4860. void CAQSvrInst::HandleAQFailure(eAQFailure eaqfFailureSituation,
  4861. HRESULT hr,
  4862. IMailMsgProperties *pIMailMsgProperties)
  4863. {
  4864. _ASSERT(eaqfFailureSituation < AQ_FAILURE_NUM_SITUATIONS);
  4865. InterlockedIncrement((PLONG) &g_cTotalAQFailures);
  4866. InterlockedIncrement((PLONG) &(g_rgcAQFailures[eaqfFailureSituation]));
  4867. BOOL fCanRetry = fShouldRetryMessage(pIMailMsgProperties);
  4868. MSG_TRACK_INFO msgTrackInfo;
  4869. ZeroMemory( &msgTrackInfo, sizeof( msgTrackInfo ) );
  4870. msgTrackInfo.dwEventId = MTE_AQ_FAILURE;
  4871. m_pISMTPServer->WriteLog( &msgTrackInfo, pIMailMsgProperties, NULL, NULL );
  4872. switch(eaqfFailureSituation)
  4873. {
  4874. case(AQ_FAILURE_CANNOT_NDR_UNRESOLVED_RECIPS):
  4875. LogAQEvent(AQUEUE_E_DSN_FAILURE, NULL, pIMailMsgProperties, NULL);
  4876. //drop through to default case
  4877. default:
  4878. //
  4879. // Throw the message in the last-ditch retry queue if the following are true:
  4880. // - We had a failure
  4881. // - We are not shutting down
  4882. // - We can retry the message (e.g., it has not been deleted)
  4883. //
  4884. if (FAILED(hr) && (AQUEUE_E_SHUTDOWN != hr) && fCanRetry)
  4885. {
  4886. InterlockedIncrement((PLONG) &m_cCurrentResourceFailedMsgsPendingRetry);
  4887. m_fmq.HandleFailedMailMsg(pIMailMsgProperties);
  4888. }
  4889. }
  4890. }
  4891. //---[ CAQSvrInst::LogAQEvent ]------------------------------------------------
  4892. //
  4893. //
  4894. // Description:
  4895. // General event logging mechanism for AQ
  4896. // Parameters:
  4897. // hrEventReason AQUEUE HRESULT describing event
  4898. // pmsgref CMsgRef of msg for event (can be NULL)
  4899. // pIMailMsgProperties pIMailMsgProperties for event (can be NULL)
  4900. // szFileName Filename if no msgs provided (can be NULL)
  4901. // Returns:
  4902. // -
  4903. // History:
  4904. // 10/9/98 - MikeSwa Created
  4905. //
  4906. //-----------------------------------------------------------------------------
  4907. void CAQSvrInst::LogAQEvent(HRESULT hrEventReason, CMsgRef *pmsgref,
  4908. IMailMsgProperties *pIMailMsgProperties,
  4909. LPSTR szFileName)
  4910. {
  4911. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::LogAQEvent");
  4912. switch (hrEventReason)
  4913. {
  4914. //$$TODO - Add actual event callouts here
  4915. case (S_OK): //Added to remove switch compile warnings
  4916. default:
  4917. ErrorTrace((LPARAM) this, "EVENT: Generic AQueue event - 0x%08X", hrEventReason);
  4918. }
  4919. TraceFunctLeave();
  4920. }
  4921. //+------------------------------------------------------------
  4922. //
  4923. // Function: CAQSvrInst::TriggerPreCategorizeEvent
  4924. //
  4925. // Synopsis: Fire the pre-cat server event
  4926. //
  4927. // Arguments:
  4928. // pIMailMsgProperties: the IMailMsgProperties interface of the mailmsg
  4929. //
  4930. // Returns: NOTHING
  4931. //
  4932. // History:
  4933. // jstamerj 1998/11/24 20:07:58: Created.
  4934. //
  4935. //-------------------------------------------------------------
  4936. VOID CAQSvrInst::TriggerPreCategorizeEvent(
  4937. IN IMailMsgProperties *pIMailMsgProperties)
  4938. {
  4939. HRESULT hr;
  4940. EVENTPARAMS_PRECATEGORIZE Params;
  4941. TraceFunctEnterEx((LPARAM)pIMailMsgProperties,
  4942. "CAQSvrInst::TriggerPreCategorizeEvent");
  4943. _ASSERT(pIMailMsgProperties);
  4944. Params.pfnCompletion = MailTransport_Completion_PreCategorization;
  4945. Params.pCCatMsgQueue = (PVOID) this;
  4946. Params.pIMailMsgProperties = pIMailMsgProperties;
  4947. //
  4948. // Addref here, release in completion
  4949. //
  4950. pIMailMsgProperties->AddRef();
  4951. AddRef();
  4952. //
  4953. // keep a count of messages in the pre-cat event
  4954. //
  4955. InterlockedIncrement((LPLONG) &m_cCurrentMsgsPendingPreCatEvent);
  4956. if(m_pISMTPServer) {
  4957. hr = TriggerServerEvent(
  4958. SMTP_MAILTRANSPORT_PRECATEGORIZE_EVENT,
  4959. &Params);
  4960. DebugTrace((LPARAM)this, "TriggerServerEvent returned hr %08lx", hr);
  4961. }
  4962. if((m_pISMTPServer == NULL) || (FAILED(hr))) {
  4963. ErrorTrace((LPARAM)this,
  4964. "Unable to dispatch server event; calling completion routine directly");
  4965. DebugTrace((LPARAM)this, "hr is %08lx", hr);
  4966. //
  4967. // Call completion routine directly
  4968. //
  4969. _VERIFY(SUCCEEDED(PreCatEventCompletion(S_OK, &Params)));
  4970. }
  4971. TraceFunctLeaveEx((LPARAM)this);
  4972. }
  4973. //+------------------------------------------------------------
  4974. //
  4975. // Function: CAQSvrInst::PreCatEventCompletion
  4976. //
  4977. // Synopsis: Called by SEO upon completipon of the precat event
  4978. //
  4979. // Arguments:
  4980. // pIMailMsgProperties: the IMailMsgProperties interface of the mailmsg
  4981. //
  4982. // Returns:
  4983. // S_OK: Success
  4984. //
  4985. // History:
  4986. // jstamerj 1998/11/24 20:17:44: Created.
  4987. //
  4988. //-------------------------------------------------------------
  4989. HRESULT CAQSvrInst::PreCatEventCompletion(
  4990. IN HRESULT hrStatus,
  4991. IN PEVENTPARAMS_PRECATEGORIZE pParams)
  4992. {
  4993. HRESULT hr;
  4994. _ASSERT(pParams);
  4995. _ASSERT(pParams->pIMailMsgProperties);
  4996. TraceFunctEnterEx((LPARAM)pParams->pIMailMsgProperties,
  4997. "CAQSvrInst::PreCatEventCompletion");
  4998. DebugTrace((LPARAM)pParams->pIMailMsgProperties, "hrStatus is %08lx", hrStatus);
  4999. //
  5000. // Decrease count of msgs in pre-cat event
  5001. //
  5002. InterlockedDecrement((LPLONG) &m_cCurrentMsgsPendingPreCatEvent);
  5003. //
  5004. // Update the message status and check for abort/badmail
  5005. //
  5006. hr = SetNextMsgStatus(MP_STATUS_SUBMITTED, pParams->pIMailMsgProperties);
  5007. if (hr == S_OK) //anything else implies that the message has been handled
  5008. {
  5009. //Only submit to categorizer if things message was not turfed.
  5010. hr = SubmitMessageToCategorizer(pParams->pIMailMsgProperties);
  5011. if(FAILED(hr))
  5012. {
  5013. _ASSERT((hr == AQUEUE_E_SHUTDOWN) && "SubmitMessageToCategorizer failed.");
  5014. ErrorTrace((LPARAM)pParams->pIMailMsgProperties,
  5015. "SubmitMessageToCategorizer returned hr %08lx",
  5016. hr);
  5017. }
  5018. }
  5019. //
  5020. // Release references added in TriggerPreCategorizeEvent
  5021. //
  5022. pParams->pIMailMsgProperties->Release();
  5023. Release();
  5024. TraceFunctLeaveEx((LPARAM)pParams->pIMailMsgProperties);
  5025. return S_OK;
  5026. }
  5027. //+------------------------------------------------------------
  5028. //
  5029. // Function: MailTransport_Completion_PreCategorization
  5030. //
  5031. // Synopsis: SEO will call this routine after all sinks for
  5032. // OnPreCategoriztion have been handeled
  5033. //
  5034. // Arguments:
  5035. // pvContext: Context passed into TriggerServerEvent
  5036. //
  5037. // Returns:
  5038. // S_OK: Success
  5039. //
  5040. // History:
  5041. // jstamerj 1998/11/24 20:26:51: Created
  5042. //
  5043. //-------------------------------------------------------------
  5044. HRESULT MailTransport_Completion_PreCategorization(
  5045. HRESULT hrStatus,
  5046. PVOID pvContext)
  5047. {
  5048. TraceFunctEnter("MailTransport_Completion_PreCategorization");
  5049. PEVENTPARAMS_PRECATEGORIZE pParams = (PEVENTPARAMS_PRECATEGORIZE) pvContext;
  5050. CAQSvrInst *paqinst = (CAQSvrInst *) pParams->pCCatMsgQueue;
  5051. TraceFunctLeave();
  5052. return paqinst->PreCatEventCompletion(
  5053. hrStatus,
  5054. pParams);
  5055. }
  5056. //---[ CAQSvrInst::SetCallbackTime ]-------------------------------------------
  5057. //
  5058. //
  5059. // Description:
  5060. // Set a callback time based on a number of minutes.
  5061. // Parameters:
  5062. // IN pCallbackFn Ptr to a callback function
  5063. // IN pvContext Context pass to callback function
  5064. // IN dwCallbackMinutes Minutes to wait before calling callback
  5065. // function.
  5066. // Returns:
  5067. //
  5068. // History:
  5069. // 12/29/98 - MikeSwa Created
  5070. //
  5071. //-----------------------------------------------------------------------------
  5072. HRESULT CAQSvrInst::SetCallbackTime(IN PSRVFN pCallbackFn,
  5073. IN PVOID pvContext,
  5074. IN DWORD dwCallbackMinutes)
  5075. {
  5076. HRESULT hr = S_OK;
  5077. if (!fTryShutdownLock())
  5078. {
  5079. hr = AQUEUE_E_SHUTDOWN;
  5080. goto Exit;
  5081. }
  5082. _ASSERT(m_pConnMgr);
  5083. if (m_pConnMgr)
  5084. hr = m_pConnMgr->SetCallbackTime(pCallbackFn, pvContext,
  5085. dwCallbackMinutes);
  5086. ShutdownUnlock();
  5087. Exit:
  5088. return hr;
  5089. }
  5090. //---[ CAQSvrInst::SetCallbackTime ]-------------------------------------------
  5091. //
  5092. //
  5093. // Description:
  5094. // Set a callback time based on a filetime.
  5095. // Parameters:
  5096. //
  5097. // Returns:
  5098. //
  5099. // History:
  5100. // 12/29/98 - MikeSwa Created
  5101. //
  5102. //-----------------------------------------------------------------------------
  5103. HRESULT CAQSvrInst::SetCallbackTime(IN PSRVFN pCallbackFn,
  5104. IN PVOID pvContext,
  5105. IN FILETIME *pft)
  5106. {
  5107. HRESULT hr = S_OK;
  5108. DWORD dwCallbackMinutes = 0;
  5109. DWORD dwTimeContext = 0;
  5110. FILETIME ftCurrentTime;
  5111. LARGE_INTEGER *pLargeIntCurrentTime = (LARGE_INTEGER *) &ftCurrentTime;
  5112. LARGE_INTEGER *pLargeIntCallbackTime = (LARGE_INTEGER *) pft;
  5113. _ASSERT(pCallbackFn);
  5114. _ASSERT(pvContext);
  5115. _ASSERT(pft);
  5116. if (!fTryShutdownLock())
  5117. {
  5118. hr = AQUEUE_E_SHUTDOWN;
  5119. goto Exit;
  5120. }
  5121. if (!fInPast(pft, &dwTimeContext))
  5122. {
  5123. //Get current time using previous context (so current time is the same)
  5124. GetExpireTime(0, &ftCurrentTime, &dwTimeContext);
  5125. //the current time must be less than the callback time
  5126. _ASSERT(pLargeIntCurrentTime->QuadPart < pLargeIntCallbackTime->QuadPart);
  5127. pLargeIntCurrentTime->QuadPart = pLargeIntCallbackTime->QuadPart -
  5128. pLargeIntCurrentTime->QuadPart;
  5129. pLargeIntCurrentTime->QuadPart /= (LONGLONG) 600000000;
  5130. //If the callback time is > 2 billion minutes... I'd
  5131. //like to know about it in debug builds
  5132. _ASSERT(!pLargeIntCurrentTime->HighPart);
  5133. dwCallbackMinutes = pLargeIntCurrentTime->LowPart;
  5134. //The only current application is for deferred delivery... I would like
  5135. //to see the internal test situations that result in a deferred delivery
  5136. _ASSERT(dwCallbackMinutes < (60*24*7));
  5137. //
  5138. // If we have rounded down to 0 minutes, we should call back in 1
  5139. // otherwise we can end up in a tight loop. If the call merely wants
  5140. // another thread, they should use the AsyncWorkQueue
  5141. //
  5142. if (!dwCallbackMinutes)
  5143. dwCallbackMinutes = 1;
  5144. }
  5145. else
  5146. {
  5147. //If in past... callback as soon as possible, but don't use this thread, in
  5148. //case there are locking complications (CShareLockNH is non-reentrant).
  5149. dwCallbackMinutes = 1;
  5150. }
  5151. _ASSERT(m_pConnMgr);
  5152. if (m_pConnMgr)
  5153. hr = m_pConnMgr->SetCallbackTime(pCallbackFn, pvContext,
  5154. dwCallbackMinutes);
  5155. ShutdownUnlock();
  5156. Exit:
  5157. return hr;
  5158. }
  5159. //---[ CAQSvrInst::SetLinkState ]----------------------------------------------
  5160. //
  5161. //
  5162. // Description:
  5163. // Implements IMailTransportRouterSetLinkState::SetLinkState
  5164. // Parameters:
  5165. // IN szLinkDomainName The Domain Name of the link (next hop)
  5166. // IN guidRouterGUID The GUID ID of the router
  5167. // IN dwScheduleID The schedule ID link
  5168. // IN szConnectorName The connector name given by the router
  5169. // IN dwFlagsToSet Link State Flags to set
  5170. // IN dwFlagsToUnset Link State Flags to unset
  5171. // IN pftNextScheduledConnection Next scheduled connection time.
  5172. // Returns:
  5173. // S_OK on success
  5174. // E_INVALIDARG if szLinkDomainName is NULL
  5175. // AQUEUE_E_SHUTDOWN if shutting down.
  5176. // History:
  5177. // 1/9/99 - MikeSwa Created
  5178. //
  5179. //-----------------------------------------------------------------------------
  5180. STDMETHODIMP CAQSvrInst::SetLinkState(
  5181. IN LPSTR szLinkDomainName,
  5182. IN GUID guidRouterGUID,
  5183. IN DWORD dwScheduleID,
  5184. IN LPSTR szConnectorName,
  5185. IN DWORD dwSetLinkState,
  5186. IN DWORD dwUnsetLinkState,
  5187. IN FILETIME *pftNextScheduledConnection,
  5188. IN IMessageRouter *pMessageRouter)
  5189. {
  5190. HRESULT hr = S_OK;
  5191. BOOL fLocked = FALSE;
  5192. DWORD cbLinkDomainName = 0;
  5193. CDomainEntry *pdentry = NULL;
  5194. CLinkMsgQueue *plmq = NULL;
  5195. CAQScheduleID aqsched(guidRouterGUID, dwScheduleID);
  5196. BOOL fRemoveOwnedSchedule = TRUE;
  5197. if (!szLinkDomainName)
  5198. {
  5199. hr = E_INVALIDARG;
  5200. goto Exit;
  5201. }
  5202. if (!fTryShutdownLock())
  5203. {
  5204. hr = AQUEUE_E_SHUTDOWN;
  5205. goto Exit;
  5206. }
  5207. fLocked = TRUE;
  5208. cbLinkDomainName = lstrlen(szLinkDomainName);
  5209. // see if they want to create a new link
  5210. if (dwSetLinkState & LINK_STATE_CREATE_IF_NECESSARY) {
  5211. // creating a link requires a pmessagerouter
  5212. if (pMessageRouter == NULL) {
  5213. hr = E_POINTER;
  5214. } else {
  5215. LinkFlags lf;
  5216. if (dwSetLinkState & LINK_STATE_TYPE_INTERNAL_SMTP) {
  5217. lf = eLinkFlagsInternalSMTPLinkInfo;
  5218. } else {
  5219. _ASSERT(dwSetLinkState & LINK_STATE_TYPE_EXTERNAL_SMTP);
  5220. lf = eLinkFlagsExternalSMTPLinkInfo;
  5221. }
  5222. dwSetLinkState &=
  5223. ~(LINK_STATE_TYPE_INTERNAL_SMTP |
  5224. LINK_STATE_TYPE_EXTERNAL_SMTP);
  5225. // get the link, and create it if it doesn't exist and they want to
  5226. // have a new link created
  5227. hr = m_dmt.HrGetOrCreateLink(szLinkDomainName,
  5228. cbLinkDomainName,
  5229. dwScheduleID,
  5230. szConnectorName,
  5231. pMessageRouter,
  5232. TRUE,
  5233. lf,
  5234. &plmq,
  5235. &fRemoveOwnedSchedule);
  5236. }
  5237. } else {
  5238. cbLinkDomainName = lstrlen(szLinkDomainName);
  5239. hr = HrGetDomainEntry(cbLinkDomainName, szLinkDomainName, &pdentry);
  5240. if (SUCCEEDED(hr))
  5241. hr = pdentry->HrGetLinkMsgQueue(&aqsched, &plmq);
  5242. }
  5243. if (FAILED(hr))
  5244. goto Exit;
  5245. // this bit is only used above, so remove it
  5246. dwSetLinkState &= ~LINK_STATE_CREATE_IF_NECESSARY;
  5247. dwUnsetLinkState &= ~LINK_STATE_CREATE_IF_NECESSARY;
  5248. _ASSERT(plmq);
  5249. //
  5250. // If this operation is dis-allowing scheduled connections... we should
  5251. // record when the next scheduled attempt will be. We should also do
  5252. // this before we modify the link state, so that the queue admin does
  5253. // not display a scheduled queue without a next connection time.
  5254. //
  5255. if (pftNextScheduledConnection &&
  5256. (pftNextScheduledConnection->dwLowDateTime ||
  5257. pftNextScheduledConnection->dwHighDateTime))
  5258. {
  5259. plmq->SetNextScheduledConnection(pftNextScheduledConnection);
  5260. }
  5261. //filter out the reserved bits for this "public" API
  5262. plmq->dwModifyLinkState(~LINK_STATE_RESERVED & dwSetLinkState,
  5263. ~LINK_STATE_RESERVED & dwUnsetLinkState);
  5264. // schedule a callback if one was requested
  5265. if (pftNextScheduledConnection->dwLowDateTime != 0 ||
  5266. pftNextScheduledConnection->dwHighDateTime != 0)
  5267. {
  5268. //callback with next attempt
  5269. plmq->AddRef(); //Addref self as context
  5270. hr = SetCallbackTime(
  5271. CLinkMsgQueue::ScheduledCallback,
  5272. plmq,
  5273. pftNextScheduledConnection);
  5274. if (FAILED(hr))
  5275. plmq->Release(); //callback will not happen... release context
  5276. }
  5277. Exit:
  5278. if (fLocked)
  5279. ShutdownUnlock();
  5280. if (pdentry)
  5281. pdentry->Release();
  5282. if (plmq)
  5283. plmq->Release();
  5284. //
  5285. // If we have not passed ownership of the shedule ID to a link,
  5286. // then we are responsible for releasing it.
  5287. //
  5288. if (fRemoveOwnedSchedule) {
  5289. IMessageRouterLinkStateNotification *pILinkStateNotify = NULL;
  5290. HRESULT hrLinkStateNotify =
  5291. pMessageRouter->QueryInterface(IID_IMessageRouterLinkStateNotification,
  5292. (VOID **) &pILinkStateNotify);
  5293. _ASSERT( SUCCEEDED( hrLinkStateNotify));
  5294. FILETIME ftNotUsed = {0,0};
  5295. DWORD dwSetNotUsed = LINK_STATE_NO_ACTION;
  5296. DWORD dwUnsetNotUsed = LINK_STATE_NO_ACTION;
  5297. hrLinkStateNotify =
  5298. pILinkStateNotify->LinkStateNotify(
  5299. szLinkDomainName,
  5300. guidRouterGUID,
  5301. dwScheduleID,
  5302. szConnectorName,
  5303. LINK_STATE_LINK_NO_LONGER_USED,
  5304. 0, //consecutive failures
  5305. &ftNotUsed,
  5306. &dwSetNotUsed,
  5307. &dwUnsetNotUsed);
  5308. _ASSERT( SUCCEEDED( hrLinkStateNotify));
  5309. if ( NULL != pILinkStateNotify) {
  5310. pILinkStateNotify->Release();
  5311. }
  5312. }
  5313. return hr;
  5314. }
  5315. //---[ CAQSvrInst::prstrGetDefaultDomain ]-------------------------------------
  5316. //
  5317. //
  5318. // Description:
  5319. // Returns the ref-counted string for the default domain
  5320. // Parameters:
  5321. // -
  5322. // Returns:
  5323. // See above
  5324. // History:
  5325. // 2/23/99 - MikeSwa Created
  5326. //
  5327. //-----------------------------------------------------------------------------
  5328. CRefCountedString *CAQSvrInst::prstrGetDefaultDomain()
  5329. {
  5330. CRefCountedString *prstrDefaultDomain = NULL;
  5331. m_slPrivateData.ShareLock();
  5332. if (m_prstrDefaultDomain)
  5333. m_prstrDefaultDomain->AddRef();
  5334. prstrDefaultDomain = m_prstrDefaultDomain;
  5335. m_slPrivateData.ShareUnlock();
  5336. return prstrDefaultDomain;
  5337. }
  5338. //+------------------------------------------------------------
  5339. //
  5340. // Function: ScheduleInternalRetry
  5341. //
  5342. // Synopsis: Schedule categorizer retry if necessary
  5343. //
  5344. // Arguments: None
  5345. //
  5346. // Returns: Nothing
  5347. //
  5348. // History:
  5349. // jstamerj 2000/06/08 17:31:30: Created.
  5350. // 1/16/2001 - MikeSwa Modified to handle all internal retries
  5351. //
  5352. //-------------------------------------------------------------
  5353. VOID CAQSvrInst::ScheduleInternalRetry(DWORD dwLinkType)
  5354. {
  5355. TraceFunctEnterEx((LPARAM) this, "CAQSvrInst::ScheduleInternalRetry");
  5356. DWORD *pcRetriesPending = NULL;
  5357. PSRVFN pCallbackFn = NULL;
  5358. DWORD cCallbackMinutes = 0;
  5359. FILETIME ftCallback = {0,0};
  5360. BOOL fHasLock = FALSE;
  5361. CQueueAdminRetryNotify *pqapiret = NULL;
  5362. //
  5363. // First try to get the shutdown lock before accessing
  5364. // anything that may go away during deinitialization (like m_pConnMgr)
  5365. //
  5366. if (!fTryShutdownLock())
  5367. goto Exit;
  5368. fHasLock = TRUE;
  5369. switch (dwLinkType) {
  5370. case LI_TYPE_PENDING_ROUTING:
  5371. pcRetriesPending = &m_cRoutingRetriesPending;
  5372. pCallbackFn = RoutingRetry;
  5373. cCallbackMinutes = g_cRoutingRetryMinutes;
  5374. pqapiret = (CQueueAdminRetryNotify *) m_dmt.pmmaqGetPreRouting();
  5375. break;
  5376. case LI_TYPE_LOCAL_DELIVERY:
  5377. pcRetriesPending = &m_cLocalRetriesPending;
  5378. pCallbackFn = LocalDeliveryRetry;
  5379. cCallbackMinutes = g_cLocalRetryMinutes;
  5380. pqapiret = (CQueueAdminRetryNotify *) m_dmt.plmqGetLocalLink();
  5381. break;
  5382. case LI_TYPE_PENDING_CAT:
  5383. pcRetriesPending = &m_cCatRetriesPending;
  5384. pCallbackFn = CatRetry;
  5385. cCallbackMinutes = g_cCatRetryMinutes;
  5386. pqapiret = (CQueueAdminRetryNotify *) m_dmt.pmmaqGetPreCategorized();
  5387. break;
  5388. case LI_TYPE_PENDING_SUBMIT:
  5389. pcRetriesPending = &m_cSubmitRetriesPending;
  5390. pCallbackFn = SubmitRetry;
  5391. cCallbackMinutes = g_cSubmissionRetryMinutes;
  5392. pqapiret = (CQueueAdminRetryNotify *) m_dmt.pmmaqGetPreSubmission();
  5393. break;
  5394. default:
  5395. //
  5396. // Someone has called with a bogus callback
  5397. //
  5398. _ASSERT(0 && "Unspecified callback for internal retry");
  5399. ErrorTrace((LPARAM) this,
  5400. "Unspecified callback for internal retry 0x%08X", dwLinkType);
  5401. goto Exit;
  5402. }
  5403. _ASSERT(pcRetriesPending);
  5404. if (pcRetriesPending && !*pcRetriesPending)
  5405. {
  5406. _ASSERT(pCallbackFn);
  5407. _ASSERT(cCallbackMinutes);
  5408. //
  5409. // Say that we are requesting a callback
  5410. //
  5411. InterlockedIncrement((PLONG) pcRetriesPending);
  5412. m_pConnMgr->SetCallbackTime(pCallbackFn, this, cCallbackMinutes);
  5413. //
  5414. // Get the expire time... so we can update our retry time
  5415. //
  5416. m_qtTime.GetExpireTime(cCallbackMinutes, &ftCallback, NULL);
  5417. //
  5418. // Update retry time
  5419. //
  5420. if (pqapiret)
  5421. pqapiret->SetNextRetry(&ftCallback);
  5422. }
  5423. Exit:
  5424. if (pqapiret)
  5425. pqapiret->Release();
  5426. if (fHasLock)
  5427. ShutdownUnlock();
  5428. TraceFunctLeave();
  5429. } // CAQSvrInst::ScheduleInternalRetry
  5430. //+------------------------------------------------------------
  5431. //
  5432. // Function: CAQSvrInst::HrAllocBoundMessage
  5433. //
  5434. // Synopsis:
  5435. // Allocates a bound message
  5436. //
  5437. // Arguments:
  5438. // ppMsg: Out param for Allocated mailmsg
  5439. // phContent: Out param for content handle. Handle is managed by mailmsg
  5440. //
  5441. // Returns:
  5442. // S_OK: Success
  5443. // E_FAIL: No ISMTPServer is available
  5444. // error from SMTP
  5445. //
  5446. // History:
  5447. // jstamerj 2001/05/11 15:39:16: Created.
  5448. //
  5449. //-------------------------------------------------------------
  5450. HRESULT CAQSvrInst::HrAllocBoundMessage(
  5451. OUT IMailMsgProperties **ppMsg,
  5452. OUT PFIO_CONTEXT *phContent)
  5453. {
  5454. HRESULT hr = S_OK;
  5455. ISMTPServerInternal *pISMTPInternal = NULL;
  5456. TraceFunctEnterEx((LPARAM)this, "CAQSvrInst::HrAllocBoundMessage");
  5457. if(m_pISMTPServer == NULL)
  5458. {
  5459. hr = E_FAIL;
  5460. goto CLEANUP;
  5461. }
  5462. hr = m_pISMTPServer->QueryInterface(
  5463. IID_ISMTPServerInternal,
  5464. (LPVOID *) &pISMTPInternal);
  5465. if(FAILED(hr))
  5466. goto CLEANUP;
  5467. hr = pISMTPInternal->AllocBoundMessage(
  5468. ppMsg,
  5469. phContent);
  5470. CLEANUP:
  5471. if(pISMTPInternal)
  5472. pISMTPInternal->Release();
  5473. DebugTrace((LPARAM)this, "returning %08lx", hr);
  5474. TraceFunctLeaveEx((LPARAM)this);
  5475. return hr;
  5476. } // CAQSvrInst::HrAllocBoundMessage
  5477. //+------------------------------------------------------------
  5478. //
  5479. // Function: CAQSvrInst::HrSubmitDSN
  5480. //
  5481. // Synopsis: Acceps a message submitted by a DSN sink
  5482. //
  5483. // Arguments:
  5484. // pIMsgOrig: Original message (for which a DSN is being generated)
  5485. // dwDSNAction: Indicates the type of DSN
  5486. // cRecipsDSNd: Number of recipients in the DSN.
  5487. // pDSNMsg: The DSN mailmsg object
  5488. //
  5489. // Returns:
  5490. // S_OK: Success
  5491. //
  5492. // History:
  5493. // jstamerj 2000/12/08 13:42:17: Created.
  5494. //
  5495. //-------------------------------------------------------------
  5496. HRESULT CAQSvrInst::HrSubmitDSN(
  5497. CDSNParams *pdsnparams,
  5498. DWORD dwDSNAction,
  5499. DWORD cRecipsDSNd,
  5500. IMailMsgProperties *pDSNMsg)
  5501. {
  5502. HRESULT hr = S_OK;
  5503. DWORD cCurrent = 0;
  5504. TraceFunctEnterEx((LPARAM)this, "CAQSvrInst::HrSubmitDSN");
  5505. DebugTrace((LPARAM)this, "dwDSNAction: %08lx", dwDSNAction);
  5506. DebugTrace((LPARAM)this, "cRecipsDSNd: %ld", cRecipsDSNd);
  5507. if(pDSNMsg == NULL)
  5508. {
  5509. hr = E_INVALIDARG;
  5510. goto CLEANUP;
  5511. }
  5512. if ((DSN_ACTION_FAILURE | DSN_ACTION_FAILURE_ALL) & dwDSNAction)
  5513. {
  5514. MSG_TRACK_INFO msgTrackInfo;
  5515. cCurrent = InterlockedIncrement((PLONG) &m_cNDRs);
  5516. DebugTrace((LPARAM) this, "INFO: NDR Generated - total %d", cCurrent);
  5517. ZeroMemory(&msgTrackInfo, sizeof(MSG_TRACK_INFO));
  5518. msgTrackInfo.dwEventId = MTE_NDR_ALL;
  5519. msgTrackInfo.pszPartnerName = "aqueue";
  5520. msgTrackInfo.dwRcptReportStatus = MP_STATUS_ABORT_DELIVERY;
  5521. m_pISMTPServer->WriteLog(&msgTrackInfo,
  5522. pdsnparams->pIMailMsgProperties,
  5523. NULL,
  5524. NULL);
  5525. }
  5526. if (DSN_ACTION_DELAYED & dwDSNAction)
  5527. {
  5528. cCurrent = InterlockedIncrement((PLONG) &m_cDelayedDSNs);
  5529. DebugTrace((LPARAM) this, "INFO: Delayed DSN Generated - total %d", cCurrent);
  5530. }
  5531. if (DSN_ACTION_RELAYED & dwDSNAction)
  5532. {
  5533. cCurrent = InterlockedIncrement((PLONG) &m_cRelayedDSNs);
  5534. DebugTrace((LPARAM) this, "INFO: Relayed DSN Generated - total %d", cCurrent);
  5535. }
  5536. if (DSN_ACTION_DELIVERED & dwDSNAction)
  5537. {
  5538. cCurrent = InterlockedIncrement((PLONG) &m_cDeliveredDSNs);
  5539. DebugTrace((LPARAM) this, "INFO: Delivery DSN Generated - total %d", cCurrent);
  5540. }
  5541. if (DSN_ACTION_EXPANDED & dwDSNAction)
  5542. {
  5543. cCurrent = InterlockedIncrement((PLONG) &m_cExpandedDSNs);
  5544. DebugTrace((LPARAM) this, "INFO: Expanded DSN Generated - total %d", cCurrent);
  5545. }
  5546. //Queue request to post DSN generation queue
  5547. hr = m_asyncqPostDSNQueue.HrQueueRequest(
  5548. pDSNMsg,
  5549. FALSE,
  5550. cCountMsgsForHandleThrottling(pDSNMsg));
  5551. if (SUCCEEDED(hr))
  5552. hr = S_OK;
  5553. pdsnparams->dwDSNTypesGenerated |= dwDSNAction;
  5554. pdsnparams->cRecips += cRecipsDSNd;
  5555. CLEANUP:
  5556. DebugTrace((LPARAM)this, "returning %08lx", hr);
  5557. TraceFunctLeaveEx((LPARAM)this);
  5558. return hr;
  5559. } // CAQSvrInst::HrSubmitDSN