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.

3424 lines
86 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name:
  4. parse.cxx
  5. Abstract:
  6. Contains functions to parse gopher information received from the server
  7. Contents:
  8. IsValidLocator
  9. IsGopherPlus
  10. CrackLocator
  11. GopherCharToType
  12. GopherTypeToChar
  13. GetDirEntry
  14. (GopherLocatorToFindData)
  15. ReadData
  16. (ExtractLine)
  17. (ExtractDisplayString)
  18. CopyToEol
  19. IsGopherPlusToken
  20. MapAttributeNameToId
  21. MapAttributeToIds
  22. MapAttributeIdToNames
  23. GetGopherNumber
  24. ExtractDateAndTime
  25. ExtractView
  26. FindAttribute
  27. FindNextAttribute
  28. EnumerateAttribute
  29. ParseIntField
  30. ParseDwordField
  31. ParseStringField
  32. ParseAdminAttribute
  33. ParseModDateAttribute
  34. ParseAbstractAttribute
  35. ParseViewAttribute
  36. ParseTreewalkAttribute
  37. ParseUnknownAttribute
  38. (ExtractAttributeName)
  39. (CharacterCount)
  40. (CountCharactersToEol)
  41. (CopyString)
  42. Author:
  43. Richard L Firth (rfirth) 17-Oct-1994
  44. Environment:
  45. Win32 user-level DLL
  46. Revision History:
  47. 17-Oct-1994 rfirth
  48. Created
  49. --*/
  50. #include <wininetp.h>
  51. #include "gfrapih.h"
  52. #include "iert.h"
  53. //
  54. // manifests
  55. //
  56. #define DEFAULT_LINE_BUFFER_LENGTH 1024 // arbitrary
  57. #define DEFAULT_ATTRIBUTE_NAME_LENGTH 64 // "
  58. #define DEFAULT_LANGUAGE_NAME_LENGTH 32 // "
  59. #define DEFAULT_CONTENT_TYPE_NAME_LENGTH 80 // "
  60. #define SEARCH_TYPE_MOD_DATE 1
  61. #define SEARCH_TYPE_VIEW 2
  62. //
  63. // macros
  64. //
  65. #define NUMERIC_CHARACTER_TO_NUMBER(c) ((int)(c) - (int)('0'))
  66. //
  67. // prototypes
  68. //
  69. PRIVATE
  70. DWORD
  71. GopherLocatorToFindData(
  72. IN LPCSTR Locator,
  73. IN DWORD Length,
  74. OUT LPGOPHER_FIND_DATA FindData
  75. );
  76. PRIVATE
  77. DWORD
  78. ExtractLine(
  79. IN LPVIEW_INFO ViewInfo,
  80. OUT LPBYTE LineBuffer,
  81. IN OUT LPDWORD LineBufferLength,
  82. IN OUT LPDWORD DataOffset
  83. );
  84. PRIVATE
  85. DWORD
  86. ExtractDisplayString(
  87. IN LPCSTR Locator,
  88. IN OUT LPSTR* StringPointer,
  89. IN DWORD BufferLength
  90. );
  91. PRIVATE
  92. BOOL
  93. SkipLeading(
  94. IN OUT LPSTR* String,
  95. IN OUT LPDWORD Length
  96. );
  97. PRIVATE
  98. DWORD
  99. ExtractAttributeName(
  100. OUT LPSTR AttributeName,
  101. IN OUT LPDWORD AttributeNameLength,
  102. IN OUT LPSTR* LinePtr,
  103. IN OUT LPDWORD LineLength
  104. );
  105. PRIVATE
  106. DWORD
  107. CharacterCount(
  108. IN OUT LPSTR* LinePtr,
  109. IN OUT LPDWORD LineLength,
  110. IN LPSTR TerminationSet
  111. );
  112. PRIVATE
  113. DWORD
  114. CountCharactersToEol(
  115. IN OUT LPSTR* LinePtr,
  116. IN OUT LPDWORD LineLength
  117. );
  118. PRIVATE
  119. VOID
  120. CopyString(
  121. IN OUT LPSTR* String,
  122. IN LPSTR Source,
  123. IN DWORD Length
  124. );
  125. //
  126. // functions
  127. //
  128. BOOL
  129. IsValidLocator(
  130. IN LPCSTR Locator,
  131. IN DWORD MaximumLength
  132. )
  133. /*++
  134. Routine Description:
  135. Given a locator string, determines whether it is a valid gopher locator. A
  136. valid gopher locator must have the form:
  137. <GopherChar><DisplayString>TAB<SelectorString>TAB<HostName>TAB<Port>[TAB<Gopher+Stuff>]<CR><LF><EOS>
  138. We don't care about the contents of DisplayString, SelectorString, HostName,
  139. Port or Gopher+Stuff, since these will be sorted out by sockets functions or
  140. the gopher protocol
  141. Arguments:
  142. Locator - pointer to locator string
  143. MaximumLength - maximum number characters that can be in the locator
  144. Return Value:
  145. BOOL
  146. TRUE - Locator is valid
  147. FALSE - Locator does not look like kosher gopher locator, already
  148. --*/
  149. {
  150. BOOL success;
  151. success = FALSE;
  152. __try {
  153. DWORD locatorLength;
  154. locatorLength = strlen(Locator);
  155. //
  156. // 1. Since there are all sorts of unspecified gopher types in the world,
  157. // we no longer test the type, but just make sure its not 0 (which would
  158. // have yielded a zero locatorLength). Also check that the locator doesn't
  159. // break the maximum length limit
  160. //
  161. if ((locatorLength != 0) && (locatorLength <= MaximumLength)) {
  162. //
  163. // 2. <DisplayString>. Can be empty. This can be any character, ANSI
  164. // or otherwise, we just don't care about its contents
  165. //
  166. ++Locator;
  167. --locatorLength;
  168. while ((*Locator != '\t') && (locatorLength != 0)) {
  169. --locatorLength;
  170. ++Locator;
  171. }
  172. if ((*Locator == '\t') && (locatorLength != 0)) {
  173. //
  174. // 3. <SelectorString>. Same rules as for DisplayString: contents
  175. // not interesting
  176. //
  177. ++Locator;
  178. --locatorLength;
  179. while ((*Locator != '\t') && (locatorLength != 0)) {
  180. --locatorLength;
  181. ++Locator;
  182. }
  183. if ((*Locator == '\t') && (locatorLength != 0)) {
  184. //
  185. // 4. <HostName>. Again, we don't care about the characters
  186. // that comprise HostName, or the length. We used to require
  187. // a non-zero length
  188. //
  189. ++Locator;
  190. --locatorLength;
  191. while ((*Locator != '\t') && (locatorLength != 0)) {
  192. --locatorLength;
  193. ++Locator;
  194. }
  195. if ((*Locator == '\t') && (locatorLength != 0)) {
  196. DWORD number;
  197. //
  198. // 5. Port. This must comprise 0..5 digit characters
  199. //
  200. ++Locator;
  201. --locatorLength;
  202. number = 0;
  203. while ((*Locator != '\t')
  204. && (*Locator != '\r')
  205. && (*Locator != '\n')
  206. && (*Locator >= '0')
  207. && (*Locator <= '9')) {
  208. //
  209. // we are kind of assuming no leading zeroes...
  210. //
  211. number = number * 10 + (DWORD)(*Locator - '0');
  212. --locatorLength;
  213. ++Locator;
  214. }
  215. if (number <= (DWORD)INTERNET_MAX_PORT_NUMBER_VALUE) {
  216. //
  217. // 6. Optional gopher+ characters. We ignore the
  218. // rest of the locator, and assume that it is
  219. // correct
  220. //
  221. if ((*Locator == '\t') && (locatorLength >= 2)) {
  222. do {
  223. ++Locator;
  224. --locatorLength;
  225. } while ( (*Locator != '\r')
  226. && (*Locator != '\n')
  227. && (locatorLength != 0) );
  228. }
  229. //
  230. // check for line termination. Because of the random
  231. // nature of gopher servers, we allow 0 or more '\r'
  232. // followed by '\n'. The locator MUST be terminated
  233. // by '\n'
  234. //
  235. while ((*Locator == '\r') && (locatorLength != 0)) {
  236. ++Locator;
  237. --locatorLength;
  238. }
  239. if ((*Locator == '\n') && (locatorLength == 1)) {
  240. success = TRUE;
  241. }
  242. }
  243. }
  244. }
  245. }
  246. }
  247. } __except(EXCEPTION_EXECUTE_HANDLER) {
  248. success = FALSE;
  249. }
  250. ENDEXCEPT
  251. return success;
  252. }
  253. BOOL
  254. IsGopherPlus(
  255. IN LPCSTR Locator
  256. )
  257. /*++
  258. Routine Description:
  259. Returns TRUE if Locator describes a gopher+ request
  260. ASSUMES: 1. Locator is valid
  261. Arguments:
  262. Locator - pointer to locator to check
  263. Return Value:
  264. BOOL
  265. --*/
  266. {
  267. LPSTR plusStuff;
  268. if (!IsValidLocator(Locator, strlen(Locator))) {
  269. return FALSE;
  270. }
  271. //
  272. // use CrackLocator to see if there is gopher+ info on this locator
  273. //
  274. CrackLocator(Locator,
  275. NULL, // Type
  276. NULL, // DisplayString
  277. NULL, // DisplayStringLength
  278. NULL, // SelectorString
  279. NULL, // SelectorStringLength
  280. NULL, // HostName
  281. NULL, // HostNameLength
  282. NULL, // GopherPort
  283. &plusStuff
  284. );
  285. return (BOOL)(plusStuff != NULL);
  286. }
  287. BOOL
  288. CrackLocator(
  289. IN LPCSTR Locator,
  290. OUT LPDWORD Type OPTIONAL,
  291. OUT LPSTR DisplayString OPTIONAL,
  292. IN OUT LPDWORD DisplayStringLength OPTIONAL,
  293. OUT LPSTR SelectorString OPTIONAL,
  294. IN OUT LPDWORD SelectorStringLength OPTIONAL,
  295. OUT LPSTR HostName OPTIONAL,
  296. IN OUT LPDWORD HostNameLength OPTIONAL,
  297. OUT LPDWORD GopherPort OPTIONAL,
  298. OUT LPSTR* ExtraStuff OPTIONAL
  299. )
  300. /*++
  301. Routine Description:
  302. Given a locator, break it into its constituent parts. The Locator argument
  303. is NOT modified
  304. ASSUMES: 1. Locator is valid
  305. 2. If an optional pointer is supplied, the associated length
  306. parameter (if applicable) must also be supplied
  307. Arguments:
  308. Locator - pointer to locator to crack
  309. Type - optional returned type character
  310. DisplayString - optional returned display string
  311. DisplayStringLength - optional in/out display string buffer length
  312. SelectorString - optional returned selector string
  313. SelectorStringLength - optional in/out selector string buffer length
  314. HostName - optional returned host name
  315. HostNameLength - optional in/out host name buffer length
  316. GopherPort - optional returned gopher port
  317. ExtraStuff - optional returned extra (gopher+) data from end of locator.
  318. This argument is a returned pointer, not a buffer. Care
  319. should be taken since this argument aliases Locator (or
  320. part thereof)
  321. Return Value:
  322. TRUE - locator cracked ok
  323. FALSE - problem encountered cracking locator, probably substring
  324. breaks buffer limit
  325. --*/
  326. {
  327. LPSTR pTab;
  328. DWORD len;
  329. LPSTR extraStuff;
  330. DWORD locatorLength;
  331. locatorLength = strlen(Locator);
  332. if (ARGUMENT_PRESENT(Type)) {
  333. *Type = GopherCharToType(*Locator);
  334. }
  335. ++Locator; // past type character
  336. --locatorLength;
  337. pTab = (LPSTR)memchr((LPVOID)Locator, '\t', locatorLength);
  338. INET_ASSERT(pTab != NULL);
  339. len = (DWORD) (pTab - Locator);
  340. if (ARGUMENT_PRESENT(DisplayString)) {
  341. INET_ASSERT(DisplayStringLength != NULL);
  342. if (*DisplayStringLength <= len) {
  343. return FALSE;
  344. }
  345. memcpy(DisplayString, Locator, len);
  346. DisplayString[len] = '\0';
  347. *DisplayStringLength = len;
  348. }
  349. Locator = pTab + 1; // past display string and TAB
  350. locatorLength -= (len + 1);
  351. pTab = (LPSTR)memchr((LPVOID)Locator, '\t', locatorLength);
  352. INET_ASSERT(pTab != NULL);
  353. len = (DWORD) (pTab - Locator);
  354. if (ARGUMENT_PRESENT(SelectorString)) {
  355. INET_ASSERT(SelectorStringLength != NULL);
  356. if (*SelectorStringLength <= len) {
  357. return FALSE;
  358. }
  359. memcpy(SelectorString, Locator, len);
  360. SelectorString[len] = '\0';
  361. *SelectorStringLength = len;
  362. }
  363. Locator = pTab + 1; // past selector string and TAB
  364. locatorLength -= (len + 1);
  365. pTab = (LPSTR)memchr((LPVOID)Locator, '\t', locatorLength);
  366. INET_ASSERT(pTab != NULL);
  367. len = (DWORD) (pTab - Locator);
  368. if (ARGUMENT_PRESENT(HostName)) {
  369. INET_ASSERT(HostNameLength != NULL);
  370. if (*HostNameLength <= len) {
  371. return FALSE;
  372. }
  373. memcpy(HostName, Locator, len);
  374. HostName[len] = '\0';
  375. *HostNameLength = len;
  376. }
  377. Locator = pTab + 1; // past host name and TAB
  378. locatorLength -= (len + 1);
  379. pTab = (LPSTR)memchr(Locator, '\t', locatorLength);
  380. if (pTab != NULL) {
  381. extraStuff = pTab + 1; // past port and TAB
  382. } else {
  383. extraStuff = NULL;
  384. }
  385. if (ARGUMENT_PRESENT(GopherPort)) {
  386. *GopherPort = (DWORD)STRTOUL(Locator, NULL, 10);
  387. }
  388. if (ARGUMENT_PRESENT(ExtraStuff)) {
  389. *ExtraStuff = extraStuff;
  390. }
  391. return TRUE;
  392. }
  393. DWORD
  394. GopherCharToType(
  395. IN CHAR GopherChar
  396. )
  397. /*++
  398. Routine Description:
  399. Converts the gopher descriptor character to a Gfr attribute
  400. Arguments:
  401. GopherChar - the gopher character to convert
  402. Return Value:
  403. DWORD
  404. mapped gopher type or GOPHER_TYPE_UNKNOWN if we don't recognise the
  405. character
  406. --*/
  407. {
  408. //
  409. // these are the types currently specified in RFC 1436 (plus a few that
  410. // aren't)
  411. //
  412. switch (GopherChar) {
  413. case GOPHER_CHAR_TEXT_FILE:
  414. return GOPHER_TYPE_TEXT_FILE;
  415. case GOPHER_CHAR_DIRECTORY:
  416. return GOPHER_TYPE_DIRECTORY;
  417. case GOPHER_CHAR_CSO:
  418. return GOPHER_TYPE_CSO;
  419. case GOPHER_CHAR_ERROR:
  420. return GOPHER_TYPE_ERROR;
  421. case GOPHER_CHAR_MAC_BINHEX:
  422. return GOPHER_TYPE_MAC_BINHEX;
  423. case GOPHER_CHAR_DOS_ARCHIVE:
  424. return GOPHER_TYPE_DOS_ARCHIVE;
  425. case GOPHER_CHAR_UNIX_UUENCODED:
  426. return GOPHER_TYPE_UNIX_UUENCODED;
  427. case GOPHER_CHAR_INDEX_SERVER:
  428. return GOPHER_TYPE_INDEX_SERVER;
  429. case GOPHER_CHAR_TELNET:
  430. return GOPHER_TYPE_TELNET;
  431. case GOPHER_CHAR_BINARY:
  432. return GOPHER_TYPE_BINARY;
  433. case GOPHER_CHAR_REDUNDANT:
  434. return GOPHER_TYPE_REDUNDANT;
  435. case GOPHER_CHAR_TN3270:
  436. return GOPHER_TYPE_TN3270;
  437. case GOPHER_CHAR_GIF:
  438. return GOPHER_TYPE_GIF;
  439. case GOPHER_CHAR_IMAGE:
  440. return GOPHER_TYPE_IMAGE;
  441. case GOPHER_CHAR_BITMAP:
  442. return GOPHER_TYPE_BITMAP;
  443. case GOPHER_CHAR_MOVIE:
  444. return GOPHER_TYPE_MOVIE;
  445. case GOPHER_CHAR_SOUND: // '<'
  446. case GOPHER_CHAR_SOUND_2: // 's'
  447. return GOPHER_TYPE_SOUND;
  448. case GOPHER_CHAR_HTML:
  449. return GOPHER_TYPE_HTML;
  450. case GOPHER_CHAR_PDF:
  451. return GOPHER_TYPE_PDF;
  452. case GOPHER_CHAR_CALENDAR:
  453. return GOPHER_TYPE_CALENDAR;
  454. case GOPHER_CHAR_INLINE:
  455. return GOPHER_TYPE_INLINE;
  456. }
  457. return GOPHER_TYPE_UNKNOWN;
  458. }
  459. CHAR
  460. GopherTypeToChar(
  461. IN DWORD GopherType
  462. )
  463. /*++
  464. Routine Description:
  465. Opposite of GopherCharToType
  466. Arguments:
  467. GopherType - bitmap of attributes. Only one file type and gopher plus
  468. attributes can be set simultaneously
  469. Return Value:
  470. CHAR
  471. Success - mapped gopher char
  472. Failure - INVALID_GOPHER_TYPE
  473. --*/
  474. {
  475. switch (GopherType & GOPHER_TYPE_MASK) {
  476. case GOPHER_TYPE_TEXT_FILE:
  477. return GOPHER_CHAR_TEXT_FILE;
  478. case GOPHER_TYPE_DIRECTORY:
  479. return GOPHER_CHAR_DIRECTORY;
  480. case GOPHER_TYPE_CSO:
  481. return GOPHER_CHAR_CSO;
  482. case GOPHER_TYPE_ERROR:
  483. return GOPHER_CHAR_ERROR;
  484. case GOPHER_TYPE_MAC_BINHEX:
  485. return GOPHER_CHAR_MAC_BINHEX;
  486. case GOPHER_TYPE_DOS_ARCHIVE:
  487. return GOPHER_CHAR_DOS_ARCHIVE;
  488. case GOPHER_TYPE_UNIX_UUENCODED:
  489. return GOPHER_CHAR_UNIX_UUENCODED;
  490. case GOPHER_TYPE_INDEX_SERVER:
  491. return GOPHER_CHAR_INDEX_SERVER;
  492. case GOPHER_TYPE_TELNET:
  493. return GOPHER_CHAR_TELNET;
  494. case GOPHER_TYPE_BINARY:
  495. return GOPHER_CHAR_BINARY;
  496. case GOPHER_TYPE_REDUNDANT:
  497. return GOPHER_CHAR_REDUNDANT;
  498. case GOPHER_TYPE_TN3270:
  499. return GOPHER_CHAR_TN3270;
  500. case GOPHER_TYPE_GIF:
  501. return GOPHER_CHAR_GIF;
  502. case GOPHER_TYPE_IMAGE:
  503. return GOPHER_CHAR_IMAGE;
  504. case GOPHER_TYPE_BITMAP:
  505. return GOPHER_CHAR_BITMAP;
  506. case GOPHER_TYPE_MOVIE:
  507. return GOPHER_CHAR_MOVIE;
  508. case GOPHER_TYPE_SOUND:
  509. return GOPHER_CHAR_SOUND;
  510. case GOPHER_TYPE_HTML:
  511. return GOPHER_CHAR_HTML;
  512. case GOPHER_TYPE_PDF:
  513. return GOPHER_CHAR_PDF;
  514. case GOPHER_TYPE_CALENDAR:
  515. return GOPHER_CHAR_CALENDAR;
  516. case GOPHER_TYPE_INLINE:
  517. return GOPHER_CHAR_INLINE;
  518. }
  519. return UNKNOWN_GOPHER_TYPE;
  520. }
  521. DWORD
  522. GetDirEntry(
  523. IN LPVIEW_INFO ViewInfo,
  524. OUT LPGOPHER_FIND_DATA FindData
  525. )
  526. /*++
  527. Routine Description:
  528. Retrieves the next directory entry from the current VIEW_INFO data buffer.
  529. The buffer pointer will be updated to point to the start of the next line
  530. or 1 character past the end of the buffer
  531. Arguments:
  532. ViewInfo - pointer to VIEW_INFO which points to BUFFER_INFO which
  533. points to buffer containing directory listing
  534. FindData - pointer to user's GOPHER_FIND_DATA structure to fill in
  535. Return Value:
  536. DWORD
  537. Success - ERROR_SUCCESS
  538. Failure - ERROR_NO_MORE_FILES
  539. End of the directory
  540. ERROR_GOPHER_PROTOCOL_ERROR
  541. --*/
  542. {
  543. DWORD error;
  544. char lineBuf[DEFAULT_LINE_BUFFER_LENGTH];
  545. DWORD lineLen;
  546. LPSTR linePtr;
  547. BOOL haveLocator;
  548. //
  549. // get the next line from the buffer. If we don't have all the data that
  550. // constitutes a line, ExtractLine will endeavour to get it
  551. //
  552. haveLocator = FALSE;
  553. lineLen = sizeof(lineBuf);
  554. linePtr = lineBuf;
  555. error = ExtractLine(ViewInfo,
  556. (LPBYTE)linePtr,
  557. &lineLen,
  558. &ViewInfo->ViewOffset
  559. );
  560. //
  561. // convert lineLen to the number of characters actually extracted, minus
  562. // one for the '\0'. Doesn't matter if ExtractLine() failed
  563. //
  564. lineLen = sizeof(lineBuf) - (lineLen + 1);
  565. //
  566. // if we got a line of data, but the buffer contains gopher+ info then we
  567. // need to move the locator pointer past the "+INFO: " token
  568. //
  569. if ((error == ERROR_SUCCESS) && (ViewInfo->Flags & VI_GOPHER_PLUS)) {
  570. DWORD tokenLength;
  571. tokenLength = IsGopherPlusToken(GOPHER_PLUS_INFO_TOKEN,
  572. GOPHER_PLUS_INFO_TOKEN_LENGTH,
  573. linePtr,
  574. lineLen
  575. );
  576. if (tokenLength != 0) {
  577. linePtr += tokenLength;
  578. lineLen -= tokenLength;
  579. } else {
  580. //
  581. // hola! The "+INFO: " doesn't exist. We'll treat this as gopher0
  582. // info (or return an error?)
  583. //
  584. INET_ASSERT(FALSE);
  585. ViewInfo->Flags &= ~VI_GOPHER_PLUS;
  586. }
  587. }
  588. //
  589. // if no error occurred, convert the locator just retrieved into the
  590. // GOPHER_FIND_DATA structure
  591. //
  592. if (error == ERROR_SUCCESS) {
  593. error = GopherLocatorToFindData(linePtr, lineLen, FindData);
  594. //
  595. // if we parsed the locator OK and the buffer contains gopher+ info
  596. // then we must get the date and size information from the +ADMIN
  597. // section Mod-Date line and +VIEWS section resp.
  598. //
  599. if ((error == ERROR_SUCCESS) && (ViewInfo->Flags & VI_GOPHER_PLUS)) {
  600. DWORD dataOffset;
  601. DWORD previousOffset;
  602. DWORD searchType;
  603. BOOL done;
  604. haveLocator = TRUE;
  605. dataOffset = ViewInfo->ViewOffset;
  606. searchType = 0;
  607. done = FALSE;
  608. //
  609. // loop, reading the next line from the directory buffer. For each
  610. // line, parse the gopher+ token looking for the Mod-Date line,
  611. // or the first view line. We just skip all other lines
  612. //
  613. do {
  614. previousOffset = dataOffset;
  615. lineLen = sizeof(lineBuf);
  616. error = ExtractLine(ViewInfo,
  617. (LPBYTE)linePtr,
  618. &lineLen,
  619. &dataOffset
  620. );
  621. if (error == ERROR_SUCCESS) {
  622. //
  623. // again, convert lineLen to the number of characters
  624. // extracted
  625. //
  626. lineLen = sizeof(lineBuf) - (lineLen + 1);
  627. //
  628. // if we found a line containing a categpry type on the
  629. // previous iteration, then parse the per-category info
  630. //
  631. if (searchType == SEARCH_TYPE_VIEW) {
  632. char contentType[DEFAULT_CONTENT_TYPE_NAME_LENGTH + 1];
  633. char language[DEFAULT_LANGUAGE_NAME_LENGTH + 1];
  634. DWORD contentTypeLength;
  635. DWORD languageLength;
  636. BOOL ok;
  637. //
  638. // must be views line. Just extract the first one
  639. //
  640. contentTypeLength = sizeof(contentType);
  641. languageLength = sizeof(language);
  642. ok = ExtractView(&linePtr,
  643. contentType,
  644. &contentTypeLength,
  645. language,
  646. &languageLength,
  647. &FindData->SizeLow
  648. );
  649. INET_ASSERT(ok);
  650. if (!ok) {
  651. error = ERROR_GOPHER_DATA_ERROR;
  652. break;
  653. }
  654. //
  655. // we have the first view line. We aren't interested in
  656. // the rest
  657. //
  658. searchType = 0;
  659. } else {
  660. LPSTR pAttribute;
  661. char attributeBuffer[DEFAULT_CONTENT_TYPE_NAME_LENGTH + 1];
  662. int i;
  663. DWORD len;
  664. LPSTR argPtr;
  665. //
  666. // pull out the first token on the line
  667. //
  668. i = 0;
  669. len = lineLen;
  670. pAttribute = linePtr;
  671. //
  672. // if this line has leading space, then skip it
  673. //
  674. while (*pAttribute == ' ') {
  675. ++pAttribute;
  676. --len;
  677. }
  678. while (len
  679. && (i < sizeof(attributeBuffer) - 1)
  680. && (pAttribute[i] != ' ')
  681. && (pAttribute[i] != ':')
  682. && (pAttribute[i] != '\r')
  683. && (pAttribute[i] != '\n')) {
  684. attributeBuffer[i] = pAttribute[i];
  685. ++i;
  686. --len;
  687. }
  688. attributeBuffer[i] = '\0';
  689. switch (MapAttributeNameToId((LPCSTR)attributeBuffer)) {
  690. case GOPHER_CATEGORY_ID_INFO:
  691. //
  692. // update the offset in the VIEW_INFO
  693. //
  694. ViewInfo->ViewOffset = previousOffset;
  695. //
  696. // we have got to the next directory entry. Quit
  697. //
  698. done = TRUE;
  699. break;
  700. case GOPHER_ATTRIBUTE_ID_MOD_DATE:
  701. //
  702. // this is the "Mod-Date" line. Find the start of
  703. // the date-time field (in angle brackets) and
  704. // extract the time and date to the GOPHER_FIND_DATE
  705. // structure
  706. //
  707. argPtr = strchr(linePtr, '<');
  708. if (argPtr != NULL) {
  709. ExtractDateAndTime(&argPtr,
  710. &FindData->LastModificationTime
  711. );
  712. }
  713. break;
  714. case GOPHER_CATEGORY_ID_VIEWS:
  715. //
  716. // we have found the +VIEWS section. Next thing to
  717. // find is the views proper
  718. //
  719. searchType = SEARCH_TYPE_VIEW;
  720. break;
  721. default:
  722. //
  723. // we just skip all other lines except the line(s)
  724. // containing view information, in which case
  725. // searchType will be set to indicate that this line
  726. // contains a view
  727. //
  728. break;
  729. }
  730. }
  731. } else {
  732. //
  733. // ExtractLine had an error
  734. //
  735. ViewInfo->ViewOffset = dataOffset;
  736. done = TRUE;
  737. }
  738. } while ( !done );
  739. }
  740. }
  741. if (error == ERROR_GOPHER_END_OF_DATA) {
  742. if (haveLocator) {
  743. error = ERROR_SUCCESS;
  744. } else {
  745. error = ERROR_NO_MORE_FILES;
  746. }
  747. }
  748. return error;
  749. }
  750. PRIVATE
  751. DWORD
  752. GopherLocatorToFindData(
  753. IN LPCSTR Locator,
  754. IN DWORD Length,
  755. OUT LPGOPHER_FIND_DATA FindData
  756. )
  757. /*++
  758. Routine Description:
  759. Fills in the GOPHER_FIND_DATA fields from a gopher locator string. The
  760. strings in the GOPHER_FIND_DATA are appended after the fixed structure.
  761. ASSUMES 1. The buffer pointed to by FindData is large enough to hold the
  762. fixed and variable parts of the GOPHER_FIND_DATA
  763. Arguments:
  764. Locator - pointer to (ASCII) locator string
  765. Length - length of Locator
  766. FindData - pointer to GOPHER_FIND_DATA structure
  767. Return Value:
  768. DWORD
  769. Success - ERROR_SUCCESS
  770. Failure - ERROR_GOPHER_PROTOCOL_ERROR
  771. --*/
  772. {
  773. LPSTR stringPointer;
  774. DWORD bufferLength;
  775. LPSTR locator;
  776. DWORD locatorLength;
  777. FindData->GopherType = GopherCharToType(*Locator);
  778. if (IsGopherPlus(Locator)) {
  779. FindData->GopherType |= GOPHER_TYPE_GOPHER_PLUS;
  780. }
  781. stringPointer = FindData->DisplayString;
  782. //
  783. // copy the display string into the GOPHER_FIND_DATA. We no longer care
  784. // about the length copied
  785. //
  786. ExtractDisplayString(Locator,
  787. &stringPointer,
  788. sizeof(FindData->DisplayString)
  789. );
  790. //
  791. // default the size and time/date fields to zero. If we received a gopher+
  792. // directory list, we will fill in these fields from the attribute info
  793. //
  794. FindData->SizeLow = 0;
  795. FindData->SizeHigh = 0;
  796. FindData->LastModificationTime.dwLowDateTime = 0;
  797. FindData->LastModificationTime.dwHighDateTime = 0;
  798. //
  799. // copy the locator into the GOPHER_FIND_DATA
  800. //
  801. stringPointer = FindData->Locator;
  802. bufferLength = sizeof(FindData->Locator);
  803. locator = (LPSTR)Locator;
  804. locatorLength = Length;
  805. if (CopyToEol(&stringPointer, &bufferLength, &locator, &locatorLength)) {
  806. if (FindData->GopherType == GOPHER_TYPE_UNKNOWN) {
  807. DEBUG_PRINT(PARSE,
  808. ERROR,
  809. ("GopherLocatorToFindData(): unknown locator type: \"%s\"\n",
  810. FindData->Locator
  811. ));
  812. }
  813. return ERROR_SUCCESS;
  814. }
  815. //
  816. // CopyToEol failed to find the end-of-line in the Locator string.
  817. // Either something's bust, or we have received a locator that breaks
  818. // our locator length limit
  819. //
  820. if ((bufferLength == 0) && (locatorLength != 0)) {
  821. char bigLocator[2 * MAX_GOPHER_LOCATOR_LENGTH + 1];
  822. //
  823. // blown our locator length limit. We will reconstruct a slightly
  824. // modified (smaller) locator
  825. //
  826. stringPointer = bigLocator;
  827. bufferLength = sizeof(bigLocator);
  828. locator = (LPSTR)Locator;
  829. locatorLength = Length;
  830. if (!CopyToEol(&stringPointer, &bufferLength, &locator, &locatorLength)) {
  831. //
  832. // CopyToEol() still fails! Either this is an extremely long
  833. // locator, or we are not parsing a directory output
  834. //
  835. // skip to \r\n?
  836. INET_ASSERT(FALSE);
  837. return ERROR_GOPHER_DATA_ERROR;
  838. }
  839. //
  840. // if we think this is a locator, albeit one that breaks our internal
  841. // locator length limit, crack it open, and then reconstitute
  842. //
  843. if (IsValidLocator(bigLocator, sizeof(bigLocator))) {
  844. DWORD gopherType;
  845. char displayString[MAX_GOPHER_DISPLAY_TEXT * 2 + 1];
  846. DWORD displayStringLength;
  847. char selectorString[MAX_GOPHER_SELECTOR_TEXT * 2 + 1];
  848. DWORD selectorStringLength;
  849. char hostName[MAX_GOPHER_HOST_NAME * 2 + 1];
  850. DWORD hostNameLength;
  851. DWORD port;
  852. displayStringLength = sizeof(displayString);
  853. selectorStringLength = sizeof(selectorString);
  854. hostNameLength = sizeof(hostName);
  855. if (CrackLocator(bigLocator,
  856. &gopherType,
  857. displayString,
  858. &displayStringLength,
  859. selectorString,
  860. &selectorStringLength,
  861. hostName,
  862. &hostNameLength,
  863. &port,
  864. NULL)) {
  865. //
  866. // we really want to ensure that only the display string is
  867. // broken, but we can get some weird FTP-based locators that
  868. // contain long selector strings. As a compromise, just add
  869. // an extra terminator at the relevamt maximum offset in each
  870. // string
  871. //
  872. displayString[MAX_GOPHER_DISPLAY_TEXT] = '\0';
  873. selectorString[MAX_GOPHER_SELECTOR_TEXT] = '\0';
  874. hostName[MAX_GOPHER_HOST_NAME] = '\0';
  875. //
  876. // and reconstruct the locator
  877. //
  878. bufferLength = sizeof(FindData->Locator);
  879. if (gopherType == GOPHER_TYPE_UNKNOWN) {
  880. //
  881. // BUGBUG - should change GopherCreateLocator() so that it
  882. // is more forgiving of 'unknown' types (accept
  883. // a character, not a bit)
  884. //
  885. gopherType = GOPHER_TYPE_ERROR;
  886. }
  887. if (GopherCreateLocator((LPCSTR)hostName,
  888. (INTERNET_PORT)port,
  889. (LPCSTR)displayString,
  890. (LPCSTR)selectorString,
  891. gopherType,
  892. FindData->Locator,
  893. &bufferLength)) {
  894. return ERROR_SUCCESS;
  895. } else {
  896. //
  897. // GopherCreateLocator() failed
  898. //
  899. INET_ASSERT(FALSE);
  900. }
  901. } else {
  902. //
  903. // CrackLocator() failed
  904. //
  905. INET_ASSERT(FALSE);
  906. }
  907. } else {
  908. //
  909. // IsValidLocator() returned FALSE
  910. //
  911. INET_ASSERT(FALSE);
  912. }
  913. } else {
  914. //
  915. // ran off the end of the directory list without finding "\r\n"?
  916. //
  917. INET_ASSERT(FALSE);
  918. }
  919. return ERROR_GOPHER_DATA_ERROR;
  920. }
  921. DWORD
  922. ReadData(
  923. IN LPVIEW_INFO ViewInfo,
  924. OUT LPDWORD BytesReturned
  925. )
  926. /*++
  927. Routine Description:
  928. Reads data from a file buffer into the caller's buffer
  929. Arguments:
  930. ViewInfo - pointer to VIEW_INFO structure
  931. BytesReturned - amount of data copied to caller's buffer
  932. Return Value:
  933. DWORD
  934. Success - ERROR_SUCCESS
  935. BytesReturned contains amount of data copied to user buffer
  936. Failure - ERROR_GOPHER_DATA_ERROR
  937. There is an inconsistency between the VIEW_INFO and the
  938. BUFFER_INFO
  939. ERROR_GOPHER_END_OF_DATA
  940. All data has been copied to the user buffer
  941. ERROR_GOPHER_TIMEOUT
  942. We got a timeout trying to communicate with the gopher
  943. server
  944. Win32 error
  945. Returned if we have a memory or heap problem
  946. WSA error
  947. Socket specific error returned by ReceiveResponse()
  948. --*/
  949. {
  950. INET_ASSERT(ViewInfo != NULL);
  951. INET_ASSERT(ViewInfo->ViewType == ViewTypeFile);
  952. INET_ASSERT(ViewInfo->BufferInfo != NULL);
  953. if (ViewInfo->BufferInfo->Flags & BI_RECEIVE_COMPLETE) {
  954. *BytesReturned = 0;
  955. return ERROR_SUCCESS;
  956. } else {
  957. return GopherReceiveResponse(ViewInfo, BytesReturned);
  958. }
  959. }
  960. PRIVATE
  961. DWORD
  962. ExtractLine(
  963. IN LPVIEW_INFO ViewInfo,
  964. OUT LPBYTE LineBuffer,
  965. IN OUT LPDWORD LineBufferLength,
  966. IN OUT LPDWORD DataOffset
  967. )
  968. /*++
  969. Routine Description:
  970. Extracts a line from a response buffer into a local buffer. If the buffer
  971. does not contain all of the current line we retrieve the next chunk by
  972. calling ReceiveResponse()
  973. Arguments:
  974. ViewInfo - describes VIEW_INFO from which to extract line
  975. LineBuffer - pointer to buffer where line will be copied
  976. LineBufferLength- IN: length of line buffer
  977. OUT: number of bytes remaining in LineBuffer
  978. DataOffset - IN: the point in the data buffer corresponding to
  979. ViewInfo->BufferInfo->Buffer at which to start the
  980. extraction
  981. OUT: The next offset in buffer at which to start
  982. Return Value:
  983. DWORD
  984. Success - ERROR_SUCCESS
  985. Failure - ERROR_GOPHER_DATA_ERROR
  986. We had an error parsing the data
  987. ERROR_GOPHER_END_OF_DATA
  988. We reached the end of the info - API returns
  989. ERROR_NO_MORE_FILES
  990. Win32 error
  991. --*/
  992. {
  993. DWORD error;
  994. BOOL copied;
  995. LPBYTE startOfLine;
  996. LPBUFFER_INFO bufferInfo;
  997. INET_ASSERT(ViewInfo != NULL);
  998. INET_ASSERT(ViewInfo->BufferInfo != NULL);
  999. bufferInfo = ViewInfo->BufferInfo;
  1000. startOfLine = LineBuffer;
  1001. error = ERROR_SUCCESS;
  1002. do {
  1003. LPBYTE bufferPointer;
  1004. LPBYTE responsePointer;
  1005. DWORD bufferAvailable;
  1006. DWORD oldBufferAvailable;
  1007. if (*DataOffset > bufferInfo->BufferLength) {
  1008. //
  1009. // we think we're further into buffer than there is available data
  1010. //
  1011. INET_ASSERT(FALSE);
  1012. error = ERROR_GOPHER_DATA_ERROR;
  1013. DEBUG_PRINT(PARSE,
  1014. ERROR,
  1015. ("ExtractLine(): *DataOffset (%d) > BufferLength (%d)\n",
  1016. *DataOffset,
  1017. bufferInfo->BufferLength
  1018. ));
  1019. goto quit;
  1020. }
  1021. if ((bufferInfo->Flags & BI_RECEIVE_COMPLETE)
  1022. && (*DataOffset == bufferInfo->BufferLength)) {
  1023. //
  1024. // the caller has already reached the end of the buffer
  1025. //
  1026. DEBUG_PRINT(PARSE,
  1027. INFO,
  1028. ("ExtractLine(): already at EOF buffer\n"
  1029. ));
  1030. error = ERROR_GOPHER_END_OF_DATA;
  1031. goto quit;
  1032. }
  1033. //
  1034. // get a pointer to the start of the buffer
  1035. //
  1036. bufferPointer = bufferInfo->Buffer;
  1037. INET_ASSERT(bufferPointer != NULL);
  1038. if (bufferPointer == NULL) {
  1039. goto last_error_exit;
  1040. }
  1041. //
  1042. // now point to the offset in the buffer where the caller thinks the
  1043. // next line begins and reduce the buffer length by the same amount
  1044. //
  1045. responsePointer = bufferPointer + *DataOffset;
  1046. bufferAvailable = bufferInfo->BufferLength - *DataOffset;
  1047. //
  1048. // copy from the current buffer position to the end of the line
  1049. //
  1050. oldBufferAvailable = bufferAvailable;
  1051. copied = CopyToEol((LPSTR *)&LineBuffer,
  1052. LineBufferLength,
  1053. (LPSTR *)&responsePointer,
  1054. &bufferAvailable
  1055. );
  1056. //
  1057. // oldBufferAvailable - bufferAvailable is the amount we copied
  1058. //
  1059. *DataOffset += oldBufferAvailable - bufferAvailable;
  1060. //
  1061. // copied is TRUE if CopyToEol copied a full line
  1062. //
  1063. if (copied) {
  1064. //
  1065. // test again for ".\r\n" terminator. Someones servers terminate
  1066. // with ".\r\r\n" which would have escaped our test in
  1067. // ReceiveResponse(), but has now been compressed to ".\r\n" by
  1068. // CopyToEol(). Other, equally unintelegent servers, terminate
  1069. // with e.g. ".\r\n\x1a" (presumably this directory was read from
  1070. // a file and squirted out via send())
  1071. //
  1072. if (memcmp(startOfLine, ".\r\n", 3) == 0) {
  1073. //
  1074. // there should be very few bytes left in the buffer, if any,
  1075. // depending on how the server terminated the buffer (".\r\r\n"
  1076. // or ".\r\n\x1a", e.g.)
  1077. //
  1078. // N.B. 8 is an arbitrary number. I don't expect too many
  1079. // garbage characters at the end of the buffer, but if there's
  1080. // more than a relatively small number, we could have a ".\r\n"
  1081. // embedded half-way down the directory listing. Implausable,
  1082. // yes, but then there's nothing so unpredictable as the results
  1083. // from a gopher server
  1084. //
  1085. // 05/23/95
  1086. //
  1087. // server at sutro.sfsu.edu returns a pile of garbage after the
  1088. // end-of-buffer mark. Probably unintentional, but causes the
  1089. // following assertion to go off:
  1090. //
  1091. // INET_ASSERT(bufferInfo->BufferLength - *DataOffset <= 8);
  1092. //
  1093. if (bufferInfo->BufferLength - *DataOffset <= 8) {
  1094. DEBUG_PRINT(PARSE,
  1095. WARNING,
  1096. ("ExtractLine(): Buffer handle %#x contains data after end-of-buffer mark\n",
  1097. bufferInfo->Buffer
  1098. ));
  1099. }
  1100. error = ERROR_GOPHER_END_OF_DATA;
  1101. }
  1102. } else {
  1103. //
  1104. // at the time we called CopyToEol, all of the current line was
  1105. // not in the response buffer. Get the next part of the response
  1106. //
  1107. AcquireBufferLock(bufferInfo);
  1108. if (!(bufferInfo->Flags & BI_RECEIVE_COMPLETE)) {
  1109. DWORD bytesReceived;
  1110. error = GopherReceiveResponse(ViewInfo, &bytesReceived);
  1111. }
  1112. ReleaseBufferLock(bufferInfo);
  1113. }
  1114. } while (!copied && (error == ERROR_SUCCESS));
  1115. quit:
  1116. return error;
  1117. last_error_exit:
  1118. error = GetLastError();
  1119. goto quit;
  1120. }
  1121. PRIVATE
  1122. DWORD
  1123. ExtractDisplayString(
  1124. IN LPCSTR Locator,
  1125. IN OUT LPSTR* StringPointer,
  1126. IN DWORD BufferLength
  1127. )
  1128. /*++
  1129. Routine Description:
  1130. Given a gopher locator string, extract the display string part
  1131. Arguments:
  1132. Locator - pointer to gopher locator
  1133. StringPointer - pointer to pointer to output string. Updated on output
  1134. BufferLength - amount of space in *StringPointer
  1135. Return Value:
  1136. DWORD Length of string extracted
  1137. --*/
  1138. {
  1139. LPSTR originalPointer = *StringPointer;
  1140. char ch;
  1141. //
  1142. // Locator starts off pointing at the type character. Move past it then
  1143. // copy everything up to the tab character
  1144. //
  1145. while (((ch = *++Locator) != '\t') && BufferLength--) {
  1146. *(*StringPointer)++ = ch;
  1147. }
  1148. *(*StringPointer)++ = '\0';
  1149. return (DWORD) (*StringPointer - originalPointer);
  1150. }
  1151. BOOL
  1152. CopyToEol(
  1153. IN OUT LPSTR* Destination,
  1154. IN OUT LPDWORD DestinationLength,
  1155. IN OUT LPSTR* Source,
  1156. IN OUT LPDWORD SourceLength
  1157. )
  1158. /*++
  1159. Routine Description:
  1160. Copies the current gopher response line up to the end of the current line
  1161. in the buffer. The destination string is zero terminated if TRUE is
  1162. returned
  1163. On exit, all parameters are updated to reflect the current positions and
  1164. lengths of the buffers so this function can be called iteratively until
  1165. the entire line is copied
  1166. ASSUMES 1. The Length is absolutely reliable - i.e. when Length == 2 and
  1167. **Source == '\r', then *(*Source + 1) == '\n'
  1168. Arguments:
  1169. Destination - pointer to place to copy to
  1170. DestinationLength - pointer to length of destination buffer, updated on output
  1171. Source - pointer to place to copy from (gopher response buffer)
  1172. SourceLength - pointer tp length of source buffer, updated on output
  1173. Return Value:
  1174. BOOL
  1175. TRUE - we copied the entire line up to \r\n
  1176. FALSE - none or part of a line was copied
  1177. --*/
  1178. {
  1179. LPSTR src;
  1180. LPSTR dest;
  1181. DWORD srcLen;
  1182. DWORD destLen;
  1183. BOOL copied;
  1184. //
  1185. // make smaller code (i.e. don't deref the parms every time)
  1186. //
  1187. src = *Source;
  1188. dest = *Destination;
  1189. srcLen = *SourceLength;
  1190. destLen = *DestinationLength;
  1191. while ((*src != '\r') && (*src != '\n') && (destLen != 0) && (srcLen != 0)) {
  1192. *dest++ = *src++;
  1193. --destLen;
  1194. --srcLen;
  1195. }
  1196. //
  1197. // we can receive multiple carriage-returns, presumably because the server
  1198. // uses sprintf("\r\n") in text mode which expands "\n" to be \r\n in the
  1199. // output buffer. We will collapse multiple carriage-returns
  1200. //
  1201. while ((*(src + 1) == '\r') && (srcLen != 0)) {
  1202. ++src;
  1203. --srcLen;
  1204. }
  1205. //INET_ASSERT((srcLen > 1) ? (*(src + 1) == '\n') : TRUE);
  1206. //
  1207. // if \r\n exist in the source buffer then copy them and update the length
  1208. //
  1209. copied = FALSE;
  1210. if (destLen >= 3) {
  1211. if ((srcLen >= 2) && (*src == '\r')) {
  1212. ++src;
  1213. --srcLen;
  1214. }
  1215. if ((srcLen >= 1) && (*src == '\n')) {
  1216. ++src;
  1217. --srcLen;
  1218. //
  1219. // we have reached a line-feed. It either exists on its own or was
  1220. // prefixed by a carriage-return. This is the end of the line...
  1221. //
  1222. // Note, even if we did not find \r\n in the source, we create \r\n
  1223. // in the destination
  1224. //
  1225. destLen -= 3; // 1 for \r, 1 for \n, 1 for \0
  1226. *dest++ = '\r';
  1227. *dest++ = '\n';
  1228. *dest++ = '\0';
  1229. copied = TRUE;
  1230. }
  1231. }
  1232. *Source = src;
  1233. *Destination = dest;
  1234. *SourceLength = srcLen;
  1235. *DestinationLength = destLen;
  1236. return copied;
  1237. }
  1238. DWORD
  1239. IsGopherPlusToken(
  1240. IN LPSTR Token,
  1241. IN DWORD TokenLength,
  1242. IN LPSTR Buffer,
  1243. IN DWORD BufferLength
  1244. )
  1245. /*++
  1246. Routine Description:
  1247. Determines if a token is the gopher+ token. In order to match we need to
  1248. match a trailing space character also. Seems that some servers return
  1249. "+INFO" and some "+INFO:". We must handle both
  1250. Arguments:
  1251. Token - pointer to gopher+ token string
  1252. TokenLength - length of PlusToken
  1253. Buffer - pointer to buffer containing token to check
  1254. BufferLength - number of bytes in BufferPointer
  1255. Return Value:
  1256. DWORD
  1257. Success - Returns number of characters matched, including trailing ' '
  1258. Failure - 0. TokenPointer does not point at PlusToken or we ran out of
  1259. buffer before we could make the distinction
  1260. --*/
  1261. {
  1262. //
  1263. // Length must contain at least the trailing space, and possibly a ':'
  1264. //
  1265. if (BufferLength >= TokenLength + 2) {
  1266. if (memcmp(Buffer, Token, TokenLength) == 0) {
  1267. if (Buffer[TokenLength] == ':') {
  1268. ++TokenLength;
  1269. }
  1270. //
  1271. // if there's a space after the token then we know its really what we
  1272. // are searching for
  1273. //
  1274. if (Buffer[TokenLength] == ' ') {
  1275. return ++TokenLength;
  1276. }
  1277. }
  1278. }
  1279. return 0;
  1280. }
  1281. DWORD
  1282. MapAttributeNameToId(
  1283. IN LPCSTR AttributeName
  1284. )
  1285. /*++
  1286. Routine Description:
  1287. Given a category or attribute name, returns an identifier to avoid having
  1288. to perform extraneous string comparisons
  1289. ASSUMES: 1. AttributeName has correct case. Comparisons are CASE-SENSITIVE
  1290. Arguments:
  1291. AttributeName - name of category or attribute
  1292. Return Value:
  1293. DWORD
  1294. relevant identifer or GOPHER_ATTRIBUTE_ID_UNKNOWN
  1295. --*/
  1296. {
  1297. if (AttributeName == NULL) {
  1298. return GOPHER_CATEGORY_ID_ALL;
  1299. } else if (!stricmp(AttributeName, GOPHER_INFO_CATEGORY)) {
  1300. return GOPHER_CATEGORY_ID_INFO;
  1301. } else if (!stricmp(AttributeName, GOPHER_ADMIN_CATEGORY)) {
  1302. return GOPHER_CATEGORY_ID_ADMIN;
  1303. } else if (!stricmp(AttributeName, GOPHER_VIEWS_CATEGORY)) {
  1304. return GOPHER_CATEGORY_ID_VIEWS;
  1305. } else if (!stricmp(AttributeName, GOPHER_ABSTRACT_CATEGORY)) {
  1306. return GOPHER_CATEGORY_ID_ABSTRACT;
  1307. } else if (!stricmp(AttributeName, GOPHER_VERONICA_CATEGORY)) {
  1308. return GOPHER_CATEGORY_ID_VERONICA;
  1309. } else if (!stricmp(AttributeName, GOPHER_ADMIN_ATTRIBUTE)) {
  1310. return GOPHER_ATTRIBUTE_ID_ADMIN;
  1311. } else if (!stricmp(AttributeName, GOPHER_MOD_DATE_ATTRIBUTE)) {
  1312. return GOPHER_ATTRIBUTE_ID_MOD_DATE;
  1313. } else if (!stricmp(AttributeName, GOPHER_TTL_ATTRIBUTE)) {
  1314. return GOPHER_ATTRIBUTE_ID_TTL;
  1315. } else if (!stricmp(AttributeName, GOPHER_SCORE_ATTRIBUTE)) {
  1316. return GOPHER_ATTRIBUTE_ID_SCORE;
  1317. } else if (!stricmp(AttributeName, GOPHER_RANGE_ATTRIBUTE)) {
  1318. return GOPHER_ATTRIBUTE_ID_RANGE;
  1319. } else if (!stricmp(AttributeName, GOPHER_SITE_ATTRIBUTE)) {
  1320. return GOPHER_ATTRIBUTE_ID_SITE;
  1321. } else if (!stricmp(AttributeName, GOPHER_ORG_ATTRIBUTE)) {
  1322. return GOPHER_ATTRIBUTE_ID_ORG;
  1323. } else if (!stricmp(AttributeName, GOPHER_LOCATION_ATTRIBUTE)) {
  1324. return GOPHER_ATTRIBUTE_ID_LOCATION;
  1325. } else if (!stricmp(AttributeName, GOPHER_GEOG_ATTRIBUTE)) {
  1326. return GOPHER_ATTRIBUTE_ID_GEOG;
  1327. } else if (!stricmp(AttributeName, GOPHER_TIMEZONE_ATTRIBUTE)) {
  1328. return GOPHER_ATTRIBUTE_ID_TIMEZONE;
  1329. } else if (!stricmp(AttributeName, GOPHER_PROVIDER_ATTRIBUTE)) {
  1330. return GOPHER_ATTRIBUTE_ID_PROVIDER;
  1331. } else if (!stricmp(AttributeName, GOPHER_VERSION_ATTRIBUTE)) {
  1332. return GOPHER_ATTRIBUTE_ID_VERSION;
  1333. } else if (!stricmp(AttributeName, GOPHER_ABSTRACT_ATTRIBUTE)) {
  1334. return GOPHER_ATTRIBUTE_ID_ABSTRACT;
  1335. } else if (!stricmp(AttributeName, GOPHER_VIEW_ATTRIBUTE)) {
  1336. return GOPHER_ATTRIBUTE_ID_VIEW;
  1337. }
  1338. return GOPHER_ATTRIBUTE_ID_UNKNOWN;
  1339. }
  1340. #if defined(GOPHER_ATTRIBUTE_SUPPORT)
  1341. VOID
  1342. MapAttributeToIds(
  1343. IN LPCSTR AttributeName,
  1344. OUT LPDWORD CategoryId,
  1345. OUT LPDWORD AttributeId
  1346. )
  1347. /*++
  1348. Routine Description:
  1349. Given a category or attribute name, returns an identifier to avoid having
  1350. to perform extraneous string comparisons
  1351. ASSUMES: 1. AttributeName has correct case. Comparisons are CASE-SENSITIVE
  1352. Arguments:
  1353. AttributeName - name of category or attribute
  1354. CategoryId - returned GOPHER_CATEGORY_ id
  1355. AttributeId - returned GOPHER_ATTRIBUTE_ id
  1356. Return Value:
  1357. None.
  1358. --*/
  1359. {
  1360. DWORD category;
  1361. DWORD attribute;
  1362. if (AttributeName == NULL) {
  1363. category = GOPHER_CATEGORY_ID_ALL;
  1364. attribute = GOPHER_ATTRIBUTE_ID_ALL;
  1365. } else if (!stricmp(AttributeName, GOPHER_INFO_CATEGORY)) {
  1366. category = GOPHER_CATEGORY_ID_INFO;
  1367. attribute = GOPHER_ATTRIBUTE_ID_ALL;
  1368. } else if (!stricmp(AttributeName, GOPHER_ADMIN_CATEGORY)) {
  1369. category = GOPHER_CATEGORY_ID_ADMIN;
  1370. attribute = GOPHER_ATTRIBUTE_ID_ALL;
  1371. } else if (!stricmp(AttributeName, GOPHER_VIEWS_CATEGORY)) {
  1372. category = GOPHER_CATEGORY_ID_VIEWS;
  1373. attribute = GOPHER_ATTRIBUTE_ID_ALL;
  1374. } else if (!stricmp(AttributeName, GOPHER_ABSTRACT_CATEGORY)) {
  1375. category = GOPHER_CATEGORY_ID_ABSTRACT;
  1376. attribute = GOPHER_ATTRIBUTE_ID_ALL;
  1377. } else if (!stricmp(AttributeName, GOPHER_VERONICA_CATEGORY)) {
  1378. category = GOPHER_CATEGORY_ID_VERONICA;
  1379. attribute = GOPHER_ATTRIBUTE_ID_ALL;
  1380. } else if (!stricmp(AttributeName, GOPHER_ADMIN_ATTRIBUTE)) {
  1381. category = GOPHER_CATEGORY_ID_ADMIN;
  1382. attribute = GOPHER_ATTRIBUTE_ID_ADMIN;
  1383. } else if (!stricmp(AttributeName, GOPHER_MOD_DATE_ATTRIBUTE)) {
  1384. category = GOPHER_CATEGORY_ID_ADMIN;
  1385. attribute = GOPHER_ATTRIBUTE_ID_MOD_DATE;
  1386. } else if (!stricmp(AttributeName, GOPHER_TTL_ATTRIBUTE)) {
  1387. category = GOPHER_CATEGORY_ID_ADMIN;
  1388. attribute = GOPHER_ATTRIBUTE_ID_TTL;
  1389. } else if (!stricmp(AttributeName, GOPHER_SCORE_ATTRIBUTE)) {
  1390. category = GOPHER_CATEGORY_ID_ADMIN;
  1391. attribute = GOPHER_ATTRIBUTE_ID_SCORE;
  1392. } else if (!stricmp(AttributeName, GOPHER_RANGE_ATTRIBUTE)) {
  1393. category = GOPHER_CATEGORY_ID_ADMIN;
  1394. attribute = GOPHER_ATTRIBUTE_ID_RANGE;
  1395. } else if (!stricmp(AttributeName, GOPHER_SITE_ATTRIBUTE)) {
  1396. category = GOPHER_CATEGORY_ID_ADMIN;
  1397. attribute = GOPHER_ATTRIBUTE_ID_SITE;
  1398. } else if (!stricmp(AttributeName, GOPHER_ORG_ATTRIBUTE)) {
  1399. category = GOPHER_CATEGORY_ID_ADMIN;
  1400. attribute = GOPHER_ATTRIBUTE_ID_ORG;
  1401. } else if (!stricmp(AttributeName, GOPHER_LOCATION_ATTRIBUTE)) {
  1402. category = GOPHER_CATEGORY_ID_ADMIN;
  1403. attribute = GOPHER_ATTRIBUTE_ID_LOCATION;
  1404. } else if (!stricmp(AttributeName, GOPHER_GEOG_ATTRIBUTE)) {
  1405. category = GOPHER_CATEGORY_ID_ADMIN;
  1406. attribute = GOPHER_CATEGORY_ID_ADMIN;
  1407. } else if (!stricmp(AttributeName, GOPHER_TIMEZONE_ATTRIBUTE)) {
  1408. category = GOPHER_CATEGORY_ID_ADMIN;
  1409. attribute = GOPHER_ATTRIBUTE_ID_TIMEZONE;
  1410. } else if (!stricmp(AttributeName, GOPHER_PROVIDER_ATTRIBUTE)) {
  1411. category = GOPHER_CATEGORY_ID_ADMIN;
  1412. attribute = GOPHER_ATTRIBUTE_ID_PROVIDER;
  1413. } else if (!stricmp(AttributeName, GOPHER_VERSION_ATTRIBUTE)) {
  1414. category = GOPHER_CATEGORY_ID_ADMIN;
  1415. attribute = GOPHER_ATTRIBUTE_ID_VERSION;
  1416. } else if (!stricmp(AttributeName, GOPHER_ABSTRACT_ATTRIBUTE)) {
  1417. category = GOPHER_CATEGORY_ID_ABSTRACT;
  1418. attribute = GOPHER_ATTRIBUTE_ID_ABSTRACT;
  1419. } else if (!stricmp(AttributeName, GOPHER_VIEW_ATTRIBUTE)) {
  1420. category = GOPHER_CATEGORY_ID_VIEWS;
  1421. attribute = GOPHER_ATTRIBUTE_ID_VIEW;
  1422. } else {
  1423. category = GOPHER_CATEGORY_ID_UNKNOWN;
  1424. attribute = GOPHER_ATTRIBUTE_ID_UNKNOWN;
  1425. }
  1426. *CategoryId = category;
  1427. *AttributeId = attribute;
  1428. }
  1429. BOOL
  1430. MapAttributeIdToNames(
  1431. IN DWORD AttributeId,
  1432. OUT LPSTR* CategoryName,
  1433. OUT LPSTR* AttributeName
  1434. )
  1435. /*++
  1436. Routine Description:
  1437. Do reverse transformation: given attribute ID, return the category and
  1438. attribute names if known
  1439. Arguments:
  1440. AttributeId - id to map
  1441. CategoryName - pointer to pointer to category name
  1442. AttributeName - pointer to pointer to attribute name
  1443. Return Value:
  1444. BOOL
  1445. TRUE - id was mapped
  1446. FALSE - id not recognized
  1447. --*/
  1448. {
  1449. BOOL success = TRUE;
  1450. switch (AttributeId) {
  1451. case GOPHER_CATEGORY_ID_ALL:
  1452. *CategoryName = NULL;
  1453. *AttributeName = NULL;
  1454. break;
  1455. case GOPHER_CATEGORY_ID_INFO:
  1456. *CategoryName = GOPHER_INFO_CATEGORY;
  1457. *AttributeName = NULL;
  1458. break;
  1459. case GOPHER_CATEGORY_ID_ADMIN:
  1460. *CategoryName = GOPHER_ADMIN_CATEGORY;
  1461. *AttributeName = NULL;
  1462. break;
  1463. case GOPHER_CATEGORY_ID_VIEWS:
  1464. *CategoryName = GOPHER_VIEWS_CATEGORY;
  1465. *AttributeName = NULL;
  1466. break;
  1467. case GOPHER_CATEGORY_ID_ABSTRACT:
  1468. *CategoryName = GOPHER_ABSTRACT_CATEGORY;
  1469. *AttributeName = NULL;
  1470. break;
  1471. case GOPHER_CATEGORY_ID_VERONICA:
  1472. *CategoryName = GOPHER_VERONICA_CATEGORY;
  1473. *AttributeName = NULL;
  1474. break;
  1475. case GOPHER_ATTRIBUTE_ID_ADMIN:
  1476. *CategoryName = GOPHER_ADMIN_CATEGORY;
  1477. *AttributeName = GOPHER_ADMIN_ATTRIBUTE;
  1478. break;
  1479. case GOPHER_ATTRIBUTE_ID_MOD_DATE:
  1480. *CategoryName = GOPHER_ADMIN_CATEGORY;
  1481. *AttributeName = GOPHER_MOD_DATE_ATTRIBUTE;
  1482. break;
  1483. case GOPHER_ATTRIBUTE_ID_TTL:
  1484. *CategoryName = GOPHER_ADMIN_CATEGORY;
  1485. *AttributeName = GOPHER_TTL_ATTRIBUTE;
  1486. break;
  1487. case GOPHER_ATTRIBUTE_ID_SCORE:
  1488. *CategoryName = GOPHER_ADMIN_CATEGORY;
  1489. *AttributeName = GOPHER_SCORE_ATTRIBUTE;
  1490. break;
  1491. case GOPHER_ATTRIBUTE_ID_RANGE:
  1492. *CategoryName = GOPHER_ADMIN_CATEGORY;
  1493. *AttributeName = GOPHER_RANGE_ATTRIBUTE;
  1494. break;
  1495. case GOPHER_ATTRIBUTE_ID_SITE:
  1496. *CategoryName = GOPHER_ADMIN_CATEGORY;
  1497. *AttributeName = GOPHER_SITE_ATTRIBUTE;
  1498. break;
  1499. case GOPHER_ATTRIBUTE_ID_ORG:
  1500. *CategoryName = GOPHER_ADMIN_CATEGORY;
  1501. *AttributeName = GOPHER_ORG_ATTRIBUTE;
  1502. break;
  1503. case GOPHER_ATTRIBUTE_ID_LOCATION:
  1504. *CategoryName = GOPHER_ADMIN_CATEGORY;
  1505. *AttributeName = GOPHER_LOCATION_ATTRIBUTE;
  1506. break;
  1507. case GOPHER_ATTRIBUTE_ID_GEOG:
  1508. *CategoryName = GOPHER_ADMIN_CATEGORY;
  1509. *AttributeName = GOPHER_GEOG_ATTRIBUTE;
  1510. break;
  1511. case GOPHER_ATTRIBUTE_ID_TIMEZONE:
  1512. *CategoryName = GOPHER_ADMIN_CATEGORY;
  1513. *AttributeName = GOPHER_TIMEZONE_ATTRIBUTE;
  1514. break;
  1515. case GOPHER_ATTRIBUTE_ID_PROVIDER:
  1516. *CategoryName = GOPHER_ADMIN_CATEGORY;
  1517. *AttributeName = GOPHER_PROVIDER_ATTRIBUTE;
  1518. break;
  1519. case GOPHER_ATTRIBUTE_ID_VERSION:
  1520. *CategoryName = GOPHER_ADMIN_CATEGORY;
  1521. *AttributeName = GOPHER_VERSION_ATTRIBUTE;
  1522. break;
  1523. case GOPHER_ATTRIBUTE_ID_ABSTRACT:
  1524. *CategoryName = GOPHER_ABSTRACT_CATEGORY;
  1525. *AttributeName = GOPHER_ABSTRACT_ATTRIBUTE;
  1526. break;
  1527. case GOPHER_ATTRIBUTE_ID_VIEW:
  1528. *CategoryName = GOPHER_VIEWS_CATEGORY;
  1529. *AttributeName = GOPHER_VIEW_ATTRIBUTE;
  1530. break;
  1531. case GOPHER_ATTRIBUTE_ID_UNKNOWN:
  1532. success = FALSE;
  1533. break;
  1534. default:
  1535. success = FALSE;
  1536. break;
  1537. }
  1538. return success;
  1539. }
  1540. #endif // defined(GOPHER_ATTRIBUTE_SUPPORT)
  1541. DWORD
  1542. GetGopherNumber(
  1543. IN OUT LPSTR* pString
  1544. )
  1545. /*++
  1546. Routine Description:
  1547. Converts a 'gopher number' to a DWORD. A gopher number is the value usually
  1548. contained with angle brackets in e.g. a +VIEWS line, and is usually a
  1549. fractional number with a 'k' suffix
  1550. Arguments:
  1551. pString - pointer to pointer to string which points at the start of the
  1552. number. The number may start with a period, indicating that it
  1553. is less than one (hence the reason why we use a double).
  1554. On output, the parameter points at the character after what we
  1555. took to be the number
  1556. Return Value:
  1557. DWORD
  1558. DWORD representation of the number at *pString
  1559. --*/
  1560. {
  1561. double number;
  1562. //
  1563. // the gopher number is usually inside angle brackets. Move the string
  1564. // pointer past the opening bracket, if the caller has not already
  1565. // done so
  1566. //
  1567. if (**pString == '<') {
  1568. ++*pString;
  1569. }
  1570. //
  1571. // allow strtod to move the string pointer forward
  1572. //
  1573. number = StrToDbl(*pString, pString);
  1574. return (DWORD)number;
  1575. }
  1576. BOOL
  1577. ExtractDateAndTime(
  1578. IN OUT LPSTR* pString,
  1579. OUT LPFILETIME pFileTime
  1580. )
  1581. /*++
  1582. Routine Description:
  1583. Converts a 'gopher time-and-date' field to a WIN32 FILETIME structure. The
  1584. gopher date-time field is a string representation of the date and time,
  1585. contained within angle brackets and has the following format:
  1586. <YYYYMMDDhhmmss>
  1587. Where:
  1588. YYYY = year (e.g. "1995")
  1589. MM = month (1..12)
  1590. DD = day of month
  1591. hh = hour of day in 24-hour format
  1592. mm = minute of hour
  1593. ss = second of minute
  1594. Assumes: 1. On input, *pString contains entire field
  1595. Arguments:
  1596. pString - IN: points to the first character in the date-time field
  1597. OUT: points to the next character in the input stream
  1598. pFileTime - pointer to returned FILETIME structure
  1599. Return Value:
  1600. BOOL
  1601. Success - TRUE - field was converted
  1602. Failure - FALSE - a problem occurred while parsing the field
  1603. --*/
  1604. {
  1605. SYSTEMTIME systemTime;
  1606. if (**pString == '<') {
  1607. ++*pString;
  1608. }
  1609. if (ExtractWord(pString, 4, &systemTime.wYear)
  1610. && ExtractWord(pString, 2, &systemTime.wMonth)
  1611. && ExtractWord(pString, 2, &systemTime.wDay)
  1612. && ExtractWord(pString, 2, &systemTime.wHour)
  1613. && ExtractWord(pString, 2, &systemTime.wMinute)
  1614. && ExtractWord(pString, 2, &systemTime.wSecond)) {
  1615. INET_ASSERT(**pString == '>');
  1616. ++*pString;
  1617. systemTime.wDayOfWeek = 0;
  1618. systemTime.wMilliseconds = 0;
  1619. return SystemTimeToFileTime(&systemTime, pFileTime);
  1620. } else {
  1621. INET_ASSERT(FALSE);
  1622. return FALSE;
  1623. }
  1624. }
  1625. BOOL
  1626. ExtractView(
  1627. IN OUT LPSTR* pString,
  1628. OUT LPSTR ContentType,
  1629. IN OUT LPDWORD ContentTypeLength,
  1630. OUT LPSTR Language,
  1631. IN OUT LPDWORD LanguageLength,
  1632. OUT LPDWORD Size
  1633. )
  1634. /*++
  1635. Routine Description:
  1636. Given a pointer to a line containing a view, parse it into its constituent
  1637. parts. A view line has the following format:
  1638. "{space}{content-type}{space}[{language-id}:]<{size}><CR><LF>"
  1639. The language-id field is optional
  1640. Arguments:
  1641. pString - pointer to pointer to view line
  1642. ContentType - pointer to returned MIME content-type string
  1643. ContentTypeLength - IN: size of content-type buffer
  1644. OUT: length of content-type without terminating 0
  1645. Language - pointer to ISO-693 language-id (or NUL string)
  1646. LanguageLength - IN: size of language buffer
  1647. OUT: length of language without terminating 0
  1648. Size - pointer to returned size of view
  1649. Return Value:
  1650. BOOL
  1651. --*/
  1652. {
  1653. LPSTR string;
  1654. DWORD contLen = 0;
  1655. DWORD langLen = 0;
  1656. string = *pString;
  1657. while (*string == ' ') {
  1658. ++string;
  1659. }
  1660. while (*string != ' ') {
  1661. if (contLen >= *ContentTypeLength)
  1662. return FALSE;
  1663. *ContentType++ = *string++;
  1664. contLen++;
  1665. }
  1666. *ContentType = '\0';
  1667. *ContentTypeLength = contLen;
  1668. while (*string == ' ') {
  1669. ++string;
  1670. }
  1671. if (*string != '<') {
  1672. //
  1673. // must be the language field. Copy up to the terminating ':' or ' '
  1674. //
  1675. while ((*string != ' ') && (*string != ':')) {
  1676. if (langLen >= *LanguageLength)
  1677. return FALSE;
  1678. *Language++ = *string++;
  1679. langLen++;
  1680. }
  1681. //
  1682. // move the string pointer to the start of the size field
  1683. //
  1684. while (*string != '<') {
  1685. ++string;
  1686. }
  1687. }
  1688. *Language = '\0';
  1689. *LanguageLength = langLen;
  1690. *Size = GetGopherNumber(&string);
  1691. *pString = string;
  1692. return TRUE;
  1693. }
  1694. #if defined(GOPHER_ATTRIBUTE_SUPPORT)
  1695. //
  1696. // manifests
  1697. //
  1698. #define SIZE_OF_GOPHER_ATTRIBUTE_FIXED_PART (2 * sizeof(DWORD))
  1699. //
  1700. // private types
  1701. //
  1702. typedef struct {
  1703. DWORD CategoryId;
  1704. DWORD AttributeId;
  1705. DWORD (*Parser)(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
  1706. DWORD NumberOfFields;
  1707. DWORD FixedSize;
  1708. } ATTRIBUTE_PARSER, *LPATTRIBUTE_PARSER;
  1709. typedef struct {
  1710. LPCSTR String;
  1711. } SINGLE_STRING_TYPE, *LPSINGLE_STRING_TYPE;
  1712. //
  1713. // private parser prototypes
  1714. //
  1715. PRIVATE DWORD ParseAdminAttribute(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
  1716. PRIVATE DWORD ParseModDateAttribute(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
  1717. PRIVATE DWORD ParseAbstractAttribute(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
  1718. PRIVATE DWORD ParseViewAttribute(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
  1719. PRIVATE DWORD ParseTreewalkAttribute(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
  1720. PRIVATE DWORD ParseIntField(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
  1721. PRIVATE DWORD ParseDwordField(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
  1722. PRIVATE DWORD ParseStringField(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
  1723. PRIVATE DWORD ParseUnknownAttribute(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
  1724. //
  1725. // data
  1726. //
  1727. ATTRIBUTE_PARSER AttributeParsers[] = {
  1728. GOPHER_CATEGORY_ID_ADMIN,
  1729. GOPHER_ATTRIBUTE_ID_ADMIN,
  1730. ParseAdminAttribute,
  1731. 2,
  1732. sizeof(GOPHER_ADMIN_ATTRIBUTE_TYPE),
  1733. GOPHER_CATEGORY_ID_ADMIN,
  1734. GOPHER_ATTRIBUTE_ID_MOD_DATE,
  1735. ParseModDateAttribute,
  1736. 1,
  1737. sizeof(GOPHER_MOD_DATE_ATTRIBUTE_TYPE),
  1738. GOPHER_CATEGORY_ID_ADMIN,
  1739. GOPHER_ATTRIBUTE_ID_TTL,
  1740. ParseDwordField,
  1741. 1,
  1742. sizeof(GOPHER_TTL_ATTRIBUTE_TYPE),
  1743. GOPHER_CATEGORY_ID_ADMIN,
  1744. GOPHER_ATTRIBUTE_ID_SCORE,
  1745. ParseIntField,
  1746. 1,
  1747. sizeof(GOPHER_SCORE_ATTRIBUTE_TYPE),
  1748. GOPHER_CATEGORY_ID_ADMIN,
  1749. GOPHER_ATTRIBUTE_ID_RANGE,
  1750. ParseIntField,
  1751. 2,
  1752. sizeof(GOPHER_SCORE_RANGE_ATTRIBUTE_TYPE),
  1753. GOPHER_CATEGORY_ID_ADMIN,
  1754. GOPHER_ATTRIBUTE_ID_SITE,
  1755. ParseStringField,
  1756. 1,
  1757. sizeof(GOPHER_SITE_ATTRIBUTE_TYPE),
  1758. GOPHER_CATEGORY_ID_ADMIN,
  1759. GOPHER_ATTRIBUTE_ID_ORG,
  1760. ParseStringField,
  1761. 1,
  1762. sizeof(GOPHER_ORGANIZATION_ATTRIBUTE_TYPE),
  1763. GOPHER_CATEGORY_ID_ADMIN,
  1764. GOPHER_ATTRIBUTE_ID_LOCATION,
  1765. ParseStringField,
  1766. 1,
  1767. sizeof(GOPHER_LOCATION_ATTRIBUTE_TYPE),
  1768. GOPHER_CATEGORY_ID_ADMIN,
  1769. GOPHER_ATTRIBUTE_ID_GEOG,
  1770. ParseIntField,
  1771. 6,
  1772. sizeof(GOPHER_GEOGRAPHICAL_LOCATION_ATTRIBUTE_TYPE),
  1773. GOPHER_CATEGORY_ID_ADMIN,
  1774. GOPHER_ATTRIBUTE_ID_TIMEZONE,
  1775. ParseIntField,
  1776. 1,
  1777. sizeof(GOPHER_TIMEZONE_ATTRIBUTE_TYPE),
  1778. GOPHER_CATEGORY_ID_ADMIN,
  1779. GOPHER_ATTRIBUTE_ID_PROVIDER,
  1780. ParseStringField,
  1781. 1,
  1782. sizeof(GOPHER_PROVIDER_ATTRIBUTE_TYPE),
  1783. GOPHER_CATEGORY_ID_ADMIN,
  1784. GOPHER_ATTRIBUTE_ID_VERSION,
  1785. ParseStringField,
  1786. 1,
  1787. sizeof(GOPHER_VERSION_ATTRIBUTE_TYPE),
  1788. GOPHER_CATEGORY_ID_ABSTRACT,
  1789. GOPHER_ATTRIBUTE_ID_ABSTRACT,
  1790. ParseAbstractAttribute,
  1791. 2,
  1792. sizeof(GOPHER_ABSTRACT_ATTRIBUTE_TYPE),
  1793. GOPHER_CATEGORY_ID_VIEWS,
  1794. GOPHER_ATTRIBUTE_ID_VIEW,
  1795. ParseViewAttribute,
  1796. 3,
  1797. sizeof(GOPHER_VIEW_ATTRIBUTE_TYPE),
  1798. GOPHER_CATEGORY_ID_VERONICA,
  1799. GOPHER_ATTRIBUTE_ID_TREEWALK,
  1800. ParseTreewalkAttribute,
  1801. 1,
  1802. sizeof(GOPHER_VERONICA_ATTRIBUTE_TYPE),
  1803. //
  1804. // N.B. Unknown must be the last parser in the list
  1805. //
  1806. GOPHER_CATEGORY_ID_UNKNOWN,
  1807. GOPHER_ATTRIBUTE_ID_UNKNOWN,
  1808. ParseUnknownAttribute,
  1809. 1,
  1810. sizeof(GOPHER_UNKNOWN_ATTRIBUTE_TYPE)
  1811. };
  1812. #define NUMBER_OF_PARSERS ARRAY_ELEMENTS(AttributeParsers)
  1813. //
  1814. // functions
  1815. //
  1816. BOOL
  1817. FindAttribute(
  1818. IN DWORD CategoryId,
  1819. IN DWORD AttributeId,
  1820. IN LPCSTR AttributeName,
  1821. IN OUT LPSTR* Buffer,
  1822. IN OUT LPDWORD BufferLength
  1823. )
  1824. /*++
  1825. Routine Description:
  1826. description-of-function.
  1827. Arguments:
  1828. CategoryId -
  1829. AttributeId -
  1830. AttributeName -
  1831. Buffer -
  1832. BufferLength -
  1833. Return Value:
  1834. BOOL
  1835. --*/
  1836. {
  1837. BOOL ok;
  1838. LPSTR categoryName;
  1839. LPSTR attributeName;
  1840. char searchName[DEFAULT_ATTRIBUTE_NAME_LENGTH + 1];
  1841. DWORD id;
  1842. int index;
  1843. int len;
  1844. if (AttributeId == GOPHER_ATTRIBUTE_ID_ALL) {
  1845. id = CategoryId;
  1846. } else {
  1847. id = AttributeId;
  1848. }
  1849. ok = MapAttributeIdToNames(id, &categoryName, &attributeName);
  1850. if (!ok) {
  1851. attributeName = (LPSTR)AttributeName;
  1852. }
  1853. if (AttributeId != GOPHER_ATTRIBUTE_ID_ALL) {
  1854. searchName[0] = ' ';
  1855. index = 1;
  1856. } else {
  1857. index = 0;
  1858. attributeName = categoryName;
  1859. }
  1860. len = strlen(attributeName);
  1861. if (len >= sizeof(searchName)) {
  1862. return FALSE;
  1863. }
  1864. strcpy(&searchName[index], attributeName);
  1865. len += index;
  1866. if (AttributeId != GOPHER_ATTRIBUTE_ID_ALL) {
  1867. searchName[len++] = ':';
  1868. searchName[len] = '\0';
  1869. }
  1870. do {
  1871. if (*BufferLength < (DWORD)len) {
  1872. return FALSE;
  1873. }
  1874. if (memcmp(*Buffer, searchName, len) == 0) {
  1875. return TRUE;
  1876. }
  1877. SkipLine(Buffer, BufferLength);
  1878. } while (*BufferLength != 0);
  1879. return FALSE;
  1880. }
  1881. VOID
  1882. FindNextAttribute(
  1883. IN DWORD CategoryId,
  1884. IN DWORD AttributeId,
  1885. IN OUT LPSTR* Buffer,
  1886. IN OUT LPDWORD BufferLength
  1887. )
  1888. /*++
  1889. Routine Description:
  1890. description-of-function.
  1891. Arguments:
  1892. CategoryId -
  1893. AttributeId -
  1894. Buffer -
  1895. BufferLength -
  1896. Return Value:
  1897. None.
  1898. --*/
  1899. {
  1900. BOOL found;
  1901. INET_ASSERT((**Buffer == '+') || (**Buffer == ' '));
  1902. if (CategoryId == GOPHER_CATEGORY_ID_UNKNOWN) {
  1903. AttributeId = !GOPHER_ATTRIBUTE_ID_ALL;
  1904. }
  1905. //
  1906. // loop looking at the next line until we find:
  1907. //
  1908. // a) the end of the buffer
  1909. // b) the next section line (starts with '+')
  1910. // c) the next attribute line (starts with ' ')
  1911. //
  1912. for (found = FALSE; !found; ) {
  1913. if (SkipLine(Buffer, BufferLength)) {
  1914. if (AttributeId == GOPHER_ATTRIBUTE_ID_ALL) {
  1915. found = (BOOL)(**Buffer == '+');
  1916. } else {
  1917. found = TRUE;
  1918. }
  1919. } else {
  1920. //
  1921. // end of buffer
  1922. //
  1923. found = TRUE;
  1924. }
  1925. }
  1926. }
  1927. DWORD
  1928. EnumerateAttribute(
  1929. IN GOPHER_ATTRIBUTE_ENUMERATOR Enumerator,
  1930. IN LPSTR LinePtr,
  1931. IN DWORD LineLength,
  1932. IN LPBYTE Buffer,
  1933. IN DWORD BufferLength,
  1934. OUT LPBOOL ResumeEnumeration
  1935. )
  1936. /*++
  1937. Routine Description:
  1938. description-of-function.
  1939. Arguments:
  1940. Enumerator -
  1941. LinePtr -
  1942. LineLength -
  1943. Buffer -
  1944. BufferLength -
  1945. ResumeEnumeration -
  1946. Return Value:
  1947. DWORD
  1948. --*/
  1949. {
  1950. DWORD error;
  1951. char attributeName[DEFAULT_ATTRIBUTE_NAME_LENGTH + 1];
  1952. DWORD nameLength;
  1953. nameLength = sizeof(attributeName);
  1954. error = ExtractAttributeName(attributeName,
  1955. &nameLength,
  1956. &LinePtr,
  1957. &LineLength
  1958. );
  1959. if (error == ERROR_SUCCESS) {
  1960. int i;
  1961. DWORD categoryId;
  1962. DWORD attributeId;
  1963. BOOL found;
  1964. MapAttributeToIds((LPCSTR)attributeName,
  1965. &categoryId,
  1966. &attributeId
  1967. );
  1968. //
  1969. // loop, looking fot the parser to handle this particular type. If we
  1970. // don't find it, we will be conveniently left at the unknown parser
  1971. // (that's why we have (NUMBER_OF_PARSERS - 1) and the unknown parser
  1972. // at the end of the list)
  1973. //
  1974. for (i = 0; i < NUMBER_OF_PARSERS - 1; ++i) {
  1975. if ((AttributeParsers[i].CategoryId == categoryId)
  1976. && (AttributeParsers[i].AttributeId == attributeId)) {
  1977. break;
  1978. }
  1979. }
  1980. if (BufferLength >= SIZE_OF_GOPHER_ATTRIBUTE_FIXED_PART) {
  1981. ((LPGOPHER_ATTRIBUTE_TYPE)Buffer)->CategoryId = categoryId;
  1982. ((LPGOPHER_ATTRIBUTE_TYPE)Buffer)->AttributeId = attributeId;
  1983. //
  1984. // remove the fixed part from the buffer size before converting
  1985. // the attribute
  1986. //
  1987. BufferLength -= SIZE_OF_GOPHER_ATTRIBUTE_FIXED_PART;
  1988. } else {
  1989. BufferLength = 0;
  1990. }
  1991. error = AttributeParsers[i].Parser(
  1992. &LinePtr,
  1993. &LineLength,
  1994. (LPBYTE)&((LPGOPHER_ATTRIBUTE_TYPE)Buffer)->AttributeType,
  1995. &BufferLength,
  1996. AttributeParsers[i].NumberOfFields,
  1997. AttributeParsers[i].FixedSize
  1998. );
  1999. //
  2000. // add back the amount of buffer space used by/required for the
  2001. // fixed part of the GOPHER_ATTRIBUTE_TYPE structure
  2002. //
  2003. *ResumeEnumeration = Enumerator((LPGOPHER_ATTRIBUTE_TYPE)Buffer, error);
  2004. }
  2005. return error;
  2006. }
  2007. PRIVATE
  2008. DWORD
  2009. ParseIntField(
  2010. IN OUT LPSTR* LinePtr,
  2011. IN OUT LPDWORD LineLength,
  2012. OUT LPBYTE Buffer,
  2013. IN OUT LPDWORD BufferLength,
  2014. IN DWORD NumberOfFields,
  2015. IN DWORD FixedSize
  2016. )
  2017. /*++
  2018. Routine Description:
  2019. description-of-function.
  2020. Arguments:
  2021. LinePtr -
  2022. LineLength -
  2023. Buffer -
  2024. BufferLength -
  2025. NumberOfFields -
  2026. FixedSize -
  2027. Return Value:
  2028. DWORD
  2029. --*/
  2030. {
  2031. DWORD error;
  2032. if (*BufferLength < FixedSize) {
  2033. error = ERROR_INSUFFICIENT_BUFFER;
  2034. } else {
  2035. error = ERROR_SUCCESS;
  2036. }
  2037. *BufferLength = FixedSize;
  2038. while ((error == ERROR_SUCCESS) && NumberOfFields--) {
  2039. if (SkipLeading(LinePtr, LineLength)) {
  2040. ExtractInt(LinePtr, 0, (LPINT)Buffer);
  2041. Buffer = (LPBYTE)((LPINT)Buffer + 1);
  2042. } else {
  2043. error = ERROR_GOPHER_DATA_ERROR;
  2044. }
  2045. }
  2046. return error;
  2047. }
  2048. PRIVATE
  2049. DWORD
  2050. ParseDwordField(
  2051. IN OUT LPSTR* LinePtr,
  2052. IN OUT LPDWORD LineLength,
  2053. OUT LPBYTE Buffer,
  2054. IN OUT LPDWORD BufferLength,
  2055. IN DWORD NumberOfFields,
  2056. IN DWORD FixedSize
  2057. )
  2058. /*++
  2059. Routine Description:
  2060. description-of-function.
  2061. Arguments:
  2062. LinePtr -
  2063. LineLength -
  2064. Buffer -
  2065. BufferLength -
  2066. NumberOfFields -
  2067. FixedSize -
  2068. Return Value:
  2069. DWORD
  2070. --*/
  2071. {
  2072. DWORD error;
  2073. if (*BufferLength < FixedSize) {
  2074. error = ERROR_INSUFFICIENT_BUFFER;
  2075. } else {
  2076. error = ERROR_SUCCESS;
  2077. }
  2078. *BufferLength = FixedSize;
  2079. while ((error == ERROR_SUCCESS) && NumberOfFields--) {
  2080. if (SkipLeading(LinePtr, LineLength)) {
  2081. ExtractDword(LinePtr, 0, (LPDWORD)Buffer);
  2082. Buffer = (LPBYTE)((LPDWORD)Buffer + 1);
  2083. } else {
  2084. error = ERROR_GOPHER_DATA_ERROR;
  2085. }
  2086. }
  2087. return error;
  2088. }
  2089. PRIVATE
  2090. DWORD
  2091. ParseStringField(
  2092. IN OUT LPSTR* LinePtr,
  2093. IN OUT LPDWORD LineLength,
  2094. OUT LPBYTE Buffer,
  2095. IN OUT LPDWORD BufferLength,
  2096. IN DWORD NumberOfFields,
  2097. IN DWORD FixedSize
  2098. )
  2099. /*++
  2100. Routine Description:
  2101. description-of-function.
  2102. Arguments:
  2103. LinePtr -
  2104. LineLength -
  2105. Buffer -
  2106. BufferLength -
  2107. NumberOfFields -
  2108. FixedSize -
  2109. Return Value:
  2110. DWORD
  2111. --*/
  2112. {
  2113. DWORD structureSize;
  2114. DWORD error;
  2115. DWORD stringLength;
  2116. LPSTR stringPtr;
  2117. LPSTR* fieldPtr;
  2118. structureSize = 0;
  2119. error = ERROR_SUCCESS;
  2120. stringPtr = (LPSTR)((LPSTR)Buffer + NumberOfFields);
  2121. fieldPtr = (LPSTR*)Buffer;
  2122. while (NumberOfFields--) {
  2123. SkipLeading(LinePtr, LineLength);
  2124. stringLength = CharacterCount(LinePtr, LineLength, "\r\n");
  2125. structureSize = sizeof(LPSTR)
  2126. + stringLength
  2127. + 1
  2128. ;
  2129. if (*BufferLength >= structureSize) {
  2130. *fieldPtr++ = stringPtr;
  2131. CopyString(&stringPtr, *LinePtr, stringLength);
  2132. *LinePtr += stringLength - 1;
  2133. } else {
  2134. error = ERROR_INSUFFICIENT_BUFFER;
  2135. }
  2136. }
  2137. *BufferLength = structureSize;
  2138. return error;
  2139. }
  2140. PRIVATE
  2141. DWORD
  2142. ParseAdminAttribute(
  2143. IN OUT LPSTR* LinePtr,
  2144. IN OUT LPDWORD LineLength,
  2145. OUT LPBYTE Buffer,
  2146. IN OUT LPDWORD BufferLength,
  2147. IN DWORD NumberOfFields,
  2148. IN DWORD FixedSize
  2149. )
  2150. /*++
  2151. Routine Description:
  2152. description-of-function.
  2153. Arguments:
  2154. LinePtr -
  2155. LineLength -
  2156. Buffer -
  2157. BufferLength -
  2158. NumberOfFields -
  2159. FixedSize -
  2160. Return Value:
  2161. DWORD
  2162. --*/
  2163. {
  2164. LPSTR comment;
  2165. DWORD commentLength;
  2166. LPSTR emailAddress;
  2167. DWORD emailAddressLength;
  2168. LPSTR pstr;
  2169. DWORD len;
  2170. DWORD structureSize;
  2171. DWORD error;
  2172. SkipLeading(LinePtr, LineLength);
  2173. comment = *LinePtr;
  2174. emailAddress = strchr(*LinePtr, '<');
  2175. if (emailAddress == NULL) {
  2176. return ERROR_GOPHER_DATA_ERROR;
  2177. }
  2178. ++emailAddress;
  2179. pstr = emailAddress;
  2180. emailAddressLength = 0;
  2181. len = *LineLength;
  2182. while ((*pstr != '>') && (len != 0)) {
  2183. ++pstr;
  2184. ++emailAddressLength;
  2185. }
  2186. commentLength = (DWORD)(emailAddress - comment);
  2187. structureSize = sizeof(GOPHER_ADMIN_ATTRIBUTE_TYPE)
  2188. + commentLength + 1
  2189. + emailAddressLength + 1
  2190. ;
  2191. if (*BufferLength < structureSize) {
  2192. error = ERROR_INSUFFICIENT_BUFFER;
  2193. } else {
  2194. error = ERROR_SUCCESS;
  2195. }
  2196. *BufferLength = structureSize;
  2197. if (error == ERROR_SUCCESS) {
  2198. LPGOPHER_ADMIN_ATTRIBUTE_TYPE pStruct;
  2199. LPSTR stringPtr;
  2200. pStruct = (LPGOPHER_ADMIN_ATTRIBUTE_TYPE)Buffer;
  2201. stringPtr = (LPSTR)pStruct + sizeof(GOPHER_ADMIN_ATTRIBUTE_TYPE);
  2202. pStruct->Comment = (LPCSTR)stringPtr;
  2203. CopyString(&stringPtr, comment, commentLength);
  2204. pStruct->EmailAddress = (LPCSTR)stringPtr;
  2205. CopyString(&stringPtr, emailAddress, emailAddressLength);
  2206. }
  2207. return error;
  2208. }
  2209. PRIVATE
  2210. DWORD
  2211. ParseModDateAttribute(
  2212. IN OUT LPSTR* LinePtr,
  2213. IN OUT LPDWORD LineLength,
  2214. OUT LPBYTE Buffer,
  2215. IN OUT LPDWORD BufferLength,
  2216. IN DWORD NumberOfFields,
  2217. IN DWORD FixedSize
  2218. )
  2219. /*++
  2220. Routine Description:
  2221. description-of-function.
  2222. Arguments:
  2223. LinePtr -
  2224. LineLength -
  2225. Buffer -
  2226. BufferLength -
  2227. NumberOfFields -
  2228. FixedSize -
  2229. Return Value:
  2230. DWORD
  2231. --*/
  2232. {
  2233. DWORD structureSize;
  2234. DWORD error;
  2235. structureSize = sizeof(GOPHER_MOD_DATE_ATTRIBUTE_TYPE);
  2236. if (*BufferLength < structureSize) {
  2237. error = ERROR_INSUFFICIENT_BUFFER;
  2238. } else {
  2239. error = ERROR_SUCCESS;
  2240. }
  2241. *BufferLength = structureSize;
  2242. if (error == ERROR_SUCCESS) {
  2243. LPSTR dateField;
  2244. dateField = strchr(*LinePtr, '<');
  2245. if (dateField != NULL) {
  2246. ExtractDateAndTime(
  2247. &dateField,
  2248. &((LPGOPHER_MOD_DATE_ATTRIBUTE_TYPE)Buffer)->DateAndTime
  2249. );
  2250. } else {
  2251. error = ERROR_GOPHER_DATA_ERROR;
  2252. }
  2253. }
  2254. return error;
  2255. }
  2256. PRIVATE
  2257. DWORD
  2258. ParseAbstractAttribute(
  2259. IN OUT LPSTR* LinePtr,
  2260. IN OUT LPDWORD LineLength,
  2261. OUT LPBYTE Buffer,
  2262. IN OUT LPDWORD BufferLength,
  2263. IN DWORD NumberOfFields,
  2264. IN DWORD FixedSize
  2265. )
  2266. /*++
  2267. Routine Description:
  2268. description-of-function.
  2269. Arguments:
  2270. LinePtr -
  2271. LineLength -
  2272. Buffer -
  2273. BufferLength -
  2274. NumberOfFields -
  2275. FixedSize -
  2276. Return Value:
  2277. DWORD
  2278. --*/
  2279. {
  2280. DWORD error;
  2281. error = ERROR_SUCCESS;
  2282. return error;
  2283. }
  2284. PRIVATE
  2285. DWORD
  2286. ParseViewAttribute(
  2287. IN OUT LPSTR* LinePtr,
  2288. IN OUT LPDWORD LineLength,
  2289. OUT LPBYTE Buffer,
  2290. IN OUT LPDWORD BufferLength,
  2291. IN DWORD NumberOfFields,
  2292. IN DWORD FixedSize
  2293. )
  2294. /*++
  2295. Routine Description:
  2296. description-of-function.
  2297. Arguments:
  2298. LinePtr -
  2299. LineLength -
  2300. Buffer -
  2301. BufferLength -
  2302. NumberOfFields -
  2303. FixedSize -
  2304. Return Value:
  2305. DWORD
  2306. --*/
  2307. {
  2308. char contentType[DEFAULT_CONTENT_TYPE_NAME_LENGTH + 1];
  2309. DWORD contentTypeLength;
  2310. char language[DEFAULT_LANGUAGE_NAME_LENGTH + 1];
  2311. DWORD languageLength;
  2312. DWORD viewSize;
  2313. BOOL ok;
  2314. DWORD error;
  2315. contentTypeLength = sizeof(contentType);
  2316. languageLength = sizeof(language);
  2317. SkipLeading(LinePtr, LineLength);
  2318. ok = ExtractView(LinePtr,
  2319. contentType,
  2320. &contentTypeLength,
  2321. language,
  2322. &languageLength,
  2323. &viewSize
  2324. );
  2325. if (ok) {
  2326. DWORD structureSize;
  2327. contentTypeLength = sizeof(contentType) - contentTypeLength;
  2328. languageLength = sizeof(language) - languageLength;
  2329. structureSize = sizeof(GOPHER_VIEW_ATTRIBUTE_TYPE)
  2330. + contentTypeLength
  2331. + languageLength
  2332. ;
  2333. if (*BufferLength >= structureSize) {
  2334. LPSTR stringPtr;
  2335. stringPtr = (LPSTR)((LPGOPHER_VIEW_ATTRIBUTE_TYPE)Buffer + 1);
  2336. ((LPGOPHER_VIEW_ATTRIBUTE_TYPE)Buffer)->ContentType = stringPtr;
  2337. memcpy(stringPtr, contentType, contentTypeLength);
  2338. stringPtr += contentTypeLength;
  2339. ((LPGOPHER_VIEW_ATTRIBUTE_TYPE)Buffer)->Language = stringPtr;
  2340. memcpy(stringPtr, language, languageLength);
  2341. ((LPGOPHER_VIEW_ATTRIBUTE_TYPE)Buffer)->Size = viewSize;
  2342. error = ERROR_SUCCESS;
  2343. } else {
  2344. error = ERROR_INSUFFICIENT_BUFFER;
  2345. }
  2346. *BufferLength = structureSize;
  2347. } else {
  2348. error = ERROR_GOPHER_DATA_ERROR;
  2349. }
  2350. return error;
  2351. }
  2352. PRIVATE
  2353. DWORD
  2354. ParseTreewalkAttribute(
  2355. IN OUT LPSTR* LinePtr,
  2356. IN OUT LPDWORD LineLength,
  2357. OUT LPBYTE Buffer,
  2358. IN OUT LPDWORD BufferLength,
  2359. IN DWORD NumberOfFields,
  2360. IN DWORD FixedSize
  2361. )
  2362. /*++
  2363. Routine Description:
  2364. description-of-function.
  2365. Arguments:
  2366. LinePtr -
  2367. LineLength -
  2368. Buffer -
  2369. BufferLength -
  2370. NumberOfFields -
  2371. FixedSize -
  2372. Return Value:
  2373. DWORD
  2374. --*/
  2375. {
  2376. DWORD structureSize;
  2377. DWORD error;
  2378. structureSize = sizeof(GOPHER_VERONICA_ATTRIBUTE_TYPE);
  2379. if (*BufferLength < structureSize) {
  2380. error = ERROR_INSUFFICIENT_BUFFER;
  2381. } else {
  2382. error = ERROR_SUCCESS;
  2383. }
  2384. *BufferLength = structureSize;
  2385. if (error == ERROR_SUCCESS) {
  2386. SkipLeading(LinePtr, LineLength);
  2387. if (*LineLength >= 3) {
  2388. BOOL ok;
  2389. ok = (BOOL)(_strnicmp(*LinePtr, "YES", 3) == 0);
  2390. ((LPGOPHER_VERONICA_ATTRIBUTE_TYPE)Buffer)->TreeWalk = ok;
  2391. } else {
  2392. error = ERROR_GOPHER_DATA_ERROR;
  2393. }
  2394. }
  2395. return error;
  2396. }
  2397. PRIVATE
  2398. DWORD
  2399. ParseUnknownAttribute(
  2400. IN OUT LPSTR* LinePtr,
  2401. IN OUT LPDWORD LineLength,
  2402. OUT LPBYTE Buffer,
  2403. IN OUT LPDWORD BufferLength,
  2404. IN DWORD NumberOfFields,
  2405. IN DWORD FixedSize
  2406. )
  2407. /*++
  2408. Routine Description:
  2409. description-of-function.
  2410. Arguments:
  2411. LinePtr -
  2412. LineLength -
  2413. Buffer -
  2414. BufferLength -
  2415. NumberOfFields -
  2416. FixedSize -
  2417. Return Value:
  2418. DWORD
  2419. --*/
  2420. {
  2421. DWORD structureSize;
  2422. DWORD error;
  2423. DWORD stringLength;
  2424. LPSTR stringPtr;
  2425. stringPtr = (LPSTR)((LPGOPHER_UNKNOWN_ATTRIBUTE_TYPE)Buffer + 1);
  2426. ((LPGOPHER_UNKNOWN_ATTRIBUTE_TYPE)Buffer)->Text = stringPtr;
  2427. SkipLeading(LinePtr, LineLength);
  2428. structureSize = sizeof(GOPHER_UNKNOWN_ATTRIBUTE_TYPE)
  2429. + *LineLength
  2430. + 1
  2431. ;
  2432. if (*BufferLength >= structureSize) {
  2433. CopyString(&stringPtr, *LinePtr, *LineLength);
  2434. } else {
  2435. error = ERROR_INSUFFICIENT_BUFFER;
  2436. }
  2437. *BufferLength = structureSize;
  2438. return error;
  2439. }
  2440. PRIVATE
  2441. DWORD
  2442. ExtractAttributeName(
  2443. OUT LPSTR AttributeName,
  2444. IN OUT LPDWORD AttributeNameLength,
  2445. IN OUT LPSTR* LinePtr,
  2446. IN OUT LPDWORD LineLength
  2447. )
  2448. /*++
  2449. Routine Description:
  2450. description-of-function.
  2451. Arguments:
  2452. AttributeName -
  2453. AttributeNameLength -
  2454. LinePtr -
  2455. LineLength -
  2456. Return Value:
  2457. DWORD
  2458. --*/
  2459. {
  2460. return ERROR_SUCCESS;
  2461. }
  2462. PRIVATE
  2463. DWORD
  2464. CharacterCount(
  2465. IN OUT LPSTR* LinePtr,
  2466. IN OUT LPDWORD LineLength,
  2467. IN LPSTR TerminationSet
  2468. )
  2469. /*++
  2470. Routine Description:
  2471. Returns the number of characters in a string, up to, but not including
  2472. the termination character. Termination character is defined as being a
  2473. member of TerminationSet
  2474. Arguments:
  2475. LinePtr - IN: Pointer to string to count characters in
  2476. OUT: Pointer to string at character found from
  2477. TerminationSet
  2478. LineLength - IN: Current length of LinePtr
  2479. OUT: Remaining length of LinePtr
  2480. TerminationSet - Pointer to string containing characters which will
  2481. terminate counting
  2482. Return Value:
  2483. DWORD
  2484. Number of character in LinePtr, up to, but not including the
  2485. termination character
  2486. --*/
  2487. {
  2488. char terminationChars[256];
  2489. int i;
  2490. DWORD count;
  2491. //
  2492. // zap discovery matrix, 4 bytes at a time
  2493. //
  2494. for (i = 0; i < ARRAY_ELEMENTS(terminationChars); i += sizeof(DWORD)) {
  2495. *(LPDWORD)&terminationChars[i] = 0;
  2496. }
  2497. //
  2498. // for each character that we are interested in, set its matrix entry to
  2499. // non-zero
  2500. //
  2501. for (i = 0; TerminationSet[i] != '\0'; ++i) {
  2502. terminationChars[(int)TerminationSet[i]] = 1;
  2503. }
  2504. //
  2505. // loop, looking for end-of-string (LineLength decremented to 0) or one
  2506. // of the termination characters
  2507. //
  2508. for (count = 0; *LineLength != 0; ) {
  2509. char ch;
  2510. ch = *(*LinePtr)++;
  2511. --*LineLength;
  2512. if (terminationChars[(int)ch]) {
  2513. break;
  2514. }
  2515. ++count;
  2516. }
  2517. return count;
  2518. }
  2519. PRIVATE
  2520. DWORD
  2521. CountCharactersToEol(
  2522. IN OUT LPSTR* LinePtr,
  2523. IN OUT LPDWORD LineLength
  2524. )
  2525. /*++
  2526. Routine Description:
  2527. Special-case version of CharacterCount - knows that the termination set
  2528. comprised <CR>, <LF>
  2529. Arguments:
  2530. LinePtr - Pointer to pointer to string to count. Updated on output
  2531. LineLength - Pointer to length of string. Updated on output
  2532. Return Value:
  2533. DWORD
  2534. Length of string
  2535. --*/
  2536. {
  2537. int i;
  2538. DWORD count;
  2539. //
  2540. // loop, looking for end-of-string (LineLength decremented to 0) or one
  2541. // of the termination characters
  2542. //
  2543. for (count = 0; *LineLength != 0; ) {
  2544. char ch;
  2545. ch = *(*LinePtr)++;
  2546. --*LineLength;
  2547. if ((ch == '\r') || (ch == '\n')) {
  2548. break;
  2549. }
  2550. ++count;
  2551. }
  2552. return count;
  2553. }
  2554. PRIVATE
  2555. BOOL
  2556. SkipLeading(
  2557. IN OUT LPSTR* String,
  2558. IN OUT LPDWORD Length
  2559. )
  2560. {
  2561. while (((**String == ' ') || (**String == ':')) && (*Length != 0)) {
  2562. ++*String;
  2563. --*Length;
  2564. }
  2565. return (BOOL)(*Length != 0);
  2566. }
  2567. PRIVATE
  2568. VOID
  2569. CopyString(
  2570. IN OUT LPSTR* String,
  2571. IN LPSTR Source,
  2572. IN DWORD Length
  2573. )
  2574. /*++
  2575. Routine Description:
  2576. description-of-function.
  2577. Arguments:
  2578. String -
  2579. Source -
  2580. Length -
  2581. Return Value:
  2582. None.
  2583. --*/
  2584. {
  2585. memcpy(*String, Source, Length);
  2586. *String += Length;
  2587. *((*String)++) = '\0';
  2588. }
  2589. #endif // defined(GOPHER_ATTRIBUTE_SUPPORT)