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.

1455 lines
50 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name :
  4. pe_out.cxx
  5. Abstract:
  6. This module defines the outbound protocol event classes
  7. Author:
  8. Keith Lau (KeithLau) 6/18/98
  9. Project:
  10. SMTP Server DLL
  11. Revision History:
  12. --*/
  13. /************************************************************
  14. * Include Headers
  15. ************************************************************/
  16. #define INCL_INETSRV_INCS
  17. #include "smtpinc.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. //
  37. // Dispatcher implementation
  38. //
  39. #include "pe_dispi.hxx"
  40. //--------------------------------------------------------------------------------
  41. // Description:
  42. // Parses a response from the remote SMTP and appends in in m_cabResponse,
  43. // setting the numerical status code. Multi-line are also handled by this
  44. // function.
  45. //
  46. // Arguments:
  47. // IN char *InputLine - Start of response to be parsed
  48. // IN DWORD BufSize - Size of response
  49. // IN OUT char **NextInputLine -
  50. // OUT LPDWORD pRemainingBufSize -
  51. // IN DWORD UndecryptedTailSize - If TLS is used, count of undecrypted bytes
  52. //
  53. // Returns:
  54. // S_OK if the response was successfully parsed and appended to
  55. // m_cabResponse and the error code was extracted and set on
  56. // m_cabResponse.
  57. //
  58. // E_INVALIDARG if the response was badly formed and could not be parsed
  59. // according the to rules specified in the RFC 2821. In this case
  60. // m_cabResponse is not set, and the connection should be dropped
  61. // (Note - It should be dropped without issuing QUIT. Trying to be
  62. // "polite" by gracefully ending the session opens up an unneccessary
  63. // risk by making us parse further badly formed data).
  64. //
  65. // Some other error HRESULT if there was some other temporary error.
  66. //--------------------------------------------------------------------------------
  67. HRESULT SMTP_CONNOUT::GetNextResponse(
  68. char *InputLine,
  69. DWORD BufSize,
  70. char **NextInputLine,
  71. LPDWORD pRemainingBufSize,
  72. DWORD UndecryptedTailSize
  73. )
  74. {
  75. HRESULT hr = S_OK;
  76. char *Buffer = NULL;
  77. char *pszSearch = NULL;
  78. BOOL fFullLine = FALSE;
  79. DWORD IntermediateSize = 0;
  80. CHAR chSave = 0;
  81. TraceFunctEnterEx((LPARAM)this,
  82. "SMTP_CONNOUT::GetNextResponse");
  83. _ASSERT(InputLine);
  84. if (!InputLine)
  85. return(E_POINTER);
  86. // Start at the beginning
  87. Buffer = InputLine;
  88. // We only process full lines (i.e. ends with CRLF)
  89. while (pszSearch = IsLineComplete(Buffer, BufSize))
  90. {
  91. // Calculate the size of the line
  92. IntermediateSize = (DWORD)((pszSearch - Buffer) + 2); //+2 for CRLF
  93. //
  94. // Make sure the response at least contains an error code = 3 digits + CRLF
  95. // If the response is syntactically invalid we should not fire the response
  96. // event when this routine returns, which would cause the response handler
  97. // to try and process potentially bad data. Instead, as soon as it is detected
  98. // that the response is badly formed, we will drop the connection by returning
  99. // and error to GlueDispatch.
  100. //
  101. if(IntermediateSize < 5 || !isdigit((UCHAR)InputLine[0]) ||
  102. !isdigit((UCHAR)InputLine[1]) || !isdigit((UCHAR)InputLine[2]))
  103. {
  104. // Unparseable response
  105. chSave = *pszSearch;
  106. *pszSearch = '\0'; // Null terminate InputLine for ErrorTrace output
  107. ErrorTrace((LPARAM)this,
  108. "Cannot parse illegal response from remote SMTP: %s", InputLine);
  109. *pszSearch = chSave;
  110. TraceFunctLeaveEx((LPARAM)this);
  111. return E_INVALIDARG;
  112. }
  113. if(IntermediateSize == 5)
  114. {
  115. //We have a 250CRLF type response - which according to
  116. //Drums draft is now legit
  117. // Get the error code
  118. Buffer[3] = '\0';
  119. m_ResponseContext.m_dwSmtpStatus = atoi(Buffer);
  120. Buffer [3] = CR;
  121. fFullLine = TRUE;
  122. }
  123. else
  124. {
  125. // If we encounter a multi-line response, we will keep
  126. // parsing
  127. if (Buffer[3] == '-')
  128. {
  129. // Try to append the line, this can only fail due
  130. // to out of memory
  131. hr = m_ResponseContext.m_cabResponse.Append(
  132. Buffer,
  133. IntermediateSize,
  134. NULL);
  135. if (FAILED(hr))
  136. return(hr);
  137. // Go to the next line
  138. Buffer += IntermediateSize;
  139. BufSize -= IntermediateSize;
  140. continue;
  141. }
  142. if (Buffer[3] == ' ')
  143. {
  144. // Get the error code
  145. Buffer[3] = '\0';
  146. m_ResponseContext.m_dwSmtpStatus = atoi(Buffer);
  147. Buffer [3] = ' ';
  148. fFullLine = TRUE;
  149. }
  150. else
  151. {
  152. //
  153. // If the response is syntactically illegal, drop the connection
  154. // immediately by returning E_INVALIDARG to GlueDispatch. We do
  155. // not try to further process badly formed data.
  156. //
  157. chSave = *pszSearch;
  158. *pszSearch = '\0'; // Null terminate InputLine for ErrorTrace output
  159. ErrorTrace((LPARAM)this,
  160. "Cannot parse illegal response from remote SMTP: %s", InputLine);
  161. *pszSearch = chSave;
  162. TraceFunctLeaveEx((LPARAM)this);
  163. return E_INVALIDARG;
  164. }
  165. }
  166. // Try to append the line, this can only fail due
  167. // to out of memory
  168. hr = m_ResponseContext.m_cabResponse.Append(
  169. Buffer,
  170. IntermediateSize,
  171. NULL);
  172. if (FAILED(hr))
  173. return(hr);
  174. // Adjust the counters
  175. Buffer += IntermediateSize;
  176. BufSize -= IntermediateSize;
  177. // If we have a full response (single or multi-line)
  178. if (fFullLine)
  179. break;
  180. }
  181. if (fFullLine)
  182. {
  183. char cTerm = '\0';
  184. // NULL-temrinate it
  185. hr = m_ResponseContext.m_cabResponse.Append(&cTerm, 1, NULL);
  186. if (FAILED(hr))
  187. return(hr);
  188. else
  189. {
  190. // We got a full response. Mark the pointers for
  191. // the next response
  192. if (NextInputLine)
  193. *NextInputLine = Buffer;
  194. if (pRemainingBufSize)
  195. *pRemainingBufSize = BufSize;
  196. }
  197. }
  198. else
  199. {
  200. // We have either a partial or empty line, either case
  201. // we need another completion for more data
  202. hr = S_FALSE;
  203. MoveMemory((void *)QueryMRcvBuffer(),
  204. Buffer,
  205. BufSize + UndecryptedTailSize);
  206. m_cbParsable = BufSize;
  207. m_cbReceived = BufSize + UndecryptedTailSize;
  208. }
  209. TraceFunctLeaveEx((LPARAM)this);
  210. return(hr);
  211. }
  212. HRESULT SMTP_CONNOUT::BuildCommandQEntry(
  213. LPOUTBOUND_COMMAND_Q_ENTRY *ppEntry,
  214. BOOL *pfUseNative
  215. )
  216. {
  217. // Build a command entry
  218. DWORD dwKeywordLength = 0;
  219. DWORD dwTotalLength = 0;
  220. LPSTR szTemp;
  221. LPSTR pTemp = NULL;
  222. LPOUTBOUND_COMMAND_Q_ENTRY pEntry;
  223. BOOL fUseNative = FALSE;
  224. if (!ppEntry)
  225. return(E_POINTER);
  226. *ppEntry = NULL;
  227. // Determine which response to use
  228. szTemp = m_OutboundContext.m_cabCommand.Buffer();
  229. if (!*szTemp)
  230. {
  231. szTemp = m_OutboundContext.m_cabNativeCommand.Buffer();
  232. dwTotalLength = m_OutboundContext.m_cabNativeCommand.Length();
  233. fUseNative = TRUE;
  234. // If we have nothing in the buffer, we will return S_FALSE
  235. if (!*szTemp)
  236. return(S_FALSE);
  237. }
  238. else
  239. dwTotalLength = m_OutboundContext.m_cabCommand.Length();
  240. _ASSERT(strlen(szTemp) == dwTotalLength-1);
  241. // Determine the length of the keyword
  242. while ((*szTemp != ' ') && (*szTemp != '\0') && (*szTemp != '\r'))
  243. {
  244. szTemp++;
  245. dwKeywordLength++;
  246. }
  247. dwKeywordLength++;
  248. // Determine the total required buffer size
  249. //NK** add a byte at end to hold the null
  250. dwTotalLength +=
  251. (sizeof(OUTBOUND_COMMAND_Q_ENTRY) + dwKeywordLength + 1);
  252. pTemp = new CHAR [dwTotalLength];
  253. if (!pTemp)
  254. return(E_OUTOFMEMORY);
  255. pEntry = (LPOUTBOUND_COMMAND_Q_ENTRY)pTemp;
  256. pTemp += sizeof(OUTBOUND_COMMAND_Q_ENTRY);
  257. pEntry->dwFlags = 0;
  258. // Set the pipelined flag
  259. if (m_OutboundContext.m_dwCommandStatus & EXPE_PIPELINED)
  260. pEntry->dwFlags |= PECQ_PIPELINED;
  261. // Copy the command keyword
  262. if (fUseNative)
  263. szTemp = m_OutboundContext.m_cabNativeCommand.Buffer();
  264. else
  265. szTemp = m_OutboundContext.m_cabCommand.Buffer();
  266. pEntry->pszCommandKeyword = (LPSTR)pTemp;
  267. if (dwKeywordLength > 1)
  268. {
  269. LPSTR pszTemp = szTemp;
  270. while (--dwKeywordLength)
  271. *pTemp++ = (CHAR)tolower(*pszTemp++);
  272. }
  273. *pTemp++ = '\0';
  274. // Copy the full command
  275. pEntry->pszFullCommand = (LPSTR)pTemp;
  276. lstrcpy((LPSTR)pTemp, (LPSTR)szTemp);
  277. *ppEntry = pEntry;
  278. *pfUseNative = fUseNative;
  279. return(S_OK);
  280. }
  281. ///////////////////////////////////////////////////////////////////
  282. //
  283. // This function should only be called from GlueDispatch
  284. //
  285. // It micro-manages sink firing scenarios
  286. HRESULT SMTP_CONNOUT::OnOutboundCommandEvent(
  287. IUnknown *pServer,
  288. IUnknown *pSession,
  289. DWORD dwEventType,
  290. BOOL fRepeatLastCommand,
  291. PMFI pDefaultOutboundHandler
  292. )
  293. {
  294. HRESULT hr = S_OK;
  295. BOOL fResult = TRUE;
  296. BOOL fFireEvent = TRUE;
  297. BOOL fFireDefaultHandler = FALSE;
  298. ISmtpOutCommandContext *pContext = (ISmtpOutCommandContext *) &m_OutboundContext;
  299. // d:\ex\staxpt\src\mail\smtp\server\pe_out.cxx(265) : fatal error C1001: INTERNAL COMPILER ERROR
  300. // (compiler file 'E:\utc\src\\P2\main.c', line 379)
  301. // Please choose the Technical Support command on the Visual C++
  302. // Help menu, or open the Technical Support help file for more information
  303. //_ASSERT(pDefaultOutboundHandler);
  304. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNOUT::OnOutboundCommandEvent");
  305. // First, we want to make sure that both the command and response
  306. // dispatchers are not NULL. If they are NULL, then we just call the
  307. // default handler, if any.
  308. if (m_pOutboundDispatcher && m_pResponseDispatcher)
  309. {
  310. // Now, we take a peek at the bindings and see if we have
  311. // any sinks installed for this event type
  312. hr = m_pOutboundDispatcher->SinksInstalled(dwEventType);
  313. if (hr != S_OK)
  314. {
  315. fFireEvent = FALSE;
  316. DebugTrace((LPARAM)this,
  317. "There are no sink bindings for this event type");
  318. }
  319. }
  320. else
  321. {
  322. fFireEvent = FALSE;
  323. DebugTrace((LPARAM)this, "Sinks will not be fired because dispatcher NULL");
  324. }
  325. // Fire the event accordingly
  326. // The outbound event is different from the other protocol events
  327. // For this event, we really don't know when the default handler
  328. // should be fired. There are two scenarios when we know to fire
  329. // the default handler:
  330. //
  331. // 1) We call the next set of sinks. If the ChainSinks call returns
  332. // S_OK and pfFireDefaultHandler returns TRUE, then we will fire
  333. // the default handler
  334. // 2) If we are not able to fire sinks due to a missing dispatcher,
  335. // we will fire the default handler (if there is one), and call
  336. // it done.
  337. LPPE_COMMAND_NODE pCommandNode = NULL;
  338. LPPE_BINDING_NODE pBindingNode = NULL;
  339. if (fFireEvent)
  340. {
  341. // Set it up for the dispatcher
  342. pBindingNode = NULL;
  343. pCommandNode = m_OutboundContext.m_pCurrentCommandContext;
  344. // Now, if we are asked to repeat the last command, we will have to
  345. // set up the binding node
  346. if (fRepeatLastCommand)
  347. {
  348. _ASSERT(m_OutboundContext.m_pCurrentCommandContext);
  349. pBindingNode = m_OutboundContext.m_pCurrentCommandContext->pFirstBinding;
  350. }
  351. hr = m_pOutboundDispatcher->ChainSinks(
  352. pServer,
  353. pSession,
  354. m_pIMsg,
  355. pContext,
  356. dwEventType,
  357. &pCommandNode,
  358. &pBindingNode
  359. );
  360. // If the event is consumed or if there are no more bindgins
  361. // left, we will jump to analyze the status codes.
  362. if ((hr == S_FALSE) || (hr == EXPE_S_CONSUMED))
  363. goto Analysis;
  364. if (hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS))
  365. goto Analysis;
  366. if (FAILED(hr))
  367. goto Abort;
  368. // See if we are asked to fire the default handler
  369. if (pBindingNode)
  370. {
  371. // We must have aborted due to default handler, verify this
  372. // Also verify that we have a default handler ...
  373. _ASSERT(pBindingNode->dwFlags & PEBN_DEFAULT);
  374. // The following line results in fatal error C1001: INTERNAL COMPILER ERROR
  375. //_ASSERT(pDefaultOutboundHandler);
  376. fFireDefaultHandler = TRUE;
  377. fResult = (this->*pDefaultOutboundHandler)(NULL, 0, 0);
  378. if(!fResult)
  379. m_OutboundContext.m_dwCommandStatus = EXPE_DROP_SESSION;
  380. // Call the dispatcher to process any remaining sinks
  381. pBindingNode = pBindingNode->pNext;
  382. if (pBindingNode)
  383. {
  384. fFireDefaultHandler = FALSE;
  385. hr = m_pOutboundDispatcher->ChainSinks(
  386. pServer,
  387. pSession,
  388. m_pIMsg,
  389. pContext,
  390. dwEventType,
  391. &pCommandNode,
  392. &pBindingNode
  393. );
  394. }
  395. }
  396. }
  397. else if (pDefaultOutboundHandler)
  398. {
  399. // If we already fired the default handler, unless we are
  400. // asked to repeat, we are plain done
  401. if (!m_fNativeHandlerFired ||
  402. fRepeatLastCommand)
  403. {
  404. fFireDefaultHandler = TRUE;
  405. fResult = (this->*pDefaultOutboundHandler)(NULL, 0, 0);
  406. if(!fResult)
  407. m_OutboundContext.m_dwCommandStatus = EXPE_DROP_SESSION;
  408. m_fNativeHandlerFired = TRUE;
  409. }
  410. else
  411. {
  412. // We are done ...
  413. hr = HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);
  414. m_fNativeHandlerFired = FALSE;
  415. m_OutboundContext.m_dwCommandStatus = EXPE_SUCCESS;
  416. }
  417. }
  418. else
  419. {
  420. // No sinks and no default handler? We're done.
  421. hr = HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);
  422. m_fNativeHandlerFired = FALSE;
  423. m_OutboundContext.m_dwCommandStatus = EXPE_SUCCESS;
  424. }
  425. Analysis:
  426. // Update the context values
  427. m_OutboundContext.m_pCurrentCommandContext = pCommandNode;
  428. m_OutboundContext.m_pCurrentBinding = pBindingNode;
  429. if (hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS))
  430. {
  431. // Reset the context values
  432. _ASSERT(!m_OutboundContext.m_pCurrentCommandContext);
  433. _ASSERT(!m_OutboundContext.m_pCurrentBinding);
  434. m_OutboundContext.m_dwCommandStatus = EXPE_SUCCESS;
  435. }
  436. else
  437. {
  438. // If we don't have a status code, something is wrong. We will
  439. // assert in debug and drop the connection in reality.
  440. //
  441. // This is possible when misbehaving high-priority sinks consume
  442. // the event without setting the status code.
  443. if (m_OutboundContext.m_dwCommandStatus == EXPE_UNHANDLED)
  444. {
  445. ErrorTrace((LPARAM)this, "The outbound status code is undefined!");
  446. m_OutboundContext.m_dwCommandStatus = EXPE_DROP_SESSION;
  447. }
  448. }
  449. Abort:
  450. // Set a diagnostic if a sink caused the connection to be dropped
  451. if(!fFireDefaultHandler && m_OutboundContext.m_dwCommandStatus == EXPE_DROP_SESSION)
  452. {
  453. CHAR szCommand[128] = "";
  454. CHAR *pszCommand = szCommand;
  455. DWORD cbCommand = sizeof(szCommand);
  456. if(FAILED(m_OutboundContext.QueryCommandKeyword(szCommand, &cbCommand)))
  457. pszCommand = NULL;
  458. SetDiagnosticInfo(
  459. AQUEUE_E_SINK_DROPPED_CONNECTION,
  460. pszCommand,
  461. NULL);
  462. }
  463. TraceFunctLeaveEx((LPARAM)this);
  464. return(hr);
  465. }
  466. HRESULT SMTP_CONNOUT::OnServerResponseEvent(
  467. IUnknown *pServer,
  468. IUnknown *pSession,
  469. PMFI pDefaultResponseHandler
  470. )
  471. {
  472. HRESULT hr = S_OK;
  473. BOOL fResult = TRUE;
  474. BOOL fFireEvent = TRUE;
  475. BOOL fFireDefaultHandler = FALSE;
  476. LPPE_COMMAND_NODE pCommandNode = NULL;
  477. LPPE_BINDING_NODE pBindingNode = NULL;
  478. ISmtpServerResponseContext *pContext = (ISmtpServerResponseContext *) &m_ResponseContext;
  479. _ASSERT(m_ResponseContext.m_pCommand);
  480. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNOUT::OnServerResponseEvent");
  481. // First, we want to make sure that both the command and response
  482. // dispatchers are not NULL. If they are NULL, then we just call the
  483. // default handler, if any.
  484. if (m_pOutboundDispatcher && m_pResponseDispatcher)
  485. {
  486. // Now, we take a peek at the bindings and see if we have
  487. // any sinks installed for this command
  488. hr = m_pResponseDispatcher->SinksInstalled(
  489. m_ResponseContext.m_pCommand->pszCommandKeyword,
  490. &pCommandNode);
  491. if (hr != S_OK)
  492. {
  493. fFireEvent = FALSE;
  494. DebugTrace((LPARAM)this,
  495. "There are no sink bindings for this command");
  496. }
  497. }
  498. else
  499. {
  500. fFireEvent = FALSE;
  501. DebugTrace((LPARAM)this, "Sinks will not be fired because dispatcher NULL");
  502. }
  503. if (fFireEvent)
  504. {
  505. _ASSERT(pCommandNode);
  506. pBindingNode = pCommandNode->pFirstBinding;
  507. _ASSERT(pBindingNode);
  508. }
  509. // Fire the event accordingly
  510. hr = S_OK;
  511. if (pDefaultResponseHandler)
  512. {
  513. DebugTrace((LPARAM)this, "Calling sinks for native command <%s>",
  514. m_ResponseContext.m_pCommand->pszCommandKeyword);
  515. if (fFireEvent)
  516. {
  517. hr = m_pResponseDispatcher->ChainSinks(
  518. pServer,
  519. pSession,
  520. m_pIMsg,
  521. pContext,
  522. PRIO_DEFAULT,
  523. pCommandNode,
  524. &pBindingNode
  525. );
  526. if ((hr == S_FALSE) || (hr == EXPE_S_CONSUMED))
  527. goto Analysis;
  528. if (FAILED(hr))
  529. goto Abort;
  530. }
  531. fFireDefaultHandler = TRUE;
  532. fResult = (this->*pDefaultResponseHandler)(
  533. m_ResponseContext.m_cabResponse.Buffer(),
  534. m_ResponseContext.m_cabResponse.Length(),
  535. 0);
  536. if(!fResult)
  537. m_ResponseContext.m_dwResponseStatus = EXPE_DROP_SESSION;
  538. if (fFireEvent && pBindingNode)
  539. fFireDefaultHandler = FALSE;
  540. hr = m_pResponseDispatcher->ChainSinks(
  541. pServer,
  542. pSession,
  543. m_pIMsg,
  544. pContext,
  545. PRIO_LOWEST,
  546. pCommandNode,
  547. &pBindingNode
  548. );
  549. }
  550. else
  551. {
  552. DebugTrace((LPARAM)this, "Calling sinks for non-native command <%s>",
  553. m_ResponseContext.m_pCommand->pszCommandKeyword);
  554. if (fFireEvent)
  555. {
  556. hr = m_pResponseDispatcher->ChainSinks(
  557. pServer,
  558. pSession,
  559. m_pIMsg,
  560. pContext,
  561. PRIO_LOWEST,
  562. pCommandNode,
  563. &pBindingNode
  564. );
  565. // Fall back to "*" sinks
  566. if ((hr == S_OK) &&
  567. (m_ResponseContext.m_dwResponseStatus == EXPE_UNHANDLED))
  568. {
  569. hr = m_pResponseDispatcher->SinksInstalled(
  570. "*",
  571. &pCommandNode);
  572. if (hr == S_OK)
  573. {
  574. _ASSERT(pCommandNode);
  575. pBindingNode = pCommandNode->pFirstBinding;
  576. _ASSERT(pBindingNode);
  577. hr = m_pResponseDispatcher->ChainSinks(
  578. pServer,
  579. pSession,
  580. m_pIMsg,
  581. pContext,
  582. PRIO_LOWEST,
  583. pCommandNode,
  584. &pBindingNode
  585. );
  586. }
  587. else
  588. hr = S_OK;
  589. }
  590. }
  591. }
  592. Analysis:
  593. // If no sinks has handled the response, we will invoke
  594. // a default handler. This will fail and drop the connection if
  595. // the SMTP response code is anything other than a complete success.
  596. if (m_ResponseContext.m_dwResponseStatus == EXPE_UNHANDLED)
  597. {
  598. if (!IsSmtpCompleteSuccess(m_ResponseContext.m_dwSmtpStatus))
  599. m_ResponseContext.m_dwResponseStatus = EXPE_DROP_SESSION;
  600. else
  601. m_ResponseContext.m_dwResponseStatus = EXPE_SUCCESS;
  602. hr = S_OK;
  603. }
  604. Abort:
  605. // Set a diagnostic if a sink caused the connection to be dropped
  606. if(!fFireDefaultHandler && m_ResponseContext.m_dwResponseStatus == EXPE_DROP_SESSION)
  607. {
  608. CHAR szCommand[128] = "";
  609. CHAR *pszCommand = szCommand;
  610. DWORD cbCommand = sizeof(szCommand);
  611. if(FAILED(m_ResponseContext.QueryCommandKeyword(szCommand, &cbCommand)))
  612. pszCommand = NULL;
  613. SetDiagnosticInfo(
  614. AQUEUE_E_SINK_DROPPED_CONNECTION,
  615. pszCommand,
  616. NULL);
  617. }
  618. TraceFunctLeaveEx((LPARAM)this);
  619. return(hr);
  620. }
  621. #define MAX_OUTSTANDING_COMMANDS 500
  622. BOOL SMTP_CONNOUT::GlueDispatch(
  623. const char *InputLine,
  624. DWORD ParameterSize,
  625. DWORD UndecryptedTailSize,
  626. DWORD dwOutboundEventType,
  627. PMFI pDefaultOutboundHandler,
  628. PMFI pDefaultResponseHandler,
  629. LPSTR szDefaultResponseHandlerKeyword,
  630. BOOL *pfDoneWithEvent,
  631. BOOL *pfAbortEvent
  632. )
  633. {
  634. HRESULT hr;
  635. BOOL fResult = TRUE;
  636. BOOL fRepeatLastCommand = FALSE;
  637. BOOL fSendBinaryBlob = FALSE;
  638. DWORD dwBuffer = 0;
  639. BOOL fStateChange = FALSE;
  640. BOOL fResponsesHandled = FALSE;
  641. PMFI pfnNextState = NULL;
  642. char chErrorPrefix = SMTP_COMPLETE_SUCCESS;
  643. TraceFunctEnterEx((LPARAM) this, "SMTP_CONNOUT::GlueDispatch");
  644. _ASSERT(m_pInstance);
  645. _ASSERT(m_pIEventRouter);
  646. m_ResponseContext.m_pCommand = NULL;
  647. // Check args.
  648. if (!InputLine || !pfDoneWithEvent || !pfAbortEvent ||
  649. !m_pInstance || !m_pIEventRouter)
  650. {
  651. ErrorTrace((LPARAM) this, "NULL Pointer");
  652. TraceFunctLeaveEx((LPARAM)this);
  653. SetLastError(E_POINTER);
  654. return(FALSE);
  655. }
  656. if (dwOutboundEventType >= PE_OET_INVALID_EVENT_TYPE)
  657. {
  658. ErrorTrace((LPARAM) this,
  659. "Skipping event because of bad outbound event type");
  660. TraceFunctLeaveEx((LPARAM)this);
  661. return(TRUE);
  662. }
  663. // By default we are still going
  664. *pfDoneWithEvent = FALSE;
  665. *pfAbortEvent = FALSE;
  666. // Get the dispatcher if we don't already have it ...
  667. if (!m_pOutboundDispatcher)
  668. {
  669. hr = m_pIEventRouter->GetDispatcherByClassFactory(
  670. CLSID_COutboundDispatcher,
  671. &g_cfOutbound,
  672. *(COutboundDispatcher::s_rgrguidEventTypes[dwOutboundEventType]),
  673. IID_ISmtpOutboundCommandDispatcher,
  674. (IUnknown **)&m_pOutboundDispatcher);
  675. if(!SUCCEEDED(hr))
  676. {
  677. // If we fail, we will not fire protocol events
  678. m_pOutboundDispatcher = NULL;
  679. ErrorTrace((LPARAM) this,
  680. "Unable to get outbound dispatcher from router (%08x)", hr);
  681. }
  682. }
  683. //
  684. // We make sure we will not send out new commands until all our
  685. // queued commands' responses are received and processed.
  686. //
  687. if (InputLine)
  688. {
  689. // Handle responses first, followed by generating commands
  690. char *pResponses = (char *)InputLine;
  691. DWORD dwRemaining = ParameterSize;
  692. while (!m_FifoQ.IsEmpty())
  693. {
  694. // Parse out the next response
  695. hr = GetNextResponse(
  696. pResponses,
  697. dwRemaining,
  698. &pResponses,
  699. &dwRemaining,
  700. UndecryptedTailSize);
  701. if (hr == S_OK)
  702. {
  703. //
  704. // jstamerj 1998/10/30 12:37:28:
  705. // Set the correct next state in the response context
  706. //
  707. m_ResponseContext.m_dwNextState = CResponseDispatcher::PeStateFromOutboundEventType((OUTBOUND_EVENT_TYPES)dwOutboundEventType);
  708. // We have a full response, now dequeue the corresponding
  709. // command entry
  710. m_ResponseContext.m_pCommand = (LPOUTBOUND_COMMAND_Q_ENTRY)m_FifoQ.Dequeue();
  711. // This should not be NULL at any time!
  712. _ASSERT(m_ResponseContext.m_pCommand);
  713. DebugTrace((LPARAM)this, "Processing Response <%s> for <%s>",
  714. m_ResponseContext.m_cabResponse.Buffer(),
  715. m_ResponseContext.m_pCommand->pszFullCommand);
  716. // All except for the last item in the queue must be pipelined
  717. if (!m_FifoQ.IsEmpty())
  718. {
  719. _ASSERT((m_ResponseContext.m_pCommand->dwFlags & PECQ_PIPELINED) != 0);
  720. }
  721. // Call the event handler
  722. hr = OnServerResponseEvent(
  723. m_pInstance->GetInstancePropertyBag(),
  724. GetSessionPropertyBag(),
  725. (strcmp(m_ResponseContext.m_pCommand->pszCommandKeyword,
  726. szDefaultResponseHandlerKeyword) == 0)?
  727. pDefaultResponseHandler:
  728. NULL
  729. );
  730. // We will ignore all responses to commands that are
  731. // pipelined. If the command that caused this response is
  732. // not pipelined, it will fall through this loop and be
  733. // handled downstream.
  734. fResponsesHandled = TRUE;
  735. // Delete the command entry ...
  736. LPBYTE pTemp = (LPBYTE)m_ResponseContext.m_pCommand;
  737. delete [] pTemp;
  738. pTemp = NULL;
  739. //Check if we really want to continue
  740. if( m_ResponseContext.m_dwResponseStatus == EXPE_TRANSIENT_FAILURE ||
  741. m_ResponseContext.m_dwResponseStatus == EXPE_COMPLETE_FAILURE )
  742. {
  743. //Free up the command list
  744. while(pTemp = (LPBYTE)m_FifoQ.Dequeue())
  745. {
  746. delete [] pTemp;
  747. pTemp = NULL;
  748. }
  749. //break out of the command loop
  750. break;
  751. }
  752. // We are done with this response, and the FIFO is not empty,
  753. // we will reset the context
  754. if (!m_FifoQ.IsEmpty())
  755. m_ResponseContext.ResetResponseContext();
  756. }
  757. else if (hr == S_FALSE)
  758. {
  759. // This means that the whole buffer received is not long enough
  760. // to hold the next response. We will wait for the remainder.
  761. // This might take several completions if needed.
  762. // Make sure we don't reset the response context at this point
  763. // or we will lose the previous buffer.
  764. break;
  765. }
  766. else if (hr == E_INVALIDARG)
  767. {
  768. //
  769. // Syntactically invalid response from other side. We will NDR
  770. // message and end the session. We set the following on the response
  771. // context so that SMTP_CONNOUT::DoCompletedMessage knows what to
  772. // do:
  773. //
  774. // - m_cabReponse is set to some string indicating what error
  775. // occurred. This should really be the actual response from
  776. // the server, but since that is garbage we put in something
  777. // that conforms to SMTP syntax. This is for our benefit only,
  778. // since the m_cabResponse is parsed by many internal functions.
  779. // We want those functions to see only syntactically validated
  780. // strings, and weed out all errors here.
  781. //
  782. // - m_dwSmtpStatus = SMTP_SERVICE_CLOSE_CODE
  783. // To indicate that the connection should be closed and that
  784. // we shouldn't try to deliver any more messages on this
  785. // connection. There is no *correct* error code here, so we
  786. // have to pick some reasonable error code.
  787. //
  788. // - m_dwReponseStatus = EXPE_COMPLETE_FAILURE to indicate that the
  789. // all messages on this connection must be NDR'ed.
  790. //
  791. // Delete the command entry
  792. LPBYTE pTemp = (LPBYTE)m_ResponseContext.m_pCommand;
  793. delete [] pTemp;
  794. // Free up the command list
  795. while(pTemp = (LPBYTE)m_FifoQ.Dequeue())
  796. {
  797. delete [] pTemp;
  798. pTemp = NULL;
  799. }
  800. m_ResponseContext.ResetResponseContext();
  801. #define SMTP_INVALID_SYTAX_ERROR "500 Non RFC-compliant response received"
  802. hr = m_ResponseContext.m_cabResponse.Append(
  803. SMTP_INVALID_SYTAX_ERROR,
  804. sizeof(SMTP_INVALID_SYTAX_ERROR),
  805. NULL);
  806. if(FAILED(hr))
  807. { // Out of memory. Nothing more we can do - drop connection abruptly
  808. TraceFunctLeaveEx((LPARAM)this);
  809. return FALSE;
  810. }
  811. m_ResponseContext.m_dwResponseStatus = EXPE_COMPLETE_FAILURE;
  812. m_ResponseContext.m_dwSmtpStatus = SMTP_SERVICE_CLOSE_CODE;
  813. m_OutboundContext.m_pCurrentCommandContext = NULL;
  814. ErrorTrace((LPARAM)this, "Syntax error in response. Message"
  815. " will be NDR'ed");
  816. fResponsesHandled = TRUE;
  817. break;
  818. }
  819. else
  820. {
  821. // Some temporary error, such as out-of-memory.
  822. ErrorTrace((LPARAM)this, "Failed to GetNextResponse (%08x)", hr);
  823. return FALSE;
  824. }
  825. }
  826. }
  827. // If we have exhausted all responses and still our command queue
  828. // is not empty, we need to pend another read for more responses
  829. if (!m_FifoQ.IsEmpty())
  830. {
  831. DebugTrace((LPARAM)this, "Pending a read for more response data");
  832. TraceFunctLeaveEx((LPARAM)this);
  833. return(TRUE);
  834. }
  835. // Okay, we are now done all queued responses. Before we send out more
  836. // commands, we want to check if a sink wanted to change the state. If
  837. // so, we will honor this state change ...
  838. if (fResponsesHandled)
  839. {
  840. switch (m_ResponseContext.m_dwResponseStatus)
  841. {
  842. case EXPE_SUCCESS:
  843. break;
  844. case EXPE_TRANSIENT_FAILURE:
  845. chErrorPrefix = SMTP_TRANSIENT_FAILURE;
  846. pfnNextState = DoCompletedMessage;
  847. fStateChange = TRUE;
  848. break;
  849. case EXPE_COMPLETE_FAILURE:
  850. chErrorPrefix = SMTP_COMPLETE_FAILURE;
  851. pfnNextState = DoCompletedMessage;
  852. fStateChange = TRUE;
  853. break;
  854. case EXPE_REPEAT_COMMAND:
  855. fRepeatLastCommand = TRUE;
  856. break;
  857. case EXPE_DROP_SESSION:
  858. DisconnectClient();
  859. *pfDoneWithEvent = TRUE;
  860. *pfAbortEvent = TRUE;
  861. fResult = FALSE;
  862. break;
  863. case EXPE_CHANGE_STATE:
  864. // Figure out which state pointer to jump to
  865. switch (m_ResponseContext.m_dwNextState)
  866. {
  867. case PE_STATE_SESSION_START:
  868. pfnNextState = DoSessionStartEvent;
  869. break;
  870. case PE_STATE_MESSAGE_START:
  871. pfnNextState = DoMessageStartEvent;
  872. break;
  873. case PE_STATE_PER_RECIPIENT:
  874. pfnNextState = DoPerRecipientEvent;
  875. break;
  876. case PE_STATE_DATA_OR_BDAT:
  877. pfnNextState = DoBeforeDataEvent;
  878. break;
  879. case PE_STATE_SESSION_END:
  880. pfnNextState = DoSessionEndEvent;
  881. break;
  882. default:
  883. ErrorTrace((LPARAM)this,
  884. "Bad next state %u, ignoring ...",
  885. m_ResponseContext.m_dwNextState);
  886. }
  887. if (pfnNextState)
  888. {
  889. *pfDoneWithEvent = TRUE;
  890. fStateChange = TRUE;
  891. }
  892. break;
  893. case EXPE_UNHANDLED:
  894. _ASSERT(FALSE);
  895. break;
  896. default:
  897. _ASSERT(FALSE);
  898. }
  899. }
  900. // OK, now we are clear to send the next command ...
  901. // If the result is already FALSE, we are going to disconnect. Then there
  902. // is no point generating more commands ...
  903. //
  904. // Also, if we are returning from some other state, we just leave
  905. if (fResult && !fStateChange)
  906. {
  907. //NK** : I moved this from outside if to inside. Need to do this to preserve the response in cases where we hit
  908. //TRANSIENT or permanent errorn
  909. // Reset the response context
  910. m_ResponseContext.ResetResponseContext();
  911. //we need to save the first pipelined address so we can
  912. //start at this address and check the replies
  913. m_FirstPipelinedAddress = m_NextAddress;
  914. do
  915. {
  916. BOOL fUseNative = FALSE;
  917. // Reset the context
  918. m_OutboundContext.ResetOutboundContext();
  919. // Catch here: set what the next address is so that other
  920. // sinks can at least have a clue who the current recipient is.
  921. //
  922. // jstamerj 1998/10/22 17:26:54: Sinks are really
  923. // interested in the recipient index in the mailmsg, not
  924. // the index into our private array. Give them that
  925. // instead.
  926. //
  927. if((dwOutboundEventType == PE_OET_PER_RECIPIENT) &&
  928. (m_NextAddress < m_NumRcpts))
  929. m_OutboundContext.m_dwCurrentRecipient = m_RcptIndexList[m_NextAddress];
  930. // Raise the outbound event ...
  931. hr = OnOutboundCommandEvent(
  932. m_pInstance->GetInstancePropertyBag(),
  933. GetSessionPropertyBag(),
  934. dwOutboundEventType,
  935. fRepeatLastCommand,
  936. pDefaultOutboundHandler);
  937. fRepeatLastCommand = FALSE;
  938. // See if we are done with this event type
  939. if (hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS))
  940. {
  941. DebugTrace((LPARAM)this, "Done processing this event, leaving ...");
  942. *pfDoneWithEvent = TRUE;
  943. break;
  944. }
  945. // See if the last command should be repeated
  946. if ((m_OutboundContext.m_dwCommandStatus != EXPE_UNHANDLED) &&
  947. (m_OutboundContext.m_dwCommandStatus & EXPE_REPEAT_COMMAND))
  948. fRepeatLastCommand = TRUE;
  949. // See if we're sending blob rather then the command
  950. if ((m_OutboundContext.m_dwCommandStatus != EXPE_UNHANDLED) &&
  951. (m_OutboundContext.m_dwCommandStatus & EXPE_BLOB_READY))
  952. fSendBinaryBlob = TRUE;
  953. // Handle the status codes, minus the repeat flag ...
  954. switch (m_OutboundContext.m_dwCommandStatus & (~EXPE_REPEAT_COMMAND) & (~EXPE_BLOB_READY))
  955. {
  956. case EXPE_PIPELINED:
  957. // If the remote server does not support pipelineing,
  958. // we will honor that.
  959. if(!IsOptionSet(PIPELINE_OPTION) || !m_pInstance->ShouldPipeLineOut())
  960. m_OutboundContext.m_dwCommandStatus = EXPE_NOT_PIPELINED;
  961. // Fall Thru ...
  962. case EXPE_SUCCESS:
  963. {
  964. LPOUTBOUND_COMMAND_Q_ENTRY pEntry = NULL;
  965. LPSTR pBuffer;
  966. // Build the entry
  967. hr = BuildCommandQEntry(&pEntry, &fUseNative);
  968. if (FAILED(hr))
  969. {
  970. chErrorPrefix = SMTP_TRANSIENT_FAILURE;
  971. pfnNextState = DoCompletedMessage;
  972. fStateChange = TRUE;
  973. break;
  974. }
  975. if (hr == S_OK)
  976. {
  977. // Push the command into the FIFO queue
  978. if (!m_FifoQ.Enqueue((LPVOID)pEntry))
  979. {
  980. // Since pEntry is never NULL here, we will always succeed
  981. _ASSERT(FALSE);
  982. }
  983. // Write out the command to the real buffer
  984. // If the buffer is not big enough, this will write and
  985. // flush multiple times until the command is sent
  986. pBuffer = (fUseNative)?
  987. m_OutboundContext.m_cabNativeCommand.Buffer():
  988. m_OutboundContext.m_cabCommand.Buffer();
  989. if ( fSendBinaryBlob) {
  990. FormatBinaryBlob( m_OutboundContext.m_pbBlob, m_OutboundContext.m_cbBlob);
  991. } else {
  992. //FormatSmtpMessage will use the first string
  993. //as a format string. Since we obviously have no
  994. //arguments, any %'s in pBuffer will cause sprintf
  995. //to AV. We must force FormatSmtpMessage to not use
  996. //pBuffer as a format string.
  997. FormatSmtpMessage(FSM_LOG_ALL, "%s", pBuffer);
  998. }
  999. }
  1000. }
  1001. break;
  1002. case EXPE_TRANSIENT_FAILURE:
  1003. chErrorPrefix = SMTP_TRANSIENT_FAILURE;
  1004. pfnNextState = DoCompletedMessage;
  1005. fStateChange = TRUE;
  1006. break;
  1007. case EXPE_COMPLETE_FAILURE:
  1008. chErrorPrefix = SMTP_COMPLETE_FAILURE;
  1009. pfnNextState = DoCompletedMessage;
  1010. fStateChange = TRUE;
  1011. break;
  1012. case EXPE_DROP_SESSION:
  1013. DisconnectClient();
  1014. *pfAbortEvent = TRUE;
  1015. *pfDoneWithEvent = TRUE;
  1016. fResult = FALSE;
  1017. break;
  1018. case EXPE_UNHANDLED:
  1019. default:
  1020. DisconnectClient();
  1021. *pfAbortEvent = TRUE;
  1022. *pfDoneWithEvent = TRUE;
  1023. fResult = FALSE;
  1024. }
  1025. } while (m_FifoQ.IsEmpty() ||
  1026. ((m_OutboundContext.m_dwCommandStatus & EXPE_PIPELINED) &&
  1027. m_FifoQ.Length() < MAX_OUTSTANDING_COMMANDS));
  1028. // OK, we can flush these commands ...
  1029. fResult = SendSmtpResponse();
  1030. }
  1031. // Make sure we free the outbound dispatcher before
  1032. // exiting or jumping states.
  1033. if (*pfDoneWithEvent || fStateChange)
  1034. {
  1035. if(fStateChange)
  1036. m_fNativeHandlerFired = FALSE;
  1037. // Release the dispatcher
  1038. if (m_pOutboundDispatcher)
  1039. {
  1040. m_pOutboundDispatcher->Release();
  1041. m_pOutboundDispatcher = NULL;
  1042. }
  1043. if (pfnNextState)
  1044. {
  1045. // Call the next state handler
  1046. *pfDoneWithEvent = TRUE;
  1047. *pfAbortEvent = TRUE;
  1048. //
  1049. // jstamerj 1998/11/01 18:51:01: Since we're jumping to
  1050. // another state, reset the variable that keeps track
  1051. // of which command we're firing.
  1052. //
  1053. m_OutboundContext.m_pCurrentCommandContext = NULL;
  1054. DebugTrace((LPARAM)this, "Jumping to another state");
  1055. fResult = (this->*pfnNextState)(&chErrorPrefix, 1, 0);
  1056. }
  1057. }
  1058. TraceFunctLeaveEx((LPARAM)this);
  1059. return(fResult);
  1060. }
  1061. BOOL SMTP_CONNOUT::DoSessionStartEvent(
  1062. char *InputLine,
  1063. DWORD ParameterSize,
  1064. DWORD UndecryptedTailSize
  1065. )
  1066. {
  1067. BOOL fResult = TRUE;
  1068. BOOL fDoneEvent = FALSE;
  1069. BOOL fAbortEvent = FALSE;
  1070. TraceFunctEnterEx((LPARAM)this, "SMTP_CONNOUT::DoSessionStartEvent");
  1071. // Always set the state to itself
  1072. SetNextState (&SMTP_CONNOUT::DoSessionStartEvent);
  1073. // Call the catch-all handler
  1074. fResult = GlueDispatch(
  1075. InputLine,
  1076. ParameterSize,
  1077. UndecryptedTailSize,
  1078. PE_OET_SESSION_START,
  1079. &SMTP_CONNOUT::DoEHLOCommand,
  1080. &SMTP_CONNOUT::DoEHLOResponse,
  1081. m_HeloSent?"helo":"ehlo",
  1082. &fDoneEvent,
  1083. &fAbortEvent);
  1084. if (fDoneEvent && !fAbortEvent)
  1085. {
  1086. if (m_TlsState == MUST_DO_TLS)
  1087. {
  1088. SetNextState(&SMTP_CONNOUT::DoSTARTTLSCommand);
  1089. fResult = DoSTARTTLSCommand(InputLine, ParameterSize, UndecryptedTailSize);
  1090. }
  1091. else if (m_MsgOptions & KNOWN_AUTH_FLAGS)
  1092. {
  1093. fResult = DoSASLCommand(InputLine, ParameterSize, UndecryptedTailSize);
  1094. }
  1095. else if (m_MsgOptions & EMPTY_CONNECTION_OPTION)
  1096. {
  1097. fResult = DoSessionEndEvent(InputLine, ParameterSize, UndecryptedTailSize);
  1098. }
  1099. else
  1100. {
  1101. fResult = DoMessageStartEvent(InputLine, ParameterSize, UndecryptedTailSize);
  1102. }
  1103. }
  1104. TraceFunctLeaveEx((LPARAM)this);
  1105. return(fResult);
  1106. }
  1107. BOOL SMTP_CONNOUT::DoMessageStartEvent(
  1108. char *InputLine,
  1109. DWORD ParameterSize,
  1110. DWORD UndecryptedTailSize
  1111. )
  1112. {
  1113. BOOL fResult = TRUE;
  1114. BOOL fDoneEvent = FALSE;
  1115. BOOL fAbortEvent = FALSE;
  1116. TraceFunctEnterEx((LPARAM)this, "SMTP_CONNOUT::DoMessageStartEvent");
  1117. //send an ETRN request if it defined
  1118. // The ETRN respond will eventually come back to this state
  1119. // again but with the ETRN_SENT option set so it's like a small loop
  1120. if((m_MsgOptions & DOMAIN_INFO_SEND_ETRN) && IsOptionSet(ETRN_OPTION)
  1121. && !IsOptionSet(ETRN_SENT))
  1122. {
  1123. return DoETRNCommand();
  1124. }
  1125. // check to see if this is an empty turn command
  1126. // we need to go back to startsession to issue the TURN
  1127. // if ((m_pIMsg == NULL) && (m_MsgOptions & DOMAIN_INFO_SEND_TURN)) {
  1128. if (m_Flags & TURN_ONLY_OPTION) {
  1129. _ASSERT((m_pIMsg == NULL) && (m_MsgOptions & DOMAIN_INFO_SEND_TURN));
  1130. return StartSession();
  1131. }
  1132. // Always set the state to itself
  1133. SetNextState (&SMTP_CONNOUT::DoMessageStartEvent);
  1134. // Call the catch-all handler
  1135. fResult = GlueDispatch(
  1136. InputLine,
  1137. ParameterSize,
  1138. UndecryptedTailSize,
  1139. PE_OET_MESSAGE_START,
  1140. &SMTP_CONNOUT::DoMAILCommand,
  1141. &SMTP_CONNOUT::DoMAILResponse,
  1142. "mail",
  1143. &fDoneEvent,
  1144. &fAbortEvent);
  1145. if (fDoneEvent && !fAbortEvent)
  1146. {
  1147. // Change to the next state if done ...
  1148. //m_NextAddress = 0;
  1149. fResult = DoPerRecipientEvent(InputLine, ParameterSize, UndecryptedTailSize);
  1150. }
  1151. return(fResult);
  1152. }
  1153. BOOL SMTP_CONNOUT::DoPerRecipientEvent(
  1154. char *InputLine,
  1155. DWORD ParameterSize,
  1156. DWORD UndecryptedTailSize
  1157. )
  1158. {
  1159. BOOL fResult = TRUE;
  1160. BOOL fDoneEvent = FALSE;
  1161. BOOL fAbortEvent = FALSE;
  1162. TraceFunctEnterEx((LPARAM)this, "SMTP_CONNOUT::DoPerRecipientEvent");
  1163. // Always set the state to itself
  1164. SetNextState (&SMTP_CONNOUT::DoPerRecipientEvent);
  1165. DebugTrace((LPARAM)this,
  1166. "Processing recipient index %u",
  1167. m_NextAddress);
  1168. // Call the catch-all handler
  1169. fResult = GlueDispatch(
  1170. InputLine,
  1171. ParameterSize,
  1172. UndecryptedTailSize,
  1173. PE_OET_PER_RECIPIENT,
  1174. &SMTP_CONNOUT::DoRCPTCommand,
  1175. &SMTP_CONNOUT::DoRCPTResponse,
  1176. "rcpt",
  1177. &fDoneEvent,
  1178. &fAbortEvent);
  1179. if (fDoneEvent && !fAbortEvent)
  1180. {
  1181. // Change to the next state if done ...
  1182. if(m_NumFailedAddrs >= m_NumRcptSentSaved)
  1183. {
  1184. TraceFunctLeaveEx((LPARAM)this);
  1185. SetNextState (&SMTP_CONNOUT::WaitForRSETResponse);
  1186. if(m_NumRcptSentSaved)
  1187. m_RsetReasonCode = ALL_RCPTS_FAILED;
  1188. else
  1189. m_RsetReasonCode = NO_RCPTS_SENT;
  1190. return DoRSETCommand(NULL, 0, 0);
  1191. }
  1192. // OK, fire the "before data" event
  1193. fResult = DoBeforeDataEvent(InputLine, ParameterSize, UndecryptedTailSize);
  1194. }
  1195. return(fResult);
  1196. }
  1197. BOOL SMTP_CONNOUT::DoBeforeDataEvent(
  1198. char *InputLine,
  1199. DWORD ParameterSize,
  1200. DWORD UndecryptedTailSize
  1201. )
  1202. {
  1203. BOOL fResult = TRUE;
  1204. BOOL fDoneEvent = FALSE;
  1205. BOOL fAbortEvent = FALSE;
  1206. TraceFunctEnterEx((LPARAM)this, "SMTP_CONNOUT::DoBeforeDataEvent");
  1207. // Always set the state to itself
  1208. SetNextState (&SMTP_CONNOUT::DoBeforeDataEvent);
  1209. // Call the catch-all handler
  1210. fResult = GlueDispatch(
  1211. InputLine,
  1212. ParameterSize,
  1213. UndecryptedTailSize,
  1214. PE_OET_BEFORE_DATA,
  1215. NULL,
  1216. NULL,
  1217. "",
  1218. &fDoneEvent,
  1219. &fAbortEvent);
  1220. if (fDoneEvent && !fAbortEvent)
  1221. {
  1222. // Change to the next state if done ...
  1223. if (m_fUseBDAT)
  1224. fResult = DoBDATCommand(InputLine, ParameterSize, UndecryptedTailSize);
  1225. else
  1226. fResult = DoDATACommand(InputLine, ParameterSize, UndecryptedTailSize);
  1227. }
  1228. TraceFunctLeaveEx((LPARAM)this);
  1229. return(fResult);
  1230. }
  1231. BOOL SMTP_CONNOUT::DoSessionEndEvent(
  1232. char *InputLine,
  1233. DWORD ParameterSize,
  1234. DWORD UndecryptedTailSize
  1235. )
  1236. {
  1237. BOOL fResult = TRUE;
  1238. BOOL fDoneEvent = FALSE;
  1239. BOOL fAbortEvent = FALSE;
  1240. TraceFunctEnterEx((LPARAM)this, "SMTP_CONNOUT::DoSessionEndEvent");
  1241. // Always set the state to itself
  1242. SetNextState (&SMTP_CONNOUT::DoSessionEndEvent);
  1243. // Call the catch-all handler
  1244. fResult = GlueDispatch(
  1245. InputLine,
  1246. ParameterSize,
  1247. UndecryptedTailSize,
  1248. PE_OET_SESSION_END,
  1249. &SMTP_CONNOUT::DoQUITCommand,
  1250. &SMTP_CONNOUT::WaitForQuitResponse,
  1251. "quit",
  1252. &fDoneEvent,
  1253. &fAbortEvent);
  1254. // Either way we cut it, if we are done, we will delete the connection
  1255. if (fDoneEvent)
  1256. fResult = FALSE;
  1257. TraceFunctLeaveEx((LPARAM)this);
  1258. return(fResult);
  1259. }