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.

1065 lines
29 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name:
  4. gfrapiu.cxx
  5. Abstract:
  6. Common sub-API level functions
  7. Contents:
  8. TestLocatorType
  9. GetAttributes
  10. MakeAttributeRequest
  11. ParseGopherUrl
  12. GopherLocatorToUrl
  13. Author:
  14. Richard L Firth (rfirth) 19-Nov-1994
  15. Environment:
  16. Win32 user-level DLL
  17. Revision History:
  18. 19-Nov-1994
  19. Created
  20. --*/
  21. #include <wininetp.h>
  22. #include "gfrapih.h"
  23. // because wininet doesnt know IStream
  24. #define NO_SHLWAPI_STREAM
  25. #include <shlwapi.h>
  26. #include <shlwapip.h>
  27. #define INTERNET_DEFAULT_CSO_PORT 105
  28. #define INTERNET_MAX_WELL_KNOWN_PORT 1023
  29. //
  30. // functions
  31. //
  32. BOOL IsInappropriateGopherPort (INTERNET_PORT port)
  33. /*++
  34. Routine Description:
  35. Gopher URLs can encode arbitrary data to arbitrary ports. This characteristic
  36. enables malicious web pages to redirect IE to exploit security holes, for
  37. example, to spoof a mailer daemon inside a firewall. Based on experimentation,
  38. Netscape apparently disables gopher on ports 1 and 7 though 25 odd. That range
  39. covers many of the well-known ports catalogued by IANA but misses many others
  40. like 137 through 139, assigned for netbios over tcp/ip . Since gopher is
  41. becoming increasingly irrelevant, we prefer to be stricter . IE3 now disables
  42. gopher on ports less than 1024, except for 70, the standard gopher port, and
  43. 105, typically used for CSO name searches.
  44. Arguments: Port number
  45. Return Value: TRUE for success, FALSE for failure
  46. --*/
  47. {
  48. if (port > INTERNET_MAX_WELL_KNOWN_PORT)
  49. return FALSE;
  50. switch (port) {
  51. case INTERNET_INVALID_PORT_NUMBER:
  52. case INTERNET_DEFAULT_GOPHER_PORT:
  53. case INTERNET_DEFAULT_CSO_PORT:
  54. return FALSE;
  55. default:
  56. return TRUE;
  57. }
  58. }
  59. DWORD
  60. TestLocatorType(
  61. IN LPCSTR Locator,
  62. IN DWORD TypeMask
  63. )
  64. /*++
  65. Routine Description:
  66. Checks that Locator is valid and checks if it is of the specified type.
  67. This function is mainly for use by GfrIsXxxx APIs
  68. Arguments:
  69. Locator - pointer to app-supplied locator string
  70. TypeMask - gopher type mask to check for
  71. Return Value:
  72. DWORD
  73. Success - ERROR_SUCCESS
  74. Locator is good and of the specified type
  75. Failure - ERROR_INVALID_PARAMETER
  76. Locator is bad
  77. ERROR_INVALID_FUNCTION
  78. Locator is good, but not of the specified type
  79. --*/
  80. {
  81. DWORD error;
  82. BOOL success = FALSE;
  83. //
  84. // BUGBUG - 1. Do we really want to test this parameter?
  85. // 2. If so, is the length sufficient?
  86. //
  87. if (IsBadStringPtr(Locator, MAX_GOPHER_LOCATOR_LENGTH)) {
  88. error = ERROR_INVALID_PARAMETER;
  89. } else {
  90. DWORD gopherType;
  91. gopherType = GopherCharToType(*Locator);
  92. if (gopherType == INVALID_GOPHER_TYPE) {
  93. //
  94. // not a recognizable type - Locator is bogus
  95. //
  96. error = ERROR_INVALID_PARAMETER;
  97. } else if (gopherType & TypeMask) {
  98. error = ERROR_SUCCESS;
  99. } else {
  100. //
  101. // slight bogosity - need an error code to differentiate matched
  102. // vs. not-matched: INVALID_FUNCTION will do
  103. //
  104. error = ERROR_INVALID_FUNCTION;
  105. }
  106. }
  107. return error;
  108. }
  109. #if defined(GOPHER_ATTRIBUTE_SUPPORT)
  110. DWORD
  111. GetAttributes(
  112. IN GOPHER_ATTRIBUTE_ENUMERATOR Enumerator,
  113. IN DWORD CategoryId,
  114. IN DWORD AttributeId,
  115. IN LPCSTR AttributeName,
  116. IN LPSTR InBuffer,
  117. IN DWORD InBufferLength,
  118. OUT LPBYTE OutBuffer,
  119. IN DWORD OutBufferLength,
  120. OUT LPDWORD CharactersReturned
  121. )
  122. /*++
  123. Routine Description:
  124. Pulls attributes out of a buffer and puts them in the caller's buffer or
  125. enumerates them (if Enumerator supplied)
  126. Arguments:
  127. Enumerator - address of caller's enumerator function
  128. CategoryId - category of attribute(s)
  129. AttributeId - the attribute to return
  130. AttributeName - name of the attribute if not a known attribute
  131. InBuffer - pointer to buffer containing gopher+ attributes
  132. InBufferLength - length of attribute buffer
  133. OutBuffer - pointer to caller's buffer where attributes returned
  134. OutBufferLength - length of caller's buffer
  135. CharactersReturned - pointer to returned buffer length
  136. Return Value:
  137. DWORD
  138. Success - ERROR_SUCCESS
  139. Failure - ERROR_GOPHER_ATTRIBUTE_NOT_FOUND
  140. We couldn't find the requested attribute/category
  141. ERROR_INSUFFICIENT_BUFFER
  142. The caller's buffer isn't large enough to contain the
  143. attributes. *lpdwCharactersReturned will contain the
  144. required length
  145. ERROR_GOPHER_DATA_ERROR
  146. We couldn't parse the attributes for some reason
  147. --*/
  148. {
  149. DWORD error;
  150. *CharactersReturned = 0;
  151. //
  152. // the buffer starts with the "+INFO:" attribute describing the locator. We
  153. // don't return this as an attribute
  154. //
  155. if (SkipLine(&InBuffer, &InBufferLength)) {
  156. LPSTR endSection;
  157. BOOL done;
  158. BOOL found;
  159. BOOL more;
  160. if (CategoryId != GOPHER_CATEGORY_ID_ALL) {
  161. //
  162. // advance InBuffer to the line that contains the requested
  163. // attribute
  164. //
  165. found = FindAttribute(CategoryId,
  166. AttributeId,
  167. AttributeName,
  168. &InBuffer,
  169. &InBufferLength
  170. );
  171. if (found) {
  172. //
  173. // if the caller requested that we return all attributes in a
  174. // section, then skip the line containing the category name
  175. //
  176. if (AttributeId == GOPHER_ATTRIBUTE_ID_ALL) {
  177. found = SkipLine(&InBuffer, &InBufferLength);
  178. }
  179. if (found) {
  180. DWORD bufferLeft;
  181. //
  182. // get the end of the section or line in endSection
  183. //
  184. endSection = InBuffer;
  185. bufferLeft = InBufferLength;
  186. FindNextAttribute(CategoryId,
  187. AttributeId,
  188. &endSection,
  189. &bufferLeft
  190. );
  191. }
  192. }
  193. error = found ? ERROR_SUCCESS : ERROR_GOPHER_ATTRIBUTE_NOT_FOUND;
  194. } else {
  195. endSection = InBuffer + InBufferLength;
  196. }
  197. more = TRUE;
  198. done = FALSE;
  199. while ((error == ERROR_SUCCESS) && (InBuffer < endSection) && more) {
  200. LPSTR linePtr;
  201. char lineBuffer[256]; // arbitrary
  202. DWORD lineLength;
  203. BOOL ok;
  204. linePtr = lineBuffer;
  205. lineLength = sizeof(lineBuffer);
  206. ok = CopyToEol(&linePtr,
  207. &lineLength,
  208. &InBuffer,
  209. &InBufferLength
  210. );
  211. if (ok) {
  212. if (Enumerator != NULL) {
  213. //
  214. // if the line starts with a '+' then (we assume) we are
  215. // enumerating all attributes, in which case this line
  216. // just serves to identify the next attribute section. We
  217. // don't return any info
  218. //
  219. if (*linePtr == '+') {
  220. char newCategory[32]; // arbitrary
  221. int i;
  222. for (i = 0; i < sizeof(newCategory); ++i) {
  223. char ch;
  224. ch = linePtr[i];
  225. if ((ch == '\r') || (ch == '\n') || (ch == ' ') || (ch == ':')) {
  226. break;
  227. }
  228. newCategory[i] = ch;
  229. }
  230. newCategory[i] = '\0';
  231. MapAttributeToIds((LPCSTR)newCategory,
  232. &CategoryId,
  233. &AttributeId
  234. );
  235. if (CategoryId == GOPHER_CATEGORY_ID_ABSTRACT) {
  236. //
  237. // BUGBUG - the remainder of this line may contain
  238. // a locator identifying the location of
  239. // a file containing the abstract
  240. //
  241. }
  242. } else {
  243. error = EnumerateAttribute(Enumerator,
  244. linePtr,
  245. lineLength,
  246. OutBuffer,
  247. OutBufferLength,
  248. &more
  249. );
  250. done = TRUE;
  251. }
  252. } else {
  253. //
  254. // get the length of the line in lineLength. N.B. We have
  255. // to subtract an extra 1 because CopyToEol adds a '\0'
  256. //
  257. lineLength = sizeof(lineBuffer) - lineLength - 1;
  258. if (OutBufferLength >= lineLength) {
  259. memcpy(OutBuffer, lineBuffer, lineLength);
  260. OutBuffer += lineLength;
  261. OutBufferLength -= lineLength;
  262. done = TRUE;
  263. } else {
  264. error = ERROR_INSUFFICIENT_BUFFER;
  265. }
  266. //
  267. // always update the characters copied/required parameter
  268. //
  269. *CharactersReturned += lineLength;
  270. }
  271. } else {
  272. error = ERROR_GOPHER_DATA_ERROR;
  273. }
  274. }
  275. //
  276. // if nothing was copied or enumerated then the attribute was not found
  277. //
  278. if (!done && (error == ERROR_SUCCESS)) {
  279. error = ERROR_GOPHER_ATTRIBUTE_NOT_FOUND;
  280. }
  281. } else {
  282. error = ERROR_GOPHER_DATA_ERROR;
  283. }
  284. return error;
  285. }
  286. LPSTR
  287. MakeAttributeRequest(
  288. IN LPSTR Selector,
  289. IN LPSTR Attribute
  290. )
  291. /*++
  292. Routine Description:
  293. Converts a gopher+ request into a request for attributes. E.g. turns
  294. "0Foo" into "0Foo\t!+ADMIN"
  295. Arguments:
  296. Selector - pointer to identifier of gopher+ item to get attributes for
  297. Attribute - pointer to name of attribute(s) to retrieve
  298. Return Value:
  299. LPSTR
  300. Success - pointer to allocated memory containing attribute requester
  301. Failure - NULL
  302. --*/
  303. {
  304. INT selectorLength;
  305. INT attributeLength;
  306. LPSTR request;
  307. selectorLength = (Selector != NULL) ? strlen(Selector) : 0;
  308. attributeLength = (Attribute != NULL) ? strlen(Attribute) : 0;
  309. request = NEW_MEMORY(selectorLength
  310. //
  311. // sizeof(GOPHER_PLUS_INFO_REQUEST) includes 2 for
  312. // <CR><LF> and 1 for terminator
  313. //
  314. + sizeof(GOPHER_PLUS_INFO_REQUEST)
  315. + attributeLength,
  316. CHAR
  317. );
  318. if (request != NULL) {
  319. if (Selector != NULL) {
  320. memcpy(request, Selector, selectorLength);
  321. }
  322. memcpy(&request[selectorLength],
  323. GOPHER_PLUS_ITEM_INFO,
  324. sizeof(GOPHER_PLUS_ITEM_INFO) - 1
  325. );
  326. selectorLength += sizeof(GOPHER_PLUS_ITEM_INFO) - 1;
  327. if (Attribute != NULL) {
  328. memcpy(&request[selectorLength], Attribute, attributeLength);
  329. selectorLength += attributeLength;
  330. }
  331. memcpy(&request[selectorLength],
  332. GOPHER_REQUEST_TERMINATOR,
  333. sizeof(GOPHER_REQUEST_TERMINATOR)
  334. );
  335. }
  336. return request;
  337. }
  338. #endif // defined(GOPHER_ATTRIBUTE_SUPPORT)
  339. DWORD
  340. ParseGopherUrl(
  341. IN OUT LPHINTERNET hInternet,
  342. IN LPSTR Url,
  343. IN DWORD SchemeLength,
  344. IN LPSTR Headers,
  345. IN DWORD HeadersLength,
  346. IN DWORD OpenFlags,
  347. IN DWORD_PTR Context
  348. )
  349. /*++
  350. Routine Description:
  351. URL parser for gopher URLs. Support function for InternetOpenUrl() and
  352. ParseUrl().
  353. This is a macro function that just cracks the URL and calls gopher APIs to
  354. do the work
  355. Arguments:
  356. hInternet - IN: Internet gateway handle
  357. OUT: if successful handle of opened item, else undefined
  358. Url - pointer to string containing gopher URL to open
  359. SchemeLength - length of the URL scheme, exluding "://"
  360. Headers - unused for gopher
  361. HeadersLength - unused for gopher
  362. OpenFlags - optional flags for opening a file (cache/no-cache, etc.)
  363. Context - app-supplied context value for call-backs
  364. Return Value:
  365. DWORD
  366. Success - ERROR_SUCCESS
  367. Failure - ERROR_INTERNET_INVALID_URL
  368. The URL passed in could not be parsed
  369. --*/
  370. {
  371. DEBUG_ENTER((DBG_GOPHER,
  372. Dword,
  373. "ParseGopherUrl",
  374. "%#x [%#x], %q, %d, %#x, %d, %#x, %#x",
  375. hInternet,
  376. *hInternet,
  377. Url,
  378. SchemeLength,
  379. Headers,
  380. HeadersLength,
  381. OpenFlags,
  382. Context
  383. ));
  384. DWORD error;
  385. DWORD gopherType;
  386. LPSTR selector;
  387. LPSTR searchString;
  388. HINTERNET hMapped = NULL;
  389. UNREFERENCED_PARAMETER(Headers);
  390. UNREFERENCED_PARAMETER(HeadersLength);
  391. //
  392. // extract the address information - no user name or password
  393. //
  394. DWORD urlLength;
  395. LPSTR pHostName;
  396. DWORD hostNameLength;
  397. INTERNET_PORT port;
  398. LPSTR lpszUrl = Url;
  399. Url += SchemeLength + sizeof("://") - 1;
  400. error = GetUrlAddress(&Url,
  401. &urlLength,
  402. NULL,
  403. NULL,
  404. NULL,
  405. NULL,
  406. &pHostName,
  407. &hostNameLength,
  408. &port,
  409. NULL
  410. );
  411. if (error != ERROR_SUCCESS) {
  412. goto quit;
  413. }
  414. if (IsInappropriateGopherPort(port)) {
  415. error = ERROR_INTERNET_INVALID_URL;
  416. goto quit;
  417. }
  418. //
  419. // a '/' between address and url-path is not significant to gopher
  420. //
  421. if (*Url == '/') {
  422. ++Url;
  423. --urlLength;
  424. //
  425. // the fact that we can ignore the '/' between address and url-path
  426. // means that it is okay to write a '\0' at the end of the host name
  427. //
  428. pHostName[hostNameLength] = '\0';
  429. }
  430. //
  431. // if the URL just consisted of gopher://host[:port] then by default we are
  432. // referencing the root gopher directory
  433. //
  434. if (*Url != '\0') {
  435. //
  436. // Before decoding, convert '?' to tab and thereafter any '+' to ' '
  437. //
  438. LPSTR lpszScan = strchr (Url, '?');
  439. if (lpszScan)
  440. {
  441. *lpszScan++ = '\t';
  442. while (*lpszScan)
  443. {
  444. INET_ASSERT (*lpszScan != '?'); // should be encoded
  445. if (*lpszScan == '+')
  446. *lpszScan = ' ';
  447. lpszScan++;
  448. }
  449. }
  450. //
  451. // we need to convert the url-path before checking it for the search
  452. // string and gopher+ fields because we need to search for '\t' which
  453. // is currently encoded
  454. //
  455. if(FAILED(UrlUnescapeInPlace(Url, 0))){
  456. goto quit;
  457. }
  458. urlLength = lstrlen(Url);
  459. //
  460. // find the type of the gopher resource; if unknown, treat as a file
  461. //
  462. gopherType = GopherCharToType(Url[0]);
  463. selector = &Url[1];
  464. //
  465. // urlLength is now the length of the converted selector
  466. //
  467. --urlLength;
  468. searchString = (LPSTR)memchr((LPVOID)selector, '\t', urlLength);
  469. if (searchString != NULL) {
  470. LPSTR plusString;
  471. //
  472. // zero-terminate the search string, then check if for a gopher+
  473. // component
  474. //
  475. *searchString++ = '\0';
  476. plusString = (LPSTR)memchr((LPVOID)searchString,
  477. '\t',
  478. urlLength - (DWORD) (searchString - selector)
  479. );
  480. if (plusString != NULL) {
  481. *plusString++ = '\0';
  482. gopherType |= GOPHER_TYPE_GOPHER_PLUS;
  483. //
  484. // if the URL defines a file then we may have a view type
  485. //
  486. //
  487. // BUGBUG - need to handle:
  488. //
  489. // - alternate file views
  490. // - attribute requests (?)
  491. // - ASK forms
  492. //
  493. }
  494. }
  495. } else {
  496. gopherType = GOPHER_TYPE_DIRECTORY;
  497. selector = NULL;
  498. searchString = NULL;
  499. }
  500. HINTERNET hConnect;
  501. //
  502. // initialize in case of error
  503. //
  504. hConnect = NULL;
  505. //
  506. // get the offline state
  507. //
  508. BOOL bOffline;
  509. DWORD dwFlags;
  510. bOffline = IsOffline();
  511. if (bOffline || (OpenFlags & INTERNET_FLAG_OFFLINE)) {
  512. dwFlags = INTERNET_FLAG_OFFLINE;
  513. } else {
  514. dwFlags = 0;
  515. }
  516. //
  517. // try to create a locator from the various parts
  518. //
  519. char locator[MAX_GOPHER_LOCATOR_LENGTH + 1];
  520. DWORD locatorLength;
  521. locatorLength = sizeof(locator);
  522. if (GopherCreateLocator(pHostName,
  523. port,
  524. NULL,
  525. selector,
  526. gopherType,
  527. locator,
  528. &locatorLength
  529. )) {
  530. //
  531. // ok, all parts present and correct; open a handle to the gopher
  532. // resource
  533. //
  534. hConnect = InternetConnect(*hInternet,
  535. pHostName,
  536. port,
  537. NULL, // lpszUserName
  538. NULL, // lpszPassword
  539. INTERNET_SERVICE_GOPHER,
  540. dwFlags,
  541. //
  542. // we are creating a "hidden" handle - don't
  543. // tell the app about it
  544. //
  545. INTERNET_NO_CALLBACK
  546. );
  547. try_again:
  548. if (hConnect != NULL) {
  549. HINTERNET handle;
  550. if ( hMapped == NULL )
  551. {
  552. error = MapHandleToAddress(hConnect, (LPVOID *)&hMapped, FALSE);
  553. if ( (error != ERROR_SUCCESS) && (hMapped == NULL) )
  554. {
  555. goto error_quit;
  556. }
  557. error = ERROR_SUCCESS;
  558. }
  559. INET_ASSERT(hMapped != NULL);
  560. ((INTERNET_CONNECT_HANDLE_OBJECT *)hMapped)->SetURL(lpszUrl);
  561. if ( IS_GOPHER_DIRECTORY(gopherType)
  562. || IS_GOPHER_SEARCH_SERVER(gopherType)) {
  563. // set htmlfind only if RAW is not asked
  564. if (!(OpenFlags & INTERNET_FLAG_RAW_DATA)) {
  565. ((INTERNET_CONNECT_HANDLE_OBJECT *)hMapped)->SetHtmlFind(TRUE);
  566. //
  567. // BUGBUG: we don't have time to handle CSO searches
  568. //
  569. if (IS_GOPHER_PHONE_SERVER (gopherType))
  570. goto cso_hack;
  571. if ( IS_GOPHER_SEARCH_SERVER(gopherType)
  572. && (!searchString || !searchString[0])) {
  573. cso_hack:
  574. handle = NULL;
  575. if (ERROR_SUCCESS == RMakeGfrFixedObjectHandle
  576. (hMapped, &handle, gopherType)) {
  577. handle = ((HANDLE_OBJECT *)handle)->GetPseudoHandle();
  578. }
  579. DereferenceObject((LPVOID)hMapped);
  580. goto got_handle;
  581. }
  582. }
  583. handle = GopherFindFirstFile(hConnect,
  584. locator,
  585. searchString,
  586. NULL,
  587. OpenFlags | dwFlags,
  588. Context
  589. );
  590. } else {
  591. handle = GopherOpenFile(hConnect,
  592. locator,
  593. NULL,
  594. OpenFlags | dwFlags,
  595. Context
  596. );
  597. }
  598. got_handle:
  599. if (handle != NULL) {
  600. //
  601. // map the handles
  602. //
  603. HINTERNET hRequestMapped;
  604. error = MapHandleToAddress(handle, (LPVOID *)&hRequestMapped, FALSE);
  605. INET_ASSERT(error == ERROR_SUCCESS);
  606. HINTERNET hConnectMapped;
  607. error = MapHandleToAddress(hConnect, (LPVOID *)&hConnectMapped, FALSE);
  608. INET_ASSERT(error == ERROR_SUCCESS);
  609. //
  610. // link the request and connect handles so that the connect handle
  611. // object will be deleted when the request handle is closed
  612. //
  613. RSetParentHandle(hRequestMapped, hConnectMapped, TRUE);
  614. //
  615. // reduce the reference counts incremented by MapHandleToAddress()
  616. //
  617. if (hRequestMapped != NULL) {
  618. DereferenceObject((LPVOID)hRequestMapped);
  619. }
  620. if (hConnectMapped != NULL) {
  621. DereferenceObject((LPVOID)hConnectMapped);
  622. }
  623. //
  624. // return the request handle to the caller
  625. //
  626. *hInternet = handle;
  627. error = ERROR_SUCCESS;
  628. goto quit;
  629. } else if (!bOffline && IsOffline() && !(dwFlags & INTERNET_FLAG_OFFLINE)) {
  630. //
  631. // we went offline during the request. Try again, this time
  632. // from cache
  633. //
  634. dwFlags = INTERNET_FLAG_OFFLINE;
  635. goto try_again;
  636. }
  637. }
  638. }
  639. error_quit:
  640. if ( hMapped != NULL )
  641. {
  642. DereferenceObject((LPVOID)hMapped);
  643. hMapped = NULL;
  644. }
  645. error = GetLastError();
  646. if (hConnect != NULL) {
  647. //
  648. // BUGBUG - this should close the item handle also (if open)
  649. //
  650. _InternetCloseHandle(hConnect);
  651. }
  652. INET_ASSERT(error != ERROR_SUCCESS);
  653. quit:
  654. DEBUG_LEAVE(error);
  655. return error;
  656. }
  657. DWORD
  658. GopherLocatorToUrl(
  659. IN LPSTR Locator,
  660. OUT LPSTR Buffer,
  661. IN DWORD BufferLength,
  662. OUT LPDWORD UrlLength
  663. )
  664. /*++
  665. Routine Description:
  666. Converts a gopher locator to a gopher URL. E.g. converts:
  667. 1foo\tFoo Directory\tfoo.host\t77\t+
  668. to the URL:
  669. gopher://foo.host:77/1Foo%20Directory%09%09%2B
  670. Arguments:
  671. Locator - pointer to gopher locator to convert
  672. Buffer - pointer to buffer where URL is written
  673. BufferLength - size of Buffer in bytes
  674. UrlLength - number of bytes written to Buffer
  675. Return Value:
  676. DWORD
  677. Success - ERROR_SUCCESS
  678. Failure - ERROR_INTERNET_INTERNAL_ERROR
  679. We blew an internal buffer limit
  680. ERROR_INSUFFICIENT_BUFFER
  681. Buffer is not large enough to hold the converted URL
  682. --*/
  683. {
  684. DWORD gopherType;
  685. char selector[MAX_GOPHER_SELECTOR_TEXT + 1];
  686. DWORD selectorLength;
  687. char hostName[MAX_GOPHER_HOST_NAME + 1];
  688. DWORD hostNameLength;
  689. DWORD gopherPort;
  690. LPSTR gopherPlus;
  691. char urlBuf[INTERNET_MAX_URL_LENGTH];
  692. DWORD urlBufferLength;
  693. LPSTR urlBuffer;
  694. DWORD error;
  695. DWORD bufLen;
  696. urlBufferLength = sizeof(urlBuf);
  697. urlBuffer = urlBuf;
  698. bufLen = BufferLength;
  699. //
  700. // start with the gopher protocol specifier
  701. //
  702. if (bufLen > sizeof("gopher://")) {
  703. memcpy(Buffer, "gopher://", sizeof("gopher://") - 1);
  704. Buffer += sizeof("gopher://") - 1;
  705. bufLen -= sizeof("gopher://") - 1;
  706. } else {
  707. return ERROR_INSUFFICIENT_BUFFER;
  708. }
  709. //
  710. // use CrackLocator() to get the individual parts of the locator
  711. //
  712. selectorLength = sizeof(selector);
  713. hostNameLength = sizeof(hostName);
  714. if (!CrackLocator(Locator,
  715. &gopherType,
  716. NULL, // DisplayString - we don't care about this in the URL
  717. NULL, // DisplayStringLength
  718. selector,
  719. &selectorLength,
  720. hostName,
  721. &hostNameLength,
  722. &gopherPort,
  723. &gopherPlus
  724. )) {
  725. //
  726. // most likely we bust a limit!
  727. //
  728. return ERROR_INTERNET_INTERNAL_ERROR;
  729. }
  730. //
  731. // add in the host name
  732. //
  733. if (bufLen > hostNameLength) {
  734. memcpy(Buffer, hostName, hostNameLength);
  735. Buffer += hostNameLength;
  736. bufLen -= hostNameLength;
  737. } else {
  738. return ERROR_INSUFFICIENT_BUFFER;
  739. }
  740. //
  741. // add the port, but only if it is not the default (70)
  742. //
  743. if (gopherPort != INTERNET_DEFAULT_GOPHER_PORT) {
  744. if (bufLen > 1 + INTERNET_MAX_PORT_NUMBER_LENGTH) {
  745. int n;
  746. n = wsprintf(Buffer, ":%u", gopherPort);
  747. Buffer += n;
  748. bufLen -= (DWORD)n;
  749. } else {
  750. return ERROR_INSUFFICIENT_BUFFER;
  751. }
  752. }
  753. //
  754. // add the URL-path separator and the locator type character
  755. //
  756. if (bufLen > 2) {
  757. *Buffer++ = '/';
  758. *Buffer++ = *Locator;
  759. bufLen -= 2;
  760. }
  761. //
  762. // copy the selector string, and any gopher+ addenda to a separater buffer
  763. //
  764. if (urlBufferLength > selectorLength) {
  765. memcpy(urlBuffer, selector, selectorLength);
  766. urlBuffer += selectorLength;
  767. urlBufferLength -= selectorLength;
  768. }
  769. //
  770. // if the locator specifies a gopher+ item then add the gopher+ indicator
  771. //
  772. if (gopherPlus != NULL) {
  773. if (urlBufferLength > 3) {
  774. memcpy(urlBuffer, "\t\t+", 3);
  775. urlBufferLength -= 3;
  776. urlBuffer += 3;
  777. }
  778. }
  779. //
  780. // finally terminate the URL
  781. //
  782. if (urlBufferLength >= 1) {
  783. *urlBuffer++ = '\0';
  784. --urlBufferLength;
  785. } else {
  786. return ERROR_INSUFFICIENT_BUFFER;
  787. }
  788. //
  789. // now escape any special characters (e.g. space, tab, etc.) in the url-path
  790. //
  791. *UrlLength = bufLen;
  792. error = EncodeUrlPath(NO_ENCODE_PATH_SEP,
  793. SCHEME_GOPHER,
  794. urlBuf,
  795. sizeof(urlBuf) - urlBufferLength - 1,
  796. Buffer,
  797. UrlLength
  798. );
  799. if (error == ERROR_SUCCESS) {
  800. *UrlLength += BufferLength - bufLen;
  801. }
  802. return error;
  803. }