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.

3148 lines
96 KiB

  1. // --------------------------------------------------------------------------------
  2. // Ixpnntp.cpp
  3. // Copyright (c)1993-1996 Microsoft Corporation, All Rights Reserved
  4. //
  5. // Eric Andrews
  6. // Steve Serdy
  7. // --------------------------------------------------------------------------------
  8. #include "pch.hxx"
  9. #include "dllmain.h"
  10. #include <stdio.h>
  11. #include "ixpnntp.h"
  12. #include "asynconn.h"
  13. #include "ixputil.h"
  14. #include "strconst.h"
  15. #include "resource.h"
  16. #include <shlwapi.h>
  17. #include "demand.h"
  18. // --------------------------------------------------------------------------------
  19. // Some handle macros for simple pointer casting
  20. // --------------------------------------------------------------------------------
  21. #define NNTPTHISIXP ((INNTPTransport *)(CIxpBase *) this)
  22. #define NUM_HEADERS 100
  23. CNNTPTransport::CNNTPTransport(void) : CIxpBase(IXP_NNTP)
  24. {
  25. ZeroMemory(&m_rMessage, sizeof(m_rMessage));
  26. ZeroMemory(&m_sicinfo, sizeof(SSPICONTEXT));
  27. DllAddRef();
  28. m_substate = NS_RESP;
  29. }
  30. CNNTPTransport::~CNNTPTransport(void)
  31. {
  32. SafeRelease(m_rMessage.pstmMsg);
  33. DllRelease();
  34. }
  35. // --------------------------------------------------------------------------------
  36. // CNNTPTransport::QueryInterface
  37. // --------------------------------------------------------------------------------
  38. HRESULT CNNTPTransport::QueryInterface(REFIID riid, LPVOID *ppv)
  39. {
  40. // Locals
  41. HRESULT hr=S_OK;
  42. // Bad param
  43. if (ppv == NULL)
  44. {
  45. hr = TrapError(E_INVALIDARG);
  46. goto exit;
  47. }
  48. // Init
  49. *ppv=NULL;
  50. // IID_IUnknown
  51. if (IID_IUnknown == riid)
  52. *ppv = ((IUnknown *)(INNTPTransport *)this);
  53. // IID_IInternetTransport
  54. else if (IID_IInternetTransport == riid)
  55. *ppv = ((IInternetTransport *)(CIxpBase *)this);
  56. // IID_INNTPTransport
  57. else if (IID_INNTPTransport == riid)
  58. *ppv = (INNTPTransport *)this;
  59. // IID_INNTPTransport2
  60. else if (IID_INNTPTransport2 == riid)
  61. *ppv = (INNTPTransport2 *)this;
  62. // If not null, addref it and return
  63. if (NULL != *ppv)
  64. {
  65. ((LPUNKNOWN)*ppv)->AddRef();
  66. goto exit;
  67. }
  68. // No Interface
  69. hr = TrapError(E_NOINTERFACE);
  70. exit:
  71. // Done
  72. return hr;
  73. }
  74. // --------------------------------------------------------------------------------
  75. // CNNTPTransport::AddRef
  76. // --------------------------------------------------------------------------------
  77. ULONG CNNTPTransport::AddRef(void)
  78. {
  79. return ++m_cRef;
  80. }
  81. // --------------------------------------------------------------------------------
  82. // CNNTPTransport::Release
  83. // --------------------------------------------------------------------------------
  84. ULONG CNNTPTransport::Release(void)
  85. {
  86. if (0 != --m_cRef)
  87. return m_cRef;
  88. delete this;
  89. return 0;
  90. }
  91. //
  92. // FUNCTION: CNNTPTransport::OnNotify()
  93. //
  94. // PURPOSE: This function get's called whenever the CAsyncConn class
  95. // sends or receives data.
  96. //
  97. // PARAMETERS:
  98. // asOld - State of the connection before this event
  99. // asNew - State of the connection after this event
  100. // ae - Identifies the event that has occured
  101. //
  102. void CNNTPTransport::OnNotify(ASYNCSTATE asOld, ASYNCSTATE asNew, ASYNCEVENT ae)
  103. {
  104. // Enter Critical Section
  105. EnterCriticalSection(&m_cs);
  106. // Handle Event
  107. switch(ae)
  108. {
  109. case AE_RECV:
  110. OnSocketReceive();
  111. break;
  112. case AE_SENDDONE:
  113. if (m_substate == NS_SEND_ENDPOST)
  114. {
  115. HrSendCommand((LPSTR) NNTP_ENDPOST, NULL, FALSE);
  116. m_substate = NS_ENDPOST_RESP;
  117. }
  118. break;
  119. case AE_LOOKUPDONE:
  120. if (AS_DISCONNECTED == asNew)
  121. {
  122. DispatchResponse(IXP_E_CANT_FIND_HOST, TRUE);
  123. OnDisconnected();
  124. }
  125. else
  126. OnStatus(IXP_CONNECTING);
  127. break;
  128. // --------------------------------------------------------------------------------
  129. case AE_CONNECTDONE:
  130. if (AS_DISCONNECTED == asNew)
  131. {
  132. DispatchResponse(IXP_E_FAILED_TO_CONNECT, TRUE);
  133. OnDisconnected();
  134. }
  135. else if (AS_HANDSHAKING == asNew)
  136. {
  137. OnStatus(IXP_SECURING);
  138. }
  139. else
  140. OnConnected();
  141. break;
  142. // --------------------------------------------------------------------------------
  143. case AE_CLOSE:
  144. if (AS_RECONNECTING != asNew && IXP_AUTHRETRY != m_status)
  145. {
  146. if (IXP_DISCONNECTING != m_status && IXP_DISCONNECTED != m_status)
  147. {
  148. if (AS_HANDSHAKING == asOld)
  149. DispatchResponse(IXP_E_SECURE_CONNECT_FAILED, TRUE);
  150. else
  151. DispatchResponse(IXP_E_CONNECTION_DROPPED, TRUE);
  152. }
  153. OnDisconnected();
  154. }
  155. break;
  156. default:
  157. CIxpBase::OnNotify(asOld, asNew, ae);
  158. break;
  159. }
  160. // Leave Critical Section
  161. LeaveCriticalSection(&m_cs);
  162. }
  163. //
  164. // FUNCTION: CNNTPTransport::InitNew()
  165. //
  166. // PURPOSE: The client calls this to specify a callback interface and a log
  167. // file path if desired.
  168. //
  169. // PARAMETERS:
  170. // pszLogFilePath - Path to the file to write logging info to.
  171. // pCallback - Pointer to the callback interface to send results etc
  172. // to.
  173. //
  174. // RETURN VALUE:
  175. // HRESULT
  176. //
  177. HRESULT CNNTPTransport::InitNew(LPSTR pszLogFilePath, INNTPCallback *pCallback)
  178. {
  179. // Let the base class handle the rest
  180. return (CIxpBase::OnInitNew("NNTP", pszLogFilePath, FILE_SHARE_READ | FILE_SHARE_WRITE,
  181. (ITransportCallback *) pCallback));
  182. }
  183. // --------------------------------------------------------------------------------
  184. // CNNTPTransport::HandsOffCallback
  185. // --------------------------------------------------------------------------------
  186. STDMETHODIMP CNNTPTransport::HandsOffCallback(void)
  187. {
  188. return CIxpBase::HandsOffCallback();
  189. }
  190. // --------------------------------------------------------------------------------
  191. // CNNTPTransport::GetStatus
  192. // --------------------------------------------------------------------------------
  193. STDMETHODIMP CNNTPTransport::GetStatus(IXPSTATUS *pCurrentStatus)
  194. {
  195. return CIxpBase::GetStatus(pCurrentStatus);
  196. }
  197. // --------------------------------------------------------------------------------
  198. // CNNTPTransport::InetServerFromAccount
  199. // --------------------------------------------------------------------------------
  200. STDMETHODIMP CNNTPTransport::InetServerFromAccount(IImnAccount *pAccount, LPINETSERVER pInetServer)
  201. {
  202. return CIxpBase::InetServerFromAccount(pAccount, pInetServer);
  203. }
  204. // --------------------------------------------------------------------------------
  205. // CNNTPTransport::Connect
  206. // --------------------------------------------------------------------------------
  207. HRESULT CNNTPTransport::Connect(LPINETSERVER pInetServer, boolean fAuthenticate,
  208. boolean fCommandLogging)
  209. {
  210. // Does user want us to always prompt for his password? Prompt him here to avoid
  211. // inactivity timeouts while the prompt is up, unless a password was supplied
  212. if (ISFLAGSET(pInetServer->dwFlags, ISF_ALWAYSPROMPTFORPASSWORD) &&
  213. '\0' == pInetServer->szPassword[0])
  214. {
  215. HRESULT hr;
  216. if (NULL != m_pCallback)
  217. hr = m_pCallback->OnLogonPrompt(pInetServer, NNTPTHISIXP);
  218. if (NULL == m_pCallback || S_OK != hr)
  219. return IXP_E_USER_CANCEL;
  220. }
  221. return CIxpBase::Connect(pInetServer, fAuthenticate, fCommandLogging);
  222. }
  223. // --------------------------------------------------------------------------------
  224. // CNNTPTransport::DropConnection
  225. // --------------------------------------------------------------------------------
  226. HRESULT CNNTPTransport::DropConnection(void)
  227. {
  228. return CIxpBase::DropConnection();
  229. }
  230. // --------------------------------------------------------------------------------
  231. // CNNTPTransport::Disconnect
  232. // --------------------------------------------------------------------------------
  233. HRESULT CNNTPTransport::Disconnect(void)
  234. {
  235. HRESULT hr = S_OK;
  236. if (SUCCEEDED(hr = CIxpBase::Disconnect()))
  237. {
  238. m_state = NS_DISCONNECTED;
  239. m_pSocket->Close();
  240. }
  241. return (hr);
  242. }
  243. // --------------------------------------------------------------------------------
  244. // CNNTPTransport::IsState
  245. // --------------------------------------------------------------------------------
  246. HRESULT CNNTPTransport::IsState(IXPISSTATE isstate)
  247. {
  248. return CIxpBase::IsState(isstate);
  249. }
  250. // --------------------------------------------------------------------------------
  251. // CNNTPTransport::GetServerInfo
  252. // --------------------------------------------------------------------------------
  253. HRESULT CNNTPTransport::GetServerInfo(LPINETSERVER pInetServer)
  254. {
  255. return CIxpBase::GetServerInfo(pInetServer);
  256. }
  257. // --------------------------------------------------------------------------------
  258. // CNNTPTransport::GetIXPType
  259. // --------------------------------------------------------------------------------
  260. IXPTYPE CNNTPTransport::GetIXPType(void)
  261. {
  262. return IXP_NNTP;
  263. }
  264. // --------------------------------------------------------------------------------
  265. // CNNTPTransport::ResetBase
  266. // --------------------------------------------------------------------------------
  267. void CNNTPTransport::ResetBase(void)
  268. {
  269. EnterCriticalSection(&m_cs);
  270. if (m_substate != NS_RECONNECTING)
  271. {
  272. m_state = NS_DISCONNECTED;
  273. m_substate = NS_RESP;
  274. m_fSupportsXRef = FALSE;
  275. m_rgHeaders = 0;
  276. m_pMemInfo = 0;
  277. if (m_sicinfo.pCallback)
  278. SSPIFreeContext(&m_sicinfo);
  279. ZeroMemory(&m_sicinfo, sizeof(m_sicinfo));
  280. m_cSecPkg = -1; // number of sec pkgs to try, -1 if not inited
  281. m_iSecPkg = -1; // current sec pkg being tried
  282. m_iAuthType = AUTHINFO_NONE;
  283. ZeroMemory(m_rgszSecPkg, sizeof(m_rgszSecPkg)); // array of pointers into m_szSecPkgs
  284. m_szSecPkgs = NULL; // string returned by "AUTHINFO TRANSACT TWINKIE"
  285. m_fRetryPkg = FALSE;
  286. m_pAuthInfo = NULL;
  287. m_fNoXover = FALSE;
  288. }
  289. LeaveCriticalSection(&m_cs);
  290. }
  291. // ------------------------------------------------------------------------------------
  292. // CNNTPTransport::DoQuit
  293. // ------------------------------------------------------------------------------------
  294. void CNNTPTransport::DoQuit(void)
  295. {
  296. CommandQUIT();
  297. }
  298. // --------------------------------------------------------------------------------
  299. // CNNTPTransport::OnConnected
  300. // --------------------------------------------------------------------------------
  301. void CNNTPTransport::OnConnected(void)
  302. {
  303. m_state = NS_CONNECT;
  304. m_substate = NS_CONNECT_RESP;
  305. CIxpBase::OnConnected();
  306. }
  307. // --------------------------------------------------------------------------------
  308. // CNNTPTransport::OnDisconnect
  309. // --------------------------------------------------------------------------------
  310. void CNNTPTransport::OnDisconnected(void)
  311. {
  312. ResetBase();
  313. CIxpBase::OnDisconnected();
  314. }
  315. // --------------------------------------------------------------------------------
  316. // CNNTPTransport::OnEnterBusy
  317. // --------------------------------------------------------------------------------
  318. void CNNTPTransport::OnEnterBusy(void)
  319. {
  320. IxpAssert(m_state == NS_IDLE);
  321. }
  322. // --------------------------------------------------------------------------------
  323. // CNNTPTransport::OnLeaveBusy
  324. // --------------------------------------------------------------------------------
  325. void CNNTPTransport::OnLeaveBusy(void)
  326. {
  327. m_state = NS_IDLE;
  328. }
  329. //
  330. // FUNCTION: CNNTPTransport::OnSocketReceive()
  331. //
  332. // PURPOSE: This function reads the data off the socket and parses that
  333. // information based on the current state of the transport.
  334. //
  335. void CNNTPTransport::OnSocketReceive(void)
  336. {
  337. HRESULT hr;
  338. UINT uiResp;
  339. EnterCriticalSection(&m_cs);
  340. // Handle the current NNTP State
  341. switch (m_state)
  342. {
  343. case NS_CONNECT:
  344. {
  345. HandleConnectResponse();
  346. break;
  347. }
  348. case NS_AUTHINFO:
  349. {
  350. HandleConnectResponse();
  351. break;
  352. }
  353. case NS_NEWGROUPS:
  354. {
  355. // If we're waiting for the original response line then get it and
  356. // parse it here
  357. if (NS_RESP == m_substate)
  358. {
  359. if (FAILED(hr = HrGetResponse()))
  360. goto exit;
  361. // If the command failed, inform the user and exit
  362. if (IXP_NNTP_NEWNEWSGROUPS_FOLLOWS != m_uiResponse)
  363. {
  364. hr = IXP_E_NNTP_NEWGROUPS_FAILED;
  365. DispatchResponse(hr, TRUE);
  366. break;
  367. }
  368. // Advance the substate to data receive
  369. m_substate = NS_DATA;
  370. }
  371. // Process the returned data
  372. ProcessListData();
  373. break;
  374. }
  375. case NS_LIST:
  376. {
  377. // If we're waiting for the original response line then get it and
  378. // parse it here.
  379. if (NS_RESP == m_substate)
  380. {
  381. if (FAILED(hr = HrGetResponse()))
  382. goto exit;
  383. // If the command failed, inform the user and exit
  384. if (IXP_NNTP_LIST_DATA_FOLLOWS != m_uiResponse)
  385. {
  386. hr = IXP_E_NNTP_LIST_FAILED;
  387. DispatchResponse(hr, TRUE);
  388. break;
  389. }
  390. // Advance the substate to data recieve
  391. m_substate = NS_DATA;
  392. }
  393. // Start processing the data retrieved from the command
  394. ProcessListData();
  395. break;
  396. }
  397. case NS_LISTGROUP:
  398. {
  399. if (NS_RESP == m_substate)
  400. {
  401. if (FAILED(hr = HrGetResponse()))
  402. goto exit;
  403. if (IXP_NNTP_GROUP_SELECTED != m_uiResponse)
  404. {
  405. hr = IXP_E_NNTP_LISTGROUP_FAILED;
  406. DispatchResponse(hr, TRUE);
  407. break;
  408. }
  409. m_substate = NS_DATA;
  410. }
  411. ProcessListGroupData();
  412. break;
  413. }
  414. case NS_GROUP:
  415. if (FAILED(hr = HrGetResponse()))
  416. goto exit;
  417. ProcessGroupResponse();
  418. break;
  419. case NS_ARTICLE:
  420. {
  421. if (NS_RESP == m_substate)
  422. {
  423. if (FAILED(hr = HrGetResponse()))
  424. goto exit;
  425. if (IXP_NNTP_ARTICLE_FOLLOWS != m_uiResponse)
  426. {
  427. hr = IXP_E_NNTP_ARTICLE_FAILED;
  428. DispatchResponse(hr, TRUE);
  429. break;
  430. }
  431. m_substate = NS_DATA;
  432. }
  433. ProcessArticleData();
  434. break;
  435. }
  436. case NS_HEAD:
  437. {
  438. if (NS_RESP == m_substate)
  439. {
  440. if (FAILED(hr = HrGetResponse()))
  441. goto exit;
  442. if (IXP_NNTP_HEAD_FOLLOWS != m_uiResponse)
  443. {
  444. hr = IXP_E_NNTP_HEAD_FAILED;
  445. DispatchResponse(hr, TRUE);
  446. break;
  447. }
  448. m_substate = NS_DATA;
  449. }
  450. ProcessArticleData();
  451. break;
  452. }
  453. case NS_BODY:
  454. {
  455. if (NS_RESP == m_substate)
  456. {
  457. if (FAILED(hr = HrGetResponse()))
  458. goto exit;
  459. if (IXP_NNTP_BODY_FOLLOWS != m_uiResponse)
  460. {
  461. hr = IXP_E_NNTP_BODY_FAILED;
  462. DispatchResponse(hr, TRUE);
  463. break;
  464. }
  465. m_substate = NS_DATA;
  466. }
  467. ProcessArticleData();
  468. break;
  469. }
  470. case NS_POST:
  471. if (NS_RESP == m_substate)
  472. {
  473. // Get the response to our post command
  474. if (FAILED(hr = HrGetResponse()))
  475. goto exit;
  476. // If the response wasn't 340, then we can't post and might
  477. // as well bail.
  478. if (IXP_NNTP_SEND_ARTICLE_TO_POST != m_uiResponse)
  479. {
  480. hr = IXP_E_NNTP_POST_FAILED;
  481. // Make sure we free up the stream
  482. SafeRelease(m_rMessage.pstmMsg);
  483. DispatchResponse(hr, TRUE);
  484. break;
  485. }
  486. // Send the message
  487. if (SUCCEEDED(hr = HrPostMessage()))
  488. {
  489. HrSendCommand((LPSTR) NNTP_ENDPOST, 0, FALSE);
  490. m_substate = NS_ENDPOST_RESP;
  491. }
  492. else if (IXP_E_WOULD_BLOCK == hr)
  493. {
  494. // We will send the crlf . crlf when we get the AE_SENDDONE
  495. // notification. This is handled in OnNotify().
  496. m_substate = NS_SEND_ENDPOST;
  497. }
  498. else
  499. {
  500. // Some unhandled error occured.
  501. hr = IXP_E_NNTP_POST_FAILED;
  502. DispatchResponse(hr, TRUE);
  503. }
  504. }
  505. else if (NS_ENDPOST_RESP == m_substate)
  506. {
  507. // Here's the response from the server regarding the post. Send
  508. // it off to the user.
  509. hr = HrGetResponse();
  510. if (IXP_NNTP_ARTICLE_POSTED_OK != m_uiResponse)
  511. hr = IXP_E_NNTP_POST_FAILED;
  512. DispatchResponse(hr, TRUE);
  513. }
  514. break;
  515. case NS_IDLE:
  516. break;
  517. case NS_DISCONNECTED:
  518. break;
  519. case NS_QUIT:
  520. if (FAILED(hr = HrGetResponse()))
  521. goto exit;
  522. DispatchResponse(S_OK, TRUE);
  523. // Make sure the socket closes if the server doesn't drop the
  524. // connection itself.
  525. m_pSocket->Close();
  526. break;
  527. case NS_LAST:
  528. if (FAILED(hr = HrGetResponse()))
  529. goto exit;
  530. ProcessNextResponse();
  531. break;
  532. case NS_NEXT:
  533. if (FAILED(hr = HrGetResponse()))
  534. goto exit;
  535. ProcessNextResponse();
  536. break;
  537. case NS_STAT:
  538. if (FAILED(hr = HrGetResponse()))
  539. goto exit;
  540. ProcessNextResponse();
  541. break;
  542. case NS_MODE:
  543. // Very little to do with this response other than return it to
  544. // the caller.
  545. if (FAILED(hr = HrGetResponse()))
  546. goto exit;
  547. DispatchResponse(S_OK);
  548. break;
  549. case NS_DATE:
  550. if (FAILED(hr = HrGetResponse()))
  551. goto exit;
  552. ProcessDateResponse();
  553. break;
  554. case NS_HEADERS:
  555. if (NS_RESP == m_substate)
  556. {
  557. // Get the response string from the socket
  558. if (FAILED(hr = HrGetResponse()))
  559. goto exit;
  560. // There's a couple of things that can happen here. First, if
  561. // the response is IXP_NNTP_OVERVIEW_FOLLOWS, then everything is
  562. // great and we can party on. If the response is > 500, then
  563. // XOVER isn't supported on this server and we have to try using
  564. // XHDR to retrieve the headers.
  565. if (m_uiResponse >= 500 && m_gethdr == GETHDR_XOVER)
  566. {
  567. hr = BuildHeadersFromXhdr(TRUE);
  568. if (FAILED(hr))
  569. DispatchResponse(hr, TRUE);
  570. break;
  571. }
  572. else if (2 != (m_uiResponse / 100))
  573. {
  574. hr = IXP_E_NNTP_HEADERS_FAILED;
  575. DispatchResponse(hr, TRUE);
  576. break;
  577. }
  578. m_substate = NS_DATA;
  579. }
  580. // Parse the returned data strings
  581. if (m_gethdr == GETHDR_XOVER)
  582. ProcessXoverData();
  583. else
  584. BuildHeadersFromXhdr(FALSE);
  585. break;
  586. case NS_XHDR:
  587. if (NS_RESP == m_substate)
  588. {
  589. if (FAILED(hr = HrGetResponse()))
  590. goto exit;
  591. if (2 != (m_uiResponse / 100))
  592. {
  593. hr = IXP_E_NNTP_XHDR_FAILED;
  594. DispatchResponse(hr, TRUE);
  595. break;
  596. }
  597. m_substate = NS_DATA;
  598. }
  599. ProcessXhdrData();
  600. break;
  601. default:
  602. IxpAssert(FALSE);
  603. break;
  604. }
  605. exit:
  606. LeaveCriticalSection(&m_cs);
  607. }
  608. HRESULT CNNTPTransport::HandleConnectResponse(void)
  609. {
  610. HRESULT hr = E_FAIL;
  611. IxpAssert(m_substate >= NS_CONNECT_RESP);
  612. switch (m_substate)
  613. {
  614. // Parse the banner and make sure we got a valid response. If so,
  615. // then issue a "MODE READER" command to inform the server that we
  616. // are a client and not another server.
  617. case NS_CONNECT_RESP:
  618. if (SUCCEEDED(hr = HrGetResponse()))
  619. {
  620. // Make sure we got a valid response from the server
  621. if (IXP_NNTP_POST_ALLOWED != m_uiResponse &&
  622. IXP_NNTP_POST_NOTALLOWED != m_uiResponse)
  623. {
  624. // If we didn't get a valid response, disconnect and inform
  625. // the client that the connect failed.
  626. Disconnect();
  627. m_state = NS_DISCONNECTED;
  628. DispatchResponse(IXP_E_NNTP_RESPONSE_ERROR, TRUE);
  629. }
  630. else
  631. {
  632. // Stash the response code so if we finally connect we can
  633. // return whether or not posting is allowed
  634. m_hrPostingAllowed =
  635. (m_uiResponse == IXP_NNTP_POST_ALLOWED) ? S_OK : S_FALSE;
  636. // Move to the next state where we issue the "MODE READER"
  637. hr = HrSendCommand((LPSTR) NNTP_MODE_READER_CRLF, NULL, FALSE);
  638. if (SUCCEEDED(hr))
  639. {
  640. m_state = NS_CONNECT;
  641. m_substate = NS_MODE_READER_RESP;
  642. }
  643. }
  644. }
  645. break;
  646. // Read the response from the "MODE READER" command off the socket. If
  647. // the user wants us to handle authentication, then start that.
  648. // Otherwise, we're done and can consider this a terminal state.
  649. case NS_MODE_READER_RESP:
  650. if (SUCCEEDED(hr = HrGetResponse()))
  651. {
  652. if (m_fConnectAuth)
  653. // This is TRUE if the user specified in the Connect() call
  654. // that we should automatically logon for them.
  655. StartLogon();
  656. else
  657. {
  658. // Otherwise consider ourselves ready to accept commands
  659. DispatchResponse(m_hrPostingAllowed, TRUE);
  660. }
  661. }
  662. break;
  663. // We issued an empty AUTHINFO GENERIC command to get a list of security
  664. // packages supported by the server. We parse that list and continue with
  665. // sicily authentication.
  666. case NS_GENERIC_TEST:
  667. if (SUCCEEDED(hr = HrGetResponse()))
  668. {
  669. if (m_uiResponse == IXP_NNTP_AUTH_OK)
  670. {
  671. m_substate = NS_GENERIC_PKG_DATA;
  672. hr = ProcessGenericTestResponse();
  673. }
  674. else
  675. {
  676. // could be an old MSN server, so try AUTHINFO TRANSACT TWINKIE
  677. hr = HrSendCommand((LPSTR) NNTP_TRANSACTTEST_CRLF, NULL, FALSE);
  678. m_substate = NS_TRANSACT_TEST;
  679. }
  680. }
  681. break;
  682. // We issued an empty AUTHINFO GENERIC command to get a list of security
  683. // packages supported by the server. We parse that list and continue with
  684. // sicily authentication.
  685. case NS_GENERIC_PKG_DATA:
  686. hr = ProcessGenericTestResponse();
  687. break;
  688. // We issued a invalid AUTHINFO TRANSACT command to get a list of security
  689. // packages supported by the server. We parse that list and continue with
  690. // sicily authentication.
  691. case NS_TRANSACT_TEST:
  692. if (SUCCEEDED(hr = HrGetResponse()))
  693. {
  694. ProcessTransactTestResponse();
  695. }
  696. break;
  697. // We issued a AUTHINFO {TRANSACT|GENERIC} <package name> to the server. Parse this
  698. // response to see if the server understands the package. If so, then send
  699. // the negotiation information, otherwise try a different security package.
  700. case NS_TRANSACT_PACKAGE:
  701. if (SUCCEEDED(hr = HrGetResponse()))
  702. {
  703. if (m_uiResponse == IXP_NNTP_PASSWORD_REQUIRED)
  704. {
  705. Assert(m_iAuthType != AUTHINFO_NONE);
  706. if (m_iAuthType == AUTHINFO_GENERIC)
  707. HrSendCommand((LPSTR) NNTP_GENERICCMD, m_sicmsg.szBuffer, FALSE);
  708. else
  709. HrSendCommand((LPSTR) NNTP_TRANSACTCMD, m_sicmsg.szBuffer, FALSE);
  710. m_substate = NS_TRANSACT_NEGO;
  711. }
  712. else
  713. {
  714. TryNextSecPkg();
  715. }
  716. }
  717. break;
  718. // We received a response to our negotiation command. If the response
  719. // is 381, then generate a response to the server's challange. Otherwise,
  720. // we disconnect and try to reconnect with the next security package.
  721. case NS_TRANSACT_NEGO:
  722. if (SUCCEEDED(hr = HrGetResponse()))
  723. {
  724. if (m_uiResponse == IXP_NNTP_PASSWORD_REQUIRED)
  725. {
  726. SSPIBUFFER Challenge, Response;
  727. // Skip over the "381 "
  728. SSPISetBuffer(m_pszResponse + 4, SSPI_STRING, 0, &Challenge);
  729. // Generate a response from the server's challange
  730. if (SUCCEEDED(hr = SSPIResponseFromChallenge(&m_sicinfo, &Challenge, &Response)))
  731. {
  732. Assert(m_iAuthType != AUTHINFO_NONE);
  733. if (m_iAuthType == AUTHINFO_GENERIC)
  734. HrSendCommand((LPSTR) NNTP_GENERICCMD, Response.szBuffer, FALSE);
  735. else
  736. HrSendCommand((LPSTR) NNTP_TRANSACTCMD, Response.szBuffer, FALSE);
  737. // if a continue is required, stay in this state
  738. if (!Response.fContinue)
  739. m_substate = NS_TRANSACT_RESP;
  740. break;
  741. }
  742. else
  743. {
  744. Disconnect();
  745. DispatchResponse(IXP_E_SICILY_LOGON_FAILED, TRUE);
  746. break;
  747. }
  748. }
  749. else
  750. m_fRetryPkg = TRUE;
  751. // We need to reset the connection if we get here
  752. m_substate = NS_RECONNECTING;
  753. OnStatus(IXP_AUTHRETRY);
  754. m_pSocket->Close();
  755. m_pSocket->Connect();
  756. }
  757. break;
  758. // This is the final response to our sicily negotiation. This should
  759. // be either that we succeeded or didn't. If we succeeded, then we've
  760. // reached a terminal state and can inform the user that we're ready
  761. // to accept commands. Otherwise, we reconnect and try the next
  762. // security package.
  763. case NS_TRANSACT_RESP:
  764. if (SUCCEEDED(hr = HrGetResponse()))
  765. {
  766. if (IXP_NNTP_AUTH_OK == m_uiResponse)
  767. {
  768. OnStatus(IXP_AUTHORIZED);
  769. DispatchResponse(m_hrPostingAllowed, TRUE);
  770. }
  771. else
  772. {
  773. // We need to reset the connection
  774. OnStatus(IXP_AUTHRETRY);
  775. m_substate = NS_RECONNECTING;
  776. m_fRetryPkg = TRUE;
  777. m_pSocket->Close();
  778. m_pSocket->Connect();
  779. }
  780. }
  781. break;
  782. // We issued an AUTHINFO USER <username> and this is the server's
  783. // response. We're expecting either that a password is required or
  784. // that we've succeesfully authenticated.
  785. case NS_AUTHINFO_USER_RESP:
  786. if (SUCCEEDED(hr = HrGetResponse()))
  787. {
  788. // If the server requires a password to go along with the username
  789. // then send it now.
  790. if (IXP_NNTP_PASSWORD_REQUIRED == m_uiResponse)
  791. {
  792. LPSTR pszPassword;
  793. // Choose the password based on if we were called from
  794. // Connect() or CommandAUTHINFO().
  795. if (m_state == NS_AUTHINFO)
  796. pszPassword = m_pAuthInfo->pszPass;
  797. else
  798. pszPassword = m_rServer.szPassword;
  799. HrSendCommand((LPSTR) NNTP_AUTHINFOPASS, pszPassword, FALSE);
  800. m_substate = NS_AUTHINFO_PASS_RESP;
  801. }
  802. // Otherwise, consider ourselves connected and in a state to accept
  803. // commands
  804. else
  805. {
  806. OnStatus(IXP_AUTHORIZED);
  807. DispatchResponse(m_hrPostingAllowed, TRUE);
  808. }
  809. }
  810. break;
  811. // We sent a AUTHINFO PASS <password> command to complement the AUTHINFO
  812. // USER command. This response will tell us whether we're authenticated
  813. // or not. Either way this is a terminal state.
  814. case NS_AUTHINFO_PASS_RESP:
  815. if (SUCCEEDED(hr = HrGetResponse()))
  816. {
  817. // If the password was accepted, consider ourselves connected
  818. // and in a state to accept commands.
  819. if (IXP_NNTP_AUTH_OK >= m_uiResponse)
  820. {
  821. OnStatus(IXP_AUTHORIZED);
  822. DispatchResponse(m_hrPostingAllowed, TRUE);
  823. }
  824. // If the password was rejected, then use the callback to prompt
  825. // the user for new credentials.
  826. else
  827. {
  828. // Need to disconnect and reconnect to make sure we're in a
  829. // known, stable state with the server.
  830. m_substate = NS_RECONNECTING;
  831. if (FAILED(LogonRetry(IXP_E_NNTP_INVALID_USERPASS)))
  832. {
  833. DispatchResponse(IXP_E_USER_CANCEL, TRUE);
  834. }
  835. }
  836. }
  837. break;
  838. // We sent a AUTHINFO SIMPLE command to the server to see if the command
  839. // is supported. If the server returns 350, then we should send the
  840. // username and password
  841. case NS_AUTHINFO_SIMPLE_RESP:
  842. if (SUCCEEDED(hr = HrGetResponse()))
  843. {
  844. // If we got a response to continue, then send the user name and
  845. // password
  846. if (IXP_NNTP_CONTINUE_AUTHORIZATION == m_uiResponse)
  847. {
  848. IxpAssert(m_pAuthInfo);
  849. if (SUCCEEDED(hr = HrSendCommand(m_pAuthInfo->pszUser,
  850. m_pAuthInfo->pszPass, FALSE)))
  851. m_substate = NS_AUTHINFO_SIMPLE_USERPASS_RESP;
  852. else
  853. DispatchResponse(hr, TRUE);
  854. }
  855. else
  856. {
  857. // Otherwise fail and inform the user.
  858. DispatchResponse(hr, TRUE);
  859. }
  860. }
  861. break;
  862. // This is the final response from the AUTHINFO SIMPLE command. All
  863. // we need to do is inform the user.
  864. case NS_AUTHINFO_SIMPLE_USERPASS_RESP:
  865. if (SUCCEEDED(hr = HrGetResponse()))
  866. {
  867. DispatchResponse(hr, TRUE);
  868. }
  869. break;
  870. }
  871. return (hr);
  872. }
  873. //
  874. // FUNCTION: CNNTPTransport::DispatchResponse()
  875. //
  876. // PURPOSE: Takes the server response string, packages it up into a
  877. // NNTPRESPONSE structure, and returns it to the callback
  878. // interface.
  879. //
  880. // PARAMETERS:
  881. // hrResult - The result code to send to the callback.
  882. // fDone - True if the command has completed.
  883. // pResponse - If the command is returning data, then this should
  884. // be filled in with the data to be returned.
  885. //
  886. void CNNTPTransport::DispatchResponse(HRESULT hrResult, BOOL fDone,
  887. LPNNTPRESPONSE pResponse)
  888. {
  889. // Locals
  890. NNTPRESPONSE rResponse;
  891. // If a response was passed in, use it...
  892. if (pResponse)
  893. CopyMemory(&rResponse, pResponse, sizeof(NNTPRESPONSE));
  894. else
  895. ZeroMemory(&rResponse, sizeof(NNTPRESPONSE));
  896. rResponse.fDone = fDone;
  897. // Set up the return structure
  898. rResponse.state = m_state;
  899. rResponse.rIxpResult.hrResult = hrResult;
  900. rResponse.rIxpResult.pszResponse = PszDupA(m_pszResponse);
  901. rResponse.rIxpResult.uiServerError = m_uiResponse;
  902. rResponse.rIxpResult.hrServerError = m_hrResponse;
  903. rResponse.rIxpResult.dwSocketError = m_pSocket->GetLastError();
  904. rResponse.rIxpResult.pszProblem = NULL;
  905. rResponse.pTransport = this;
  906. // If Done...
  907. if (fDone)
  908. {
  909. // No current command
  910. m_state = NS_IDLE;
  911. m_substate = NS_RESP;
  912. // Reset Last Response
  913. SafeMemFree(m_pszResponse);
  914. m_hrResponse = S_OK;
  915. m_uiResponse = 0;
  916. // If we have user/pass info cached, free it
  917. if (m_pAuthInfo)
  918. {
  919. SafeMemFree(m_pAuthInfo->pszUser);
  920. SafeMemFree(m_pAuthInfo->pszPass);
  921. SafeMemFree(m_pAuthInfo);
  922. }
  923. // Leave Busy State
  924. LeaveBusy();
  925. }
  926. // Give the Response to the client
  927. if (m_pCallback)
  928. ((INNTPCallback *) m_pCallback)->OnResponse(&rResponse);
  929. SafeMemFree(rResponse.rIxpResult.pszResponse);
  930. }
  931. //
  932. // FUNCTION: CNNTPTransport::HrGetResponse()
  933. //
  934. // PURPOSE: Reads the server response string off the socket and stores
  935. // the response info in local members.
  936. //
  937. // RETURN VALUE:
  938. // HRESULT
  939. //
  940. HRESULT CNNTPTransport::HrGetResponse(void)
  941. {
  942. HRESULT hr = S_OK;
  943. int cbLine;
  944. // Clear response
  945. if (m_pszResponse != NULL)
  946. SafeMemFree(m_pszResponse);
  947. // Read the line from the socket
  948. hr = m_pSocket->ReadLine(&m_pszResponse, &cbLine);
  949. // Handle incomplete lines
  950. if (hr == IXP_E_INCOMPLETE)
  951. goto exit;
  952. // Socket error
  953. if (FAILED(hr))
  954. {
  955. hr = TrapError(IXP_E_SOCKET_READ_ERROR);
  956. goto exit;
  957. }
  958. // Strip the trailing CRLF
  959. StripCRLF(m_pszResponse, (ULONG *) &cbLine);
  960. // Log it
  961. if (m_pLogFile)
  962. m_pLogFile->WriteLog(LOGFILE_RX, m_pszResponse);
  963. // Get the response code from the beginning of the string
  964. m_uiResponse = StrToInt(m_pszResponse);
  965. // Tell the client about the server response
  966. if (m_pCallback)
  967. m_pCallback->OnCommand(CMD_RESP, m_pszResponse, hr, NNTPTHISIXP);
  968. exit:
  969. return (hr);
  970. }
  971. //
  972. // FUNCTION: CNNTPTransport::StartLogon()
  973. //
  974. // PURPOSE: Starts the login process with the server based on information
  975. // provided by the user in Connect().
  976. //
  977. void CNNTPTransport::StartLogon(void)
  978. {
  979. HRESULT hr;
  980. // If not using sicily or it's not installed, try simple USER/PASS authentication
  981. if (m_rServer.fTrySicily)
  982. {
  983. // If sicily is installed
  984. if (FIsSicilyInstalled())
  985. {
  986. // Status
  987. OnStatus(IXP_AUTHORIZING);
  988. // If we haven't enumerated the security packages yet, do so
  989. if (m_cSecPkg == -1)
  990. {
  991. hr = HrSendCommand((LPSTR) NNTP_GENERICTEST_CRLF, NULL, FALSE);
  992. m_substate = NS_GENERIC_TEST;
  993. }
  994. else
  995. {
  996. // We've reconnected, try the next security package
  997. TryNextSecPkg();
  998. }
  999. }
  1000. else
  1001. {
  1002. Disconnect();
  1003. DispatchResponse(IXP_E_LOAD_SICILY_FAILED, TRUE);
  1004. }
  1005. }
  1006. else
  1007. {
  1008. hr = MaybeTryAuthinfo();
  1009. if (FAILED(hr))
  1010. {
  1011. OnError(hr);
  1012. DropConnection();
  1013. DispatchResponse(hr, TRUE);
  1014. }
  1015. }
  1016. }
  1017. HRESULT CNNTPTransport::LogonRetry(HRESULT hrLogon)
  1018. {
  1019. HRESULT hr = S_OK;
  1020. // Let the user know that the logon failed
  1021. OnError(hrLogon);
  1022. // Update the transport status
  1023. OnStatus(IXP_AUTHRETRY);
  1024. // Enter Auth Retry State
  1025. m_pSocket->Close();
  1026. // Turn off the watchdog timer
  1027. m_pSocket->StopWatchDog();
  1028. // Ask the user to provide credetials
  1029. if (NULL == m_pCallback || S_FALSE == m_pCallback->OnLogonPrompt(&m_rServer, NNTPTHISIXP))
  1030. {
  1031. OnDisconnected();
  1032. return (E_FAIL);
  1033. }
  1034. // Finding Host Progress
  1035. OnStatus(IXP_FINDINGHOST);
  1036. // Connect to server
  1037. hr = m_pSocket->Connect();
  1038. if (FAILED(hr))
  1039. {
  1040. OnError(TrapError(IXP_E_SOCKET_CONNECT_ERROR));
  1041. OnDisconnected();
  1042. return hr;
  1043. }
  1044. // Start WatchDog
  1045. m_pSocket->StartWatchDog();
  1046. return (S_OK);
  1047. }
  1048. /////////////////////////////////////////////////////////////////////////////
  1049. //
  1050. // CNNTPTransport::ProcessGenericTestResponse
  1051. //
  1052. // processes AUTHINFO GENERIC response
  1053. //
  1054. HRESULT CNNTPTransport::ProcessGenericTestResponse()
  1055. {
  1056. HRESULT hr;
  1057. LPSTR pszLines = NULL;
  1058. int iRead, iLines;
  1059. m_cSecPkg = 0;
  1060. if (SUCCEEDED(hr = m_pSocket->ReadLines(&pszLines, &iRead, &iLines)))
  1061. {
  1062. LPSTR pszT = pszLines, pszPkg;
  1063. while (*pszT && m_cSecPkg < MAX_SEC_PKGS)
  1064. {
  1065. // check for end of list condition
  1066. if ((pszT[0] == '.') && ((pszT[1] == '\r' && pszT[2] == '\n') || (pszT[1] == '\n')))
  1067. break;
  1068. pszPkg = pszT;
  1069. // look for an LF or CRLF to end the line
  1070. while (*pszT && !(pszT[0] == '\n' || (pszT[0] == '\r' && pszT[1] == '\n')))
  1071. pszT++;
  1072. // strip the LF or CRLF
  1073. while (*pszT == '\r' || *pszT == '\n')
  1074. *pszT++ = 0;
  1075. m_rgszSecPkg[m_cSecPkg++] = PszDupA(pszPkg);
  1076. }
  1077. // we've reached the end of the list, otherwise there is more data expected
  1078. if (pszT[0] == '.')
  1079. {
  1080. Assert(pszT[1] == '\r' && pszT[2] == '\n');
  1081. m_iAuthType = AUTHINFO_GENERIC;
  1082. hr = TryNextSecPkg();
  1083. }
  1084. MemFree(pszLines);
  1085. }
  1086. return hr;
  1087. }
  1088. /////////////////////////////////////////////////////////////////////////////
  1089. //
  1090. // CNNTPTransport::ProcessTransactTestResponse
  1091. //
  1092. // processes AUTHINFO TRANSACT TWINKIE response
  1093. //
  1094. HRESULT CNNTPTransport::ProcessTransactTestResponse()
  1095. {
  1096. HRESULT hr = NOERROR;
  1097. m_cSecPkg = 0;
  1098. if (m_uiResponse == IXP_NNTP_PROTOCOLS_SUPPORTED)
  1099. {
  1100. LPSTR pszT;
  1101. pszT = m_szSecPkgs = PszDupA(m_pszResponse + 3); // skip over 485
  1102. while (*pszT && IsSpace(pszT))
  1103. pszT++;
  1104. while (*pszT && m_cSecPkg < MAX_SEC_PKGS)
  1105. {
  1106. m_rgszSecPkg[m_cSecPkg++] = pszT;
  1107. while (*pszT && !IsSpace(pszT))
  1108. pszT++;
  1109. while (*pszT && IsSpace(pszT))
  1110. *pszT++ = 0;
  1111. }
  1112. m_iAuthType = AUTHINFO_TRANSACT;
  1113. return TryNextSecPkg();
  1114. }
  1115. else
  1116. {
  1117. Disconnect();
  1118. DispatchResponse(IXP_E_SICILY_LOGON_FAILED, TRUE);
  1119. return NOERROR;
  1120. }
  1121. }
  1122. /////////////////////////////////////////////////////////////////////////////
  1123. //
  1124. // CNNTPTransport::TryNextSecPkg
  1125. //
  1126. // tries the next security package, or reverts to basic if necessary
  1127. //
  1128. HRESULT CNNTPTransport::TryNextSecPkg()
  1129. {
  1130. HRESULT hr;
  1131. Assert(m_cSecPkg != -1);
  1132. Assert(m_iAuthType != AUTHINFO_NONE);
  1133. TryNext:
  1134. if (!m_fRetryPkg)
  1135. m_iSecPkg++;
  1136. if (m_iSecPkg < m_cSecPkg)
  1137. {
  1138. Assert(m_cSecPkg);
  1139. SSPIFreeContext(&m_sicinfo);
  1140. if (!lstrcmpi(m_rgszSecPkg[m_iSecPkg], NNTP_BASIC))
  1141. return MaybeTryAuthinfo();
  1142. // In case the Sicily function brings up UI, we need to turn off the
  1143. // watchdog so we don't time out waiting for the user
  1144. m_pSocket->StopWatchDog();
  1145. if (SUCCEEDED(hr = SSPILogon(&m_sicinfo, m_fRetryPkg, SSPI_BASE64, m_rgszSecPkg[m_iSecPkg], &m_rServer, m_pCallback)))
  1146. {
  1147. if (m_fRetryPkg)
  1148. {
  1149. m_fRetryPkg = FALSE;
  1150. }
  1151. if (SUCCEEDED(hr = SSPIGetNegotiate(&m_sicinfo, &m_sicmsg)))
  1152. {
  1153. DOUTL(2, "Trying to connect using %s security...", m_rgszSecPkg[m_iSecPkg]);
  1154. if (m_iAuthType == AUTHINFO_GENERIC)
  1155. hr = HrSendCommand((LPSTR) NNTP_GENERICCMD, m_rgszSecPkg[m_iSecPkg], FALSE);
  1156. else
  1157. hr = HrSendCommand((LPSTR) NNTP_TRANSACTCMD, m_rgszSecPkg[m_iSecPkg], FALSE);
  1158. // Restart the watchdog timer now that we've issued our next command to the server.
  1159. m_pSocket->StartWatchDog();
  1160. m_substate = NS_TRANSACT_PACKAGE;
  1161. }
  1162. else
  1163. {
  1164. hr = IXP_E_SICILY_LOGON_FAILED;
  1165. goto TryNext;
  1166. }
  1167. }
  1168. else
  1169. {
  1170. m_fRetryPkg = FALSE;
  1171. goto TryNext;
  1172. }
  1173. }
  1174. else
  1175. {
  1176. OnError(IXP_E_SICILY_LOGON_FAILED);
  1177. DropConnection();
  1178. DispatchResponse(IXP_E_SICILY_LOGON_FAILED, TRUE);
  1179. hr = NOERROR;
  1180. }
  1181. return hr;
  1182. }
  1183. /////////////////////////////////////////////////////////////////////////////
  1184. //
  1185. // CNNTPTransport::MaybeTryAuthinfo
  1186. //
  1187. // tries basic authinfo if necessary, else moves to connected state
  1188. //
  1189. HRESULT CNNTPTransport::MaybeTryAuthinfo()
  1190. {
  1191. HRESULT hr;
  1192. if (*m_rServer.szUserName)
  1193. {
  1194. OnStatus(IXP_AUTHORIZING);
  1195. hr = HrSendCommand((LPSTR) NNTP_AUTHINFOUSER, m_rServer.szUserName, FALSE);
  1196. m_substate = NS_AUTHINFO_USER_RESP;
  1197. }
  1198. else
  1199. {
  1200. // Logon not needed for this news server (or so we'll have to assume)
  1201. OnStatus(IXP_AUTHORIZED);
  1202. DispatchResponse(S_OK, TRUE);
  1203. hr = NOERROR;
  1204. }
  1205. return hr;
  1206. }
  1207. #define Whitespace(_ch) (((_ch) == ' ') || ((_ch) == '\t') || ((_ch) == '\n'))
  1208. BOOL ScanNum(LPSTR *ppsz, BOOL fEnd, DWORD *pdw)
  1209. {
  1210. DWORD n = 0;
  1211. LPSTR psz;
  1212. Assert(ppsz != NULL);
  1213. Assert(pdw != NULL);
  1214. psz = *ppsz;
  1215. if (*psz == 0 || Whitespace(*psz))
  1216. return(FALSE);
  1217. while (*psz != 0 && !Whitespace(*psz))
  1218. {
  1219. if (*psz >= '0' && *psz <= '9')
  1220. {
  1221. n *= 10;
  1222. n += *psz - '0';
  1223. psz++;
  1224. }
  1225. else
  1226. {
  1227. return(FALSE);
  1228. }
  1229. }
  1230. if (Whitespace(*psz))
  1231. {
  1232. if (fEnd)
  1233. return(FALSE);
  1234. while (*psz != 0 && Whitespace(*psz))
  1235. psz++;
  1236. }
  1237. else
  1238. {
  1239. Assert(*psz == 0);
  1240. if (!fEnd)
  1241. return(FALSE);
  1242. }
  1243. *ppsz = psz;
  1244. *pdw = n;
  1245. return(TRUE);
  1246. }
  1247. BOOL ScanWord(LPCSTR psz, LPSTR pszDest)
  1248. {
  1249. Assert(psz != NULL);
  1250. Assert(pszDest != NULL);
  1251. if (*psz == 0 || Whitespace(*psz))
  1252. return(FALSE);
  1253. while (*psz != 0 && !Whitespace(*psz))
  1254. {
  1255. *pszDest = *psz;
  1256. psz++;
  1257. pszDest++;
  1258. }
  1259. *pszDest = 0;
  1260. return(TRUE);
  1261. }
  1262. /////////////////////////////////////////////////////////////////////////////
  1263. //
  1264. // CNNTPTransport::ProcessGroupResponse
  1265. //
  1266. // processes the GROUP response
  1267. //
  1268. HRESULT CNNTPTransport::ProcessGroupResponse(void)
  1269. {
  1270. NNTPGROUP rGroup;
  1271. DWORD dwResp;
  1272. NNTPRESPONSE rResp;
  1273. LPSTR psz;
  1274. LPSTR pszGroup = 0;
  1275. ZeroMemory(&rGroup, sizeof(NNTPGROUP));
  1276. ZeroMemory(&rResp, sizeof(NNTPRESPONSE));
  1277. if (m_uiResponse == IXP_NNTP_GROUP_SELECTED)
  1278. {
  1279. rResp.fMustRelease = TRUE;
  1280. pszGroup = PszDupA(m_pszResponse);
  1281. psz = m_pszResponse;
  1282. if (!ScanNum(&psz, FALSE, &dwResp) ||
  1283. !ScanNum(&psz, FALSE, &rGroup.dwCount) ||
  1284. !ScanNum(&psz, FALSE, &rGroup.dwFirst) ||
  1285. !ScanNum(&psz, FALSE, &rGroup.dwLast) ||
  1286. !ScanWord(psz, pszGroup))
  1287. {
  1288. m_hrResponse = IXP_E_NNTP_RESPONSE_ERROR;
  1289. }
  1290. else
  1291. {
  1292. if (pszGroup)
  1293. {
  1294. rGroup.pszGroup = PszDupA(pszGroup);
  1295. }
  1296. rResp.rGroup = rGroup;
  1297. }
  1298. }
  1299. else if (m_uiResponse == IXP_NNTP_NO_SUCH_NEWSGROUP)
  1300. m_hrResponse = IXP_E_NNTP_GROUP_NOTFOUND;
  1301. else
  1302. m_hrResponse = IXP_E_NNTP_GROUP_FAILED;
  1303. DispatchResponse(m_hrResponse, TRUE, &rResp);
  1304. SafeMemFree(pszGroup);
  1305. return (m_hrResponse);
  1306. }
  1307. HRESULT CNNTPTransport::ProcessNextResponse(void)
  1308. {
  1309. LPSTR psz;
  1310. NNTPNEXT rNext;
  1311. DWORD dwResp;
  1312. NNTPRESPONSE rResp;
  1313. ZeroMemory(&rNext, sizeof(NNTPNEXT));
  1314. ZeroMemory(&rResp, sizeof(NNTPRESPONSE));
  1315. // If success was returned, then parse the response
  1316. if (m_uiResponse == IXP_NNTP_ARTICLE_RETRIEVED)
  1317. {
  1318. rResp.fMustRelease = TRUE;
  1319. // Allocate a buffer for the message id
  1320. rNext.pszMessageId = PszAllocA(lstrlen(m_pszResponse));
  1321. if (NULL != rNext.pszMessageId)
  1322. {
  1323. psz = m_pszResponse;
  1324. if (!ScanNum(&psz, FALSE, &dwResp) ||
  1325. !ScanNum(&psz, FALSE, &rNext.dwArticleNum) ||
  1326. !ScanWord(psz, rNext.pszMessageId))
  1327. {
  1328. m_hrResponse = IXP_E_NNTP_RESPONSE_ERROR;
  1329. }
  1330. else
  1331. {
  1332. m_hrResponse = S_OK;
  1333. // Since this is just a union, a little sleeze and we wouldn't
  1334. // actually need to to this...
  1335. if (m_state == NS_NEXT)
  1336. rResp.rNext = rNext;
  1337. else if (m_state == NS_LAST)
  1338. rResp.rLast = rNext;
  1339. else
  1340. rResp.rStat = rNext;
  1341. }
  1342. }
  1343. else
  1344. {
  1345. m_hrResponse = TrapError(E_OUTOFMEMORY);
  1346. }
  1347. }
  1348. else if ((m_state == NS_NEXT && m_uiResponse == IXP_NNTP_NO_NEXT_ARTICLE) ||
  1349. (m_state == NS_LAST && m_uiResponse == IXP_NNTP_NO_PREV_ARTICLE) ||
  1350. (m_state == NS_STAT && m_uiResponse == IXP_NNTP_NO_SUCH_ARTICLE_NUM))
  1351. {
  1352. m_hrResponse = IXP_E_NNTP_NEXT_FAILED;
  1353. }
  1354. else
  1355. m_hrResponse = S_OK;
  1356. DispatchResponse(m_hrResponse, TRUE, &rResp);
  1357. return (m_hrResponse);
  1358. }
  1359. HRESULT CNNTPTransport::ProcessListData(void)
  1360. {
  1361. HRESULT hr = S_OK;
  1362. LPSTR pszLines = NULL;
  1363. DWORD dwRead, dwLines;
  1364. NNTPLIST rList;
  1365. LPSTR pszT;
  1366. NNTPRESPONSE rResponse;
  1367. ZeroMemory(&rList, sizeof(NNTPLIST));
  1368. ZeroMemory(&rResponse, sizeof(NNTPRESPONSE));
  1369. // Get the remaining data off the socket
  1370. if (SUCCEEDED(hr = m_pSocket->ReadLines(&pszLines, (int *)&dwRead, (int *)&dwLines)))
  1371. {
  1372. // Allocate and array to hold the lines
  1373. if (!MemAlloc((LPVOID*) &rList.rgszLines, dwLines * sizeof(LPSTR)))
  1374. {
  1375. OnError(E_OUTOFMEMORY);
  1376. hr = E_OUTOFMEMORY;
  1377. goto error;
  1378. }
  1379. ZeroMemory(rList.rgszLines, sizeof(LPSTR) * dwLines);
  1380. // Parse the buffer returned from the protocol. We need to find the
  1381. // end of the list.
  1382. pszT = pszLines;
  1383. while (*pszT)
  1384. {
  1385. // Check for the end of list condition
  1386. if ((pszT[0] == '.') && ((pszT[1] == '\r' && pszT[2] == '\n') || (pszT[1] == '\n')))
  1387. break;
  1388. // Save the line
  1389. rList.rgszLines[rList.cLines++] = pszT;
  1390. // Find the LF or CRLF at the end of the line
  1391. while (*pszT && !(pszT[0] == '\n' || (pszT[0] == '\r' && pszT[1] == '\n')))
  1392. pszT++;
  1393. // Strip off the LF or CRLF and add a terminator
  1394. while (*pszT == '\r' || *pszT == '\n')
  1395. *pszT++ = 0;
  1396. }
  1397. // If we parsed more lines off of the buffer than was returned to us,
  1398. // then either we have a parsing bug, or the socket class has a counting
  1399. // bug.
  1400. IxpAssert(rList.cLines <= dwLines);
  1401. // We've readed the end of the list, otherwise there is more data expected
  1402. if (pszT[0] == '.')
  1403. {
  1404. // Double check that this dot is followed by a CRLF
  1405. IxpAssert(pszT[1] == '\r' && pszT[2] == '\n');
  1406. rResponse.fDone = TRUE;
  1407. }
  1408. rResponse.rList = rList;
  1409. rResponse.fMustRelease = TRUE;
  1410. DispatchResponse(S_OK, rResponse.fDone, &rResponse);
  1411. }
  1412. return (hr);
  1413. error:
  1414. SafeMemFree(pszLines);
  1415. return (hr);
  1416. }
  1417. HRESULT CNNTPTransport::ProcessListGroupData(void)
  1418. {
  1419. HRESULT hr = S_OK;
  1420. LPSTR pszLines = NULL;
  1421. LPSTR pszBeginLine = NULL;
  1422. DWORD dwRead, dwLines;
  1423. NNTPLISTGROUP rListGroup;
  1424. LPSTR pszT;
  1425. NNTPRESPONSE rResp;
  1426. ZeroMemory(&rListGroup, sizeof(NNTPLIST));
  1427. ZeroMemory(&rResp, sizeof(NNTPRESPONSE));
  1428. // Get the remaining data off the socket
  1429. if (SUCCEEDED(hr = m_pSocket->ReadLines(&pszLines, (int *)&dwRead, (int *)&dwLines)))
  1430. {
  1431. // Allocate and array to hold the lines
  1432. if (!MemAlloc((LPVOID*) &rListGroup.rgArticles, dwLines * sizeof(DWORD)))
  1433. {
  1434. hr = E_OUTOFMEMORY;
  1435. OnError(E_OUTOFMEMORY);
  1436. goto error;
  1437. }
  1438. // Parse the buffer returned from the protocol. We need to find the
  1439. // end of the list.
  1440. pszT = pszLines;
  1441. rListGroup.cArticles = 0;
  1442. while (*pszT)
  1443. {
  1444. // Check for the end of list condition
  1445. if ((pszT[0] == '.') && ((pszT[1] == '\r' && pszT[2] == '\n') || (pszT[1] == '\n')))
  1446. break;
  1447. // Save the beginning of the line
  1448. pszBeginLine = pszT;
  1449. // Find the LF or CRLF at the end of the line
  1450. while (*pszT && !(pszT[0] == '\n' || (pszT[0] == '\r' && pszT[1] == '\n')))
  1451. pszT++;
  1452. // Strip off the LF or CRLF and add a terminator
  1453. while (*pszT == '\r' || *pszT == '\n')
  1454. *pszT++ = 0;
  1455. // Convert the line to a number and add it to the array
  1456. rListGroup.rgArticles[rListGroup.cArticles] = StrToInt(pszBeginLine);
  1457. if (rListGroup.rgArticles[rListGroup.cArticles])
  1458. rListGroup.cArticles++;
  1459. }
  1460. // If we parsed more lines off of the buffer than was returned to us,
  1461. // then either we have a parsing bug, or the socket class has a counting
  1462. // bug.
  1463. IxpAssert(rListGroup.cArticles <= dwLines);
  1464. // We've readed the end of the list, otherwise there is more data expected
  1465. if (pszT[0] == '.')
  1466. {
  1467. // Double check that this dot is followed by a CRLF
  1468. IxpAssert(pszT[1] == '\r' && pszT[2] == '\n');
  1469. rResp.fDone = TRUE;
  1470. }
  1471. rResp.rListGroup = rListGroup;
  1472. rResp.fMustRelease = TRUE;
  1473. DispatchResponse(S_OK, rResp.fDone, &rResp);
  1474. }
  1475. error:
  1476. SafeMemFree(pszLines);
  1477. return (hr);
  1478. }
  1479. BOOL CharsToNum(LPCSTR psz, int cch, WORD *pw)
  1480. {
  1481. int i;
  1482. WORD w = 0;
  1483. Assert(psz != NULL);
  1484. Assert(pw != NULL);
  1485. for (i = 0; i < cch; i++)
  1486. {
  1487. if (*psz >= '0' && *psz <= '9')
  1488. {
  1489. w *= 10;
  1490. w += *psz - '0';
  1491. psz++;
  1492. }
  1493. else
  1494. {
  1495. return(FALSE);
  1496. }
  1497. }
  1498. *pw = w;
  1499. return(TRUE);
  1500. }
  1501. HRESULT CNNTPTransport::ProcessDateResponse(void)
  1502. {
  1503. HRESULT hr = S_OK;
  1504. SYSTEMTIME st;
  1505. NNTPRESPONSE rResp;
  1506. DWORD dwResp;
  1507. LPSTR psz;
  1508. ZeroMemory(&rResp, sizeof(NNTPRESPONSE));
  1509. // This information is returned in the format YYYYMMDDhhmmss
  1510. if (m_uiResponse == IXP_NNTP_DATE_RESPONSE)
  1511. {
  1512. ZeroMemory(&st, sizeof(SYSTEMTIME));
  1513. psz = StrChr(m_pszResponse, ' ');
  1514. if (psz == NULL ||
  1515. !CharsToNum(++psz, 4, &st.wYear) ||
  1516. !CharsToNum(&psz[4], 2, &st.wMonth) ||
  1517. !CharsToNum(&psz[6], 2, &st.wDay) ||
  1518. !CharsToNum(&psz[8], 2, &st.wHour) ||
  1519. !CharsToNum(&psz[10], 2, &st.wMinute) ||
  1520. !CharsToNum(&psz[12], 2, &st.wSecond))
  1521. {
  1522. m_hrResponse = IXP_E_NNTP_RESPONSE_ERROR;
  1523. }
  1524. else
  1525. {
  1526. rResp.rDate = st;
  1527. m_hrResponse = S_OK;
  1528. }
  1529. }
  1530. else
  1531. m_hrResponse = IXP_E_NNTP_DATE_FAILED;
  1532. DispatchResponse(m_hrResponse, TRUE, &rResp);
  1533. return (hr);
  1534. }
  1535. HRESULT CNNTPTransport::ProcessArticleData(void)
  1536. {
  1537. HRESULT hr;
  1538. DWORD dwRead, dwLines;
  1539. LPSTR psz;
  1540. DWORD cbSubtract;
  1541. NNTPARTICLE rArticle;
  1542. NNTPRESPONSE rResp;
  1543. ZeroMemory(&rArticle, sizeof(NNTPARTICLE));
  1544. ZeroMemory(&rResp, sizeof(NNTPRESPONSE));
  1545. // Bug #25073 - Get the article number from the response string
  1546. DWORD dwT;
  1547. psz = m_pszResponse;
  1548. ScanNum(&psz, FALSE, &dwT);
  1549. ScanNum(&psz, TRUE, &rArticle.dwArticleNum);
  1550. // Read the waiting data off the socket
  1551. hr = m_pSocket->ReadLines(&rArticle.pszLines, (int*) &dwRead, (int*) &dwLines);
  1552. if (hr == IXP_E_INCOMPLETE)
  1553. return (hr);
  1554. // Check forfailure
  1555. if (FAILED(hr))
  1556. {
  1557. DispatchResponse(hr);
  1558. return (hr);
  1559. }
  1560. // See if this is the end of the response
  1561. if (FEndRetrRecvBodyNews(rArticle.pszLines, dwRead, &cbSubtract))
  1562. {
  1563. // Remove the trailing dot from the buffer
  1564. dwRead -= cbSubtract;
  1565. rArticle.pszLines[dwRead] = 0;
  1566. rResp.fDone = TRUE;
  1567. }
  1568. // Unstuff the dots
  1569. UnStuffDotsFromLines(rArticle.pszLines, (int *)&dwRead);
  1570. rArticle.pszLines[dwRead] ='\0';
  1571. // Fill out the response
  1572. rResp.rArticle = rArticle;
  1573. rResp.rArticle.cbLines = dwRead;
  1574. rResp.rArticle.cLines = dwLines;
  1575. rResp.fMustRelease = TRUE;
  1576. DispatchResponse(hr, rResp.fDone, &rResp);
  1577. return hr;
  1578. }
  1579. HRESULT CNNTPTransport::ProcessXoverData(void)
  1580. {
  1581. HRESULT hr;
  1582. LPSTR pszLines = NULL;
  1583. LPSTR pszNextLine = NULL;
  1584. LPSTR pszField = NULL;
  1585. LPSTR pszNextField = NULL;
  1586. int iRead, iLines;
  1587. NNTPHEADERRESP rHdrResp;
  1588. NNTPRESPONSE rResp;
  1589. NNTPHEADER *rgHdr;
  1590. PMEMORYINFO pMemInfo = 0;
  1591. ZeroMemory(&rHdrResp, sizeof(NNTPHEADERRESP));
  1592. ZeroMemory(&rResp, sizeof(NNTPRESPONSE));
  1593. // Read the data that is waiting on the socket
  1594. if (SUCCEEDED(hr = m_pSocket->ReadLines(&pszLines, &iRead, &iLines)))
  1595. {
  1596. // Allocate the MEMORYINFO struct we use to stash the pointers
  1597. if (!MemAlloc((LPVOID*) &pMemInfo, sizeof(MEMORYINFO)))
  1598. {
  1599. OnError(E_OUTOFMEMORY);
  1600. hr = E_OUTOFMEMORY;
  1601. goto error;
  1602. }
  1603. pMemInfo->cPointers = 1;
  1604. pMemInfo->rgPointers[0] = pszLines;
  1605. rHdrResp.dwReserved = (DWORD_PTR) pMemInfo;
  1606. // Allocate the array of headers
  1607. Assert(iLines);
  1608. if (!MemAlloc((LPVOID*) &(rHdrResp.rgHeaders), iLines * sizeof(NNTPHEADER)))
  1609. {
  1610. OnError(E_OUTOFMEMORY);
  1611. hr = E_OUTOFMEMORY;
  1612. goto error;
  1613. }
  1614. rgHdr = rHdrResp.rgHeaders;
  1615. // Loop until we either run out of lines or we find a line that begins
  1616. // with "."
  1617. pszNextLine = pszLines;
  1618. while (*pszNextLine && *pszNextLine != '.')
  1619. {
  1620. pszField = pszNextLine;
  1621. // Look ahead to find the beginning of the next line
  1622. while (*pszNextLine)
  1623. {
  1624. if (*pszNextLine == '\n')
  1625. {
  1626. // NULL out a CR followed by a LF
  1627. if (pszNextLine > pszField && *(pszNextLine - 1) == '\r')
  1628. *(pszNextLine - 1) = 0;
  1629. // NULL out and skip over the LF
  1630. *pszNextLine++ = 0;
  1631. break;
  1632. }
  1633. pszNextLine++;
  1634. }
  1635. // At this point, pszField points to the beginning of this XOVER
  1636. // line, and pszNextLine points to the next.
  1637. // Parse the article number field
  1638. if (pszNextField = GetNextField(pszField))
  1639. {
  1640. rgHdr[rHdrResp.cHeaders].dwArticleNum = StrToInt(pszField);
  1641. pszField = pszNextField;
  1642. }
  1643. else
  1644. goto badrecord;
  1645. // Parse the Subject: field
  1646. if (pszNextField = GetNextField(pszField))
  1647. {
  1648. rgHdr[rHdrResp.cHeaders].pszSubject = pszField;
  1649. pszField = pszNextField;
  1650. }
  1651. else
  1652. goto badrecord;
  1653. // Parse the From: field
  1654. if (pszNextField = GetNextField(pszField))
  1655. {
  1656. rgHdr[rHdrResp.cHeaders].pszFrom = pszField;
  1657. pszField = pszNextField;
  1658. }
  1659. else
  1660. goto badrecord;
  1661. // Parse the Date: field
  1662. if (pszNextField = GetNextField(pszField))
  1663. {
  1664. rgHdr[rHdrResp.cHeaders].pszDate = pszField;
  1665. pszField = pszNextField;
  1666. }
  1667. else
  1668. goto badrecord;
  1669. // Parse the Message-ID field
  1670. if (pszNextField = GetNextField(pszField))
  1671. {
  1672. rgHdr[rHdrResp.cHeaders].pszMessageId = pszField;
  1673. pszField = pszNextField;
  1674. }
  1675. else
  1676. goto badrecord;
  1677. rgHdr[rHdrResp.cHeaders].pszReferences = pszField;
  1678. pszField = GetNextField(pszField);
  1679. // Parse the bytes field (we can live without this)
  1680. if (pszField)
  1681. {
  1682. rgHdr[rHdrResp.cHeaders].dwBytes = StrToInt(pszField);
  1683. pszField = GetNextField(pszField);
  1684. }
  1685. else
  1686. {
  1687. rgHdr[rHdrResp.cHeaders].dwBytes = 0;
  1688. }
  1689. // Parse the article size in lines (we can live without this also)
  1690. if (pszField)
  1691. {
  1692. rgHdr[rHdrResp.cHeaders].dwLines = StrToInt(pszField);
  1693. pszField = GetNextField(pszField);
  1694. }
  1695. else
  1696. {
  1697. rgHdr[rHdrResp.cHeaders].dwLines = 0;
  1698. }
  1699. // NOTE: The XRef: field in the XOver record is an optional field
  1700. // that a server may or may not support. Also, if the message is
  1701. // not crossposted, then the XRef: field won't be present either.
  1702. // Therefore just cause we don't find any XRef: fields doesn't mean
  1703. // it isn't supported.
  1704. // Look for aditional fields that might contain XRef
  1705. rgHdr[rHdrResp.cHeaders].pszXref = 0;
  1706. while (pszField)
  1707. {
  1708. if (!StrCmpNI(pszField, c_szXrefColon, 5))
  1709. {
  1710. // We found at least one case where the xref: was supplied.
  1711. // We now know for sure that this server supports the xref:
  1712. // field in it's XOver records.
  1713. m_fSupportsXRef = TRUE;
  1714. rgHdr[rHdrResp.cHeaders].pszXref = pszField + 6;
  1715. break;
  1716. }
  1717. pszField = GetNextField(pszField);
  1718. }
  1719. rHdrResp.cHeaders++;
  1720. // If we've found a bad record, then we just skip right over it
  1721. // and move on to the next.
  1722. badrecord:
  1723. ;
  1724. }
  1725. // We've reached the end of the list, otherwise there is more data
  1726. // expected.
  1727. rResp.fDone = (*pszNextLine == '.');
  1728. rHdrResp.fSupportsXRef = m_fSupportsXRef;
  1729. // Return what we've retrieved
  1730. rResp.fMustRelease = TRUE;
  1731. rResp.rHeaders = rHdrResp;
  1732. DispatchResponse(hr, rResp.fDone, &rResp);
  1733. return (S_OK);
  1734. }
  1735. return (hr);
  1736. error:
  1737. // Free anything we've allocated
  1738. SafeMemFree(rHdrResp.rgHeaders);
  1739. SafeMemFree(pMemInfo);
  1740. SafeMemFree(pszLines);
  1741. DispatchResponse(hr, TRUE);
  1742. return (hr);
  1743. }
  1744. // Data comes in the form "<article number> <header>"
  1745. HRESULT CNNTPTransport::ProcessXhdrData(void)
  1746. {
  1747. HRESULT hr;
  1748. LPSTR pszLines = NULL;
  1749. LPSTR pszNextLine = NULL;
  1750. LPSTR pszField = NULL;
  1751. LPSTR pszNextField = NULL;
  1752. int iRead, iLines;
  1753. NNTPXHDRRESP rXhdr;
  1754. NNTPRESPONSE rResp;
  1755. NNTPXHDR *rgHdr = 0;
  1756. PMEMORYINFO pMemInfo = 0;
  1757. ZeroMemory(&rXhdr, sizeof(NNTPXHDRRESP));
  1758. ZeroMemory(&rResp, sizeof(NNTPRESPONSE));
  1759. // Read the data that is waiting on the socket
  1760. if (SUCCEEDED(hr = m_pSocket->ReadLines(&pszLines, &iRead, &iLines)))
  1761. {
  1762. // Allocate the MEMORYINFO struct we use to stash the pointers
  1763. if (!MemAlloc((LPVOID*) &pMemInfo, sizeof(MEMORYINFO)))
  1764. {
  1765. OnError(E_OUTOFMEMORY);
  1766. hr = E_OUTOFMEMORY;
  1767. goto error;
  1768. }
  1769. pMemInfo->cPointers = 1;
  1770. pMemInfo->rgPointers[0] = pszLines;
  1771. rXhdr.dwReserved = (DWORD_PTR) pMemInfo;
  1772. // Allocate the array of XHDRs
  1773. Assert(iLines);
  1774. if (!MemAlloc((LPVOID*) &(rXhdr.rgHeaders), iLines * sizeof(NNTPXHDR)))
  1775. {
  1776. // This is _very_ broken. We leave a whole bunch of data on the
  1777. // socket. What should we do?
  1778. OnError(E_OUTOFMEMORY);
  1779. hr = E_OUTOFMEMORY;
  1780. goto error;
  1781. }
  1782. rgHdr = rXhdr.rgHeaders;
  1783. // Loop until we either run out of lines or we find a line that begins
  1784. // with "."
  1785. pszNextLine = pszLines;
  1786. while (*pszNextLine && *pszNextLine != '.')
  1787. {
  1788. pszField = pszNextLine;
  1789. // Scan ahead and find the end of the line
  1790. while (*pszNextLine)
  1791. {
  1792. if (*pszNextLine == '\n')
  1793. {
  1794. // NULL out a CR followed by a LF
  1795. if (pszNextLine > pszField && *(pszNextLine - 1) == '\r')
  1796. *(pszNextLine - 1) = 0;
  1797. // NULL out and skip over the LF
  1798. *pszNextLine++ = 0;
  1799. break;
  1800. }
  1801. pszNextLine++;
  1802. }
  1803. // Parse the article number
  1804. rgHdr[rXhdr.cHeaders].dwArticleNum = StrToInt(pszField);
  1805. // Find the seperating space
  1806. rgHdr[rXhdr.cHeaders].pszHeader = 0;
  1807. while (*pszField && *pszField != ' ')
  1808. pszField++;
  1809. // Make the beginning of the header point to the first character
  1810. // after the space.
  1811. if (*(pszField + 1))
  1812. rgHdr[rXhdr.cHeaders].pszHeader = (pszField + 1);
  1813. if (rgHdr[rXhdr.cHeaders].dwArticleNum && rgHdr[rXhdr.cHeaders].pszHeader)
  1814. rXhdr.cHeaders++;
  1815. }
  1816. // We've reached the end of the list, otherwise there is more data
  1817. // expected.
  1818. rResp.fDone = (*pszNextLine == '.');
  1819. // Return what we've retrieved
  1820. rResp.rXhdr = rXhdr;
  1821. rResp.fMustRelease = TRUE;
  1822. DispatchResponse(hr, rResp.fDone, &rResp);
  1823. return (S_OK);
  1824. }
  1825. error:
  1826. SafeMemFree(rgHdr);
  1827. SafeMemFree(pMemInfo);
  1828. SafeMemFree(pszLines);
  1829. return (hr);
  1830. }
  1831. LPSTR CNNTPTransport::GetNextField(LPSTR pszField)
  1832. {
  1833. while (*pszField && *pszField != '\t')
  1834. pszField++;
  1835. if (*pszField == '\t')
  1836. {
  1837. *pszField++ = 0;
  1838. return pszField;
  1839. }
  1840. return NULL;
  1841. }
  1842. HRESULT CNNTPTransport::CommandAUTHINFO(LPNNTPAUTHINFO pAuthInfo)
  1843. {
  1844. HRESULT hr;
  1845. if (!pAuthInfo)
  1846. return (E_INVALIDARG);
  1847. // Make a copy of this struct so we can use the info during the callback
  1848. // if necessary
  1849. if (pAuthInfo->authtype == AUTHTYPE_USERPASS ||
  1850. pAuthInfo->authtype == AUTHTYPE_SIMPLE)
  1851. {
  1852. if (!MemAlloc((LPVOID*) &m_pAuthInfo, sizeof(NNTPAUTHINFO)))
  1853. {
  1854. OnError(E_OUTOFMEMORY);
  1855. return (E_OUTOFMEMORY);
  1856. }
  1857. ZeroMemory(m_pAuthInfo, sizeof(NNTPAUTHINFO));
  1858. m_pAuthInfo->pszUser = PszDupA(pAuthInfo->pszUser);
  1859. m_pAuthInfo->pszPass = PszDupA(pAuthInfo->pszUser);
  1860. }
  1861. EnterCriticalSection(&m_cs);
  1862. // Issue the command as appropriate
  1863. switch (pAuthInfo->authtype)
  1864. {
  1865. case AUTHTYPE_USERPASS:
  1866. hr = HrSendCommand((LPSTR) NNTP_AUTHINFOUSER, pAuthInfo->pszUser);
  1867. if (SUCCEEDED(hr))
  1868. m_substate = NS_AUTHINFO_USER_RESP;
  1869. break;
  1870. case AUTHTYPE_SIMPLE:
  1871. hr = HrSendCommand((LPSTR) NNTP_AUTHINFOSIMPLE_CRLF, NULL);
  1872. if (SUCCEEDED(hr))
  1873. m_substate = NS_AUTHINFO_SIMPLE_RESP;
  1874. break;
  1875. case AUTHTYPE_SASL:
  1876. // If we haven't enumerated the security packages yet, do so
  1877. if (m_cSecPkg == -1)
  1878. {
  1879. hr = HrSendCommand((LPSTR) NNTP_GENERICTEST_CRLF, NULL, FALSE);
  1880. if (SUCCEEDED(hr))
  1881. m_substate = NS_GENERIC_TEST;
  1882. }
  1883. else
  1884. {
  1885. // We've reconnected, try the next security package
  1886. TryNextSecPkg();
  1887. }
  1888. break;
  1889. }
  1890. if (SUCCEEDED(hr))
  1891. m_state = NS_AUTHINFO;
  1892. LeaveCriticalSection(&m_cs);
  1893. return (hr);
  1894. }
  1895. HRESULT CNNTPTransport::CommandGROUP(LPSTR pszGroup)
  1896. {
  1897. HRESULT hr;
  1898. if (!pszGroup)
  1899. return (E_INVALIDARG);
  1900. EnterCriticalSection(&m_cs);
  1901. hr = HrSendCommand((LPSTR) NNTP_GROUP, pszGroup);
  1902. if (SUCCEEDED(hr))
  1903. m_state = NS_GROUP;
  1904. LeaveCriticalSection(&m_cs);
  1905. return (hr);
  1906. }
  1907. HRESULT CNNTPTransport::CommandLAST(void)
  1908. {
  1909. HRESULT hr;
  1910. EnterCriticalSection(&m_cs);
  1911. hr = HrSendCommand((LPSTR) NNTP_LAST_CRLF, NULL);
  1912. if (SUCCEEDED(hr))
  1913. m_state = NS_LAST;
  1914. LeaveCriticalSection(&m_cs);
  1915. return (hr);
  1916. }
  1917. HRESULT CNNTPTransport::CommandNEXT(void)
  1918. {
  1919. HRESULT hr;
  1920. EnterCriticalSection(&m_cs);
  1921. hr = HrSendCommand((LPSTR) NNTP_NEXT_CRLF, NULL);
  1922. if (SUCCEEDED(hr))
  1923. m_state = NS_NEXT;
  1924. LeaveCriticalSection(&m_cs);
  1925. return (hr);
  1926. }
  1927. HRESULT CNNTPTransport::CommandSTAT(LPARTICLEID pArticleId)
  1928. {
  1929. HRESULT hr;
  1930. char szTemp[20];
  1931. EnterCriticalSection(&m_cs);
  1932. // Check to see if the optional article number/id was provided
  1933. if (pArticleId)
  1934. {
  1935. // If we were given a message id, then use that
  1936. if (pArticleId->idType == AID_MSGID)
  1937. hr = HrSendCommand((LPSTR) NNTP_STAT, pArticleId->pszMessageId);
  1938. else
  1939. {
  1940. // Convert the article number to a string and send the command
  1941. wnsprintf(szTemp, ARRAYSIZE(szTemp), "%d", pArticleId->dwArticleNum);
  1942. hr = HrSendCommand((LPSTR) NNTP_STAT, szTemp);
  1943. }
  1944. }
  1945. else
  1946. {
  1947. // No number or id, so just send the command
  1948. hr = HrSendCommand((LPSTR) NNTP_STAT, (LPSTR) c_szCRLF);
  1949. }
  1950. if (SUCCEEDED(hr))
  1951. {
  1952. m_state = NS_STAT;
  1953. }
  1954. LeaveCriticalSection(&m_cs);
  1955. return (hr);
  1956. }
  1957. HRESULT CNNTPTransport::CommandARTICLE(LPARTICLEID pArticleId)
  1958. {
  1959. HRESULT hr;
  1960. char szTemp[20];
  1961. EnterCriticalSection(&m_cs);
  1962. // Check to see if the optional article number/id was provided
  1963. if (pArticleId)
  1964. {
  1965. // Send the command appropriate to what type of article id was given
  1966. if (pArticleId->idType == AID_MSGID)
  1967. hr = HrSendCommand((LPSTR) NNTP_ARTICLE, pArticleId->pszMessageId);
  1968. else
  1969. {
  1970. // convert the article number to a string and send the command
  1971. wnsprintf(szTemp, ARRAYSIZE(szTemp), "%d", pArticleId->dwArticleNum);
  1972. hr = HrSendCommand((LPSTR) NNTP_ARTICLE, szTemp);
  1973. }
  1974. }
  1975. else
  1976. {
  1977. hr = HrSendCommand((LPSTR) NNTP_ARTICLE, (LPSTR) c_szCRLF);
  1978. }
  1979. if (SUCCEEDED(hr))
  1980. {
  1981. m_state = NS_ARTICLE;
  1982. m_substate = NS_RESP;
  1983. }
  1984. LeaveCriticalSection(&m_cs);
  1985. return (hr);
  1986. }
  1987. HRESULT CNNTPTransport::CommandHEAD(LPARTICLEID pArticleId)
  1988. {
  1989. HRESULT hr;
  1990. char szTemp[20];
  1991. EnterCriticalSection(&m_cs);
  1992. // Check to see if the optional article number/id was provided
  1993. if (pArticleId)
  1994. {
  1995. // Send the command appropriate to what type of article id was given
  1996. if (pArticleId->idType == AID_MSGID)
  1997. hr = HrSendCommand((LPSTR) NNTP_HEAD, pArticleId->pszMessageId);
  1998. else
  1999. {
  2000. // convert the article number to a string and send the command
  2001. wnsprintf(szTemp, ARRAYSIZE(szTemp), "%d", pArticleId->dwArticleNum);
  2002. hr = HrSendCommand((LPSTR) NNTP_HEAD, szTemp);
  2003. }
  2004. }
  2005. else
  2006. {
  2007. hr = HrSendCommand((LPSTR) NNTP_HEAD, (LPSTR) c_szCRLF);
  2008. }
  2009. if (SUCCEEDED(hr))
  2010. {
  2011. m_state = NS_HEAD;
  2012. m_substate = NS_RESP;
  2013. }
  2014. LeaveCriticalSection(&m_cs);
  2015. return (hr);
  2016. }
  2017. HRESULT CNNTPTransport::CommandBODY(LPARTICLEID pArticleId)
  2018. {
  2019. HRESULT hr;
  2020. char szTemp[20];
  2021. EnterCriticalSection(&m_cs);
  2022. // Check to see if the optional article number/id was provided
  2023. if (pArticleId)
  2024. {
  2025. // Send the command appropriate to what type of article id was given
  2026. if (pArticleId->idType == AID_MSGID)
  2027. hr = HrSendCommand((LPSTR) NNTP_BODY, pArticleId->pszMessageId);
  2028. else
  2029. {
  2030. // convert the article number to a string and send the command
  2031. wnsprintf(szTemp, ARRAYSIZE(szTemp), "%d", pArticleId->dwArticleNum);
  2032. hr = HrSendCommand((LPSTR) NNTP_BODY, szTemp);
  2033. }
  2034. }
  2035. else
  2036. {
  2037. hr = HrSendCommand((LPSTR) NNTP_BODY, (LPSTR) c_szCRLF);
  2038. }
  2039. if (SUCCEEDED(hr))
  2040. {
  2041. m_state = NS_BODY;
  2042. m_substate = NS_RESP;
  2043. }
  2044. LeaveCriticalSection(&m_cs);
  2045. return (hr);
  2046. }
  2047. HRESULT CNNTPTransport::CommandPOST(LPNNTPMESSAGE pMessage)
  2048. {
  2049. HRESULT hr;
  2050. if (!pMessage || (pMessage && !pMessage->pstmMsg))
  2051. return (E_INVALIDARG);
  2052. EnterCriticalSection(&m_cs);
  2053. // Make a copy of the message struct so we have it when we get
  2054. // an response from the server that it's OK to post
  2055. #pragma prefast(suppress:11, "noise")
  2056. m_rMessage.cbSize = pMessage->cbSize;
  2057. SafeRelease(m_rMessage.pstmMsg);
  2058. #pragma prefast(suppress:11, "noise")
  2059. m_rMessage.pstmMsg = pMessage->pstmMsg;
  2060. m_rMessage.pstmMsg->AddRef();
  2061. hr = HrSendCommand((LPSTR) NNTP_POST_CRLF, NULL);
  2062. if (SUCCEEDED(hr))
  2063. {
  2064. m_state = NS_POST;
  2065. m_substate = NS_RESP;
  2066. }
  2067. LeaveCriticalSection(&m_cs);
  2068. return (hr);
  2069. }
  2070. HRESULT CNNTPTransport::CommandLIST(LPSTR pszArgs)
  2071. {
  2072. HRESULT hr;
  2073. EnterCriticalSection(&m_cs);
  2074. if (pszArgs)
  2075. hr = HrSendCommand((LPSTR) NNTP_LIST, pszArgs);
  2076. else
  2077. hr = HrSendCommand((LPSTR) NNTP_LIST, (LPSTR) c_szCRLF);
  2078. if (SUCCEEDED(hr))
  2079. {
  2080. m_state = NS_LIST;
  2081. m_substate = NS_RESP;
  2082. }
  2083. LeaveCriticalSection(&m_cs);
  2084. return (hr);
  2085. }
  2086. HRESULT CNNTPTransport::CommandLISTGROUP(LPSTR pszGroup)
  2087. {
  2088. HRESULT hr;
  2089. EnterCriticalSection(&m_cs);
  2090. if (pszGroup)
  2091. hr = HrSendCommand((LPSTR) NNTP_LISTGROUP, pszGroup);
  2092. else
  2093. hr = HrSendCommand((LPSTR) NNTP_LISTGROUP, (LPSTR) c_szCRLF);
  2094. if (SUCCEEDED(hr))
  2095. {
  2096. m_state = NS_LISTGROUP;
  2097. m_substate = NS_RESP;
  2098. }
  2099. LeaveCriticalSection(&m_cs);
  2100. return (hr);
  2101. }
  2102. HRESULT CNNTPTransport::CommandNEWGROUPS(SYSTEMTIME *pstLast, LPSTR pszDist)
  2103. {
  2104. HRESULT hr = S_OK;
  2105. LPSTR pszCmd = NULL;
  2106. DWORD cchCmd = 18;
  2107. // Make sure a SYSTEMTIME struct is provided
  2108. if (!pstLast)
  2109. return (E_INVALIDARG);
  2110. // Allocate enough room for the command string "NEWGROUPS YYMMDD HHMMSS <pszDist>"
  2111. if (pszDist)
  2112. cchCmd += lstrlen(pszDist);
  2113. if (!MemAlloc((LPVOID*) &pszCmd, cchCmd))
  2114. {
  2115. OnError(E_OUTOFMEMORY);
  2116. return (E_OUTOFMEMORY);
  2117. }
  2118. // Put the command arguments together
  2119. wnsprintf(pszCmd, cchCmd, "%02d%02d%02d %02d%02d%02d ", pstLast->wYear - (100 * (pstLast->wYear / 100)),
  2120. pstLast->wMonth, pstLast->wDay, pstLast->wHour, pstLast->wMinute,
  2121. pstLast->wSecond);
  2122. if (pszDist)
  2123. StrCatBuff(pszCmd, pszDist, cchCmd);
  2124. // Send the command
  2125. EnterCriticalSection(&m_cs);
  2126. hr = HrSendCommand((LPSTR) NNTP_NEWGROUPS, pszCmd);
  2127. if (SUCCEEDED(hr))
  2128. {
  2129. m_state = NS_NEWGROUPS;
  2130. m_substate = NS_RESP;
  2131. }
  2132. LeaveCriticalSection(&m_cs);
  2133. SafeMemFree(pszCmd);
  2134. return (hr);
  2135. }
  2136. HRESULT CNNTPTransport::CommandDATE(void)
  2137. {
  2138. HRESULT hr;
  2139. EnterCriticalSection(&m_cs);
  2140. hr = HrSendCommand((LPSTR) NNTP_DATE_CRLF, NULL);
  2141. if (SUCCEEDED(hr))
  2142. m_state = NS_DATE;
  2143. LeaveCriticalSection(&m_cs);
  2144. return (hr);
  2145. }
  2146. HRESULT CNNTPTransport::CommandMODE(LPSTR pszMode)
  2147. {
  2148. HRESULT hr;
  2149. // Make sure the caller provided a mode command to send
  2150. if (!pszMode || (pszMode && !*pszMode))
  2151. return (E_INVALIDARG);
  2152. EnterCriticalSection(&m_cs);
  2153. hr = HrSendCommand((LPSTR) NNTP_MODE, pszMode);
  2154. if (SUCCEEDED(hr))
  2155. m_state = NS_MODE;
  2156. LeaveCriticalSection(&m_cs);
  2157. return (hr);
  2158. }
  2159. HRESULT CNNTPTransport::CommandXHDR(LPSTR pszHeader, LPRANGE pRange, LPSTR pszMessageId)
  2160. {
  2161. HRESULT hr = S_OK;
  2162. LPSTR pszArgs = 0;
  2163. DWORD cc = 0;
  2164. // You can't specify BOTH a range and a message id
  2165. if (pRange && pszMessageId)
  2166. return (E_INVALIDARG);
  2167. if (!pszHeader)
  2168. return (E_INVALIDARG);
  2169. // Make sure the range information is valid
  2170. if (pRange)
  2171. {
  2172. if ((pRange->idType != RT_SINGLE && pRange->idType != RT_RANGE) ||
  2173. pRange->dwFirst == 0 ||
  2174. (pRange->idType == RT_RANGE && pRange->dwLast < pRange->dwFirst))
  2175. return (E_INVALIDARG);
  2176. }
  2177. // Allocate a string for the arguments
  2178. cc = 32 + lstrlen(pszHeader) + (pszMessageId ? lstrlen(pszMessageId) : 0);
  2179. if (!MemAlloc((LPVOID*) &pszArgs, cc))
  2180. {
  2181. OnError(E_OUTOFMEMORY);
  2182. return (E_OUTOFMEMORY);
  2183. }
  2184. EnterCriticalSection(&m_cs);
  2185. // Handle the message-id case first
  2186. if (pszMessageId)
  2187. {
  2188. wnsprintf(pszArgs, cc, "%s %s", pszHeader, pszMessageId);
  2189. hr = HrSendCommand((LPSTR) NNTP_XHDR, pszArgs);
  2190. }
  2191. else if (pRange)
  2192. {
  2193. // Range case
  2194. if (pRange->idType == RT_SINGLE)
  2195. {
  2196. wnsprintf(pszArgs, cc, "%s %ld", pszHeader, pRange->dwFirst);
  2197. hr = HrSendCommand((LPSTR) NNTP_XHDR, pszArgs);
  2198. }
  2199. else if (pRange->idType == RT_RANGE)
  2200. {
  2201. wnsprintf(pszArgs, cc, "%s %ld-%ld", pszHeader, pRange->dwFirst, pRange->dwLast);
  2202. hr = HrSendCommand((LPSTR) NNTP_XHDR, pszArgs);
  2203. }
  2204. }
  2205. else
  2206. {
  2207. // Current article case
  2208. hr = HrSendCommand((LPSTR) NNTP_XHDR, pszHeader);
  2209. }
  2210. // If we succeeded to send the command to the server, then update our state
  2211. // to receive the response from the XHDR command
  2212. if (SUCCEEDED(hr))
  2213. {
  2214. m_state = NS_XHDR;
  2215. m_substate = NS_RESP;
  2216. }
  2217. LeaveCriticalSection(&m_cs);
  2218. SafeMemFree(pszArgs);
  2219. return (hr);
  2220. }
  2221. HRESULT CNNTPTransport::CommandQUIT(void)
  2222. {
  2223. HRESULT hr = IXP_E_NOT_CONNECTED;
  2224. EnterCriticalSection(&m_cs);
  2225. // Make sure we're actually connected to the server
  2226. if (m_state != NS_DISCONNECTED && m_state != NS_CONNECT || (m_state == NS_CONNECT && m_substate != NS_RECONNECTING))
  2227. {
  2228. // Send the QUIT command to the server
  2229. hr = HrSendCommand((LPSTR) NNTP_QUIT_CRLF, NULL);
  2230. if (SUCCEEDED(hr))
  2231. m_state = NS_QUIT;
  2232. }
  2233. LeaveCriticalSection(&m_cs);
  2234. return (hr);
  2235. }
  2236. HRESULT CNNTPTransport::GetHeaders(LPRANGE pRange)
  2237. {
  2238. HRESULT hr;
  2239. char szRange[32];
  2240. // Make sure the range information is valid
  2241. if (!pRange)
  2242. return (E_INVALIDARG);
  2243. if ((pRange->idType != RT_SINGLE && pRange->idType != RT_RANGE) ||
  2244. pRange->dwFirst == 0 ||
  2245. (pRange->idType == RT_RANGE && pRange->dwLast < pRange->dwFirst))
  2246. return (E_INVALIDARG);
  2247. if (pRange->idType == RT_SINGLE)
  2248. pRange->dwLast = pRange->dwFirst;
  2249. // In case XOVER isn't supported on this server, we'll store this range so
  2250. // we can try XHDR instead.
  2251. m_rRange = *pRange;
  2252. // Check to see if we know that XOVER will fail
  2253. if (m_fNoXover)
  2254. {
  2255. return (BuildHeadersFromXhdr(TRUE));
  2256. }
  2257. EnterCriticalSection(&m_cs);
  2258. // If dwLast == 0, then the person is requesting a single record, otherwise
  2259. // the person is requesting a range. Build the commands appropriately.
  2260. if (RT_RANGE == pRange->idType)
  2261. wnsprintf(szRange, ARRAYSIZE(szRange), "%s %lu-%lu\r\n", NNTP_XOVER, pRange->dwFirst, pRange->dwLast);
  2262. else
  2263. wnsprintf(szRange, ARRAYSIZE(szRange), "%s %lu\r\n", NNTP_XOVER, pRange->dwFirst);
  2264. hr = HrSendCommand(szRange, NULL);
  2265. if (SUCCEEDED(hr))
  2266. {
  2267. m_state = NS_HEADERS;
  2268. m_substate = NS_RESP;
  2269. m_gethdr = GETHDR_XOVER;
  2270. }
  2271. LeaveCriticalSection(&m_cs);
  2272. return (hr);
  2273. }
  2274. HRESULT CNNTPTransport::ReleaseResponse(LPNNTPRESPONSE pResp)
  2275. {
  2276. HRESULT hr = S_OK;
  2277. DWORD i;
  2278. // First double check to see if this even needs to be released
  2279. if (FALSE == pResp->fMustRelease)
  2280. return (S_FALSE);
  2281. switch (pResp->state)
  2282. {
  2283. case NS_GROUP:
  2284. SafeMemFree(pResp->rGroup.pszGroup);
  2285. break;
  2286. case NS_LAST:
  2287. SafeMemFree(pResp->rLast.pszMessageId);
  2288. break;
  2289. case NS_NEXT:
  2290. SafeMemFree(pResp->rNext.pszMessageId);
  2291. break;
  2292. case NS_STAT:
  2293. SafeMemFree(pResp->rStat.pszMessageId);
  2294. break;
  2295. case NS_ARTICLE:
  2296. SafeMemFree(pResp->rArticle.pszMessageId);
  2297. SafeMemFree(pResp->rArticle.pszLines);
  2298. break;
  2299. case NS_HEAD:
  2300. SafeMemFree(pResp->rHead.pszMessageId);
  2301. SafeMemFree(pResp->rHead.pszLines);
  2302. break;
  2303. case NS_BODY:
  2304. SafeMemFree(pResp->rBody.pszMessageId);
  2305. SafeMemFree(pResp->rBody.pszLines);
  2306. break;
  2307. case NS_NEWGROUPS:
  2308. // Since the response here is just one buffer, then we can just
  2309. // free the first line and all the others will be freed as well.
  2310. if (pResp->rNewgroups.rgszLines)
  2311. {
  2312. SafeMemFree(pResp->rNewgroups.rgszLines[0]);
  2313. MemFree(pResp->rNewgroups.rgszLines);
  2314. }
  2315. break;
  2316. case NS_LIST:
  2317. // Since the response here is just one buffer, then we can just
  2318. // free the first line and all the others will be freed as well.
  2319. if (pResp->rList.rgszLines)
  2320. {
  2321. MemFree(pResp->rList.rgszLines[0]);
  2322. MemFree(pResp->rList.rgszLines);
  2323. }
  2324. break;
  2325. case NS_LISTGROUP:
  2326. SafeMemFree(pResp->rListGroup.rgArticles);
  2327. break;
  2328. case NS_HEADERS:
  2329. {
  2330. // This frees the memory that contains all of the
  2331. PMEMORYINFO pMemInfo = (PMEMORYINFO) pResp->rHeaders.dwReserved;
  2332. for (UINT i = 0; i < pMemInfo->cPointers; i++)
  2333. SafeMemFree(pMemInfo->rgPointers[i]);
  2334. SafeMemFree(pMemInfo);
  2335. // This frees the array that pointed to the parsed xhdr responses
  2336. SafeMemFree(pResp->rHeaders.rgHeaders);
  2337. break;
  2338. }
  2339. case NS_XHDR:
  2340. {
  2341. // This frees the memory that contains all of the
  2342. PMEMORYINFO pMemInfo = (PMEMORYINFO) pResp->rXhdr.dwReserved;
  2343. SafeMemFree(pMemInfo->rgPointers[0]);
  2344. SafeMemFree(pMemInfo);
  2345. // This frees the array that pointed to the parsed xhdr responses
  2346. SafeMemFree(pResp->rXhdr.rgHeaders);
  2347. break;
  2348. }
  2349. default:
  2350. // If we get here that means one of two things:
  2351. // (1) the user messed with pResp->fMustRelease flag and is an idiot
  2352. // (2) Somewhere in the transport we set fMustRelease when we didn't
  2353. // actually return data that needs to be freed. This is bad and
  2354. // should be tracked.
  2355. IxpAssert(FALSE);
  2356. }
  2357. return (hr);
  2358. }
  2359. HRESULT CNNTPTransport::BuildHeadersFromXhdr(BOOL fFirst)
  2360. {
  2361. HRESULT hr = S_OK;
  2362. DWORD cHeaders;
  2363. BOOL fDone = FALSE;
  2364. if (fFirst)
  2365. {
  2366. // Set the header retrieval type
  2367. m_gethdr = GETHDR_XHDR;
  2368. m_fNoXover = TRUE;
  2369. m_cHeaders = 0;
  2370. // Get the first range of headers to retrieve
  2371. m_rRangeCur.dwFirst = m_rRange.dwFirst;
  2372. m_rRangeCur.dwLast = min(m_rRange.dwLast, m_rRangeCur.dwFirst + NUM_HEADERS);
  2373. cHeaders = m_rRangeCur.dwLast - m_rRangeCur.dwFirst + 1;
  2374. // Allocate an array for the headers
  2375. Assert(m_rgHeaders == 0);
  2376. if (!MemAlloc((LPVOID*) &m_rgHeaders, cHeaders * sizeof(NNTPHEADER)))
  2377. {
  2378. SafeMemFree(m_pMemInfo);
  2379. OnError(E_OUTOFMEMORY);
  2380. DispatchResponse(E_OUTOFMEMORY);
  2381. return (E_OUTOFMEMORY);
  2382. }
  2383. ZeroMemory(m_rgHeaders, cHeaders * sizeof(NNTPHEADER));
  2384. // Set the state correctly
  2385. m_hdrtype = HDR_SUBJECT;
  2386. // Issue first request
  2387. hr = SendNextXhdrCommand();
  2388. }
  2389. else
  2390. {
  2391. Assert(m_substate == NS_DATA);
  2392. // Parse the data and add it to our array
  2393. hr = ProcessNextXhdrResponse(&fDone);
  2394. // fDone will be TRUE when we've received all the data from the
  2395. // preceeding request.
  2396. if (fDone)
  2397. {
  2398. // If there are still headers left to retrieve, then advance the
  2399. // header type state and issue the next command.
  2400. if (m_hdrtype < HDR_XREF)
  2401. {
  2402. m_hdrtype++;
  2403. // issue command
  2404. hr = SendNextXhdrCommand();
  2405. }
  2406. else
  2407. {
  2408. // All done with this batch. Send the response to the caller.
  2409. NNTPRESPONSE rResp;
  2410. ZeroMemory(&rResp, sizeof(NNTPRESPONSE));
  2411. rResp.rHeaders.cHeaders = m_cHeaders;
  2412. rResp.rHeaders.rgHeaders = m_rgHeaders;
  2413. rResp.rHeaders.fSupportsXRef = TRUE;
  2414. rResp.rHeaders.dwReserved = (DWORD_PTR) m_pMemInfo;
  2415. rResp.fMustRelease = TRUE;
  2416. // It's the caller's responsibility to free this now
  2417. m_rgHeaders = NULL;
  2418. m_cHeaders = 0;
  2419. m_pMemInfo = 0;
  2420. // If these are equal, then we've retrieved all of the headers
  2421. // that were requested
  2422. if (m_rRange.dwLast == m_rRangeCur.dwLast)
  2423. {
  2424. rResp.fDone = TRUE;
  2425. DispatchResponse(S_OK, TRUE, &rResp);
  2426. }
  2427. else
  2428. {
  2429. rResp.fDone = FALSE;
  2430. DispatchResponse(S_OK, FALSE, &rResp);
  2431. // There are headers we haven't retrieved yet. Go ahead
  2432. // and issue the next group of xhdrs.
  2433. m_rRange.dwFirst = m_rRangeCur.dwLast + 1;
  2434. Assert(m_rRange.dwFirst <= m_rRange.dwLast);
  2435. BuildHeadersFromXhdr(TRUE);
  2436. }
  2437. }
  2438. }
  2439. }
  2440. return (hr);
  2441. }
  2442. HRESULT CNNTPTransport::SendNextXhdrCommand(void)
  2443. {
  2444. char szTemp[256];
  2445. HRESULT hr;
  2446. LPCSTR c_rgHdr[HDR_MAX] = { NNTP_HDR_SUBJECT,
  2447. NNTP_HDR_FROM,
  2448. NNTP_HDR_DATE,
  2449. NNTP_HDR_MESSAGEID,
  2450. NNTP_HDR_REFERENCES,
  2451. NNTP_HDR_LINES,
  2452. NNTP_HDR_XREF };
  2453. // Build the command string to send to the server
  2454. wnsprintf(szTemp, ARRAYSIZE(szTemp), "%s %s %ld-%ld\r\n", NNTP_XHDR, c_rgHdr[m_hdrtype],
  2455. m_rRangeCur.dwFirst, m_rRangeCur.dwLast);
  2456. EnterCriticalSection(&m_cs);
  2457. // Send the command to the server
  2458. hr = HrSendCommand(szTemp, NULL, FALSE);
  2459. if (SUCCEEDED(hr))
  2460. {
  2461. m_state = NS_HEADERS;
  2462. m_substate = NS_RESP;
  2463. m_iHeader = 0;
  2464. }
  2465. LeaveCriticalSection(&m_cs);
  2466. return (hr);
  2467. }
  2468. HRESULT CNNTPTransport::ProcessNextXhdrResponse(BOOL* pfDone)
  2469. {
  2470. HRESULT hr;
  2471. LPSTR pszLines = NULL;
  2472. LPSTR pszNextLine = NULL;
  2473. LPSTR pszField = NULL;
  2474. LPSTR pszNextField = NULL;
  2475. int iRead, iLines;
  2476. DWORD dwTemp;
  2477. // Read the data that is waiting on the socket
  2478. if (SUCCEEDED(hr = m_pSocket->ReadLines(&pszLines, &iRead, &iLines)))
  2479. {
  2480. // Realloc our array of pointers to free and add this pszLines to the end
  2481. if (m_pMemInfo)
  2482. {
  2483. if (MemRealloc((LPVOID*) &m_pMemInfo, sizeof(MEMORYINFO)
  2484. + (((m_pMemInfo ? m_pMemInfo->cPointers : 0) + 1) * sizeof(LPVOID))))
  2485. {
  2486. m_pMemInfo->rgPointers[m_pMemInfo->cPointers] = (LPVOID) pszLines;
  2487. m_pMemInfo->cPointers++;
  2488. }
  2489. }
  2490. else
  2491. {
  2492. if (MemAlloc((LPVOID*) &m_pMemInfo, sizeof(MEMORYINFO)))
  2493. {
  2494. m_pMemInfo->rgPointers[0] = pszLines;
  2495. m_pMemInfo->cPointers = 1;
  2496. }
  2497. }
  2498. // Loop until we either run out of lines or we find a line that begins
  2499. // with "."
  2500. pszNextLine = pszLines;
  2501. while (*pszNextLine && *pszNextLine != '.')
  2502. {
  2503. pszField = pszNextLine;
  2504. // Scan ahead and find the end of the line
  2505. while (*pszNextLine)
  2506. {
  2507. if (*pszNextLine == '\n')
  2508. {
  2509. // NULL out a CR followed by a LF
  2510. if (pszNextLine > pszField && *(pszNextLine - 1) == '\r')
  2511. *(pszNextLine - 1) = 0;
  2512. // NULL out and skip over the LF
  2513. *pszNextLine++ = 0;
  2514. break;
  2515. }
  2516. pszNextLine++;
  2517. }
  2518. // Parse the article number
  2519. if (m_hdrtype == HDR_SUBJECT)
  2520. {
  2521. m_rgHeaders[m_iHeader].dwArticleNum = StrToInt(pszField);
  2522. m_cHeaders++;
  2523. }
  2524. else
  2525. {
  2526. // Make sure this field matches the header that's next in the array
  2527. if (m_rgHeaders[m_iHeader].dwArticleNum != (DWORD) StrToInt(pszField))
  2528. {
  2529. dwTemp = m_iHeader;
  2530. // If the number is less, then we can loop until we find it
  2531. while (m_iHeader < (m_rRangeCur.dwLast - m_rRangeCur.dwFirst) &&
  2532. m_rgHeaders[m_iHeader].dwArticleNum < (DWORD) StrToInt(pszField))
  2533. {
  2534. m_iHeader++;
  2535. }
  2536. // We never found a matching header, so we should consider this record
  2537. // bogus.
  2538. if (m_iHeader >= (m_rRangeCur.dwLast - m_rRangeCur.dwFirst + 1))
  2539. {
  2540. IxpAssert(0);
  2541. m_iHeader = dwTemp;
  2542. goto BadRecord;
  2543. }
  2544. }
  2545. }
  2546. // Find the seperating space
  2547. while (*pszField && *pszField != ' ')
  2548. pszField++;
  2549. // Advance past the space
  2550. if (*pszField)
  2551. pszField++;
  2552. // Parse the actual data field into our header array. Make
  2553. // the beginning of the header point to the first character
  2554. // after the space.
  2555. switch (m_hdrtype)
  2556. {
  2557. case HDR_SUBJECT:
  2558. m_rgHeaders[m_iHeader].pszSubject = pszField;
  2559. break;
  2560. case HDR_FROM:
  2561. m_rgHeaders[m_iHeader].pszFrom = pszField;
  2562. break;
  2563. case HDR_DATE:
  2564. m_rgHeaders[m_iHeader].pszDate = pszField;
  2565. break;
  2566. case HDR_MSGID:
  2567. m_rgHeaders[m_iHeader].pszMessageId = pszField;
  2568. break;
  2569. case HDR_REFERENCES:
  2570. m_rgHeaders[m_iHeader].pszReferences = pszField;
  2571. break;
  2572. case HDR_LINES:
  2573. m_rgHeaders[m_iHeader].dwLines = StrToInt(pszField);
  2574. break;
  2575. case HDR_XREF:
  2576. m_rgHeaders[m_iHeader].pszXref = pszField;
  2577. break;
  2578. default:
  2579. // How the heck do we get here?
  2580. IxpAssert(0);
  2581. }
  2582. m_iHeader++;
  2583. BadRecord:
  2584. ;
  2585. }
  2586. // We've reached the end of the list, otherwise there is more data
  2587. // expected.
  2588. *pfDone = (*pszNextLine == '.');
  2589. return (S_OK);
  2590. }
  2591. return (hr);
  2592. }
  2593. HRESULT CNNTPTransport::HrPostMessage(void)
  2594. {
  2595. HRESULT hr;
  2596. int cbSent = 0;
  2597. EnterCriticalSection(&m_cs);
  2598. hr = m_pSocket->SendStream(m_rMessage.pstmMsg, &cbSent, TRUE);
  2599. SafeRelease(m_rMessage.pstmMsg);
  2600. LeaveCriticalSection(&m_cs);
  2601. return (hr);
  2602. }
  2603. //***************************************************************************
  2604. // Function: SetWindow
  2605. //
  2606. // Purpose:
  2607. // This function creates the current window handle for async winsock process.
  2608. //
  2609. // Returns:
  2610. // HRESULT indicating success or failure.
  2611. //***************************************************************************
  2612. STDMETHODIMP CNNTPTransport::SetWindow(void)
  2613. {
  2614. HRESULT hr;
  2615. Assert(NULL != m_pSocket);
  2616. if(m_pSocket)
  2617. hr= m_pSocket->SetWindow();
  2618. else
  2619. hr= E_UNEXPECTED;
  2620. return hr;
  2621. }
  2622. //***************************************************************************
  2623. // Function: ResetWindow
  2624. //
  2625. // Purpose:
  2626. // This function closes the current window handle for async winsock process.
  2627. //
  2628. // Returns:
  2629. // HRESULT indicating success or failure.
  2630. //***************************************************************************
  2631. STDMETHODIMP CNNTPTransport::ResetWindow(void)
  2632. {
  2633. HRESULT hr;
  2634. Assert(NULL != m_pSocket);
  2635. if(m_pSocket)
  2636. hr= m_pSocket->ResetWindow();
  2637. else
  2638. hr= E_UNEXPECTED;
  2639. return hr;
  2640. }