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.

1082 lines
30 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. sockets.cxx
  5. Abstract:
  6. Contains functions to interface between gopher APIs and Winsock
  7. Contents:
  8. GopherConnect
  9. GopherDisconnect
  10. GopherSendRequest
  11. GopherReceiveResponse
  12. Author:
  13. Richard L Firth (rfirth) 11-Oct-1994
  14. Environment:
  15. Win32(s) user-mode DLL
  16. Revision History:
  17. 11-Oct-1994 rfirth
  18. Created
  19. --*/
  20. #include <wininetp.h>
  21. #include "gfrapih.h"
  22. //
  23. // manifests
  24. //
  25. #define DEFAULT_RESPONSE_BUFFER_LENGTH (4 K)
  26. //
  27. // functions
  28. //
  29. DWORD
  30. GopherConnect(
  31. IN LPVIEW_INFO ViewInfo
  32. )
  33. /*++
  34. Routine Description:
  35. Makes a connection to a (gopher) server. Sets a receive timeout on the
  36. connected socket
  37. Arguments:
  38. ViewInfo - pointer to VIEW_INFO containing pointer to SESSION_INFO which
  39. describes server to connect to
  40. Return Value:
  41. DWORD
  42. Success - ERROR_SUCCESS
  43. Failure - WSA error
  44. --*/
  45. {
  46. DEBUG_ENTER((DBG_SOCKETS,
  47. Dword,
  48. "GopherConnect",
  49. "%#x",
  50. ViewInfo
  51. ));
  52. DWORD error;
  53. BOOL fSuccess;
  54. INTERNET_CONNECT_HANDLE_OBJECT *pConnect;
  55. PROXY_STATE *pProxyState = NULL;
  56. INET_ASSERT(ViewInfo->BufferInfo != NULL);
  57. INET_ASSERT(ViewInfo->SessionInfo != NULL);
  58. //
  59. // determine sync or async
  60. //
  61. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  62. if (lpThreadInfo == NULL) {
  63. INET_ASSERT(FALSE);
  64. error = ERROR_INTERNET_INTERNAL_ERROR;
  65. goto quit;
  66. }
  67. DWORD asyncFlags;
  68. asyncFlags = 0;
  69. //asyncFlags = lpThreadInfo->IsAsyncWorkerThread ? SF_NON_BLOCKING : 0;
  70. //
  71. // Set the port we're using on the socket object.
  72. //
  73. ViewInfo->BufferInfo->Socket->SetPort((INTERNET_PORT) ViewInfo->SessionInfo->Port);
  74. //
  75. // Using the object handle, check to see if we have a socks proxy.
  76. // If so, use it to do our connections.
  77. //
  78. INTERNET_HANDLE_OBJECT * pInternet;
  79. HINTERNET hConnectMapped;
  80. INET_ASSERT(lpThreadInfo != NULL);
  81. INET_ASSERT(lpThreadInfo->hObjectMapped != NULL);
  82. INET_ASSERT(ViewInfo->SessionInfo->Host != NULL);
  83. //
  84. // Get the Mapped Connect Handle Object...
  85. //
  86. INET_ASSERT( (ViewInfo->ViewType == ViewTypeFile) ||
  87. (ViewInfo->ViewType == ViewTypeFind) );
  88. hConnectMapped = ((HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)->GetParent();
  89. INET_ASSERT(hConnectMapped);
  90. //
  91. // Finally get the Internet Object, so we can query proxy information
  92. // out of it.
  93. //
  94. pConnect = (INTERNET_CONNECT_HANDLE_OBJECT *) hConnectMapped;
  95. pInternet = (INTERNET_HANDLE_OBJECT *)
  96. ((INTERNET_CONNECT_HANDLE_OBJECT *)hConnectMapped)->GetParent();
  97. INET_ASSERT(pInternet);
  98. {
  99. AUTO_PROXY_ASYNC_MSG proxyInfoQuery(
  100. INTERNET_SCHEME_GOPHER,
  101. pConnect->GetURL(),
  102. lstrlen(pConnect->GetURL()),
  103. ViewInfo->SessionInfo->Host,
  104. lstrlen(ViewInfo->SessionInfo->Host),
  105. (INTERNET_PORT) ViewInfo->SessionInfo->Port
  106. );
  107. AUTO_PROXY_ASYNC_MSG *pProxyInfoQuery;
  108. proxyInfoQuery.SetBlockUntilCompletetion(TRUE);
  109. pProxyInfoQuery = &proxyInfoQuery;
  110. error = pInternet->GetProxyInfo(
  111. &pProxyInfoQuery
  112. );
  113. if ( error != ERROR_SUCCESS )
  114. {
  115. goto quit;
  116. }
  117. if (pProxyInfoQuery->IsUseProxy() &&
  118. pProxyInfoQuery->GetProxyScheme() == INTERNET_SCHEME_SOCKS &&
  119. pProxyInfoQuery->_lpszProxyHostName )
  120. {
  121. //
  122. // If there is Socks enabled, then turned it on.
  123. //
  124. error = ViewInfo->BufferInfo->Socket->EnableSocks(
  125. pProxyInfoQuery->_lpszProxyHostName,
  126. pProxyInfoQuery->_nProxyHostPort
  127. );
  128. }
  129. if ( pProxyInfoQuery && pProxyInfoQuery->IsAlloced() )
  130. {
  131. delete pProxyInfoQuery;
  132. pProxyInfoQuery = NULL;
  133. }
  134. if ( error != ERROR_SUCCESS )
  135. {
  136. goto quit;
  137. }
  138. }
  139. error = ViewInfo->BufferInfo->Socket->Connect(
  140. GetTimeoutValue(INTERNET_OPTION_CONNECT_TIMEOUT),
  141. GetTimeoutValue(INTERNET_OPTION_CONNECT_RETRIES),
  142. SF_INDICATE | asyncFlags
  143. );
  144. if (error == ERROR_SUCCESS) {
  145. DEBUG_PRINT(SOCKETS,
  146. INFO,
  147. ("GopherConnect(): ConnectSocket() returns socket %#x\n",
  148. //ViewInfo->BufferInfo->ConnectedSocket.Socket
  149. ViewInfo->BufferInfo->Socket->GetSocket()
  150. ));
  151. //
  152. // we have made a connection with the server. Set the receive timeout.
  153. // If this fails for any reason, ignore it (although the socket is
  154. // probably bad if this is true)
  155. //
  156. ViewInfo->BufferInfo->Socket->SetTimeout(
  157. RECEIVE_TIMEOUT,
  158. GetTimeoutValue(INTERNET_OPTION_RECEIVE_TIMEOUT)
  159. );
  160. } else {
  161. DEBUG_PRINT(SOCKETS,
  162. ERROR,
  163. ("GopherConnect(): ConnectSocket() returns %d\n",
  164. error
  165. ));
  166. }
  167. quit:
  168. DEBUG_LEAVE(error);
  169. return error;
  170. }
  171. DWORD
  172. GopherDisconnect(
  173. IN LPVIEW_INFO ViewInfo,
  174. IN BOOL AbortConnection
  175. )
  176. /*++
  177. Routine Description:
  178. Disconnects from the gopher server if the session is not flagged as
  179. persistent. The socket is closed
  180. Arguments:
  181. ViewInfo - pointer to VIEW_INFO containing pointer to SESSION_INFO
  182. which describes connection to gopher server
  183. AbortConnection - TRUE if the connection is to be terminated, even if it is
  184. a persistent connection
  185. Return Value:
  186. DWORD
  187. Success - ERROR_SUCCESS
  188. Failure - WSA error
  189. --*/
  190. {
  191. DEBUG_ENTER((DBG_SOCKETS,
  192. Dword,
  193. "GopherDisconnect",
  194. "%#x, %B",
  195. ViewInfo,
  196. AbortConnection
  197. ));
  198. DWORD error;
  199. LPSESSION_INFO sessionInfo;
  200. INET_ASSERT(ViewInfo->SessionInfo != NULL);
  201. sessionInfo = ViewInfo->SessionInfo;
  202. if (!(sessionInfo->Flags & SI_PERSISTENT) || AbortConnection) {
  203. LPBUFFER_INFO bufferInfo;
  204. INET_ASSERT(ViewInfo->BufferInfo != NULL);
  205. bufferInfo = ViewInfo->BufferInfo;
  206. DEBUG_PRINT(SOCKETS,
  207. INFO,
  208. ("GopherDisconnect(): closing socket %#x\n",
  209. //bufferInfo->ConnectedSocket.Socket
  210. bufferInfo->Socket->GetSocket()
  211. ));
  212. error = bufferInfo->Socket->Disconnect();
  213. if (error != ERROR_SUCCESS) {
  214. DEBUG_PRINT(SOCKETS,
  215. ERROR,
  216. ("GopherDisconnect(): Disconnect(%#x) returns %d\n",
  217. ViewInfo->BufferInfo->Socket->GetSocket(),
  218. error
  219. ));
  220. }
  221. } else {
  222. error = ERROR_SUCCESS;
  223. }
  224. DEBUG_LEAVE(error);
  225. return error;
  226. }
  227. DWORD
  228. GopherSendRequest(
  229. IN LPVIEW_INFO ViewInfo
  230. )
  231. /*++
  232. Routine Description:
  233. Sends a gopher request to the server we are currently connected to. The
  234. request is the selector string used to tell the gopher server what to return
  235. Arguments:
  236. ViewInfo - pointer to VIEW_INFO describing request. Contains pointer to
  237. CR/LF terminated text string gopher request
  238. Return Value:
  239. DWORD
  240. Success - ERROR_SUCCESS
  241. Failure - WSA error
  242. --*/
  243. {
  244. DEBUG_ENTER((DBG_SOCKETS,
  245. Dword,
  246. "GopherSendRequest",
  247. "%#x",
  248. ViewInfo
  249. ));
  250. DWORD error;
  251. //
  252. // determine sync or async
  253. //
  254. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  255. if (lpThreadInfo == NULL) {
  256. INET_ASSERT(FALSE);
  257. error = ERROR_INTERNET_INTERNAL_ERROR;
  258. goto quit;
  259. }
  260. DWORD asyncFlags;
  261. asyncFlags = 0;
  262. //asyncFlags = lpThreadInfo->IsAsyncWorkerThread ? SF_NON_BLOCKING : 0;
  263. error = ViewInfo->BufferInfo->Socket->Send(ViewInfo->Request,
  264. ViewInfo->RequestLength,
  265. SF_INDICATE
  266. );
  267. quit:
  268. DEBUG_LEAVE(error);
  269. return error;
  270. }
  271. DWORD
  272. GopherReceiveResponse(
  273. IN OUT LPVIEW_INFO ViewInfo,
  274. OUT LPDWORD BytesReceived
  275. )
  276. /*++
  277. Routine Description:
  278. This function is called iteratively to receive the gopher response data. The
  279. first time this function is called, we determine the type of response -
  280. success or failure, gopher0 or gopher+.
  281. The buffer information passed in can refer to a buffer that we maintain
  282. internally (for directories), or to a buffer that the caller supplies to the
  283. GopherReadFile() function. In the latter case, the buffer address and length
  284. may be 0.
  285. This function assumes that directory responses will be received into a
  286. buffer which we allocate in this function, and that file requests are
  287. received into caller-supplied (user-supplied) buffers
  288. Arguments:
  289. ViewInfo - pointer to VIEW_INFO structure (including BUFFER_INFO)
  290. describing the gopher request and the response from the
  291. server
  292. BytesReceived - number of bytes received in this response
  293. Return Value:
  294. DWORD
  295. Success - ERROR_SUCCESS
  296. Failure - ERROR_INTERNET_EXTENDED_ERROR
  297. The server sent us some form of error text. The app should
  298. use InternetGetLastResponseInfo() to get the text
  299. --*/
  300. {
  301. DEBUG_ENTER((DBG_SOCKETS,
  302. Dword,
  303. "GopherReceiveResponse",
  304. "%#x, %#x",
  305. ViewInfo,
  306. BytesReceived
  307. ));
  308. LPBUFFER_INFO bufferInfo;
  309. DWORD error;
  310. int bufferLength;
  311. LPBYTE buffer;
  312. LPBYTE responseBuffer;
  313. int bytesReceived;
  314. BOOL discardBuffer;
  315. BOOL terminateConnection;
  316. BOOL checkResponse;
  317. *BytesReceived = 0;
  318. bytesReceived = 0;
  319. terminateConnection = FALSE;
  320. checkResponse = FALSE;
  321. //
  322. // variables for SocketReceive()
  323. //
  324. LPVOID psrBuffer;
  325. DWORD srLength;
  326. DWORD srLeft;
  327. DWORD srReceived;
  328. BOOL eof;
  329. bufferInfo = ViewInfo->BufferInfo;
  330. INET_ASSERT(bufferInfo != NULL);
  331. //
  332. // determine sync or async
  333. //
  334. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  335. if (lpThreadInfo == NULL) {
  336. INET_ASSERT(FALSE);
  337. error = ERROR_INTERNET_INTERNAL_ERROR;
  338. goto cleanup;
  339. }
  340. DWORD asyncFlags;
  341. asyncFlags = 0;
  342. //asyncFlags = lpThreadInfo->IsAsyncWorkerThread ? SF_NON_BLOCKING : 0;
  343. //
  344. // if this is the first receive for this buffer then figure out what we
  345. // received. At this point we may have no buffer (GopherOpenFile()) so we
  346. // set up the BUFFER_INFO with the required information for the next
  347. // GopherReadFile()
  348. //
  349. if (bufferInfo->Flags & BI_FIRST_RECEIVE) {
  350. //
  351. // determine what we have received
  352. //
  353. psrBuffer = (LPVOID)bufferInfo->ResponseInfo;
  354. srLength = sizeof(bufferInfo->ResponseInfo);
  355. srLeft = sizeof(bufferInfo->ResponseInfo);
  356. srReceived = 0;
  357. error = bufferInfo->Socket->Receive(&psrBuffer,
  358. &srLength,
  359. &srLeft,
  360. &srReceived,
  361. 0, // dwExtraSpace
  362. SF_INDICATE,
  363. &eof
  364. );
  365. if (error != ERROR_SUCCESS) {
  366. goto cleanup;
  367. } else if (srReceived == 0) {
  368. INET_ASSERT(eof);
  369. //
  370. // the server has closed the connection already. We either end up
  371. // with a zero-length file, or return ERROR_NO_MORE_FILES from a
  372. // directory listing
  373. //
  374. DEBUG_PRINT(SOCKETS,
  375. ERROR,
  376. ("SocketsReceive() returns 0 bytes on initial read\n"
  377. ));
  378. bufferInfo->Flags |= BI_RECEIVE_COMPLETE;
  379. goto cleanup;
  380. }
  381. //
  382. // BytesRemaining is the number of data bytes in ResponseInfo
  383. //
  384. bufferInfo->BytesRemaining = srReceived;
  385. bufferInfo->DataBytes = (LPBYTE)bufferInfo->ResponseInfo;
  386. if (ViewInfo->Flags & VI_GOPHER_PLUS) {
  387. //
  388. // gopher+ request: expecting gopher+ response: +/-#\r\n
  389. //
  390. if (bufferInfo->ResponseInfo[0] == GOPHER_PLUS_ERROR_INDICATOR) {
  391. bufferInfo->Flags |= BI_ERROR_RESPONSE;
  392. } else if (bufferInfo->ResponseInfo[0] != GOPHER_PLUS_SUCCESS_INDICATOR) {
  393. //
  394. // buffer does not start with + or -; assume we received a
  395. // gopher0 response, and down-grade the request
  396. //
  397. ViewInfo->Flags &= ~VI_GOPHER_PLUS;
  398. DEBUG_PRINT(SOCKETS,
  399. WARNING,
  400. ("Gopher+ request resulted in gopher0 response!\n"
  401. ));
  402. }
  403. } else if ((ViewInfo->ViewType != ViewTypeFile)
  404. && (bufferInfo->ResponseInfo[0] == GOPHER_PLUS_ERROR_INDICATOR)) {
  405. //
  406. // if this is a gopher+ error response then we promote the request/
  407. // response to gopher+ and handle the error below. If it actually
  408. // turns out that someone has invented a locator that starts '-'
  409. // then we will again down-grade the request
  410. //
  411. bufferInfo->Flags |= BI_ERROR_RESPONSE;
  412. ViewInfo->Flags |= VI_GOPHER_PLUS;
  413. DEBUG_PRINT(SOCKETS,
  414. WARNING,
  415. ("Gopher0 request resulted in gopher+ response!\n"
  416. ));
  417. }
  418. //
  419. // if we still think the request/response is gopher+ then extract the
  420. // response length
  421. //
  422. if (ViewInfo->Flags & VI_GOPHER_PLUS) {
  423. BOOL ok;
  424. LPSTR pNumber;
  425. pNumber = &bufferInfo->ResponseInfo[1];
  426. ok = ExtractInt(&pNumber,
  427. 0,
  428. &bufferInfo->ResponseLength
  429. );
  430. if (!ok || (*pNumber != '\r') || (*(pNumber + 1) != '\n')) {
  431. ViewInfo->Flags &= ~VI_GOPHER_PLUS;
  432. DEBUG_PRINT(SOCKETS,
  433. WARNING,
  434. ("Gopher+ request resulted in gopher0 response!\n"
  435. ));
  436. //
  437. // probably isn't an error either
  438. //
  439. bufferInfo->Flags &= ~BI_ERROR_RESPONSE;
  440. } else {
  441. int numberLength;
  442. //
  443. // this is the number of bytes we copied to the ResponseInfo
  444. // buffer that belong in the response proper
  445. //
  446. numberLength = (int) (pNumber - bufferInfo->ResponseInfo) + 2;
  447. bufferInfo->DataBytes = (LPBYTE)&bufferInfo->ResponseInfo[numberLength];
  448. bufferInfo->BytesRemaining -= numberLength;
  449. }
  450. }
  451. //
  452. // if we are receiving a gopher0 (directory) response then we need to
  453. // check below if we really received an error
  454. //
  455. if (!(ViewInfo->Flags & VI_GOPHER_PLUS)) {
  456. checkResponse = TRUE;
  457. }
  458. //
  459. // no longer the first time receive
  460. //
  461. bufferInfo->Flags &= ~BI_FIRST_RECEIVE;
  462. }
  463. //
  464. // either get the user buffer pointer, or allocate/resize a moveable buffer
  465. // (for directory listings and errors)
  466. //
  467. // N.B. A special case here is an error received in response to a gopher0
  468. // directory request: we don't know at this point if a gopher0 directory
  469. // request has resulted in an error, but we have to allocate a buffer
  470. // anyway, so we will determine error status after we have received the
  471. // data
  472. //
  473. discardBuffer = FALSE;
  474. if (!(bufferInfo->Flags & (BI_BUFFER_RESPONSE | BI_ERROR_RESPONSE))) {
  475. buffer = bufferInfo->Buffer;
  476. bufferLength = bufferInfo->BufferLength;
  477. //
  478. // if we are given a zero length user buffer then quit now; the bytes
  479. // received parameter has already been zeroed
  480. //
  481. if (bufferLength == 0) {
  482. goto quit;
  483. }
  484. } else {
  485. //
  486. // if we know how much data is in the response AND it is less than the
  487. // default length, then allocate a buffer that will exactly fit the
  488. // response. Otherwise allocate a buffer large enough to fit the default
  489. // response length
  490. //
  491. if (bufferInfo->ResponseLength > 0) {
  492. bufferLength = min(bufferInfo->ResponseLength,
  493. DEFAULT_RESPONSE_BUFFER_LENGTH
  494. );
  495. } else {
  496. //
  497. // the response length is -1 or -2, or its a gopher0 response: we
  498. // don't know how large it is
  499. //
  500. bufferLength = DEFAULT_RESPONSE_BUFFER_LENGTH;
  501. }
  502. //
  503. // if this is the first time we have called this function, allocate the
  504. // buffer, else grow it to the new size
  505. //
  506. bufferInfo->Buffer = (LPBYTE)ResizeBuffer(bufferInfo->Buffer,
  507. bufferInfo->BufferLength
  508. + bufferLength,
  509. FALSE
  510. );
  511. if (bufferInfo->Buffer == NULL) {
  512. terminateConnection = TRUE;
  513. INET_ASSERT(FALSE);
  514. goto last_error_exit;
  515. }
  516. bufferInfo->Flags |= BI_OWN_BUFFER;
  517. //
  518. // start receiving the next chunk at the end of the previous one
  519. //
  520. buffer = bufferInfo->Buffer + bufferInfo->BufferLength;
  521. }
  522. //
  523. // if we haven't already received all the data (in DetermineGopherResponse)
  524. // then receive the next chunk
  525. //
  526. responseBuffer = buffer;
  527. bytesReceived = bufferLength;
  528. //
  529. // receive the data. N.B. Don't change this loop without first examining
  530. // the checks below
  531. //
  532. int n;
  533. for (n = 0; bufferLength > 0; ) {
  534. //
  535. // if there is still data to copy in ResponseInfo then copy that
  536. //
  537. if (bufferInfo->BytesRemaining) {
  538. n = min(bufferInfo->BytesRemaining, bufferLength);
  539. memcpy(buffer, bufferInfo->DataBytes, n);
  540. bufferInfo->BytesRemaining -= n;
  541. bufferInfo->DataBytes += n;
  542. } else {
  543. //
  544. // if there is data left to copy in the look-ahead buffer that we
  545. // allocated in DetermineGopherResponse() then copy that data to
  546. // the response buffer
  547. //
  548. psrBuffer = (LPVOID)buffer;
  549. srLength = bufferLength;
  550. srLeft = bufferLength;
  551. srReceived = 0;
  552. error = bufferInfo->Socket->Receive(&psrBuffer,
  553. &srLength,
  554. &srLeft,
  555. &srReceived,
  556. 0, // dwExtraSpace
  557. SF_INDICATE,
  558. &eof
  559. );
  560. if (error != ERROR_SUCCESS) {
  561. discardBuffer = TRUE;
  562. terminateConnection = TRUE;
  563. goto cleanup;
  564. }
  565. n = srReceived;
  566. if (n == 0) {
  567. break;
  568. }
  569. }
  570. bufferLength -= n;
  571. buffer += n;
  572. }
  573. //
  574. // at this point we have one of the following:
  575. //
  576. // * we reached the end of the response (n == 0)
  577. // * we reached the end of the buffer (n != 0 && bufferLength == 0)
  578. //
  579. //
  580. // get the actual number of bytes received for this iteration
  581. //
  582. bytesReceived -= bufferLength;
  583. //
  584. // if we are receiving a gopher0 directory response then we need to check
  585. // whether we actually received an error
  586. //
  587. if (checkResponse) {
  588. //
  589. // allow for long locators (!)
  590. //
  591. char locator[2 * MAX_GOPHER_LOCATOR_LENGTH + 1];
  592. LPSTR destination;
  593. LPSTR source;
  594. DWORD destinationLength;
  595. DWORD sourceLength;
  596. //
  597. // we have a gopher0 error if the response starts with a '3' but there
  598. // is only one locator, or the response data is unformatted
  599. //
  600. //
  601. // N.B. We are making an assumption here that the locator(s) in the
  602. // response is(are) not larger than our buffer above. This should be a
  603. // safe assumption, but this test could fail if we get a large locator
  604. // but then a large locator will probably break all other gopher
  605. // clients? The reason we copy the locator is so that we get a zero-
  606. // terminated string for IsValidLocator(), and CopyToEol() compresses
  607. // multiple carriage-returns which some servers are uninteligent to
  608. // return
  609. //
  610. source = (LPSTR)responseBuffer;
  611. sourceLength = bytesReceived;
  612. destination = locator;
  613. destinationLength = sizeof(locator);
  614. if (CopyToEol(&destination,
  615. &destinationLength,
  616. &source,
  617. &sourceLength)) {
  618. if (IsValidLocator(locator, sizeof(locator))) {
  619. //
  620. // response contains at least one valid locator. If it starts
  621. // with the gopher0 error indicator ('3') and its the only one
  622. // then this is an error response (although it *could* be the
  623. // one and only locator describing the directory, and the admin
  624. // decided to make it an error for some reason. We can't
  625. // differentiate in this case)
  626. //
  627. if (locator[0] == GOPHER_CHAR_ERROR) {
  628. destination = locator;
  629. destinationLength = sizeof(locator);
  630. if (!CopyToEol(&destination,
  631. &destinationLength,
  632. &source,
  633. &sourceLength)) {
  634. bufferInfo->Flags |= BI_ERROR_RESPONSE;
  635. } else if (!IsValidLocator(locator, sizeof(locator))) {
  636. bufferInfo->Flags |= BI_ERROR_RESPONSE;
  637. }
  638. }
  639. } else {
  640. //
  641. // response doesn't contain a valid locator: must be an error
  642. //
  643. bufferInfo->Flags |= BI_ERROR_RESPONSE;
  644. }
  645. } else {
  646. //
  647. // we are receiving 4K but couldn't find an end-of-line? Must be an
  648. // error
  649. //
  650. bufferInfo->Flags |= BI_ERROR_RESPONSE;
  651. }
  652. }
  653. //
  654. // if we finished receivig the response then we can set the error code or
  655. // shrink the buffer
  656. //
  657. if ((n == 0)
  658. || (bytesReceived == bufferInfo->ResponseLength)
  659. || (bufferInfo->Flags & BI_ERROR_RESPONSE)) {
  660. //
  661. // no more data in the response. We have completed the transfer
  662. //
  663. bufferInfo->Flags |= BI_RECEIVE_COMPLETE;
  664. //
  665. // if this response type is terminated with a line containing only a dot
  666. // then remove the dot
  667. //
  668. if (bufferInfo->Flags & BI_DOT_AT_END) {
  669. //
  670. // BUGBUG - this is insufficient - need to check for different line
  671. // termination schemes ('\n', '\r\r\n', '\r\n', etc.)
  672. //
  673. if ((bytesReceived >= 3) && (memcmp(buffer - 3, ".\r\n", 3) == 0)) {
  674. bytesReceived -= 3;
  675. }
  676. bufferInfo->Flags &= ~BI_DOT_AT_END;
  677. }
  678. //
  679. // if we received an error response then we must set the last error
  680. // response data
  681. //
  682. if (bufferInfo->Flags & BI_ERROR_RESPONSE) {
  683. if (bytesReceived > 0) {
  684. InternetSetLastError(0,
  685. (LPSTR)responseBuffer,
  686. bytesReceived,
  687. SLE_ZERO_TERMINATE
  688. );
  689. //
  690. // let the app know that it needs to call InternetGetLastResponseInfo()
  691. // to retrieve the error text
  692. //
  693. error = ERROR_INTERNET_EXTENDED_ERROR;
  694. } else {
  695. error = ERROR_SUCCESS;
  696. }
  697. //
  698. // we have copied the data in the error response, no more need for
  699. // the buffer
  700. //
  701. discardBuffer = TRUE;
  702. //
  703. // need to indicate to the caller that they need to get the last
  704. // error response
  705. //
  706. } else {
  707. error = ERROR_SUCCESS;
  708. }
  709. } else {
  710. //
  711. // this chunk received OK, more to go
  712. //
  713. error = ERROR_SUCCESS;
  714. }
  715. cleanup:
  716. //
  717. // if we no longer need the buffer then discard it, else if we have completed
  718. // receiving the response, shrink the buffer to free up any unused space
  719. //
  720. if (bufferInfo->Flags & BI_OWN_BUFFER) {
  721. DWORD newBufferLength;
  722. BOOL resize;
  723. if (discardBuffer) {
  724. newBufferLength = 0;
  725. resize = TRUE;
  726. } else {
  727. //
  728. // update the amount of data received - i.e. the number of bytes in
  729. // the buffer (excluding header info)
  730. //
  731. bufferInfo->BufferLength += bytesReceived;
  732. if (bufferInfo->Flags & BI_RECEIVE_COMPLETE) {
  733. newBufferLength = bufferInfo->BufferLength;
  734. resize = TRUE;
  735. DEBUG_PRINT(SOCKETS,
  736. ERROR,
  737. ("received 0 bytes in response\n"
  738. ));
  739. } else {
  740. resize = FALSE;
  741. }
  742. }
  743. //
  744. // if resize is TRUE then we are either shrinking the data buffer to
  745. // exclude any unused space, or we are shrinking it to nothing because
  746. // we have no data or it is being discarded
  747. //
  748. if (resize) {
  749. bufferInfo->Buffer = (LPBYTE)ResizeBuffer(bufferInfo->Buffer,
  750. newBufferLength,
  751. FALSE
  752. );
  753. if (newBufferLength == 0) {
  754. //
  755. // we no longer own the buffer
  756. //
  757. bufferInfo->Flags &= ~BI_OWN_BUFFER;
  758. //
  759. // DEBUG version: ensure that the buffer has really been freed
  760. //
  761. INET_ASSERT(bufferInfo->Buffer == NULL);
  762. }
  763. }
  764. }
  765. //
  766. // if this is a gopher+ response and we know the length of the response then
  767. // reduce the outstanding size of the response by the amount we received
  768. //
  769. if (bufferInfo->ResponseLength > 0) {
  770. bufferInfo->ResponseLength -= bytesReceived;
  771. }
  772. //
  773. // if we completed the response and the connection is not persistent, or an
  774. // error occurred, close the connection
  775. //
  776. if ((bufferInfo->Flags & BI_RECEIVE_COMPLETE) || (error != ERROR_SUCCESS)) {
  777. //
  778. // let the app know we have finished receiving data
  779. //
  780. InternetIndicateStatus(INTERNET_STATUS_RESPONSE_RECEIVED,
  781. &bytesReceived,
  782. sizeof(bytesReceived)
  783. );
  784. GopherDisconnect(ViewInfo, terminateConnection);
  785. }
  786. //
  787. // if we didn't receive error text from the server then we return the amount
  788. // of data received. This is only interesting to ReadData()
  789. //
  790. if (!(bufferInfo->Flags & BI_ERROR_RESPONSE)) {
  791. *BytesReceived = bytesReceived;
  792. }
  793. quit:
  794. DEBUG_LEAVE(error);
  795. return error;
  796. last_error_exit:
  797. error = GetLastError();
  798. INET_ASSERT(error != ERROR_SUCCESS);
  799. goto cleanup;
  800. }