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.

1047 lines
31 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name :
  4. pe_dispi.cxx
  5. Abstract:
  6. This module provides the implementation for the protocol
  7. event dispatchers
  8. Author:
  9. Keith Lau (KeithLau) 6/24/98
  10. Project:
  11. SMTP Server DLL
  12. Revision History:
  13. KeithLau Created
  14. --*/
  15. #define INCL_INETSRV_INCS
  16. #include "smtpinc.h"
  17. #include "initguid.h"
  18. //
  19. // ATL includes
  20. //
  21. #define _ATL_NO_DEBUG_CRT
  22. #define _ASSERTE _ASSERT
  23. #define _WINDLL
  24. #include "atlbase.h"
  25. extern CComModule _Module;
  26. #include "atlcom.h"
  27. #undef _WINDLL
  28. //
  29. // SEO includes
  30. //
  31. #include "seo.h"
  32. #include "seolib.h"
  33. #include <memory.h>
  34. #include "smtpcli.hxx"
  35. #include "smtpout.hxx"
  36. #include <smtpguid.h>
  37. //
  38. // Dispatcher implementation
  39. //
  40. #include "pe_dispi.hxx"
  41. // =================================================================
  42. // Outbound command generation dispatcher
  43. //
  44. //
  45. // These are the commands that the default handlers hook
  46. //
  47. char *COutboundDispatcher::s_rgszDefaultCommand[PE_STATE_MAX_STATES] =
  48. {
  49. "ehlo",
  50. "mail",
  51. "rcpt",
  52. NULL,
  53. "quit"
  54. };
  55. //
  56. // This is what we use to map an event type to an internal index
  57. //
  58. const GUID *COutboundDispatcher::s_rgrguidEventTypes[PE_STATE_MAX_STATES] =
  59. {
  60. &CATID_SMTP_ON_SESSION_START,
  61. &CATID_SMTP_ON_MESSAGE_START,
  62. &CATID_SMTP_ON_PER_RECIPIENT,
  63. &CATID_SMTP_ON_BEFORE_DATA,
  64. &CATID_SMTP_ON_SESSION_END
  65. };
  66. //
  67. // Generic dispatcher methods
  68. //
  69. HRESULT CGenericProtoclEventDispatcher::GetLowerAnsiStringFromVariant(
  70. CComVariant &vString,
  71. LPSTR pszString,
  72. DWORD *pdwLength
  73. )
  74. {
  75. HRESULT hr = S_OK;
  76. DWORD dwInLength;
  77. if (!pszString)
  78. return(E_POINTER);
  79. if (!pdwLength)
  80. return(E_INVALIDARG);
  81. // Default to NULL
  82. *pszString = NULL;
  83. if (vString.vt == VT_BSTR)
  84. {
  85. DWORD dwLength = lstrlenW(vString.bstrVal) + 1;
  86. // Set the size anyway
  87. dwInLength = *pdwLength;
  88. *pdwLength = dwLength;
  89. if (dwLength > dwInLength)
  90. return(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
  91. // copy the rule into an ascii string
  92. if (WideCharToMultiByte(CP_ACP, 0, vString.bstrVal,
  93. -1, pszString, dwLength, NULL, NULL) <= 0)
  94. return(HRESULT_FROM_WIN32(GetLastError()));
  95. // Convert to lower case once and for all to avoid strcmpi
  96. while (*pszString)
  97. {
  98. *pszString = (CHAR)tolower(*pszString);
  99. pszString++;
  100. }
  101. }
  102. else
  103. hr = E_INVALIDARG;
  104. return(hr);
  105. }
  106. HRESULT CGenericProtoclEventDispatcher::InsertBinding(
  107. LPPE_COMMAND_NODE *ppHeadNode,
  108. LPPE_BINDING_NODE pBinding,
  109. LPSTR pszCommandKeyword,
  110. DWORD dwCommandKeywordSize
  111. )
  112. {
  113. DWORD dwPriority;
  114. BOOL fReorder = FALSE;
  115. LPPE_COMMAND_NODE pNode;
  116. TraceFunctEnter("CGenericProtoclEventDispatcher::InsertBinding");
  117. if (!ppHeadNode || !pBinding || !pszCommandKeyword)
  118. return(E_POINTER);
  119. pNode = *ppHeadNode;
  120. while (pNode)
  121. {
  122. // All strings come in lower case
  123. if (!strcmp(pszCommandKeyword, pNode->pszCommandKeyword))
  124. {
  125. DebugTrace((LPARAM)0, "Found command %s", pszCommandKeyword);
  126. // Now we determine if we have changed the max priority
  127. // if we increased the priority (i.e. lower value), then
  128. // we will have to reposition out node
  129. dwPriority = pBinding->dwPriority;
  130. if (pBinding->dwPriority < dwPriority)
  131. {
  132. pBinding->dwPriority = dwPriority;
  133. fReorder = TRUE;
  134. DebugTrace((LPARAM)0, "Reordering commands");
  135. }
  136. // Now insert the binding in the right order
  137. LPPE_BINDING_NODE pWalk = pNode->pFirstBinding;
  138. LPPE_BINDING_NODE pPrev = (LPPE_BINDING_NODE)&(pNode->pFirstBinding);
  139. if (!pWalk)
  140. {
  141. pBinding->pNext = pNode->pFirstBinding;
  142. pNode->pFirstBinding = pBinding;
  143. }
  144. else while (pWalk)
  145. {
  146. if (pWalk->dwPriority > dwPriority)
  147. break;
  148. pPrev = pWalk;
  149. pWalk = pWalk->pNext;
  150. }
  151. pBinding->pNext = pWalk;
  152. pPrev->pNext = pBinding;
  153. if (!fReorder)
  154. return(S_OK);
  155. else
  156. break;
  157. }
  158. // Next!
  159. pNode = pNode->pNext;
  160. }
  161. if (!fReorder)
  162. {
  163. char *pTemp;
  164. DWORD dwSize = sizeof(PE_COMMAND_NODE);
  165. // Not found, create a new command node, plus a buffer
  166. // for the command keyword
  167. dwSize += dwCommandKeywordSize;
  168. pTemp = new char [dwSize];
  169. if (!pTemp)
  170. return(E_OUTOFMEMORY);
  171. pNode = (LPPE_COMMAND_NODE)pTemp;
  172. pTemp = (char *)(pNode + 1);
  173. DebugTrace((LPARAM)0,
  174. "Creating node for command %s",
  175. pszCommandKeyword);
  176. strcpy(pTemp, pszCommandKeyword);
  177. dwPriority = pBinding->dwPriority;
  178. pBinding->pNext = NULL;
  179. pNode->pszCommandKeyword = pTemp;
  180. pNode->dwHighestPriority = dwPriority;
  181. pNode->pFirstBinding = pBinding;
  182. }
  183. // Insert based on top priority
  184. LPPE_COMMAND_NODE pWalk = *ppHeadNode;
  185. LPPE_COMMAND_NODE pPrev = (LPPE_COMMAND_NODE)ppHeadNode;
  186. if (!pWalk)
  187. {
  188. pNode->pNext = NULL;
  189. *ppHeadNode = pNode;
  190. return(S_OK);
  191. }
  192. while (pWalk)
  193. {
  194. if (pWalk->dwHighestPriority > dwPriority)
  195. break;
  196. pPrev = pWalk;
  197. pWalk = pWalk->pNext;
  198. }
  199. pNode->pNext = pWalk;
  200. pPrev->pNext = pNode;
  201. TraceFunctLeave();
  202. return(S_OK);
  203. }
  204. HRESULT CGenericProtoclEventDispatcher::InsertBindingWithHash(
  205. LPPE_COMMAND_NODE *rgpHeadNodes,
  206. DWORD dwHashSize,
  207. LPPE_BINDING_NODE pBinding,
  208. LPSTR pszCommandKeyword,
  209. DWORD dwCommandKeywordSize
  210. )
  211. {
  212. TraceFunctEnter("CGenericProtoclEventDispatcher::InsertBindingWithHash");
  213. if (!rgpHeadNodes || !pBinding || !pszCommandKeyword)
  214. return(E_POINTER);
  215. DWORD dwHash = GetHashValue(
  216. dwHashSize,
  217. pszCommandKeyword,
  218. dwCommandKeywordSize);
  219. HRESULT hr = InsertBinding(
  220. &(rgpHeadNodes[dwHash]),
  221. pBinding,
  222. pszCommandKeyword,
  223. dwCommandKeywordSize);
  224. TraceFunctLeave();
  225. return(hr);
  226. }
  227. HRESULT CGenericProtoclEventDispatcher::FindCommandFromHash(
  228. LPPE_COMMAND_NODE *rgpHeadNodes,
  229. DWORD dwHashSize,
  230. LPSTR pszCommandKeyword,
  231. DWORD dwCommandKeywordSize,
  232. LPPE_COMMAND_NODE *ppCommandNode
  233. )
  234. {
  235. TraceFunctEnter("CGenericProtoclEventDispatcher::FindCommandFromHash");
  236. if (!rgpHeadNodes || !pszCommandKeyword || !ppCommandNode)
  237. return(E_POINTER);
  238. DWORD dwHash = GetHashValue(
  239. dwHashSize,
  240. pszCommandKeyword,
  241. dwCommandKeywordSize);
  242. LPPE_COMMAND_NODE pNode = rgpHeadNodes[dwHash];
  243. while (pNode)
  244. {
  245. if (!strcmp(pszCommandKeyword, pNode->pszCommandKeyword))
  246. {
  247. *ppCommandNode = pNode;
  248. TraceFunctLeave();
  249. return(S_OK);
  250. }
  251. pNode = pNode->pNext;
  252. }
  253. TraceFunctLeave();
  254. return(S_FALSE);
  255. }
  256. HRESULT CGenericProtoclEventDispatcher::CleanupCommandNodes(
  257. LPPE_COMMAND_NODE pHeadNode,
  258. LPPE_COMMAND_NODE pSkipNode
  259. )
  260. {
  261. LPPE_COMMAND_NODE pNode;
  262. while (pHeadNode)
  263. {
  264. pNode = pHeadNode->pNext;
  265. if (pHeadNode != pSkipNode)
  266. {
  267. char *pTemp = (char *)pHeadNode;
  268. delete [] pTemp;
  269. }
  270. pHeadNode = pNode;
  271. }
  272. return(S_OK);
  273. }
  274. //
  275. // Inbound dispatcher methods
  276. //
  277. HRESULT CInboundDispatcher::AllocBinding(
  278. REFGUID rguidEventType,
  279. IEventBinding *piBinding,
  280. CBinding **ppNewBinding
  281. )
  282. {
  283. if (ppNewBinding)
  284. *ppNewBinding = NULL;
  285. if (!piBinding || !ppNewBinding)
  286. return(E_POINTER);
  287. *ppNewBinding = new CInboundBinding(this);
  288. if (*ppNewBinding == NULL)
  289. return(E_OUTOFMEMORY);
  290. return(S_OK);
  291. }
  292. HRESULT STDMETHODCALLTYPE CInboundDispatcher::ChainSinks(
  293. IUnknown *pServer,
  294. IUnknown *pSession,
  295. IMailMsgProperties *pMsg,
  296. ISmtpInCommandContext *pContext,
  297. DWORD dwStopAtPriority,
  298. LPPE_COMMAND_NODE pCommandNode,
  299. LPPE_BINDING_NODE *ppResumeFrom
  300. )
  301. {
  302. HRESULT hr = S_OK;
  303. LPPE_BINDING_NODE pBinding = NULL;
  304. TraceFunctEnterEx((LPARAM)this, "CInboundDispatcher::ChainSinks");
  305. // These are the essential pointers that CANNOT be NULL
  306. if (!pContext || !pCommandNode || !ppResumeFrom)
  307. return(E_POINTER);
  308. // What we do is strictly determined by the ppPreviousCommand
  309. // and ppResumeFrom pointers. The logic is as follows:
  310. //
  311. // pCmdNode ppResumeFrom Action
  312. // NULL X Error (E_POINTER)
  313. // !NULL NULL Error (ERROR_NO_MORE_ITEMS)
  314. // !NULL !NULL Start from the exact binding specified in
  315. // *ppResumeFrom
  316. //
  317. // On exit, these pointers will be updated to the following:
  318. //
  319. // *ppResumeFrom - This will be set to the next binding to resume from
  320. // This will be set to NULL if there are no more bindings
  321. pBinding = *ppResumeFrom;
  322. if (!pBinding)
  323. {
  324. _ASSERT(FALSE);
  325. ErrorTrace((LPARAM)this, "ERROR! Empty binding chain!!");
  326. return(HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS));
  327. }
  328. // We know exactly where to start chaining ... Do it.
  329. // Break when:
  330. // 1) The default handler binding is encountered, or
  331. // 2) Sinks results are S_OK
  332. // 3) No more bindings left
  333. CInboundBinding *pInboundBinding;
  334. IUnknown *pUnkSink = NULL;
  335. ISmtpInCommandSink *pSink;
  336. hr = S_OK;
  337. *ppResumeFrom = pBinding;
  338. while (pBinding && (hr == S_OK))
  339. {
  340. // One of the exiting conditions
  341. if (pBinding->dwPriority > dwStopAtPriority)
  342. break;
  343. // Get the containing binding class
  344. pInboundBinding = CONTAINING_RECORD(
  345. pBinding, CInboundBinding, m_bnInfo);
  346. // Call the sink
  347. hr = m_piEventManager->CreateSink(
  348. pInboundBinding->m_piBinding,
  349. NULL,
  350. &pUnkSink);
  351. if (SUCCEEDED(hr))
  352. {
  353. hr = pUnkSink->QueryInterface(
  354. IID_ISmtpInCommandSink,
  355. (LPVOID *)&pSink);
  356. pUnkSink->Release();
  357. pUnkSink = NULL;
  358. if (SUCCEEDED(hr))
  359. {
  360. hr = pSink->OnSmtpInCommand(
  361. pServer,
  362. pSession,
  363. pMsg,
  364. pContext);
  365. pSink->Release();
  366. if (hr == MAILTRANSPORT_S_PENDING)
  367. break;
  368. }
  369. else
  370. hr = S_OK;
  371. }
  372. else
  373. hr = S_OK;
  374. // Next
  375. pBinding = pBinding->pNext;
  376. *ppResumeFrom = pBinding;
  377. }
  378. TraceFunctLeaveEx((LPARAM)this);
  379. return(hr);
  380. }
  381. //
  382. // CInboundDispatcher::CInboundBinding methods
  383. //
  384. HRESULT CInboundDispatcher::CInboundBinding::Init(
  385. IEventBinding *piBinding
  386. )
  387. {
  388. HRESULT hr;
  389. CComPtr<IEventPropertyBag> piEventProperties;
  390. CComVariant vRule;
  391. CHAR szCommandKeyword[256];
  392. DWORD cchCommandKeyword = 0;
  393. TraceFunctEnterEx((LPARAM)this,
  394. "CInboundDispatcher::CInboundBinding::Init");
  395. if (!piBinding || !m_pDispatcher)
  396. return(E_POINTER);
  397. // get the parent initialized
  398. hr = CBinding::Init(piBinding);
  399. if (FAILED(hr)) return hr;
  400. // Store the priority
  401. m_bnInfo.dwPriority = CBinding::m_dwPriority;
  402. m_bnInfo.dwFlags = 0;
  403. // get the binding database
  404. hr = m_piBinding->get_SourceProperties(&piEventProperties);
  405. if (FAILED(hr)) return hr;
  406. // get the rule from the binding database
  407. hr = piEventProperties->Item(&CComVariant("Rule"), &vRule);
  408. if (FAILED(hr)) return hr;
  409. // Process the VARIANT to obtain a lower-case ANSI string
  410. if (hr == S_OK)
  411. {
  412. cchCommandKeyword = sizeof(szCommandKeyword);
  413. hr = GetLowerAnsiStringFromVariant(
  414. vRule,
  415. szCommandKeyword,
  416. &cchCommandKeyword);
  417. }
  418. // We cannot proceed without a rule, so we discard this binding
  419. if (FAILED(hr))
  420. {
  421. ErrorTrace((LPARAM)this,
  422. "Failed to get keyword, error %08x", hr);
  423. return(hr);
  424. }
  425. if (!cchCommandKeyword || !(*szCommandKeyword))
  426. return(E_INVALIDARG);
  427. DebugTrace((LPARAM)this, "Rule: %s", szCommandKeyword);
  428. // Call the dispatcher to insert the node into the command list
  429. hr = CGenericProtoclEventDispatcher::InsertBindingWithHash(
  430. m_pDispatcher->m_rgpCommandList,
  431. m_pDispatcher->m_dwHashSize,
  432. &m_bnInfo,
  433. szCommandKeyword,
  434. cchCommandKeyword);
  435. if (SUCCEEDED(hr))
  436. m_pDispatcher->m_fSinksInstalled = TRUE;
  437. TraceFunctLeaveEx((LPARAM)this);
  438. return(hr);
  439. }
  440. //
  441. // Outbound dispatcher methods
  442. //
  443. void COutboundDispatcher::InitializeDefaultCommandBindings()
  444. {
  445. for (DWORD i = 0; i < PE_STATE_MAX_STATES; i++)
  446. {
  447. if (s_rgszDefaultCommand[i])
  448. {
  449. // Set up the list head
  450. m_rgpCommandList[i] = &(m_rgcnDefaultCommand[i]);
  451. // Set up the command node
  452. m_rgpCommandList[i]->pNext = NULL;
  453. m_rgpCommandList[i]->pszCommandKeyword = s_rgszDefaultCommand[i];
  454. m_rgpCommandList[i]->dwHighestPriority = PRIO_DEFAULT;
  455. m_rgpCommandList[i]->pFirstBinding = &(m_rgbnDefaultCommand[i]);
  456. // Set up the binding node
  457. m_rgbnDefaultCommand[i].pNext = NULL;
  458. m_rgbnDefaultCommand[i].dwPriority = PRIO_DEFAULT;
  459. m_rgbnDefaultCommand[i].dwFlags = PEBN_DEFAULT;
  460. }
  461. else
  462. m_rgpCommandList[i] = NULL;
  463. }
  464. }
  465. HRESULT COutboundDispatcher::AllocBinding(
  466. REFGUID rguidEventType,
  467. IEventBinding *piBinding,
  468. CBinding **ppNewBinding
  469. )
  470. {
  471. if (ppNewBinding)
  472. *ppNewBinding = NULL;
  473. if (!piBinding || !ppNewBinding)
  474. return(E_POINTER);
  475. *ppNewBinding = new COutboundBinding(this, rguidEventType);
  476. if (*ppNewBinding == NULL)
  477. return(E_OUTOFMEMORY);
  478. return(S_OK);
  479. }
  480. HRESULT STDMETHODCALLTYPE COutboundDispatcher::ChainSinks(
  481. IUnknown *pServer,
  482. IUnknown *pSession,
  483. IMailMsgProperties *pMsg,
  484. ISmtpOutCommandContext *pContext,
  485. DWORD dwEventType,
  486. LPPE_COMMAND_NODE *ppPreviousCommand,
  487. LPPE_BINDING_NODE *ppResumeFrom
  488. )
  489. {
  490. HRESULT hr = S_OK;
  491. LPPE_COMMAND_NODE pCommand = NULL;
  492. LPPE_BINDING_NODE pBinding = NULL;
  493. _ASSERT(dwEventType < PE_OET_INVALID_EVENT_TYPE);
  494. TraceFunctEnterEx((LPARAM)this, "COutboundDispatcher::ChainSinks");
  495. // These are the essential pointers that CANNOT be NULL
  496. if (!pContext || !ppPreviousCommand || !ppResumeFrom)
  497. return(E_POINTER);
  498. // If we encounter a bad event type, just gracefully return
  499. // without doing anythig
  500. if (dwEventType >= PE_OET_INVALID_EVENT_TYPE)
  501. {
  502. *ppResumeFrom = NULL;
  503. ErrorTrace((LPARAM)this,
  504. "Skipping event due to bad event type %u",
  505. dwEventType);
  506. return(S_OK);
  507. }
  508. // What we do is strictly determined by the ppPreviousCommand
  509. // and ppResumeFrom pointers. The logic is as follows:
  510. //
  511. // ppPrevCmd ppResumeFrom Action
  512. // NULL NULL Start from the first command for this event type
  513. // NULL !NULL Start from the first command for this event type
  514. // !NULL NULL Start from the first binding for the command
  515. // that follows *ppPreviousCommand
  516. // !NULL !NULL Start from the exact binding specified in
  517. // *ppResumeFrom
  518. //
  519. // On exit, these pointers will be updated to the following:
  520. //
  521. // *ppPreviousCommand - This will point to the command node of the command
  522. // that was just processed
  523. // *ppResumeFrom - This will be set to the next binding to resume from
  524. // This will be set to NULL if there are no more bindings
  525. if (!*ppPreviousCommand)
  526. {
  527. pCommand = m_rgpCommandList[dwEventType];
  528. if (!pCommand)
  529. {
  530. // Ooooops! We peeked with SinksInstalled() but now we have no sinks
  531. // This is an error but we will recover gracefully
  532. _ASSERT(pCommand);
  533. ErrorTrace((LPARAM)this, "ERROR! Empty command chain!!");
  534. return(HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS));
  535. }
  536. // We check this downstream
  537. pBinding = pCommand->pFirstBinding;
  538. }
  539. else if (*ppResumeFrom)
  540. {
  541. // We resume from the exact binding as specified
  542. pCommand = *ppPreviousCommand;
  543. pBinding = *ppResumeFrom;
  544. }
  545. else
  546. {
  547. // We start with the next command in the list
  548. pCommand = (*ppPreviousCommand)->pNext;
  549. if (!pCommand)
  550. {
  551. DebugTrace((LPARAM)this, "No more commands to chain");
  552. *ppPreviousCommand = NULL;
  553. *ppResumeFrom = NULL;
  554. return(HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS));
  555. }
  556. // We check this downstream
  557. pBinding = pCommand->pFirstBinding;
  558. }
  559. // DEBUG Check: we want the retail perf to be as high as possible
  560. // and we want it to be fail safe as well. We will do an internal
  561. // check here to make sure that any command node has at least one
  562. // binding, and that we have bindings when we expect them.
  563. #ifdef DEBUG
  564. if (!pBinding)
  565. {
  566. // Ooooops! We have a command node without a binding. This is a blatant
  567. // error but we recover gracefully
  568. _ASSERT(pBinding);
  569. ErrorTrace((LPARAM)this, "ERROR! Empty binding chain!!");
  570. return(HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS));
  571. }
  572. #endif
  573. // We know exactly where to start chaining ... Do it.
  574. // Break when:
  575. // 1) The default handler binding is encountered, or
  576. // 2) Sinks results are S_OK
  577. // 3) No more bindings left
  578. COutboundBinding *pOutboundBinding;
  579. IUnknown *pUnkSink = NULL;
  580. ISmtpOutCommandSink *pSink;
  581. COutboundContext *pCContext;
  582. // Initialize pContext->m_pCurrentCommandContext
  583. pCContext = (COutboundContext *)pContext;
  584. pCContext->m_pCurrentCommandContext = pCommand;
  585. hr = S_OK;
  586. while (pBinding && (hr == S_OK))
  587. {
  588. // One of the exiting conditions
  589. if (pBinding->dwFlags & PEBN_DEFAULT)
  590. break;
  591. // Get the containing binding class
  592. pOutboundBinding = CONTAINING_RECORD(
  593. pBinding, COutboundBinding, m_bnInfo);
  594. // Call the sink
  595. hr = m_piEventManager->CreateSink(
  596. pOutboundBinding->m_piBinding,
  597. NULL,
  598. &pUnkSink);
  599. if (SUCCEEDED(hr))
  600. {
  601. hr = pUnkSink->QueryInterface(
  602. IID_ISmtpOutCommandSink,
  603. (LPVOID *)&pSink);
  604. pUnkSink->Release();
  605. pUnkSink = NULL;
  606. if (SUCCEEDED(hr))
  607. {
  608. // Pre-fill in the next binding to avoid
  609. // race conditions in the async case
  610. *ppResumeFrom = pBinding->pNext;
  611. hr = pSink->OnSmtpOutCommand(
  612. pServer,
  613. pSession,
  614. pMsg,
  615. pContext);
  616. pSink->Release();
  617. if (hr == MAILTRANSPORT_S_PENDING)
  618. hr = S_OK;
  619. // break;
  620. }
  621. else
  622. hr = S_OK;
  623. }
  624. else
  625. hr = S_OK;
  626. // Next
  627. pBinding = pBinding->pNext;
  628. }
  629. // Return where to resume from ...
  630. *ppPreviousCommand = pCommand;
  631. *ppResumeFrom = pBinding;
  632. TraceFunctLeaveEx((LPARAM)this);
  633. return(hr);
  634. }
  635. //
  636. // COutboundDispatcher::COutboundBinding methods
  637. //
  638. COutboundDispatcher::COutboundBinding::COutboundBinding(
  639. COutboundDispatcher *pDispatcher,
  640. REFGUID rguidEventType
  641. )
  642. {
  643. _ASSERT(pDispatcher);
  644. m_pDispatcher = pDispatcher;
  645. // Based on the event type GUID, we can calculate which
  646. // event type id this goes to
  647. for (m_dwEventType = 0;
  648. m_dwEventType < PE_OET_INVALID_EVENT_TYPE;
  649. m_dwEventType++)
  650. if (rguidEventType == *(s_rgrguidEventTypes[m_dwEventType]))
  651. break;
  652. // If the GUID is not recognized, then the final value of
  653. // m_dwEventType will be PE_OET_INVALID_EVENT_TYPE, which
  654. // we will not process in this dispatcher
  655. }
  656. HRESULT COutboundDispatcher::COutboundBinding::Init(
  657. IEventBinding *piBinding
  658. )
  659. {
  660. HRESULT hr;
  661. CComPtr<IEventPropertyBag> piEventProperties;
  662. CComVariant vRule;
  663. CHAR szCommandKeyword[256];
  664. DWORD cchCommandKeyword = 0;
  665. TraceFunctEnterEx((LPARAM)this,
  666. "COutboundDispatcher::COutboundBinding::Init");
  667. if (!piBinding || !m_pDispatcher)
  668. return(E_POINTER);
  669. // If the event type GUID is unknown, invalidate this binding
  670. if (m_dwEventType >= PE_OET_INVALID_EVENT_TYPE)
  671. return(E_INVALIDARG);
  672. // get the parent initialized
  673. hr = CBinding::Init(piBinding);
  674. if (FAILED(hr)) return hr;
  675. // Store the priority
  676. m_bnInfo.dwPriority = CBinding::m_dwPriority;
  677. m_bnInfo.dwFlags = 0;
  678. // get the binding database
  679. hr = m_piBinding->get_SourceProperties(&piEventProperties);
  680. if (FAILED(hr)) return hr;
  681. // get the rule from the binding database
  682. hr = piEventProperties->Item(&CComVariant("Rule"), &vRule);
  683. if (FAILED(hr)) return hr;
  684. // Process the VARIANT to obtain a lower-case ANSI string
  685. if (hr == S_OK)
  686. {
  687. cchCommandKeyword = sizeof(szCommandKeyword);
  688. hr = GetLowerAnsiStringFromVariant(
  689. vRule,
  690. szCommandKeyword,
  691. &cchCommandKeyword);
  692. }
  693. else if(hr == S_FALSE)
  694. {
  695. //
  696. // Treat no rule as an empty string rule
  697. //
  698. szCommandKeyword[0] = '\0';
  699. cchCommandKeyword = 1;
  700. }
  701. // We cannot proceed without a rule, so we discard this binding
  702. if (FAILED(hr))
  703. {
  704. ErrorTrace((LPARAM)this,
  705. "Failed to get keyword, error %08x", hr);
  706. return(hr);
  707. }
  708. if (!cchCommandKeyword)
  709. return(E_INVALIDARG);
  710. DebugTrace((LPARAM)this, "Rule: %s", szCommandKeyword);
  711. // Call the dispatcher to insert the node into the command list
  712. hr = CGenericProtoclEventDispatcher::InsertBinding(
  713. &((m_pDispatcher->m_rgpCommandList)[m_dwEventType]),
  714. &m_bnInfo,
  715. szCommandKeyword,
  716. cchCommandKeyword);
  717. if (SUCCEEDED(hr))
  718. m_pDispatcher->m_fSinksInstalled = TRUE;
  719. TraceFunctLeaveEx((LPARAM)this);
  720. return(hr);
  721. }
  722. //
  723. // CResponseDispatcher mathods
  724. //
  725. HRESULT CResponseDispatcher::AllocBinding(
  726. REFGUID rguidEventType,
  727. IEventBinding *piBinding,
  728. CBinding **ppNewBinding
  729. )
  730. {
  731. if (ppNewBinding)
  732. *ppNewBinding = NULL;
  733. if (!piBinding || !ppNewBinding)
  734. return(E_POINTER);
  735. *ppNewBinding = new CResponseBinding(this);
  736. if (*ppNewBinding == NULL)
  737. return(E_OUTOFMEMORY);
  738. return(S_OK);
  739. }
  740. HRESULT STDMETHODCALLTYPE CResponseDispatcher::ChainSinks(
  741. IUnknown *pServer,
  742. IUnknown *pSession,
  743. IMailMsgProperties *pMsg,
  744. ISmtpServerResponseContext *pContext,
  745. DWORD dwStopAtPriority,
  746. LPPE_COMMAND_NODE pCommandNode,
  747. LPPE_BINDING_NODE *ppResumeFrom
  748. )
  749. {
  750. HRESULT hr = S_OK;
  751. LPPE_BINDING_NODE pBinding = NULL;
  752. TraceFunctEnterEx((LPARAM)this, "CResponseDispatcher::ChainSinks");
  753. // These are the essential pointers that CANNOT be NULL
  754. if (!pContext || !pCommandNode || !ppResumeFrom)
  755. return(E_POINTER);
  756. // What we do is strictly determined by the ppPreviousCommand
  757. // and ppResumeFrom pointers. The logic is as follows:
  758. //
  759. // pCmdNode ppResumeFrom Action
  760. // NULL X Error (E_POINTER)
  761. // !NULL NULL Error (ERROR_NO_MORE_ITEMS)
  762. // !NULL !NULL Start from the exact binding specified in
  763. // *ppResumeFrom
  764. //
  765. // On exit, these pointers will be updated to the following:
  766. //
  767. // *ppResumeFrom - This will be set to the next binding to resume from
  768. // This will be set to NULL if there are no more bindings
  769. pBinding = *ppResumeFrom;
  770. if (!pBinding)
  771. {
  772. _ASSERT(FALSE);
  773. ErrorTrace((LPARAM)this, "ERROR! Empty binding chain!!");
  774. return(HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS));
  775. }
  776. // We know exactly where to start chaining ... Do it.
  777. // Break when:
  778. // 1) The default handler binding is encountered, or
  779. // 2) Sinks results are S_OK
  780. // 3) No more bindings left
  781. CResponseBinding *pResponseBinding;
  782. IUnknown *pUnkSink = NULL;
  783. ISmtpServerResponseSink *pSink;
  784. hr = S_OK;
  785. *ppResumeFrom = pBinding;
  786. while (pBinding && (hr == S_OK))
  787. {
  788. // One of the exiting conditions
  789. if (pBinding->dwPriority > dwStopAtPriority)
  790. break;
  791. // Get the containing binding class
  792. pResponseBinding = CONTAINING_RECORD(
  793. pBinding, CResponseBinding, m_bnInfo);
  794. // Call the sink
  795. hr = m_piEventManager->CreateSink(
  796. pResponseBinding->m_piBinding,
  797. NULL,
  798. &pUnkSink);
  799. if (SUCCEEDED(hr))
  800. {
  801. hr = pUnkSink->QueryInterface(
  802. IID_ISmtpServerResponseSink,
  803. (LPVOID *)&pSink);
  804. pUnkSink->Release();
  805. pUnkSink = NULL;
  806. if (SUCCEEDED(hr))
  807. {
  808. // Pre-fill in the next binding to avoid
  809. // race conditions in the async case
  810. hr = pSink->OnSmtpServerResponse(
  811. pServer,
  812. pSession,
  813. pMsg,
  814. pContext);
  815. pSink->Release();
  816. if (hr == MAILTRANSPORT_S_PENDING)
  817. hr = S_OK;
  818. // break;
  819. }
  820. else
  821. hr = S_OK;
  822. }
  823. else
  824. hr = S_OK;
  825. // Next
  826. pBinding = pBinding->pNext;
  827. *ppResumeFrom = pBinding;
  828. }
  829. TraceFunctLeaveEx((LPARAM)this);
  830. return(hr);
  831. }
  832. //
  833. // CResponseDispatcher::CResponseBinding methods
  834. //
  835. HRESULT CResponseDispatcher::CResponseBinding::Init(
  836. IEventBinding *piBinding
  837. )
  838. {
  839. HRESULT hr;
  840. CComPtr<IEventPropertyBag> piEventProperties;
  841. CComVariant vRule;
  842. CHAR szCommandKeyword[256];
  843. DWORD cchCommandKeyword = 0;
  844. TraceFunctEnterEx((LPARAM)this,
  845. "CResponseDispatcher::CResponseBinding::Init");
  846. if (!piBinding || !m_pDispatcher)
  847. return(E_POINTER);
  848. // get the parent initialized
  849. hr = CBinding::Init(piBinding);
  850. if (FAILED(hr)) return hr;
  851. // Store the priority
  852. m_bnInfo.dwPriority = CBinding::m_dwPriority;
  853. m_bnInfo.dwFlags = 0;
  854. // get the binding database
  855. hr = m_piBinding->get_SourceProperties(&piEventProperties);
  856. if (FAILED(hr)) return hr;
  857. // get the rule from the binding database
  858. hr = piEventProperties->Item(&CComVariant("Rule"), &vRule);
  859. if (FAILED(hr)) return hr;
  860. // Process the VARIANT to obtain a lower-case ANSI string
  861. if (hr == S_OK)
  862. {
  863. cchCommandKeyword = sizeof(szCommandKeyword);
  864. hr = GetLowerAnsiStringFromVariant(
  865. vRule,
  866. szCommandKeyword,
  867. &cchCommandKeyword);
  868. }
  869. // We cannot proceed without a rule, so we discard this binding
  870. if (FAILED(hr))
  871. {
  872. ErrorTrace((LPARAM)this,
  873. "Failed to get keyword, error %08x", hr);
  874. return(hr);
  875. }
  876. if (!cchCommandKeyword || !(*szCommandKeyword))
  877. return(E_INVALIDARG);
  878. DebugTrace((LPARAM)this, "Rule: %s", szCommandKeyword);
  879. // Call the dispatcher to insert the node into the command list
  880. hr = CGenericProtoclEventDispatcher::InsertBindingWithHash(
  881. m_pDispatcher->m_rgpCommandList,
  882. m_pDispatcher->m_dwHashSize,
  883. &m_bnInfo,
  884. szCommandKeyword,
  885. cchCommandKeyword);
  886. if (SUCCEEDED(hr))
  887. m_pDispatcher->m_fSinksInstalled = TRUE;
  888. TraceFunctLeaveEx((LPARAM)this);
  889. return(hr);
  890. }