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.

1591 lines
41 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name:
  4. gfrapir.cxx
  5. Abstract:
  6. Contains the remote-side gopher API worker functions. In each case, the API
  7. proper validates the arguments. The worker functions contained herein just
  8. perform the requested operation with the supplied arguments.
  9. These functions are the remote side of the RPC interface. If the DLL is
  10. the abstract0 version (no RPC) then the A forms of the functions simply
  11. call the w functions
  12. Contents:
  13. wGopherFindFirst
  14. wGopherFindNext
  15. wGopherFindClose
  16. wGopherOpenFile
  17. wGopherReadFile
  18. wGopherQueryDataAvailable
  19. wGopherCloseHandle
  20. wGopherGetAttribute
  21. wGopherConnect
  22. wGopherDisconnect
  23. (GetView)
  24. Author:
  25. Richard L Firth (rfirth) 14-Oct-1994
  26. Environment:
  27. Win32 DLL
  28. Revision History:
  29. 14-Oct-1994 rfirth
  30. Created
  31. --*/
  32. #include <wininetp.h>
  33. #include "gfrapih.h"
  34. //
  35. // manifests
  36. //
  37. #define DEFAULT_REQUEST_BUFFER_LENGTH (MAX_GOPHER_SELECTOR_TEXT + GOPHER_REQUEST_TERMINATOR_LENGTH + 1)
  38. //
  39. // private data
  40. //
  41. char szQuery[] = "query "; // prepend to CSO searches
  42. //
  43. // private prototypes
  44. //
  45. PRIVATE
  46. DWORD
  47. GetView(
  48. IN LPSESSION_INFO SessionInfo,
  49. IN VIEW_TYPE ViewType,
  50. IN LPSTR Request,
  51. IN BOOL RequestIsGopherPlus,
  52. IN DWORD ResponseFlags,
  53. OUT LPVIEW_INFO* pViewInfo
  54. );
  55. extern
  56. DWORD
  57. InbGopherLocalEndCacheWrite(
  58. IN HINTERNET hGopherFile,
  59. IN LPSTR lpszFileExtension,
  60. IN BOOL fNormal
  61. );
  62. //
  63. // functions
  64. //
  65. DWORD
  66. wGopherFindFirst(
  67. IN LPCSTR lpszLocator,
  68. IN LPCSTR lpszSearchString OPTIONAL,
  69. OUT LPGOPHER_FIND_DATA lpBuffer OPTIONAL,
  70. OUT LPHINTERNET lpHandle
  71. )
  72. /*++
  73. Routine Description:
  74. Connects to the gopher server, sends a request to get directory information,
  75. gets the response and converts the gopher descriptor strings to
  76. GOPHER_FIND_DATA structures
  77. Arguments:
  78. lpszLocator - pointer to descriptor of information to get
  79. lpszSearchString - pointer to strings to search for if Locator is search
  80. server. This argument MUST be present if Locator is
  81. an search server
  82. lpBuffer - pointer to user-allocated buffer in which to return
  83. info
  84. lpHandle - pointer to returned handle if ERROR_SUCCESS returned
  85. Return Value:
  86. DWORD
  87. Success - ERROR_SUCCESS
  88. Failure - ERROR_NOT_ENOUGH_MEMORY
  89. WSA error
  90. --*/
  91. {
  92. DEBUG_ENTER((DBG_GOPHER,
  93. Dword,
  94. "wGopherFindFirst",
  95. "%q, %q, %#x, %#x",
  96. lpszLocator,
  97. lpszSearchString,
  98. lpBuffer,
  99. lpHandle
  100. ));
  101. DWORD gopherType;
  102. LPSTR requestPtr;
  103. DWORD requestLen;
  104. char hostName[MAX_GOPHER_HOST_NAME + 1];
  105. DWORD hostNameLen;
  106. DWORD port;
  107. LPSTR gopherPlus;
  108. LPSESSION_INFO sessionInfo;
  109. DWORD error;
  110. HINTERNET findHandle;
  111. LPVIEW_INFO viewInfo;
  112. DWORD newRequestLength;
  113. DWORD searchStringsLength;
  114. //
  115. // initialise variables in case of early exit (via goto)
  116. //
  117. sessionInfo = NULL;
  118. //
  119. // grab a buffer for the request string
  120. //
  121. requestLen = DEFAULT_REQUEST_BUFFER_LENGTH;
  122. requestPtr = (LPSTR)ResizeBuffer(NULL, requestLen, FALSE);
  123. if (requestPtr == NULL) {
  124. DEBUG_LEAVE(ERROR_NOT_ENOUGH_MEMORY);
  125. return ERROR_NOT_ENOUGH_MEMORY;
  126. }
  127. //
  128. // pull the individual fields out of the locator. Not interested in display
  129. // string
  130. //
  131. hostNameLen = sizeof(hostName);
  132. if (!CrackLocator(lpszLocator,
  133. &gopherType,
  134. NULL, // DisplayString
  135. NULL, // DisplayStringLength
  136. requestPtr, // SelectorString
  137. &requestLen, // SelectorStringLength
  138. hostName,
  139. &hostNameLen,
  140. &port,
  141. &gopherPlus
  142. )) {
  143. error = ERROR_GOPHER_INVALID_LOCATOR;
  144. goto quit;
  145. }
  146. //
  147. // find the session 'object'. If we don't have one describing the requested
  148. // gopher server then create one
  149. //
  150. sessionInfo = FindOrCreateSession(hostName, port, &error);
  151. if (sessionInfo == NULL) {
  152. goto quit;
  153. }
  154. //
  155. // if the request is gopher+ or plain gopher but we know the server is
  156. // gopher+ then we automatically promote the request to be gopher+. It
  157. // potentially makes life easier for this DLL (the server could tell us
  158. // the exact length of the response or be more discerning about errors)
  159. // and potentially gives more information to the app. Either way, the app
  160. // doesn't lose by this
  161. //
  162. if (gopherPlus || IsGopherPlusSession(sessionInfo)) {
  163. gopherType |= GOPHER_TYPE_GOPHER_PLUS;
  164. }
  165. //
  166. // calculate the length of the extra strings we have to add to the selector
  167. //
  168. newRequestLength = requestLen;
  169. if ( IS_GOPHER_SEARCH_SERVER(gopherType)) {
  170. INET_ASSERT(lpszSearchString != NULL);
  171. INET_ASSERT(*lpszSearchString != '\0');
  172. //
  173. // add search strings length
  174. //
  175. searchStringsLength = strlen(lpszSearchString);
  176. newRequestLength += searchStringsLength;
  177. if (IS_GOPHER_INDEX_SERVER(gopherType)) {
  178. newRequestLength++; // for tab
  179. } else {
  180. newRequestLength += sizeof(szQuery) - 1;
  181. }
  182. } else {
  183. searchStringsLength = 0;
  184. }
  185. //
  186. // gopher+ requests have "\t+" or "\t$" at the end of the request
  187. //
  188. if (IS_GOPHER_PLUS(gopherType)) {
  189. newRequestLength += 2;
  190. }
  191. //
  192. // all requests terminated by "\r\n". Add 1 for string terminator
  193. //
  194. newRequestLength += sizeof(GOPHER_REQUEST_TERMINATOR);
  195. //
  196. // grow the buffer if necessary
  197. //
  198. if (newRequestLength > DEFAULT_REQUEST_BUFFER_LENGTH) {
  199. requestPtr = (LPSTR)ResizeBuffer((HLOCAL)requestPtr, newRequestLength, FALSE);
  200. if (requestPtr == NULL) {
  201. error = ERROR_NOT_ENOUGH_MEMORY;
  202. goto quit;
  203. }
  204. }
  205. //
  206. // add the additional strings
  207. //
  208. if (searchStringsLength != 0) {
  209. if (IS_GOPHER_INDEX_SERVER(gopherType)) {
  210. requestPtr[requestLen++] = GOPHER_FIELD_SEPARATOR;
  211. } else {
  212. memcpy (requestPtr + requestLen, szQuery, sizeof(szQuery) - 1);
  213. requestLen += sizeof(szQuery) - 1;
  214. }
  215. memcpy(&requestPtr[requestLen], lpszSearchString, searchStringsLength);
  216. requestLen += searchStringsLength;
  217. }
  218. if (IS_GOPHER_PLUS(gopherType)) {
  219. requestPtr[requestLen++] = GOPHER_FIELD_SEPARATOR;
  220. requestPtr[requestLen++] =
  221. IS_GOPHER_SEARCH_SERVER(gopherType)? '+' : '$';
  222. }
  223. memcpy(&requestPtr[requestLen],
  224. GOPHER_REQUEST_TERMINATOR,
  225. sizeof(GOPHER_REQUEST_TERMINATOR) // don't scrub the '\0' in this case
  226. );
  227. //
  228. // selector munged; get the directory listing
  229. //
  230. error = GetView(sessionInfo,
  231. ViewTypeFind,
  232. requestPtr,
  233. IS_GOPHER_PLUS(gopherType) ? TRUE : FALSE,
  234. BI_DOT_AT_END,
  235. &viewInfo
  236. );
  237. //
  238. // if no error was reported then we can return a directory entry
  239. //
  240. if (error == ERROR_SUCCESS) {
  241. //
  242. // if the caller supplied an output buffer then convert the first
  243. // directory entry to the API buffer format, else the caller wants
  244. // all gopher directory information returned by InternetFindNextFile()
  245. //
  246. if (ARGUMENT_PRESENT(lpBuffer)) {
  247. error = GetDirEntry(viewInfo, lpBuffer);
  248. }
  249. if (error == ERROR_SUCCESS) {
  250. findHandle = viewInfo->Handle;
  251. } else {
  252. DereferenceView(viewInfo);
  253. }
  254. }
  255. quit:
  256. //
  257. // dereference the session. If we have an active VIEW_INFO then the
  258. // reference count will still be > 0
  259. //
  260. if (sessionInfo != NULL) {
  261. DereferenceSession(sessionInfo);
  262. }
  263. //
  264. // if we allocated a new request buffer for a large search request then
  265. // free it
  266. //
  267. if (requestPtr != NULL) {
  268. requestPtr = (LPSTR)ResizeBuffer((HLOCAL)requestPtr, 0, FALSE);
  269. INET_ASSERT(requestPtr == NULL);
  270. }
  271. //
  272. // set the handle value - ignored by caller if we don't return ERROR_SUCCESS
  273. //
  274. *lpHandle = findHandle;
  275. DEBUG_LEAVE(error);
  276. return error;
  277. }
  278. DWORD
  279. wGopherFindNext(
  280. IN HINTERNET hFind,
  281. OUT LPGOPHER_FIND_DATA lpBuffer
  282. )
  283. /*++
  284. Routine Description:
  285. Remote side of GopherFindNext API. All parameters have been validated by the
  286. time this function is called, so we know that Buffer is large enough to
  287. hold all the returned data
  288. Arguments:
  289. hFind - handle of FIND_DATA, created by GopherFindFirstFile()
  290. lpBuffer - pointer to user-allocated buffer for GOPHER_FIND_DATA
  291. Return Value:
  292. DWORD
  293. Success - ERROR_SUCCESS
  294. Buffer contains next GOPHER_FIND_DATA structure
  295. Failure - ERROR_INVALID_HANDLE
  296. Can't find the VIEW_INFO corresponding to hFind
  297. ERROR_NO_MORE_FILES
  298. We have reached the end of the directory info
  299. --*/
  300. {
  301. DEBUG_ENTER((DBG_GOPHER,
  302. Dword,
  303. "wGopherFindNext",
  304. "%#x, %#x",
  305. hFind,
  306. lpBuffer
  307. ));
  308. LPVIEW_INFO viewInfo;
  309. DWORD error;
  310. //
  311. // locate the VIEW_INFO corresponding to hFind and the SESSION_INFO
  312. // that owns it
  313. //
  314. viewInfo = FindViewByHandle(hFind, ViewTypeFind);
  315. if (viewInfo != NULL) {
  316. //
  317. // just read out the next directory entry
  318. //
  319. error = GetDirEntry(viewInfo, lpBuffer);
  320. //
  321. // if the Find has been closed or this was the last entry, the following
  322. // dereference will cause the VIEW_INFO to be deleted, and if there are no
  323. // more requests outstanding on the SESSION_INFO then it too will be
  324. // deleted (via a dereference)
  325. //
  326. DereferenceView(viewInfo);
  327. } else {
  328. error = ERROR_INVALID_HANDLE;
  329. }
  330. DEBUG_LEAVE(error);
  331. return error;
  332. }
  333. DWORD
  334. wGopherFindClose(
  335. IN HINTERNET hFind
  336. )
  337. /*++
  338. Routine Description:
  339. Causes the VIEW_INFO described by hFind to be removed from the SESSION_INFO
  340. and freed. If there are no other links to the data buffer then it too is
  341. deallocated
  342. Arguments:
  343. hFind - handle describing the VIEW_INFO to terminate
  344. Return Value:
  345. DWORD
  346. Success - ERROR_SUCCESS
  347. Failure - ERROR_INVALID_HANDLE
  348. --*/
  349. {
  350. DEBUG_ENTER((DBG_GOPHER,
  351. Dword,
  352. "wGopherFindClose",
  353. "%#x",
  354. hFind
  355. ));
  356. DWORD error;
  357. //
  358. // atomically find and dereference the VIEW_INFO given the handle
  359. //
  360. error = DereferenceViewByHandle(hFind, ViewTypeFind);
  361. DEBUG_LEAVE(error);
  362. return error;
  363. }
  364. DWORD
  365. wGopherOpenFile(
  366. IN LPCSTR lpszLocator,
  367. IN LPCSTR lpszView OPTIONAL,
  368. OUT LPHINTERNET lpHandle
  369. )
  370. /*++
  371. Routine Description:
  372. 'Opens' a gopher file by copying it locally and returning a handle to the
  373. buffer
  374. Arguments:
  375. lpszLocator - pointer to locator describing file to open
  376. lpszView - pointer to view name - identifies type of file to retrieve
  377. lpHandle - pointer to returned handle if ERROR_SUCCESS returned
  378. Return Value:
  379. DWORD
  380. Success - ERROR_SUCCESS
  381. Failure - ERROR_NOT_ENOUGH_MEMORY
  382. WSA error
  383. --*/
  384. {
  385. DEBUG_ENTER((DBG_GOPHER,
  386. Dword,
  387. "wGopherOpenFile",
  388. "%q, %q, %#x",
  389. lpszLocator,
  390. lpszView,
  391. lpHandle
  392. ));
  393. DWORD error;
  394. DWORD gopherType;
  395. LPSTR requestPtr;
  396. DWORD requestLen;
  397. char hostName[MAX_GOPHER_HOST_NAME + 1];
  398. DWORD hostNameLen;
  399. DWORD port;
  400. LPSTR gopherPlus;
  401. LPSESSION_INFO sessionInfo;
  402. LPVIEW_INFO viewInfo;
  403. HINTERNET fileHandle;
  404. DWORD viewLen;
  405. //
  406. // initialise variables in case of early exit (via goto)
  407. //
  408. sessionInfo = NULL;
  409. fileHandle = NULL;
  410. //
  411. // grab a buffer for the request string
  412. //
  413. requestLen = MAX_GOPHER_SELECTOR_TEXT + 1;
  414. requestPtr = (LPSTR)ResizeBuffer((HLOCAL)NULL, requestLen, FALSE);
  415. if (requestPtr == NULL) {
  416. error = ERROR_NOT_ENOUGH_MEMORY;
  417. goto quit;
  418. }
  419. //
  420. // pull the individual fields out of the locator. Not interested in display
  421. // string
  422. //
  423. hostNameLen = sizeof(hostName);
  424. if (!CrackLocator(lpszLocator,
  425. &gopherType,
  426. NULL, // DisplayString
  427. NULL, // DisplayStringLength
  428. requestPtr, // SelectorString
  429. &requestLen, // SelectorStringLength
  430. hostName,
  431. &hostNameLen,
  432. &port,
  433. &gopherPlus
  434. )) {
  435. error = ERROR_GOPHER_INVALID_LOCATOR;
  436. goto quit;
  437. }
  438. //
  439. // find the session 'object'. If we don't have one describing the requested
  440. // gopher server then create one
  441. //
  442. sessionInfo = FindOrCreateSession(hostName, port, &error);
  443. if (sessionInfo == NULL) {
  444. goto quit;
  445. }
  446. //
  447. // (see GopherFindFirstFile()). If gopher+ is requested or available then
  448. // make this a gopher+ request
  449. //
  450. if (gopherPlus || IsGopherPlusSession(sessionInfo)) {
  451. gopherType |= GOPHER_TYPE_GOPHER_PLUS;
  452. //
  453. // we at least need some space for "\t+"
  454. //
  455. viewLen = sizeof(GOPHER_PLUS_INDICATOR) - 1;
  456. //
  457. // get the amount of space required for the alternate view, if supplied
  458. //
  459. if (ARGUMENT_PRESENT(lpszView)) {
  460. //
  461. // the extra +1 here is for the '+' between the selector and the
  462. // alternate view string: the '\0' is handled by
  463. // sizeof(GOPHER_REQUEST_TERMINATOR)
  464. //
  465. viewLen += strlen(lpszView);
  466. }
  467. } else {
  468. //
  469. // the caller may have supplied an alternate view for a gopher0 even
  470. // though it is meaningless. Ensure that it is not used
  471. //
  472. lpszView = NULL;
  473. viewLen = 0;
  474. }
  475. //
  476. // grow the buffer if it is not large enough to hold the view etc. Note, if
  477. // this is true, then we have bust one of our internal limits, which is
  478. // unexpected to say the least. But this way, we can allow apps to present
  479. // completely bogus (so we think) parameters, and at least give them a try
  480. //
  481. if ((requestLen + viewLen + sizeof(GOPHER_REQUEST_TERMINATOR))
  482. > (MAX_GOPHER_SELECTOR_TEXT + 1)) {
  483. requestPtr = (LPSTR)ResizeBuffer((HLOCAL)requestPtr,
  484. requestLen
  485. + viewLen
  486. + sizeof(GOPHER_REQUEST_TERMINATOR),
  487. FALSE
  488. );
  489. if (requestPtr == NULL) {
  490. error = ERROR_NOT_ENOUGH_MEMORY;
  491. goto quit;
  492. }
  493. }
  494. //
  495. // if this is a gopher plus request, then add the "\t+". If there is an
  496. // alternate view then it will be appended to the plus, else we just add
  497. // the line terminator
  498. //
  499. if (IS_GOPHER_PLUS(gopherType)) {
  500. memcpy(&requestPtr[requestLen],
  501. GOPHER_PLUS_INDICATOR,
  502. sizeof(GOPHER_PLUS_INDICATOR) - 1
  503. );
  504. requestLen += sizeof(GOPHER_PLUS_INDICATOR) - 1;
  505. }
  506. //
  507. // add the alternate view information, if any was supplied
  508. //
  509. if (ARGUMENT_PRESENT(lpszView)) {
  510. memcpy(&requestPtr[requestLen], lpszView, viewLen);
  511. requestLen += viewLen;
  512. }
  513. //
  514. // in gopher0 and gopher+ cases we must terminate the selector by CR-LF
  515. //
  516. memcpy(&requestPtr[requestLen],
  517. GOPHER_REQUEST_TERMINATOR,
  518. sizeof(GOPHER_REQUEST_TERMINATOR)
  519. );
  520. //
  521. // selector munged; get the file
  522. //
  523. error = GetView(sessionInfo,
  524. ViewTypeFile,
  525. requestPtr,
  526. (gopherPlus != NULL)
  527. ? TRUE
  528. : FALSE,
  529. IS_DOT_TERMINATED_REQUEST(gopherType)
  530. ? BI_DOT_AT_END
  531. : 0,
  532. &viewInfo
  533. );
  534. if (error == ERROR_SUCCESS) {
  535. fileHandle = viewInfo->Handle;
  536. }
  537. quit:
  538. //
  539. // free the request buffer
  540. //
  541. if (requestPtr != NULL) {
  542. requestPtr = (LPSTR)ResizeBuffer((HLOCAL)requestPtr, 0, FALSE);
  543. INET_ASSERT(requestPtr == NULL);
  544. }
  545. //
  546. // dereference the session - this may cause it to be deleted
  547. //
  548. if (sessionInfo != NULL) {
  549. DereferenceSession(sessionInfo);
  550. }
  551. //
  552. // set the handle value - ignored by caller if we don't return ERROR_SUCCESS
  553. //
  554. *lpHandle = fileHandle;
  555. DEBUG_LEAVE(error);
  556. return error;
  557. }
  558. DWORD
  559. wGopherReadFile(
  560. IN HINTERNET hFile,
  561. OUT LPBYTE lpBuffer,
  562. IN DWORD dwBufferLength,
  563. OUT LPDWORD lpdwBytesReturned
  564. )
  565. /*++
  566. Routine Description:
  567. Reads the next dwBufferLength bytes (or as much as is remaining) from the
  568. file identified by hFile and writes to lpBuffer
  569. Arguments:
  570. hFile - identifies file
  571. lpBuffer - place to return file data
  572. dwBufferLength - length of Buffer
  573. lpdwBytesReturned - amount of data written to Buffer
  574. Return Value:
  575. DWORD
  576. Success - ERROR_SUCCESS
  577. *lpdwBytesReturned written to lpBuffer
  578. Failure - ERROR_INVALID_HANDLE
  579. Couldn't find the VIEW_INFO corresponding to hFile
  580. --*/
  581. {
  582. DEBUG_ENTER((DBG_GOPHER,
  583. Dword,
  584. "wGopherReadFile",
  585. "%#x, %#x, %d, %#x",
  586. hFile,
  587. lpBuffer,
  588. dwBufferLength,
  589. lpdwBytesReturned
  590. ));
  591. LPVIEW_INFO viewInfo;
  592. DWORD error;
  593. viewInfo = FindViewByHandle(hFile, ViewTypeFile);
  594. if (viewInfo != NULL) {
  595. LPBUFFER_INFO bufferInfo;
  596. INET_ASSERT(viewInfo->BufferInfo != NULL);
  597. bufferInfo = viewInfo->BufferInfo;
  598. bufferInfo->Buffer = lpBuffer;
  599. bufferInfo->BufferLength = dwBufferLength;
  600. error = ReadData(viewInfo, lpdwBytesReturned);
  601. DereferenceView(viewInfo);
  602. } else {
  603. error = ERROR_INVALID_HANDLE;
  604. }
  605. DEBUG_LEAVE(error);
  606. return error;
  607. }
  608. DWORD
  609. wGopherQueryDataAvailable(
  610. IN HINTERNET hFile,
  611. OUT LPDWORD lpdwNumberOfBytesAvailable
  612. )
  613. /*++
  614. Routine Description:
  615. Determines the amount of data available to be read on the socket
  616. Arguments:
  617. hFile - identifies gopher file
  618. lpdwNumberOfBytesAvailable - where number of available bytes returned
  619. Return Value:
  620. DWORD
  621. Success - ERROR_SUCCESS
  622. Failure - ERROR_INVALID_HANDLE
  623. --*/
  624. {
  625. DEBUG_ENTER((DBG_GOPHER,
  626. Dword,
  627. "wGopherQueryDataAvailable",
  628. "%#x, %#x",
  629. hFile,
  630. lpdwNumberOfBytesAvailable
  631. ));
  632. DWORD bytesAvailable = 0;
  633. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  634. DWORD error;
  635. HINTERNET_HANDLE_TYPE handleType;
  636. error = RGetHandleType(lpThreadInfo->hObjectMapped, &handleType);
  637. if (error != ERROR_SUCCESS) {
  638. return (error);
  639. }
  640. if (lpThreadInfo == NULL) {
  641. INET_ASSERT(FALSE);
  642. error = ERROR_INTERNET_INTERNAL_ERROR;
  643. goto quit;
  644. }
  645. LPVIEW_INFO viewInfo;
  646. VIEW_TYPE viewType;
  647. //
  648. // assume this is a directory list first
  649. //
  650. if ((viewInfo = FindViewByHandle(hFile, ViewTypeFind)) != NULL) {
  651. viewType = ViewTypeFind;
  652. error = ERROR_SUCCESS;
  653. if (viewInfo->ViewOffset < viewInfo->BufferInfo->BufferLength) {
  654. bytesAvailable = sizeof(GOPHER_FIND_DATA);
  655. }
  656. } else if ((viewInfo = FindViewByHandle(hFile, ViewTypeFile)) != NULL) {
  657. viewType = ViewTypeFile;
  658. error = ERROR_SUCCESS;
  659. } else {
  660. error = ERROR_INVALID_HANDLE;
  661. }
  662. if ((error == ERROR_SUCCESS) && (bytesAvailable == 0)) {
  663. INET_ASSERT(viewInfo->BufferInfo != NULL);
  664. ICSocket *socket = viewInfo->BufferInfo->Socket;
  665. if (socket->IsValid()) {
  666. error = socket->DataAvailable(&bytesAvailable);
  667. }
  668. if ((error == ERROR_SUCCESS)
  669. && (viewType == ViewTypeFind)
  670. && (bytesAvailable != 0)) {
  671. bytesAvailable = sizeof(GOPHER_FIND_DATA);
  672. }
  673. } else {
  674. error = ERROR_SUCCESS;
  675. }
  676. if (viewInfo != NULL) {
  677. DereferenceView(viewInfo);
  678. }
  679. *lpdwNumberOfBytesAvailable = bytesAvailable;
  680. quit:
  681. if ((error == ERROR_SUCCESS) && !bytesAvailable) {
  682. InbGopherLocalEndCacheWrite(lpThreadInfo->hObjectMapped,
  683. ((handleType==TypeFtpFindHandleHtml)
  684. ?"htm":NULL),
  685. TRUE);
  686. }
  687. DEBUG_LEAVE(error);
  688. return error;
  689. }
  690. DWORD
  691. wGopherCloseHandle(
  692. IN HINTERNET hFile
  693. )
  694. /*++
  695. Routine Description:
  696. Causes the VIEW_INFO described by hFile to be removed from the
  697. SESSION_INFO and freed. If there are no other links to the data buffer
  698. then it too is deallocated
  699. Arguments:
  700. hFile - handle describing the VIEW_INFO to terminate
  701. Return Value:
  702. BOOL
  703. Success - ERROR_SUCCESS
  704. Failure - ERROR_INVALID_HANDLE
  705. --*/
  706. {
  707. DEBUG_ENTER((DBG_GOPHER,
  708. Dword,
  709. "wGopherCloseHandle",
  710. "%#x",
  711. hFile
  712. ));
  713. DWORD error;
  714. //
  715. // atomically find and dereference the VIEW_INFO given the handle
  716. //
  717. error = DereferenceViewByHandle(hFile, ViewTypeFile);
  718. DEBUG_LEAVE(error);
  719. return error;
  720. }
  721. #if defined(GOPHER_ATTRIBUTE_SUPPORT)
  722. DWORD
  723. wGopherGetAttribute(
  724. IN LPCSTR lpszLocator,
  725. IN LPCSTR lpszAttribute,
  726. OUT LPBYTE lpBuffer,
  727. IN OUT LPDWORD lpdwBufferLength
  728. )
  729. /*++
  730. Routine Description:
  731. Retrieves the requested attribute
  732. Arguments:
  733. lpszLocator - descriptor of item for which attribute information will
  734. be retrieved
  735. lpszAttribute - the attribute name, e.g. +VIEWS
  736. lpBuffer - to receive attributes
  737. lpdwBufferLength - IN: length of lpBuffer
  738. OUT: number of bytes returned in lpBuffer
  739. Return Value:
  740. DWORD
  741. Success - ERROR_SUCCESS
  742. Failure - ERROR_GOPHER_ATTRIBUTE_NOT_FOUND
  743. ERROR_GOPHER_NOT_GOPHER_PLUS
  744. ERROR_NOT_ENOUGH_MEMORY
  745. --*/
  746. {
  747. DEBUG_ENTER((DBG_GOPHER,
  748. Dword,
  749. "wGopherGetAttribute",
  750. "%q, %q, %#x, %#x [%d]",
  751. lpszLocator,
  752. lpszAttribute,
  753. lpBuffer,
  754. lpdwBufferLength
  755. ));
  756. DWORD error;
  757. //
  758. // the locator we are requested to get attributes for may have come from a
  759. // server other than that identified in the locator. We will search any
  760. // VIEW_INFO buffers we have looking for the locator. In the worst case
  761. // we won't find it and will have to send a request to the server
  762. //
  763. error = SearchSessionsForAttribute((LPSTR)lpszLocator,
  764. (LPSTR)lpszAttribute,
  765. lpBuffer,
  766. lpdwBufferLength
  767. );
  768. if (error == ERROR_GOPHER_ATTRIBUTE_NOT_FOUND) {
  769. char request[MAX_GOPHER_SELECTOR_TEXT + 1];
  770. DWORD requestLen;
  771. char hostName[MAX_GOPHER_HOST_NAME + 1];
  772. DWORD hostNameLen;
  773. DWORD port;
  774. LPSTR gopherPlus;
  775. LPSESSION_INFO sessionInfo;
  776. DWORD requestType;
  777. //
  778. // its the worst case - we don't (or no longer) have the requested
  779. // locator/attributes. We must request them again from the server
  780. //
  781. //
  782. // pull the individual fields out of the locator. Not interested in display
  783. // string
  784. //
  785. requestLen = sizeof(request);
  786. hostNameLen = sizeof(hostName);
  787. if (!CrackLocator(lpszLocator,
  788. &requestType,
  789. NULL, // DisplayString
  790. NULL, // DisplayStringLength
  791. request, // SelectorString
  792. &requestLen, // SelectorStringLength
  793. hostName,
  794. &hostNameLen,
  795. &port,
  796. &gopherPlus
  797. )) {
  798. error = ERROR_GOPHER_INVALID_LOCATOR;
  799. goto quit;
  800. }
  801. //
  802. // if we already have a session to the server identified in the locator
  803. // then we will check if we already have the information stored in a
  804. // VIEW_INFO. If not then we must send the request for the attribute info
  805. //
  806. sessionInfo = FindOrCreateSession(hostName, port, &error);
  807. if (sessionInfo != NULL) {
  808. //
  809. // BUGBUG - IsGopherPlusSession needs to perform discovery if
  810. // unknown
  811. //
  812. // if (IsGopherPlusSession(sessionInfo)) {
  813. if (TRUE) {
  814. LPSTR attributeRequest;
  815. //
  816. // convert the request to a request for attributes that the
  817. // gopher server understands
  818. //
  819. attributeRequest = MakeAttributeRequest(request,
  820. (LPSTR)lpszAttribute
  821. );
  822. if (attributeRequest != NULL) {
  823. LPVIEW_INFO viewInfo;
  824. error = GetView(sessionInfo,
  825. ViewTypeFind,
  826. attributeRequest,
  827. TRUE, // RequestIsGopherPlus
  828. 0,
  829. &viewInfo
  830. );
  831. //
  832. // done with attribute request buffer (created by
  833. // MakeAttributeRequest)
  834. //
  835. DEL(attributeRequest);
  836. //
  837. // copy everything that came back to the caller's buffer
  838. // if there's enough space
  839. //
  840. if (error == ERROR_SUCCESS) {
  841. DWORD amountToCopy;
  842. INET_ASSERT(viewInfo->BufferInfo->Flags & BI_RECEIVE_COMPLETE);
  843. AcquireBufferLock(viewInfo->BufferInfo);
  844. amountToCopy = viewInfo->BufferInfo->BufferLength;
  845. //
  846. // if the buffer contains dot-terminated info, then
  847. // account for the dot
  848. //
  849. if ((viewInfo->BufferInfo->Flags & BI_DOT_AT_END)
  850. //
  851. // this *SHOULD* always be true, but just in case we
  852. // have an anomalous situation, we don't want to
  853. // return a negative value (i.e. a large DWORD value)
  854. //
  855. && (amountToCopy > GOPHER_DOT_TERMINATOR_LENGTH)) {
  856. amountToCopy -= GOPHER_DOT_TERMINATOR_LENGTH;
  857. }
  858. if (amountToCopy <= *lpdwBufferLength) {
  859. LPBYTE attributeBuffer;
  860. attributeBuffer = viewInfo->BufferInfo->Buffer;
  861. INET_ASSERT(attributeBuffer != NULL);
  862. memcpy(lpBuffer, attributeBuffer, amountToCopy);
  863. }
  864. //
  865. // whether we copied the data or not, indicate to the
  866. // caller how much data is available
  867. //
  868. *lpdwBufferLength = amountToCopy;
  869. //
  870. // we are done with the buffer. Unlock the BUFFER_INFO
  871. // and the VIEW_INFO. Both will probably be destroyed
  872. //
  873. ReleaseBufferLock(viewInfo->BufferInfo);
  874. DereferenceView(viewInfo);
  875. }
  876. } else {
  877. error = ERROR_NOT_ENOUGH_MEMORY;
  878. }
  879. } else {
  880. error = ERROR_GOPHER_NOT_GOPHER_PLUS;
  881. }
  882. //
  883. // dereference the session, possibly destroying it
  884. //
  885. DereferenceSession(sessionInfo);
  886. }
  887. }
  888. quit:
  889. DEBUG_LEAVE(error);
  890. return error;
  891. }
  892. #endif // defined(GOPHER_ATTRIBUTE_SUPPORT)
  893. //
  894. //DWORD
  895. //wGopherConnect(
  896. // IN LPCSTR lpszServerName,
  897. // IN INTERNET_PORT nServerPort,
  898. // IN LPCSTR lpszUsername,
  899. // IN LPCSTR lpszPassword,
  900. // IN DWORD dwService,
  901. // IN DWORD dwFlags,
  902. // OUT LPHINTERNET lpConnectHandle
  903. // )
  904. //
  905. ///*++
  906. //
  907. //Routine Description:
  908. //
  909. // Creates a default gopher connection information 'object' from the
  910. // parameters supplied in InternetConnect()
  911. //
  912. //Arguments:
  913. //
  914. // lpszServerName - pointer to default gopher server
  915. //
  916. // nServerPort - default configured gopher port (0 for use default of 70)
  917. //
  918. // lpszUsername - our user's name
  919. //
  920. // lpszPassword - and password
  921. //
  922. // dwService - INTERNET_SERVICE_GOPHER
  923. //
  924. // dwFlags - unused
  925. //
  926. // lpConnectHandle - where we return the pointer to the 'object'
  927. //
  928. //Return Value:
  929. //
  930. // DWORD
  931. // Success - ERROR_SUCCESS
  932. //
  933. // Failure - ERROR_NOT_ENOUGH_MEMORY
  934. //
  935. //--*/
  936. //
  937. //{
  938. // LPGOPHER_DEFAULT_CONNECT_INFO info;
  939. // DWORD error;
  940. //
  941. // DEBUG_ENTER((DBG_GOPHER,
  942. // Dword,
  943. // "wGopherConnect",
  944. // "%q, %d, %q, %q, %d, %#x, %#x",
  945. // lpszServerName,
  946. // nServerPort,
  947. // lpszUsername,
  948. // lpszPassword,
  949. // dwService,
  950. // dwFlags,
  951. // lpConnectHandle
  952. // ));
  953. //
  954. // UNREFERENCED_PARAMETER(dwService);
  955. // UNREFERENCED_PARAMETER(dwFlags);
  956. //
  957. // info = NEW(GOPHER_DEFAULT_CONNECT_INFO);
  958. // if (info != NULL) {
  959. // error = ERROR_SUCCESS;
  960. // if (lpszServerName != NULL) {
  961. // info->HostName = NEW_STRING((LPSTR)lpszServerName);
  962. // if (info->HostName == NULL) {
  963. // error = ERROR_NOT_ENOUGH_MEMORY;
  964. // }
  965. // }
  966. // info->Port = nServerPort;
  967. // if ((lpszUsername != NULL) && (error == ERROR_SUCCESS)) {
  968. // info->UserName = NEW_STRING((LPSTR)lpszUsername);
  969. // if (info->UserName == NULL) {
  970. // error = ERROR_NOT_ENOUGH_MEMORY;
  971. // }
  972. // }
  973. // if ((lpszPassword != NULL) && (error == ERROR_NOT_ENOUGH_MEMORY)) {
  974. // info->Password = NEW_STRING((LPSTR)lpszPassword);
  975. // if (info->Password == NULL) {
  976. // error = ERROR_NOT_ENOUGH_MEMORY;
  977. // }
  978. // }
  979. // } else {
  980. // error = ERROR_NOT_ENOUGH_MEMORY;
  981. // }
  982. // if (error == ERROR_SUCCESS) {
  983. // *lpConnectHandle = (HINTERNET)info;
  984. // } else {
  985. // if (info != NULL) {
  986. // if (info->HostName != NULL) {
  987. // DEL_STRING(info->HostName);
  988. // }
  989. // if (info->UserName != NULL) {
  990. // DEL_STRING(info->UserName);
  991. // }
  992. // if (info->Password != NULL) {
  993. // DEL_STRING(info->Password);
  994. // }
  995. // DEL(info);
  996. // }
  997. // }
  998. //
  999. // DEBUG_LEAVE(error);
  1000. //
  1001. // return error;
  1002. //}
  1003. //
  1004. //
  1005. //DWORD
  1006. //wGopherDisconnect(
  1007. // IN HINTERNET hInternet
  1008. // )
  1009. //
  1010. ///*++
  1011. //
  1012. //Routine Description:
  1013. //
  1014. // Undoes the work of wGopherConnect
  1015. //
  1016. //Arguments:
  1017. //
  1018. // hInternet - handle to object created by wGopherConnect. Actually just a
  1019. // pointer to GOPHER_DEFAULT_CONNECT_INFO structure
  1020. //
  1021. //Return Value:
  1022. //
  1023. // DWORD
  1024. // Success - ERROR_SUCCESS
  1025. //
  1026. // Failure - ERROR_INTERNET_INTERNAL_ERROR
  1027. // hInternet does not identify a GOPHER_DEFAULT_CONNECT_INFO
  1028. //
  1029. //--*/
  1030. //
  1031. //{
  1032. // LPGOPHER_DEFAULT_CONNECT_INFO info;
  1033. // DWORD error;
  1034. //
  1035. // DEBUG_ENTER((DBG_GOPHER,
  1036. // Dword,
  1037. // "wGopherDisconnect",
  1038. // "%#x",
  1039. // hInternet
  1040. // ));
  1041. //
  1042. // //
  1043. // // BUGBUG - not expecting bogus pointer?!
  1044. // //
  1045. //
  1046. // info = (LPGOPHER_DEFAULT_CONNECT_INFO)hInternet;
  1047. // if (info != NULL) {
  1048. // if (info->HostName != NULL) {
  1049. // DEL_STRING(info->HostName);
  1050. // }
  1051. // if (info->UserName != NULL) {
  1052. // DEL_STRING(info->UserName);
  1053. // }
  1054. // if (info->Password != NULL) {
  1055. // DEL_STRING(info->Password);
  1056. // }
  1057. // DEL(info);
  1058. // error = ERROR_SUCCESS;
  1059. // } else {
  1060. // error = ERROR_INTERNET_INTERNAL_ERROR;
  1061. // }
  1062. //
  1063. // DEBUG_LEAVE(error);
  1064. //
  1065. // return error;
  1066. //}
  1067. //
  1068. // private functions
  1069. //
  1070. PRIVATE
  1071. DWORD
  1072. GetView(
  1073. IN LPSESSION_INFO SessionInfo,
  1074. IN VIEW_TYPE ViewType,
  1075. IN LPSTR Request,
  1076. IN BOOL RequestIsGopherPlus,
  1077. IN DWORD ResponseFlags,
  1078. OUT LPVIEW_INFO* pViewInfo
  1079. )
  1080. /*++
  1081. Routine Description:
  1082. Creates a 'view object'. Sends a request to the gopher server and receives
  1083. the response. The response data is the view. Multiple simultaneous
  1084. requests for the same data from the same server are serialized. It is
  1085. possible that when this function terminates, the entire response may not
  1086. have been received, but we have enough to return to the caller. In this
  1087. case, a background thread may be actively receiving the remainder of the
  1088. response
  1089. Arguments:
  1090. SessionInfo - pointer to SESSION_INFO describing the gopher server
  1091. ViewType - type of view being requested, File or Find
  1092. Request - gopher request string
  1093. RequestIsGopherPlus - TRUE if this is a gopher+ request
  1094. ResponseFlags - bit-mask describing expected response buffer
  1095. pViewInfo - returned view info
  1096. Return Value:
  1097. DWORD
  1098. Success - ERROR_SUCCESS
  1099. Failure - ERROR_NOT_ENOUGH_MEMORY
  1100. --*/
  1101. {
  1102. DEBUG_ENTER((DBG_GOPHER,
  1103. Dword,
  1104. "GetView",
  1105. "%#x, %s, %q, %B, %#x, %#x",
  1106. SessionInfo,
  1107. (ViewType == ViewTypeFile) ? "File" : "Find",
  1108. Request,
  1109. RequestIsGopherPlus,
  1110. ResponseFlags,
  1111. pViewInfo
  1112. ));
  1113. DWORD error;
  1114. LPVIEW_INFO viewInfo;
  1115. BOOL viewCloned;
  1116. //
  1117. // find the view info that contains the results of sending Request to
  1118. // the server identified by the session info. If this succeeds then we
  1119. // will have added another reference to the session info
  1120. //
  1121. viewInfo = CreateView(SessionInfo, ViewType, Request, &error, &viewCloned);
  1122. if (viewInfo != NULL) {
  1123. LPBUFFER_INFO bufferInfo;
  1124. //
  1125. // if this is a gopher+ request then mark it in the VIEW_INFO
  1126. //
  1127. if (RequestIsGopherPlus) {
  1128. viewInfo->Flags |= VI_GOPHER_PLUS;
  1129. }
  1130. bufferInfo = viewInfo->BufferInfo;
  1131. INET_ASSERT(bufferInfo != NULL);
  1132. //
  1133. // set the expected response type (dot-terminated or not.) Only really
  1134. // useful if request is gopher0 file
  1135. //
  1136. bufferInfo->Flags |= ResponseFlags;
  1137. if (!viewCloned) {
  1138. BOOL receiveComplete;
  1139. //
  1140. // if this is a directory view then we need to communicate the fact
  1141. // that ReceiveResponse needs to allocate a buffer
  1142. //
  1143. if (ViewType == ViewTypeFind) {
  1144. bufferInfo->Flags |= BI_BUFFER_RESPONSE;
  1145. }
  1146. //
  1147. // the view was created. We must make the request to the gopher
  1148. // server
  1149. //
  1150. error = GopherTransaction(viewInfo);
  1151. //
  1152. // if there were multiple simultaneous requests for the same data
  1153. // then we must signal the request event to restart those other
  1154. // threads. If no other threads are waiting then we can dispense
  1155. // with the request event
  1156. //
  1157. // AcquireViewLock(SessionInfo, ViewType);
  1158. // if (bufferInfo->RequestWaiters != 0) {
  1159. //
  1160. // the request has completed, maybe with an error. In both
  1161. // cases, we signal the event in the BUFFER_INFO to allow
  1162. // any concurrent requesters of the same information to
  1163. // continue
  1164. //
  1165. // SetEvent(bufferInfo->RequestEvent);
  1166. // } else {
  1167. //
  1168. // no other concurrent waiters. The system can have an event
  1169. // back
  1170. //
  1171. // CloseHandle(bufferInfo->RequestEvent);
  1172. // bufferInfo->RequestEvent = NULL;
  1173. // }
  1174. // ReleaseViewLock(SessionInfo, ViewType);
  1175. } else {
  1176. //
  1177. // we no longer allow file views to be cloned - each request for a
  1178. // file must now make a separate connection to the server
  1179. //
  1180. INET_ASSERT(ViewType != ViewTypeFile);
  1181. //
  1182. // the view was cloned. If we made the request at the same time
  1183. // another thread was making the same request to the server then
  1184. // wait for that other thread to signal the request event. If the
  1185. // request event handle is NULL then we don't have to wait
  1186. //
  1187. // if (bufferInfo->RequestEvent != NULL) {
  1188. // WAIT_FOR_SINGLE_OBJECT(bufferInfo->RequestEvent, error);
  1189. // } else {
  1190. // error = WAIT_OBJECT_0;
  1191. // }
  1192. //
  1193. // if RequestEvent existed when this clone was generated then we
  1194. // decrement the number of waiters, now that we have access to the
  1195. // data.
  1196. // If the number of requesters goes to zero, we grab the view lock
  1197. // for this list. If the number of requesters is still zero then
  1198. // we close the event handle.
  1199. // The event handle was only required for a special purpose - to
  1200. // hold off multiple simultaneous requesters of the same data from
  1201. // the same server. Once the data has been retrieved, there is no
  1202. // need to keep the event
  1203. //
  1204. // if (bufferInfo->RequestWaiters != 0) {
  1205. // if (InterlockedDecrement(&bufferInfo->RequestWaiters) == 0) {
  1206. // AcquireViewLock(SessionInfo, ViewType);
  1207. // if (bufferInfo->RequestWaiters == 0) {
  1208. // CloseHandle(bufferInfo->RequestEvent);
  1209. // bufferInfo->RequestEvent = NULL;
  1210. // }
  1211. // ReleaseViewLock(SessionInfo, ViewType);
  1212. // }
  1213. // }
  1214. }
  1215. //
  1216. // if an error occurred then dereferencing the view should destroy it
  1217. //
  1218. if (error != ERROR_SUCCESS) {
  1219. viewInfo = DereferenceView(viewInfo);
  1220. }
  1221. }
  1222. *pViewInfo = viewInfo;
  1223. DEBUG_LEAVE(error);
  1224. return error;
  1225. }