3425 lines
86 KiB
C++
3425 lines
86 KiB
C++
/*++
|
||
|
||
Copyright (c) 1994 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
parse.cxx
|
||
|
||
Abstract:
|
||
|
||
Contains functions to parse gopher information received from the server
|
||
|
||
Contents:
|
||
IsValidLocator
|
||
IsGopherPlus
|
||
CrackLocator
|
||
GopherCharToType
|
||
GopherTypeToChar
|
||
GetDirEntry
|
||
(GopherLocatorToFindData)
|
||
ReadData
|
||
(ExtractLine)
|
||
(ExtractDisplayString)
|
||
CopyToEol
|
||
IsGopherPlusToken
|
||
MapAttributeNameToId
|
||
MapAttributeToIds
|
||
MapAttributeIdToNames
|
||
GetGopherNumber
|
||
ExtractDateAndTime
|
||
ExtractView
|
||
FindAttribute
|
||
FindNextAttribute
|
||
EnumerateAttribute
|
||
ParseIntField
|
||
ParseDwordField
|
||
ParseStringField
|
||
ParseAdminAttribute
|
||
ParseModDateAttribute
|
||
ParseAbstractAttribute
|
||
ParseViewAttribute
|
||
ParseTreewalkAttribute
|
||
ParseUnknownAttribute
|
||
(ExtractAttributeName)
|
||
(CharacterCount)
|
||
(CountCharactersToEol)
|
||
(CopyString)
|
||
|
||
Author:
|
||
|
||
Richard L Firth (rfirth) 17-Oct-1994
|
||
|
||
Environment:
|
||
|
||
Win32 user-level DLL
|
||
|
||
Revision History:
|
||
|
||
17-Oct-1994 rfirth
|
||
Created
|
||
|
||
--*/
|
||
|
||
#include <wininetp.h>
|
||
#include "gfrapih.h"
|
||
#include "iert.h"
|
||
|
||
//
|
||
// manifests
|
||
//
|
||
|
||
#define DEFAULT_LINE_BUFFER_LENGTH 1024 // arbitrary
|
||
#define DEFAULT_ATTRIBUTE_NAME_LENGTH 64 // "
|
||
#define DEFAULT_LANGUAGE_NAME_LENGTH 32 // "
|
||
#define DEFAULT_CONTENT_TYPE_NAME_LENGTH 80 // "
|
||
#define SEARCH_TYPE_MOD_DATE 1
|
||
#define SEARCH_TYPE_VIEW 2
|
||
|
||
//
|
||
// macros
|
||
//
|
||
|
||
#define NUMERIC_CHARACTER_TO_NUMBER(c) ((int)(c) - (int)('0'))
|
||
|
||
//
|
||
// prototypes
|
||
//
|
||
|
||
PRIVATE
|
||
DWORD
|
||
GopherLocatorToFindData(
|
||
IN LPCSTR Locator,
|
||
IN DWORD Length,
|
||
OUT LPGOPHER_FIND_DATA FindData
|
||
);
|
||
|
||
PRIVATE
|
||
DWORD
|
||
ExtractLine(
|
||
IN LPVIEW_INFO ViewInfo,
|
||
OUT LPBYTE LineBuffer,
|
||
IN OUT LPDWORD LineBufferLength,
|
||
IN OUT LPDWORD DataOffset
|
||
);
|
||
|
||
PRIVATE
|
||
DWORD
|
||
ExtractDisplayString(
|
||
IN LPCSTR Locator,
|
||
IN OUT LPSTR* StringPointer,
|
||
IN DWORD BufferLength
|
||
);
|
||
|
||
PRIVATE
|
||
BOOL
|
||
SkipLeading(
|
||
IN OUT LPSTR* String,
|
||
IN OUT LPDWORD Length
|
||
);
|
||
|
||
PRIVATE
|
||
DWORD
|
||
ExtractAttributeName(
|
||
OUT LPSTR AttributeName,
|
||
IN OUT LPDWORD AttributeNameLength,
|
||
IN OUT LPSTR* LinePtr,
|
||
IN OUT LPDWORD LineLength
|
||
);
|
||
|
||
PRIVATE
|
||
DWORD
|
||
CharacterCount(
|
||
IN OUT LPSTR* LinePtr,
|
||
IN OUT LPDWORD LineLength,
|
||
IN LPSTR TerminationSet
|
||
);
|
||
|
||
PRIVATE
|
||
DWORD
|
||
CountCharactersToEol(
|
||
IN OUT LPSTR* LinePtr,
|
||
IN OUT LPDWORD LineLength
|
||
);
|
||
|
||
PRIVATE
|
||
VOID
|
||
CopyString(
|
||
IN OUT LPSTR* String,
|
||
IN LPSTR Source,
|
||
IN DWORD Length
|
||
);
|
||
|
||
//
|
||
// functions
|
||
//
|
||
|
||
|
||
BOOL
|
||
IsValidLocator(
|
||
IN LPCSTR Locator,
|
||
IN DWORD MaximumLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Given a locator string, determines whether it is a valid gopher locator. A
|
||
valid gopher locator must have the form:
|
||
|
||
<GopherChar><DisplayString>TAB<SelectorString>TAB<HostName>TAB<Port>[TAB<Gopher+Stuff>]<CR><LF><EOS>
|
||
|
||
We don't care about the contents of DisplayString, SelectorString, HostName,
|
||
Port or Gopher+Stuff, since these will be sorted out by sockets functions or
|
||
the gopher protocol
|
||
|
||
Arguments:
|
||
|
||
Locator - pointer to locator string
|
||
|
||
MaximumLength - maximum number characters that can be in the locator
|
||
|
||
Return Value:
|
||
|
||
BOOL
|
||
TRUE - Locator is valid
|
||
|
||
FALSE - Locator does not look like kosher gopher locator, already
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL success;
|
||
|
||
success = FALSE;
|
||
__try {
|
||
|
||
DWORD locatorLength;
|
||
|
||
locatorLength = strlen(Locator);
|
||
|
||
//
|
||
// 1. Since there are all sorts of unspecified gopher types in the world,
|
||
// we no longer test the type, but just make sure its not 0 (which would
|
||
// have yielded a zero locatorLength). Also check that the locator doesn't
|
||
// break the maximum length limit
|
||
//
|
||
|
||
if ((locatorLength != 0) && (locatorLength <= MaximumLength)) {
|
||
|
||
//
|
||
// 2. <DisplayString>. Can be empty. This can be any character, ANSI
|
||
// or otherwise, we just don't care about its contents
|
||
//
|
||
|
||
++Locator;
|
||
--locatorLength;
|
||
while ((*Locator != '\t') && (locatorLength != 0)) {
|
||
--locatorLength;
|
||
++Locator;
|
||
}
|
||
|
||
if ((*Locator == '\t') && (locatorLength != 0)) {
|
||
|
||
//
|
||
// 3. <SelectorString>. Same rules as for DisplayString: contents
|
||
// not interesting
|
||
//
|
||
|
||
++Locator;
|
||
--locatorLength;
|
||
while ((*Locator != '\t') && (locatorLength != 0)) {
|
||
--locatorLength;
|
||
++Locator;
|
||
}
|
||
|
||
if ((*Locator == '\t') && (locatorLength != 0)) {
|
||
|
||
//
|
||
// 4. <HostName>. Again, we don't care about the characters
|
||
// that comprise HostName, or the length. We used to require
|
||
// a non-zero length
|
||
//
|
||
|
||
++Locator;
|
||
--locatorLength;
|
||
while ((*Locator != '\t') && (locatorLength != 0)) {
|
||
--locatorLength;
|
||
++Locator;
|
||
}
|
||
|
||
if ((*Locator == '\t') && (locatorLength != 0)) {
|
||
|
||
DWORD number;
|
||
|
||
//
|
||
// 5. Port. This must comprise 0..5 digit characters
|
||
//
|
||
|
||
++Locator;
|
||
--locatorLength;
|
||
number = 0;
|
||
while ((*Locator != '\t')
|
||
&& (*Locator != '\r')
|
||
&& (*Locator != '\n')
|
||
&& (*Locator >= '0')
|
||
&& (*Locator <= '9')) {
|
||
|
||
//
|
||
// we are kind of assuming no leading zeroes...
|
||
//
|
||
|
||
number = number * 10 + (DWORD)(*Locator - '0');
|
||
--locatorLength;
|
||
++Locator;
|
||
}
|
||
|
||
if (number <= (DWORD)INTERNET_MAX_PORT_NUMBER_VALUE) {
|
||
|
||
//
|
||
// 6. Optional gopher+ characters. We ignore the
|
||
// rest of the locator, and assume that it is
|
||
// correct
|
||
//
|
||
|
||
if ((*Locator == '\t') && (locatorLength >= 2)) {
|
||
do {
|
||
++Locator;
|
||
--locatorLength;
|
||
} while ( (*Locator != '\r')
|
||
&& (*Locator != '\n')
|
||
&& (locatorLength != 0) );
|
||
}
|
||
|
||
//
|
||
// check for line termination. Because of the random
|
||
// nature of gopher servers, we allow 0 or more '\r'
|
||
// followed by '\n'. The locator MUST be terminated
|
||
// by '\n'
|
||
//
|
||
|
||
while ((*Locator == '\r') && (locatorLength != 0)) {
|
||
++Locator;
|
||
--locatorLength;
|
||
}
|
||
if ((*Locator == '\n') && (locatorLength == 1)) {
|
||
success = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
||
success = FALSE;
|
||
}
|
||
ENDEXCEPT
|
||
return success;
|
||
}
|
||
|
||
|
||
BOOL
|
||
IsGopherPlus(
|
||
IN LPCSTR Locator
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns TRUE if Locator describes a gopher+ request
|
||
|
||
ASSUMES: 1. Locator is valid
|
||
|
||
Arguments:
|
||
|
||
Locator - pointer to locator to check
|
||
|
||
Return Value:
|
||
|
||
BOOL
|
||
|
||
--*/
|
||
|
||
{
|
||
LPSTR plusStuff;
|
||
|
||
if (!IsValidLocator(Locator, strlen(Locator))) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// use CrackLocator to see if there is gopher+ info on this locator
|
||
//
|
||
|
||
CrackLocator(Locator,
|
||
NULL, // Type
|
||
NULL, // DisplayString
|
||
NULL, // DisplayStringLength
|
||
NULL, // SelectorString
|
||
NULL, // SelectorStringLength
|
||
NULL, // HostName
|
||
NULL, // HostNameLength
|
||
NULL, // GopherPort
|
||
&plusStuff
|
||
);
|
||
return (BOOL)(plusStuff != NULL);
|
||
}
|
||
|
||
|
||
BOOL
|
||
CrackLocator(
|
||
IN LPCSTR Locator,
|
||
OUT LPDWORD Type OPTIONAL,
|
||
OUT LPSTR DisplayString OPTIONAL,
|
||
IN OUT LPDWORD DisplayStringLength OPTIONAL,
|
||
OUT LPSTR SelectorString OPTIONAL,
|
||
IN OUT LPDWORD SelectorStringLength OPTIONAL,
|
||
OUT LPSTR HostName OPTIONAL,
|
||
IN OUT LPDWORD HostNameLength OPTIONAL,
|
||
OUT LPDWORD GopherPort OPTIONAL,
|
||
OUT LPSTR* ExtraStuff OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Given a locator, break it into its constituent parts. The Locator argument
|
||
is NOT modified
|
||
|
||
ASSUMES: 1. Locator is valid
|
||
|
||
2. If an optional pointer is supplied, the associated length
|
||
parameter (if applicable) must also be supplied
|
||
|
||
Arguments:
|
||
|
||
Locator - pointer to locator to crack
|
||
|
||
Type - optional returned type character
|
||
|
||
DisplayString - optional returned display string
|
||
|
||
DisplayStringLength - optional in/out display string buffer length
|
||
|
||
SelectorString - optional returned selector string
|
||
|
||
SelectorStringLength - optional in/out selector string buffer length
|
||
|
||
HostName - optional returned host name
|
||
|
||
HostNameLength - optional in/out host name buffer length
|
||
|
||
GopherPort - optional returned gopher port
|
||
|
||
ExtraStuff - optional returned extra (gopher+) data from end of locator.
|
||
This argument is a returned pointer, not a buffer. Care
|
||
should be taken since this argument aliases Locator (or
|
||
part thereof)
|
||
|
||
Return Value:
|
||
|
||
TRUE - locator cracked ok
|
||
|
||
FALSE - problem encountered cracking locator, probably substring
|
||
breaks buffer limit
|
||
|
||
--*/
|
||
|
||
{
|
||
LPSTR pTab;
|
||
DWORD len;
|
||
LPSTR extraStuff;
|
||
DWORD locatorLength;
|
||
|
||
locatorLength = strlen(Locator);
|
||
if (ARGUMENT_PRESENT(Type)) {
|
||
*Type = GopherCharToType(*Locator);
|
||
}
|
||
|
||
++Locator; // past type character
|
||
--locatorLength;
|
||
pTab = (LPSTR)memchr((LPVOID)Locator, '\t', locatorLength);
|
||
|
||
INET_ASSERT(pTab != NULL);
|
||
|
||
len = (DWORD) (pTab - Locator);
|
||
if (ARGUMENT_PRESENT(DisplayString)) {
|
||
|
||
INET_ASSERT(DisplayStringLength != NULL);
|
||
|
||
if (*DisplayStringLength <= len) {
|
||
return FALSE;
|
||
}
|
||
memcpy(DisplayString, Locator, len);
|
||
DisplayString[len] = '\0';
|
||
*DisplayStringLength = len;
|
||
}
|
||
|
||
Locator = pTab + 1; // past display string and TAB
|
||
locatorLength -= (len + 1);
|
||
|
||
pTab = (LPSTR)memchr((LPVOID)Locator, '\t', locatorLength);
|
||
|
||
INET_ASSERT(pTab != NULL);
|
||
|
||
len = (DWORD) (pTab - Locator);
|
||
if (ARGUMENT_PRESENT(SelectorString)) {
|
||
|
||
INET_ASSERT(SelectorStringLength != NULL);
|
||
|
||
if (*SelectorStringLength <= len) {
|
||
return FALSE;
|
||
}
|
||
memcpy(SelectorString, Locator, len);
|
||
SelectorString[len] = '\0';
|
||
*SelectorStringLength = len;
|
||
}
|
||
|
||
Locator = pTab + 1; // past selector string and TAB
|
||
locatorLength -= (len + 1);
|
||
|
||
pTab = (LPSTR)memchr((LPVOID)Locator, '\t', locatorLength);
|
||
|
||
INET_ASSERT(pTab != NULL);
|
||
|
||
len = (DWORD) (pTab - Locator);
|
||
if (ARGUMENT_PRESENT(HostName)) {
|
||
|
||
INET_ASSERT(HostNameLength != NULL);
|
||
|
||
if (*HostNameLength <= len) {
|
||
return FALSE;
|
||
}
|
||
memcpy(HostName, Locator, len);
|
||
HostName[len] = '\0';
|
||
*HostNameLength = len;
|
||
}
|
||
|
||
Locator = pTab + 1; // past host name and TAB
|
||
locatorLength -= (len + 1);
|
||
pTab = (LPSTR)memchr(Locator, '\t', locatorLength);
|
||
if (pTab != NULL) {
|
||
extraStuff = pTab + 1; // past port and TAB
|
||
} else {
|
||
extraStuff = NULL;
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT(GopherPort)) {
|
||
*GopherPort = (DWORD)STRTOUL(Locator, NULL, 10);
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT(ExtraStuff)) {
|
||
*ExtraStuff = extraStuff;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
DWORD
|
||
GopherCharToType(
|
||
IN CHAR GopherChar
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Converts the gopher descriptor character to a Gfr attribute
|
||
|
||
Arguments:
|
||
|
||
GopherChar - the gopher character to convert
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
mapped gopher type or GOPHER_TYPE_UNKNOWN if we don't recognise the
|
||
character
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// these are the types currently specified in RFC 1436 (plus a few that
|
||
// aren't)
|
||
//
|
||
|
||
switch (GopherChar) {
|
||
case GOPHER_CHAR_TEXT_FILE:
|
||
return GOPHER_TYPE_TEXT_FILE;
|
||
|
||
case GOPHER_CHAR_DIRECTORY:
|
||
return GOPHER_TYPE_DIRECTORY;
|
||
|
||
case GOPHER_CHAR_CSO:
|
||
return GOPHER_TYPE_CSO;
|
||
|
||
case GOPHER_CHAR_ERROR:
|
||
return GOPHER_TYPE_ERROR;
|
||
|
||
case GOPHER_CHAR_MAC_BINHEX:
|
||
return GOPHER_TYPE_MAC_BINHEX;
|
||
|
||
case GOPHER_CHAR_DOS_ARCHIVE:
|
||
return GOPHER_TYPE_DOS_ARCHIVE;
|
||
|
||
case GOPHER_CHAR_UNIX_UUENCODED:
|
||
return GOPHER_TYPE_UNIX_UUENCODED;
|
||
|
||
case GOPHER_CHAR_INDEX_SERVER:
|
||
return GOPHER_TYPE_INDEX_SERVER;
|
||
|
||
case GOPHER_CHAR_TELNET:
|
||
return GOPHER_TYPE_TELNET;
|
||
|
||
case GOPHER_CHAR_BINARY:
|
||
return GOPHER_TYPE_BINARY;
|
||
|
||
case GOPHER_CHAR_REDUNDANT:
|
||
return GOPHER_TYPE_REDUNDANT;
|
||
|
||
case GOPHER_CHAR_TN3270:
|
||
return GOPHER_TYPE_TN3270;
|
||
|
||
case GOPHER_CHAR_GIF:
|
||
return GOPHER_TYPE_GIF;
|
||
|
||
case GOPHER_CHAR_IMAGE:
|
||
return GOPHER_TYPE_IMAGE;
|
||
|
||
case GOPHER_CHAR_BITMAP:
|
||
return GOPHER_TYPE_BITMAP;
|
||
|
||
case GOPHER_CHAR_MOVIE:
|
||
return GOPHER_TYPE_MOVIE;
|
||
|
||
case GOPHER_CHAR_SOUND: // '<'
|
||
case GOPHER_CHAR_SOUND_2: // 's'
|
||
return GOPHER_TYPE_SOUND;
|
||
|
||
case GOPHER_CHAR_HTML:
|
||
return GOPHER_TYPE_HTML;
|
||
|
||
case GOPHER_CHAR_PDF:
|
||
return GOPHER_TYPE_PDF;
|
||
|
||
case GOPHER_CHAR_CALENDAR:
|
||
return GOPHER_TYPE_CALENDAR;
|
||
|
||
case GOPHER_CHAR_INLINE:
|
||
return GOPHER_TYPE_INLINE;
|
||
}
|
||
return GOPHER_TYPE_UNKNOWN;
|
||
}
|
||
|
||
|
||
CHAR
|
||
GopherTypeToChar(
|
||
IN DWORD GopherType
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Opposite of GopherCharToType
|
||
|
||
Arguments:
|
||
|
||
GopherType - bitmap of attributes. Only one file type and gopher plus
|
||
attributes can be set simultaneously
|
||
|
||
Return Value:
|
||
|
||
CHAR
|
||
Success - mapped gopher char
|
||
Failure - INVALID_GOPHER_TYPE
|
||
|
||
--*/
|
||
|
||
{
|
||
switch (GopherType & GOPHER_TYPE_MASK) {
|
||
case GOPHER_TYPE_TEXT_FILE:
|
||
return GOPHER_CHAR_TEXT_FILE;
|
||
|
||
case GOPHER_TYPE_DIRECTORY:
|
||
return GOPHER_CHAR_DIRECTORY;
|
||
|
||
case GOPHER_TYPE_CSO:
|
||
return GOPHER_CHAR_CSO;
|
||
|
||
case GOPHER_TYPE_ERROR:
|
||
return GOPHER_CHAR_ERROR;
|
||
|
||
case GOPHER_TYPE_MAC_BINHEX:
|
||
return GOPHER_CHAR_MAC_BINHEX;
|
||
|
||
case GOPHER_TYPE_DOS_ARCHIVE:
|
||
return GOPHER_CHAR_DOS_ARCHIVE;
|
||
|
||
case GOPHER_TYPE_UNIX_UUENCODED:
|
||
return GOPHER_CHAR_UNIX_UUENCODED;
|
||
|
||
case GOPHER_TYPE_INDEX_SERVER:
|
||
return GOPHER_CHAR_INDEX_SERVER;
|
||
|
||
case GOPHER_TYPE_TELNET:
|
||
return GOPHER_CHAR_TELNET;
|
||
|
||
case GOPHER_TYPE_BINARY:
|
||
return GOPHER_CHAR_BINARY;
|
||
|
||
case GOPHER_TYPE_REDUNDANT:
|
||
return GOPHER_CHAR_REDUNDANT;
|
||
|
||
case GOPHER_TYPE_TN3270:
|
||
return GOPHER_CHAR_TN3270;
|
||
|
||
case GOPHER_TYPE_GIF:
|
||
return GOPHER_CHAR_GIF;
|
||
|
||
case GOPHER_TYPE_IMAGE:
|
||
return GOPHER_CHAR_IMAGE;
|
||
|
||
case GOPHER_TYPE_BITMAP:
|
||
return GOPHER_CHAR_BITMAP;
|
||
|
||
case GOPHER_TYPE_MOVIE:
|
||
return GOPHER_CHAR_MOVIE;
|
||
|
||
case GOPHER_TYPE_SOUND:
|
||
return GOPHER_CHAR_SOUND;
|
||
|
||
case GOPHER_TYPE_HTML:
|
||
return GOPHER_CHAR_HTML;
|
||
|
||
case GOPHER_TYPE_PDF:
|
||
return GOPHER_CHAR_PDF;
|
||
|
||
case GOPHER_TYPE_CALENDAR:
|
||
return GOPHER_CHAR_CALENDAR;
|
||
|
||
case GOPHER_TYPE_INLINE:
|
||
return GOPHER_CHAR_INLINE;
|
||
}
|
||
return UNKNOWN_GOPHER_TYPE;
|
||
}
|
||
|
||
|
||
DWORD
|
||
GetDirEntry(
|
||
IN LPVIEW_INFO ViewInfo,
|
||
OUT LPGOPHER_FIND_DATA FindData
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Retrieves the next directory entry from the current VIEW_INFO data buffer.
|
||
The buffer pointer will be updated to point to the start of the next line
|
||
or 1 character past the end of the buffer
|
||
|
||
Arguments:
|
||
|
||
ViewInfo - pointer to VIEW_INFO which points to BUFFER_INFO which
|
||
points to buffer containing directory listing
|
||
|
||
FindData - pointer to user's GOPHER_FIND_DATA structure to fill in
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
|
||
Failure - ERROR_NO_MORE_FILES
|
||
End of the directory
|
||
|
||
ERROR_GOPHER_PROTOCOL_ERROR
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD error;
|
||
char lineBuf[DEFAULT_LINE_BUFFER_LENGTH];
|
||
DWORD lineLen;
|
||
LPSTR linePtr;
|
||
BOOL haveLocator;
|
||
|
||
//
|
||
// get the next line from the buffer. If we don't have all the data that
|
||
// constitutes a line, ExtractLine will endeavour to get it
|
||
//
|
||
|
||
haveLocator = FALSE;
|
||
lineLen = sizeof(lineBuf);
|
||
linePtr = lineBuf;
|
||
error = ExtractLine(ViewInfo,
|
||
(LPBYTE)linePtr,
|
||
&lineLen,
|
||
&ViewInfo->ViewOffset
|
||
);
|
||
|
||
//
|
||
// convert lineLen to the number of characters actually extracted, minus
|
||
// one for the '\0'. Doesn't matter if ExtractLine() failed
|
||
//
|
||
|
||
lineLen = sizeof(lineBuf) - (lineLen + 1);
|
||
|
||
//
|
||
// if we got a line of data, but the buffer contains gopher+ info then we
|
||
// need to move the locator pointer past the "+INFO: " token
|
||
//
|
||
|
||
if ((error == ERROR_SUCCESS) && (ViewInfo->Flags & VI_GOPHER_PLUS)) {
|
||
|
||
DWORD tokenLength;
|
||
|
||
tokenLength = IsGopherPlusToken(GOPHER_PLUS_INFO_TOKEN,
|
||
GOPHER_PLUS_INFO_TOKEN_LENGTH,
|
||
linePtr,
|
||
lineLen
|
||
);
|
||
if (tokenLength != 0) {
|
||
linePtr += tokenLength;
|
||
lineLen -= tokenLength;
|
||
} else {
|
||
|
||
//
|
||
// hola! The "+INFO: " doesn't exist. We'll treat this as gopher0
|
||
// info (or return an error?)
|
||
//
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
ViewInfo->Flags &= ~VI_GOPHER_PLUS;
|
||
}
|
||
}
|
||
|
||
//
|
||
// if no error occurred, convert the locator just retrieved into the
|
||
// GOPHER_FIND_DATA structure
|
||
//
|
||
|
||
if (error == ERROR_SUCCESS) {
|
||
error = GopherLocatorToFindData(linePtr, lineLen, FindData);
|
||
|
||
//
|
||
// if we parsed the locator OK and the buffer contains gopher+ info
|
||
// then we must get the date and size information from the +ADMIN
|
||
// section Mod-Date line and +VIEWS section resp.
|
||
//
|
||
|
||
if ((error == ERROR_SUCCESS) && (ViewInfo->Flags & VI_GOPHER_PLUS)) {
|
||
|
||
DWORD dataOffset;
|
||
DWORD previousOffset;
|
||
DWORD searchType;
|
||
BOOL done;
|
||
|
||
haveLocator = TRUE;
|
||
|
||
dataOffset = ViewInfo->ViewOffset;
|
||
searchType = 0;
|
||
done = FALSE;
|
||
|
||
//
|
||
// loop, reading the next line from the directory buffer. For each
|
||
// line, parse the gopher+ token looking for the Mod-Date line,
|
||
// or the first view line. We just skip all other lines
|
||
//
|
||
|
||
do {
|
||
previousOffset = dataOffset;
|
||
lineLen = sizeof(lineBuf);
|
||
error = ExtractLine(ViewInfo,
|
||
(LPBYTE)linePtr,
|
||
&lineLen,
|
||
&dataOffset
|
||
);
|
||
if (error == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// again, convert lineLen to the number of characters
|
||
// extracted
|
||
//
|
||
|
||
lineLen = sizeof(lineBuf) - (lineLen + 1);
|
||
|
||
//
|
||
// if we found a line containing a categpry type on the
|
||
// previous iteration, then parse the per-category info
|
||
//
|
||
|
||
if (searchType == SEARCH_TYPE_VIEW) {
|
||
|
||
char contentType[DEFAULT_CONTENT_TYPE_NAME_LENGTH + 1];
|
||
char language[DEFAULT_LANGUAGE_NAME_LENGTH + 1];
|
||
DWORD contentTypeLength;
|
||
DWORD languageLength;
|
||
BOOL ok;
|
||
|
||
//
|
||
// must be views line. Just extract the first one
|
||
//
|
||
|
||
contentTypeLength = sizeof(contentType);
|
||
languageLength = sizeof(language);
|
||
ok = ExtractView(&linePtr,
|
||
contentType,
|
||
&contentTypeLength,
|
||
language,
|
||
&languageLength,
|
||
&FindData->SizeLow
|
||
);
|
||
|
||
INET_ASSERT(ok);
|
||
if (!ok) {
|
||
error = ERROR_GOPHER_DATA_ERROR;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// we have the first view line. We aren't interested in
|
||
// the rest
|
||
//
|
||
|
||
searchType = 0;
|
||
} else {
|
||
|
||
LPSTR pAttribute;
|
||
char attributeBuffer[DEFAULT_CONTENT_TYPE_NAME_LENGTH + 1];
|
||
int i;
|
||
DWORD len;
|
||
LPSTR argPtr;
|
||
|
||
//
|
||
// pull out the first token on the line
|
||
//
|
||
|
||
i = 0;
|
||
len = lineLen;
|
||
pAttribute = linePtr;
|
||
|
||
//
|
||
// if this line has leading space, then skip it
|
||
//
|
||
|
||
while (*pAttribute == ' ') {
|
||
++pAttribute;
|
||
--len;
|
||
}
|
||
while (len
|
||
&& (i < sizeof(attributeBuffer) - 1)
|
||
&& (pAttribute[i] != ' ')
|
||
&& (pAttribute[i] != ':')
|
||
&& (pAttribute[i] != '\r')
|
||
&& (pAttribute[i] != '\n')) {
|
||
attributeBuffer[i] = pAttribute[i];
|
||
++i;
|
||
--len;
|
||
}
|
||
attributeBuffer[i] = '\0';
|
||
switch (MapAttributeNameToId((LPCSTR)attributeBuffer)) {
|
||
case GOPHER_CATEGORY_ID_INFO:
|
||
|
||
//
|
||
// update the offset in the VIEW_INFO
|
||
//
|
||
|
||
ViewInfo->ViewOffset = previousOffset;
|
||
|
||
//
|
||
// we have got to the next directory entry. Quit
|
||
//
|
||
|
||
done = TRUE;
|
||
break;
|
||
|
||
case GOPHER_ATTRIBUTE_ID_MOD_DATE:
|
||
|
||
//
|
||
// this is the "Mod-Date" line. Find the start of
|
||
// the date-time field (in angle brackets) and
|
||
// extract the time and date to the GOPHER_FIND_DATE
|
||
// structure
|
||
//
|
||
|
||
argPtr = strchr(linePtr, '<');
|
||
if (argPtr != NULL) {
|
||
ExtractDateAndTime(&argPtr,
|
||
&FindData->LastModificationTime
|
||
);
|
||
}
|
||
break;
|
||
|
||
case GOPHER_CATEGORY_ID_VIEWS:
|
||
|
||
//
|
||
// we have found the +VIEWS section. Next thing to
|
||
// find is the views proper
|
||
//
|
||
|
||
searchType = SEARCH_TYPE_VIEW;
|
||
break;
|
||
|
||
default:
|
||
|
||
//
|
||
// we just skip all other lines except the line(s)
|
||
// containing view information, in which case
|
||
// searchType will be set to indicate that this line
|
||
// contains a view
|
||
//
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// ExtractLine had an error
|
||
//
|
||
|
||
ViewInfo->ViewOffset = dataOffset;
|
||
done = TRUE;
|
||
}
|
||
} while ( !done );
|
||
}
|
||
}
|
||
if (error == ERROR_GOPHER_END_OF_DATA) {
|
||
if (haveLocator) {
|
||
error = ERROR_SUCCESS;
|
||
} else {
|
||
error = ERROR_NO_MORE_FILES;
|
||
}
|
||
}
|
||
return error;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
DWORD
|
||
GopherLocatorToFindData(
|
||
IN LPCSTR Locator,
|
||
IN DWORD Length,
|
||
OUT LPGOPHER_FIND_DATA FindData
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Fills in the GOPHER_FIND_DATA fields from a gopher locator string. The
|
||
strings in the GOPHER_FIND_DATA are appended after the fixed structure.
|
||
|
||
ASSUMES 1. The buffer pointed to by FindData is large enough to hold the
|
||
fixed and variable parts of the GOPHER_FIND_DATA
|
||
|
||
Arguments:
|
||
|
||
Locator - pointer to (ASCII) locator string
|
||
|
||
Length - length of Locator
|
||
|
||
FindData - pointer to GOPHER_FIND_DATA structure
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
|
||
Failure - ERROR_GOPHER_PROTOCOL_ERROR
|
||
|
||
--*/
|
||
|
||
{
|
||
LPSTR stringPointer;
|
||
DWORD bufferLength;
|
||
LPSTR locator;
|
||
DWORD locatorLength;
|
||
|
||
FindData->GopherType = GopherCharToType(*Locator);
|
||
|
||
if (IsGopherPlus(Locator)) {
|
||
FindData->GopherType |= GOPHER_TYPE_GOPHER_PLUS;
|
||
}
|
||
|
||
stringPointer = FindData->DisplayString;
|
||
|
||
//
|
||
// copy the display string into the GOPHER_FIND_DATA. We no longer care
|
||
// about the length copied
|
||
//
|
||
|
||
ExtractDisplayString(Locator,
|
||
&stringPointer,
|
||
sizeof(FindData->DisplayString)
|
||
);
|
||
|
||
//
|
||
// default the size and time/date fields to zero. If we received a gopher+
|
||
// directory list, we will fill in these fields from the attribute info
|
||
//
|
||
|
||
FindData->SizeLow = 0;
|
||
FindData->SizeHigh = 0;
|
||
FindData->LastModificationTime.dwLowDateTime = 0;
|
||
FindData->LastModificationTime.dwHighDateTime = 0;
|
||
|
||
//
|
||
// copy the locator into the GOPHER_FIND_DATA
|
||
//
|
||
|
||
stringPointer = FindData->Locator;
|
||
bufferLength = sizeof(FindData->Locator);
|
||
locator = (LPSTR)Locator;
|
||
locatorLength = Length;
|
||
if (CopyToEol(&stringPointer, &bufferLength, &locator, &locatorLength)) {
|
||
|
||
if (FindData->GopherType == GOPHER_TYPE_UNKNOWN) {
|
||
|
||
DEBUG_PRINT(PARSE,
|
||
ERROR,
|
||
("GopherLocatorToFindData(): unknown locator type: \"%s\"\n",
|
||
FindData->Locator
|
||
));
|
||
|
||
}
|
||
|
||
return ERROR_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// CopyToEol failed to find the end-of-line in the Locator string.
|
||
// Either something's bust, or we have received a locator that breaks
|
||
// our locator length limit
|
||
//
|
||
|
||
if ((bufferLength == 0) && (locatorLength != 0)) {
|
||
|
||
char bigLocator[2 * MAX_GOPHER_LOCATOR_LENGTH + 1];
|
||
|
||
//
|
||
// blown our locator length limit. We will reconstruct a slightly
|
||
// modified (smaller) locator
|
||
//
|
||
|
||
stringPointer = bigLocator;
|
||
bufferLength = sizeof(bigLocator);
|
||
locator = (LPSTR)Locator;
|
||
locatorLength = Length;
|
||
if (!CopyToEol(&stringPointer, &bufferLength, &locator, &locatorLength)) {
|
||
|
||
//
|
||
// CopyToEol() still fails! Either this is an extremely long
|
||
// locator, or we are not parsing a directory output
|
||
//
|
||
|
||
// skip to \r\n?
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
return ERROR_GOPHER_DATA_ERROR;
|
||
}
|
||
|
||
//
|
||
// if we think this is a locator, albeit one that breaks our internal
|
||
// locator length limit, crack it open, and then reconstitute
|
||
//
|
||
|
||
if (IsValidLocator(bigLocator, sizeof(bigLocator))) {
|
||
|
||
DWORD gopherType;
|
||
char displayString[MAX_GOPHER_DISPLAY_TEXT * 2 + 1];
|
||
DWORD displayStringLength;
|
||
char selectorString[MAX_GOPHER_SELECTOR_TEXT * 2 + 1];
|
||
DWORD selectorStringLength;
|
||
char hostName[MAX_GOPHER_HOST_NAME * 2 + 1];
|
||
DWORD hostNameLength;
|
||
DWORD port;
|
||
|
||
displayStringLength = sizeof(displayString);
|
||
selectorStringLength = sizeof(selectorString);
|
||
hostNameLength = sizeof(hostName);
|
||
if (CrackLocator(bigLocator,
|
||
&gopherType,
|
||
displayString,
|
||
&displayStringLength,
|
||
selectorString,
|
||
&selectorStringLength,
|
||
hostName,
|
||
&hostNameLength,
|
||
&port,
|
||
NULL)) {
|
||
|
||
//
|
||
// we really want to ensure that only the display string is
|
||
// broken, but we can get some weird FTP-based locators that
|
||
// contain long selector strings. As a compromise, just add
|
||
// an extra terminator at the relevamt maximum offset in each
|
||
// string
|
||
//
|
||
|
||
displayString[MAX_GOPHER_DISPLAY_TEXT] = '\0';
|
||
selectorString[MAX_GOPHER_SELECTOR_TEXT] = '\0';
|
||
hostName[MAX_GOPHER_HOST_NAME] = '\0';
|
||
|
||
//
|
||
// and reconstruct the locator
|
||
//
|
||
|
||
bufferLength = sizeof(FindData->Locator);
|
||
if (gopherType == GOPHER_TYPE_UNKNOWN) {
|
||
|
||
//
|
||
// BUGBUG - should change GopherCreateLocator() so that it
|
||
// is more forgiving of 'unknown' types (accept
|
||
// a character, not a bit)
|
||
//
|
||
|
||
gopherType = GOPHER_TYPE_ERROR;
|
||
}
|
||
if (GopherCreateLocator((LPCSTR)hostName,
|
||
(INTERNET_PORT)port,
|
||
(LPCSTR)displayString,
|
||
(LPCSTR)selectorString,
|
||
gopherType,
|
||
FindData->Locator,
|
||
&bufferLength)) {
|
||
return ERROR_SUCCESS;
|
||
} else {
|
||
|
||
//
|
||
// GopherCreateLocator() failed
|
||
//
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// CrackLocator() failed
|
||
//
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// IsValidLocator() returned FALSE
|
||
//
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// ran off the end of the directory list without finding "\r\n"?
|
||
//
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
}
|
||
return ERROR_GOPHER_DATA_ERROR;
|
||
}
|
||
|
||
|
||
DWORD
|
||
ReadData(
|
||
IN LPVIEW_INFO ViewInfo,
|
||
OUT LPDWORD BytesReturned
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Reads data from a file buffer into the caller's buffer
|
||
|
||
Arguments:
|
||
|
||
ViewInfo - pointer to VIEW_INFO structure
|
||
|
||
BytesReturned - amount of data copied to caller's buffer
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
BytesReturned contains amount of data copied to user buffer
|
||
|
||
Failure - ERROR_GOPHER_DATA_ERROR
|
||
There is an inconsistency between the VIEW_INFO and the
|
||
BUFFER_INFO
|
||
|
||
ERROR_GOPHER_END_OF_DATA
|
||
All data has been copied to the user buffer
|
||
|
||
ERROR_GOPHER_TIMEOUT
|
||
We got a timeout trying to communicate with the gopher
|
||
server
|
||
|
||
Win32 error
|
||
Returned if we have a memory or heap problem
|
||
|
||
WSA error
|
||
Socket specific error returned by ReceiveResponse()
|
||
|
||
--*/
|
||
|
||
{
|
||
INET_ASSERT(ViewInfo != NULL);
|
||
INET_ASSERT(ViewInfo->ViewType == ViewTypeFile);
|
||
INET_ASSERT(ViewInfo->BufferInfo != NULL);
|
||
|
||
if (ViewInfo->BufferInfo->Flags & BI_RECEIVE_COMPLETE) {
|
||
*BytesReturned = 0;
|
||
return ERROR_SUCCESS;
|
||
} else {
|
||
return GopherReceiveResponse(ViewInfo, BytesReturned);
|
||
}
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
DWORD
|
||
ExtractLine(
|
||
IN LPVIEW_INFO ViewInfo,
|
||
OUT LPBYTE LineBuffer,
|
||
IN OUT LPDWORD LineBufferLength,
|
||
IN OUT LPDWORD DataOffset
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Extracts a line from a response buffer into a local buffer. If the buffer
|
||
does not contain all of the current line we retrieve the next chunk by
|
||
calling ReceiveResponse()
|
||
|
||
Arguments:
|
||
|
||
ViewInfo - describes VIEW_INFO from which to extract line
|
||
|
||
LineBuffer - pointer to buffer where line will be copied
|
||
|
||
LineBufferLength- IN: length of line buffer
|
||
OUT: number of bytes remaining in LineBuffer
|
||
|
||
DataOffset - IN: the point in the data buffer corresponding to
|
||
ViewInfo->BufferInfo->Buffer at which to start the
|
||
extraction
|
||
|
||
OUT: The next offset in buffer at which to start
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - ERROR_SUCCESS
|
||
|
||
Failure - ERROR_GOPHER_DATA_ERROR
|
||
We had an error parsing the data
|
||
|
||
ERROR_GOPHER_END_OF_DATA
|
||
We reached the end of the info - API returns
|
||
ERROR_NO_MORE_FILES
|
||
|
||
Win32 error
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD error;
|
||
BOOL copied;
|
||
LPBYTE startOfLine;
|
||
LPBUFFER_INFO bufferInfo;
|
||
|
||
INET_ASSERT(ViewInfo != NULL);
|
||
INET_ASSERT(ViewInfo->BufferInfo != NULL);
|
||
|
||
bufferInfo = ViewInfo->BufferInfo;
|
||
startOfLine = LineBuffer;
|
||
error = ERROR_SUCCESS;
|
||
|
||
do {
|
||
|
||
LPBYTE bufferPointer;
|
||
LPBYTE responsePointer;
|
||
DWORD bufferAvailable;
|
||
DWORD oldBufferAvailable;
|
||
|
||
if (*DataOffset > bufferInfo->BufferLength) {
|
||
|
||
//
|
||
// we think we're further into buffer than there is available data
|
||
//
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
error = ERROR_GOPHER_DATA_ERROR;
|
||
|
||
DEBUG_PRINT(PARSE,
|
||
ERROR,
|
||
("ExtractLine(): *DataOffset (%d) > BufferLength (%d)\n",
|
||
*DataOffset,
|
||
bufferInfo->BufferLength
|
||
));
|
||
|
||
goto quit;
|
||
}
|
||
|
||
if ((bufferInfo->Flags & BI_RECEIVE_COMPLETE)
|
||
&& (*DataOffset == bufferInfo->BufferLength)) {
|
||
|
||
//
|
||
// the caller has already reached the end of the buffer
|
||
//
|
||
|
||
DEBUG_PRINT(PARSE,
|
||
INFO,
|
||
("ExtractLine(): already at EOF buffer\n"
|
||
));
|
||
|
||
error = ERROR_GOPHER_END_OF_DATA;
|
||
goto quit;
|
||
}
|
||
|
||
//
|
||
// get a pointer to the start of the buffer
|
||
//
|
||
|
||
bufferPointer = bufferInfo->Buffer;
|
||
|
||
INET_ASSERT(bufferPointer != NULL);
|
||
|
||
if (bufferPointer == NULL) {
|
||
goto last_error_exit;
|
||
}
|
||
|
||
//
|
||
// now point to the offset in the buffer where the caller thinks the
|
||
// next line begins and reduce the buffer length by the same amount
|
||
//
|
||
|
||
responsePointer = bufferPointer + *DataOffset;
|
||
bufferAvailable = bufferInfo->BufferLength - *DataOffset;
|
||
|
||
//
|
||
// copy from the current buffer position to the end of the line
|
||
//
|
||
|
||
oldBufferAvailable = bufferAvailable;
|
||
copied = CopyToEol((LPSTR *)&LineBuffer,
|
||
LineBufferLength,
|
||
(LPSTR *)&responsePointer,
|
||
&bufferAvailable
|
||
);
|
||
|
||
//
|
||
// oldBufferAvailable - bufferAvailable is the amount we copied
|
||
//
|
||
|
||
*DataOffset += oldBufferAvailable - bufferAvailable;
|
||
|
||
//
|
||
// copied is TRUE if CopyToEol copied a full line
|
||
//
|
||
|
||
if (copied) {
|
||
|
||
//
|
||
// test again for ".\r\n" terminator. Someones servers terminate
|
||
// with ".\r\r\n" which would have escaped our test in
|
||
// ReceiveResponse(), but has now been compressed to ".\r\n" by
|
||
// CopyToEol(). Other, equally unintelegent servers, terminate
|
||
// with e.g. ".\r\n\x1a" (presumably this directory was read from
|
||
// a file and squirted out via send())
|
||
//
|
||
|
||
if (memcmp(startOfLine, ".\r\n", 3) == 0) {
|
||
|
||
//
|
||
// there should be very few bytes left in the buffer, if any,
|
||
// depending on how the server terminated the buffer (".\r\r\n"
|
||
// or ".\r\n\x1a", e.g.)
|
||
//
|
||
// N.B. 8 is an arbitrary number. I don't expect too many
|
||
// garbage characters at the end of the buffer, but if there's
|
||
// more than a relatively small number, we could have a ".\r\n"
|
||
// embedded half-way down the directory listing. Implausable,
|
||
// yes, but then there's nothing so unpredictable as the results
|
||
// from a gopher server
|
||
//
|
||
// 05/23/95
|
||
//
|
||
// server at sutro.sfsu.edu returns a pile of garbage after the
|
||
// end-of-buffer mark. Probably unintentional, but causes the
|
||
// following assertion to go off:
|
||
//
|
||
// INET_ASSERT(bufferInfo->BufferLength - *DataOffset <= 8);
|
||
//
|
||
|
||
if (bufferInfo->BufferLength - *DataOffset <= 8) {
|
||
|
||
DEBUG_PRINT(PARSE,
|
||
WARNING,
|
||
("ExtractLine(): Buffer handle %#x contains data after end-of-buffer mark\n",
|
||
bufferInfo->Buffer
|
||
));
|
||
}
|
||
|
||
error = ERROR_GOPHER_END_OF_DATA;
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// at the time we called CopyToEol, all of the current line was
|
||
// not in the response buffer. Get the next part of the response
|
||
//
|
||
|
||
AcquireBufferLock(bufferInfo);
|
||
if (!(bufferInfo->Flags & BI_RECEIVE_COMPLETE)) {
|
||
|
||
DWORD bytesReceived;
|
||
|
||
error = GopherReceiveResponse(ViewInfo, &bytesReceived);
|
||
}
|
||
ReleaseBufferLock(bufferInfo);
|
||
}
|
||
} while (!copied && (error == ERROR_SUCCESS));
|
||
|
||
quit:
|
||
|
||
return error;
|
||
|
||
last_error_exit:
|
||
|
||
error = GetLastError();
|
||
goto quit;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
DWORD
|
||
ExtractDisplayString(
|
||
IN LPCSTR Locator,
|
||
IN OUT LPSTR* StringPointer,
|
||
IN DWORD BufferLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Given a gopher locator string, extract the display string part
|
||
|
||
Arguments:
|
||
|
||
Locator - pointer to gopher locator
|
||
|
||
StringPointer - pointer to pointer to output string. Updated on output
|
||
|
||
BufferLength - amount of space in *StringPointer
|
||
|
||
Return Value:
|
||
|
||
DWORD Length of string extracted
|
||
|
||
--*/
|
||
|
||
{
|
||
LPSTR originalPointer = *StringPointer;
|
||
char ch;
|
||
|
||
//
|
||
// Locator starts off pointing at the type character. Move past it then
|
||
// copy everything up to the tab character
|
||
//
|
||
|
||
while (((ch = *++Locator) != '\t') && BufferLength--) {
|
||
*(*StringPointer)++ = ch;
|
||
}
|
||
*(*StringPointer)++ = '\0';
|
||
return (DWORD) (*StringPointer - originalPointer);
|
||
}
|
||
|
||
|
||
BOOL
|
||
CopyToEol(
|
||
IN OUT LPSTR* Destination,
|
||
IN OUT LPDWORD DestinationLength,
|
||
IN OUT LPSTR* Source,
|
||
IN OUT LPDWORD SourceLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Copies the current gopher response line up to the end of the current line
|
||
in the buffer. The destination string is zero terminated if TRUE is
|
||
returned
|
||
|
||
On exit, all parameters are updated to reflect the current positions and
|
||
lengths of the buffers so this function can be called iteratively until
|
||
the entire line is copied
|
||
|
||
ASSUMES 1. The Length is absolutely reliable - i.e. when Length == 2 and
|
||
**Source == '\r', then *(*Source + 1) == '\n'
|
||
|
||
Arguments:
|
||
|
||
Destination - pointer to place to copy to
|
||
|
||
DestinationLength - pointer to length of destination buffer, updated on output
|
||
|
||
Source - pointer to place to copy from (gopher response buffer)
|
||
|
||
SourceLength - pointer tp length of source buffer, updated on output
|
||
|
||
Return Value:
|
||
|
||
BOOL
|
||
TRUE - we copied the entire line up to \r\n
|
||
|
||
FALSE - none or part of a line was copied
|
||
|
||
--*/
|
||
|
||
{
|
||
LPSTR src;
|
||
LPSTR dest;
|
||
DWORD srcLen;
|
||
DWORD destLen;
|
||
BOOL copied;
|
||
|
||
//
|
||
// make smaller code (i.e. don't deref the parms every time)
|
||
//
|
||
|
||
src = *Source;
|
||
dest = *Destination;
|
||
srcLen = *SourceLength;
|
||
destLen = *DestinationLength;
|
||
|
||
while ((*src != '\r') && (*src != '\n') && (destLen != 0) && (srcLen != 0)) {
|
||
*dest++ = *src++;
|
||
--destLen;
|
||
--srcLen;
|
||
}
|
||
|
||
//
|
||
// we can receive multiple carriage-returns, presumably because the server
|
||
// uses sprintf("\r\n") in text mode which expands "\n" to be \r\n in the
|
||
// output buffer. We will collapse multiple carriage-returns
|
||
//
|
||
|
||
while ((*(src + 1) == '\r') && (srcLen != 0)) {
|
||
++src;
|
||
--srcLen;
|
||
}
|
||
|
||
//INET_ASSERT((srcLen > 1) ? (*(src + 1) == '\n') : TRUE);
|
||
|
||
//
|
||
// if \r\n exist in the source buffer then copy them and update the length
|
||
//
|
||
|
||
copied = FALSE;
|
||
if (destLen >= 3) {
|
||
if ((srcLen >= 2) && (*src == '\r')) {
|
||
++src;
|
||
--srcLen;
|
||
}
|
||
if ((srcLen >= 1) && (*src == '\n')) {
|
||
++src;
|
||
--srcLen;
|
||
|
||
//
|
||
// we have reached a line-feed. It either exists on its own or was
|
||
// prefixed by a carriage-return. This is the end of the line...
|
||
//
|
||
// Note, even if we did not find \r\n in the source, we create \r\n
|
||
// in the destination
|
||
//
|
||
|
||
destLen -= 3; // 1 for \r, 1 for \n, 1 for \0
|
||
*dest++ = '\r';
|
||
*dest++ = '\n';
|
||
*dest++ = '\0';
|
||
copied = TRUE;
|
||
}
|
||
}
|
||
|
||
*Source = src;
|
||
*Destination = dest;
|
||
*SourceLength = srcLen;
|
||
*DestinationLength = destLen;
|
||
return copied;
|
||
}
|
||
|
||
|
||
DWORD
|
||
IsGopherPlusToken(
|
||
IN LPSTR Token,
|
||
IN DWORD TokenLength,
|
||
IN LPSTR Buffer,
|
||
IN DWORD BufferLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determines if a token is the gopher+ token. In order to match we need to
|
||
match a trailing space character also. Seems that some servers return
|
||
"+INFO" and some "+INFO:". We must handle both
|
||
|
||
Arguments:
|
||
|
||
Token - pointer to gopher+ token string
|
||
|
||
TokenLength - length of PlusToken
|
||
|
||
Buffer - pointer to buffer containing token to check
|
||
|
||
BufferLength - number of bytes in BufferPointer
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Success - Returns number of characters matched, including trailing ' '
|
||
|
||
Failure - 0. TokenPointer does not point at PlusToken or we ran out of
|
||
buffer before we could make the distinction
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Length must contain at least the trailing space, and possibly a ':'
|
||
//
|
||
|
||
if (BufferLength >= TokenLength + 2) {
|
||
if (memcmp(Buffer, Token, TokenLength) == 0) {
|
||
if (Buffer[TokenLength] == ':') {
|
||
++TokenLength;
|
||
}
|
||
|
||
//
|
||
// if there's a space after the token then we know its really what we
|
||
// are searching for
|
||
//
|
||
|
||
if (Buffer[TokenLength] == ' ') {
|
||
return ++TokenLength;
|
||
}
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
DWORD
|
||
MapAttributeNameToId(
|
||
IN LPCSTR AttributeName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Given a category or attribute name, returns an identifier to avoid having
|
||
to perform extraneous string comparisons
|
||
|
||
ASSUMES: 1. AttributeName has correct case. Comparisons are CASE-SENSITIVE
|
||
|
||
Arguments:
|
||
|
||
AttributeName - name of category or attribute
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
relevant identifer or GOPHER_ATTRIBUTE_ID_UNKNOWN
|
||
|
||
--*/
|
||
|
||
{
|
||
if (AttributeName == NULL) {
|
||
return GOPHER_CATEGORY_ID_ALL;
|
||
} else if (!stricmp(AttributeName, GOPHER_INFO_CATEGORY)) {
|
||
return GOPHER_CATEGORY_ID_INFO;
|
||
} else if (!stricmp(AttributeName, GOPHER_ADMIN_CATEGORY)) {
|
||
return GOPHER_CATEGORY_ID_ADMIN;
|
||
} else if (!stricmp(AttributeName, GOPHER_VIEWS_CATEGORY)) {
|
||
return GOPHER_CATEGORY_ID_VIEWS;
|
||
} else if (!stricmp(AttributeName, GOPHER_ABSTRACT_CATEGORY)) {
|
||
return GOPHER_CATEGORY_ID_ABSTRACT;
|
||
} else if (!stricmp(AttributeName, GOPHER_VERONICA_CATEGORY)) {
|
||
return GOPHER_CATEGORY_ID_VERONICA;
|
||
} else if (!stricmp(AttributeName, GOPHER_ADMIN_ATTRIBUTE)) {
|
||
return GOPHER_ATTRIBUTE_ID_ADMIN;
|
||
} else if (!stricmp(AttributeName, GOPHER_MOD_DATE_ATTRIBUTE)) {
|
||
return GOPHER_ATTRIBUTE_ID_MOD_DATE;
|
||
} else if (!stricmp(AttributeName, GOPHER_TTL_ATTRIBUTE)) {
|
||
return GOPHER_ATTRIBUTE_ID_TTL;
|
||
} else if (!stricmp(AttributeName, GOPHER_SCORE_ATTRIBUTE)) {
|
||
return GOPHER_ATTRIBUTE_ID_SCORE;
|
||
} else if (!stricmp(AttributeName, GOPHER_RANGE_ATTRIBUTE)) {
|
||
return GOPHER_ATTRIBUTE_ID_RANGE;
|
||
} else if (!stricmp(AttributeName, GOPHER_SITE_ATTRIBUTE)) {
|
||
return GOPHER_ATTRIBUTE_ID_SITE;
|
||
} else if (!stricmp(AttributeName, GOPHER_ORG_ATTRIBUTE)) {
|
||
return GOPHER_ATTRIBUTE_ID_ORG;
|
||
} else if (!stricmp(AttributeName, GOPHER_LOCATION_ATTRIBUTE)) {
|
||
return GOPHER_ATTRIBUTE_ID_LOCATION;
|
||
} else if (!stricmp(AttributeName, GOPHER_GEOG_ATTRIBUTE)) {
|
||
return GOPHER_ATTRIBUTE_ID_GEOG;
|
||
} else if (!stricmp(AttributeName, GOPHER_TIMEZONE_ATTRIBUTE)) {
|
||
return GOPHER_ATTRIBUTE_ID_TIMEZONE;
|
||
} else if (!stricmp(AttributeName, GOPHER_PROVIDER_ATTRIBUTE)) {
|
||
return GOPHER_ATTRIBUTE_ID_PROVIDER;
|
||
} else if (!stricmp(AttributeName, GOPHER_VERSION_ATTRIBUTE)) {
|
||
return GOPHER_ATTRIBUTE_ID_VERSION;
|
||
} else if (!stricmp(AttributeName, GOPHER_ABSTRACT_ATTRIBUTE)) {
|
||
return GOPHER_ATTRIBUTE_ID_ABSTRACT;
|
||
} else if (!stricmp(AttributeName, GOPHER_VIEW_ATTRIBUTE)) {
|
||
return GOPHER_ATTRIBUTE_ID_VIEW;
|
||
}
|
||
return GOPHER_ATTRIBUTE_ID_UNKNOWN;
|
||
}
|
||
|
||
#if defined(GOPHER_ATTRIBUTE_SUPPORT)
|
||
|
||
|
||
VOID
|
||
MapAttributeToIds(
|
||
IN LPCSTR AttributeName,
|
||
OUT LPDWORD CategoryId,
|
||
OUT LPDWORD AttributeId
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Given a category or attribute name, returns an identifier to avoid having
|
||
to perform extraneous string comparisons
|
||
|
||
ASSUMES: 1. AttributeName has correct case. Comparisons are CASE-SENSITIVE
|
||
|
||
Arguments:
|
||
|
||
AttributeName - name of category or attribute
|
||
|
||
CategoryId - returned GOPHER_CATEGORY_ id
|
||
|
||
AttributeId - returned GOPHER_ATTRIBUTE_ id
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD category;
|
||
DWORD attribute;
|
||
|
||
if (AttributeName == NULL) {
|
||
category = GOPHER_CATEGORY_ID_ALL;
|
||
attribute = GOPHER_ATTRIBUTE_ID_ALL;
|
||
} else if (!stricmp(AttributeName, GOPHER_INFO_CATEGORY)) {
|
||
category = GOPHER_CATEGORY_ID_INFO;
|
||
attribute = GOPHER_ATTRIBUTE_ID_ALL;
|
||
} else if (!stricmp(AttributeName, GOPHER_ADMIN_CATEGORY)) {
|
||
category = GOPHER_CATEGORY_ID_ADMIN;
|
||
attribute = GOPHER_ATTRIBUTE_ID_ALL;
|
||
} else if (!stricmp(AttributeName, GOPHER_VIEWS_CATEGORY)) {
|
||
category = GOPHER_CATEGORY_ID_VIEWS;
|
||
attribute = GOPHER_ATTRIBUTE_ID_ALL;
|
||
} else if (!stricmp(AttributeName, GOPHER_ABSTRACT_CATEGORY)) {
|
||
category = GOPHER_CATEGORY_ID_ABSTRACT;
|
||
attribute = GOPHER_ATTRIBUTE_ID_ALL;
|
||
} else if (!stricmp(AttributeName, GOPHER_VERONICA_CATEGORY)) {
|
||
category = GOPHER_CATEGORY_ID_VERONICA;
|
||
attribute = GOPHER_ATTRIBUTE_ID_ALL;
|
||
} else if (!stricmp(AttributeName, GOPHER_ADMIN_ATTRIBUTE)) {
|
||
category = GOPHER_CATEGORY_ID_ADMIN;
|
||
attribute = GOPHER_ATTRIBUTE_ID_ADMIN;
|
||
} else if (!stricmp(AttributeName, GOPHER_MOD_DATE_ATTRIBUTE)) {
|
||
category = GOPHER_CATEGORY_ID_ADMIN;
|
||
attribute = GOPHER_ATTRIBUTE_ID_MOD_DATE;
|
||
} else if (!stricmp(AttributeName, GOPHER_TTL_ATTRIBUTE)) {
|
||
category = GOPHER_CATEGORY_ID_ADMIN;
|
||
attribute = GOPHER_ATTRIBUTE_ID_TTL;
|
||
} else if (!stricmp(AttributeName, GOPHER_SCORE_ATTRIBUTE)) {
|
||
category = GOPHER_CATEGORY_ID_ADMIN;
|
||
attribute = GOPHER_ATTRIBUTE_ID_SCORE;
|
||
} else if (!stricmp(AttributeName, GOPHER_RANGE_ATTRIBUTE)) {
|
||
category = GOPHER_CATEGORY_ID_ADMIN;
|
||
attribute = GOPHER_ATTRIBUTE_ID_RANGE;
|
||
} else if (!stricmp(AttributeName, GOPHER_SITE_ATTRIBUTE)) {
|
||
category = GOPHER_CATEGORY_ID_ADMIN;
|
||
attribute = GOPHER_ATTRIBUTE_ID_SITE;
|
||
} else if (!stricmp(AttributeName, GOPHER_ORG_ATTRIBUTE)) {
|
||
category = GOPHER_CATEGORY_ID_ADMIN;
|
||
attribute = GOPHER_ATTRIBUTE_ID_ORG;
|
||
} else if (!stricmp(AttributeName, GOPHER_LOCATION_ATTRIBUTE)) {
|
||
category = GOPHER_CATEGORY_ID_ADMIN;
|
||
attribute = GOPHER_ATTRIBUTE_ID_LOCATION;
|
||
} else if (!stricmp(AttributeName, GOPHER_GEOG_ATTRIBUTE)) {
|
||
category = GOPHER_CATEGORY_ID_ADMIN;
|
||
attribute = GOPHER_CATEGORY_ID_ADMIN;
|
||
} else if (!stricmp(AttributeName, GOPHER_TIMEZONE_ATTRIBUTE)) {
|
||
category = GOPHER_CATEGORY_ID_ADMIN;
|
||
attribute = GOPHER_ATTRIBUTE_ID_TIMEZONE;
|
||
} else if (!stricmp(AttributeName, GOPHER_PROVIDER_ATTRIBUTE)) {
|
||
category = GOPHER_CATEGORY_ID_ADMIN;
|
||
attribute = GOPHER_ATTRIBUTE_ID_PROVIDER;
|
||
} else if (!stricmp(AttributeName, GOPHER_VERSION_ATTRIBUTE)) {
|
||
category = GOPHER_CATEGORY_ID_ADMIN;
|
||
attribute = GOPHER_ATTRIBUTE_ID_VERSION;
|
||
} else if (!stricmp(AttributeName, GOPHER_ABSTRACT_ATTRIBUTE)) {
|
||
category = GOPHER_CATEGORY_ID_ABSTRACT;
|
||
attribute = GOPHER_ATTRIBUTE_ID_ABSTRACT;
|
||
} else if (!stricmp(AttributeName, GOPHER_VIEW_ATTRIBUTE)) {
|
||
category = GOPHER_CATEGORY_ID_VIEWS;
|
||
attribute = GOPHER_ATTRIBUTE_ID_VIEW;
|
||
} else {
|
||
category = GOPHER_CATEGORY_ID_UNKNOWN;
|
||
attribute = GOPHER_ATTRIBUTE_ID_UNKNOWN;
|
||
}
|
||
*CategoryId = category;
|
||
*AttributeId = attribute;
|
||
}
|
||
|
||
|
||
BOOL
|
||
MapAttributeIdToNames(
|
||
IN DWORD AttributeId,
|
||
OUT LPSTR* CategoryName,
|
||
OUT LPSTR* AttributeName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Do reverse transformation: given attribute ID, return the category and
|
||
attribute names if known
|
||
|
||
Arguments:
|
||
|
||
AttributeId - id to map
|
||
|
||
CategoryName - pointer to pointer to category name
|
||
|
||
AttributeName - pointer to pointer to attribute name
|
||
|
||
Return Value:
|
||
|
||
BOOL
|
||
TRUE - id was mapped
|
||
|
||
FALSE - id not recognized
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL success = TRUE;
|
||
|
||
switch (AttributeId) {
|
||
case GOPHER_CATEGORY_ID_ALL:
|
||
*CategoryName = NULL;
|
||
*AttributeName = NULL;
|
||
break;
|
||
|
||
case GOPHER_CATEGORY_ID_INFO:
|
||
*CategoryName = GOPHER_INFO_CATEGORY;
|
||
*AttributeName = NULL;
|
||
break;
|
||
|
||
case GOPHER_CATEGORY_ID_ADMIN:
|
||
*CategoryName = GOPHER_ADMIN_CATEGORY;
|
||
*AttributeName = NULL;
|
||
break;
|
||
|
||
case GOPHER_CATEGORY_ID_VIEWS:
|
||
*CategoryName = GOPHER_VIEWS_CATEGORY;
|
||
*AttributeName = NULL;
|
||
break;
|
||
|
||
case GOPHER_CATEGORY_ID_ABSTRACT:
|
||
*CategoryName = GOPHER_ABSTRACT_CATEGORY;
|
||
*AttributeName = NULL;
|
||
break;
|
||
|
||
case GOPHER_CATEGORY_ID_VERONICA:
|
||
*CategoryName = GOPHER_VERONICA_CATEGORY;
|
||
*AttributeName = NULL;
|
||
break;
|
||
|
||
case GOPHER_ATTRIBUTE_ID_ADMIN:
|
||
*CategoryName = GOPHER_ADMIN_CATEGORY;
|
||
*AttributeName = GOPHER_ADMIN_ATTRIBUTE;
|
||
break;
|
||
|
||
case GOPHER_ATTRIBUTE_ID_MOD_DATE:
|
||
*CategoryName = GOPHER_ADMIN_CATEGORY;
|
||
*AttributeName = GOPHER_MOD_DATE_ATTRIBUTE;
|
||
break;
|
||
|
||
case GOPHER_ATTRIBUTE_ID_TTL:
|
||
*CategoryName = GOPHER_ADMIN_CATEGORY;
|
||
*AttributeName = GOPHER_TTL_ATTRIBUTE;
|
||
break;
|
||
|
||
case GOPHER_ATTRIBUTE_ID_SCORE:
|
||
*CategoryName = GOPHER_ADMIN_CATEGORY;
|
||
*AttributeName = GOPHER_SCORE_ATTRIBUTE;
|
||
break;
|
||
|
||
case GOPHER_ATTRIBUTE_ID_RANGE:
|
||
*CategoryName = GOPHER_ADMIN_CATEGORY;
|
||
*AttributeName = GOPHER_RANGE_ATTRIBUTE;
|
||
break;
|
||
|
||
case GOPHER_ATTRIBUTE_ID_SITE:
|
||
*CategoryName = GOPHER_ADMIN_CATEGORY;
|
||
*AttributeName = GOPHER_SITE_ATTRIBUTE;
|
||
break;
|
||
|
||
case GOPHER_ATTRIBUTE_ID_ORG:
|
||
*CategoryName = GOPHER_ADMIN_CATEGORY;
|
||
*AttributeName = GOPHER_ORG_ATTRIBUTE;
|
||
break;
|
||
|
||
case GOPHER_ATTRIBUTE_ID_LOCATION:
|
||
*CategoryName = GOPHER_ADMIN_CATEGORY;
|
||
*AttributeName = GOPHER_LOCATION_ATTRIBUTE;
|
||
break;
|
||
|
||
case GOPHER_ATTRIBUTE_ID_GEOG:
|
||
*CategoryName = GOPHER_ADMIN_CATEGORY;
|
||
*AttributeName = GOPHER_GEOG_ATTRIBUTE;
|
||
break;
|
||
|
||
case GOPHER_ATTRIBUTE_ID_TIMEZONE:
|
||
*CategoryName = GOPHER_ADMIN_CATEGORY;
|
||
*AttributeName = GOPHER_TIMEZONE_ATTRIBUTE;
|
||
break;
|
||
|
||
case GOPHER_ATTRIBUTE_ID_PROVIDER:
|
||
*CategoryName = GOPHER_ADMIN_CATEGORY;
|
||
*AttributeName = GOPHER_PROVIDER_ATTRIBUTE;
|
||
break;
|
||
|
||
case GOPHER_ATTRIBUTE_ID_VERSION:
|
||
*CategoryName = GOPHER_ADMIN_CATEGORY;
|
||
*AttributeName = GOPHER_VERSION_ATTRIBUTE;
|
||
break;
|
||
|
||
case GOPHER_ATTRIBUTE_ID_ABSTRACT:
|
||
*CategoryName = GOPHER_ABSTRACT_CATEGORY;
|
||
*AttributeName = GOPHER_ABSTRACT_ATTRIBUTE;
|
||
break;
|
||
|
||
case GOPHER_ATTRIBUTE_ID_VIEW:
|
||
*CategoryName = GOPHER_VIEWS_CATEGORY;
|
||
*AttributeName = GOPHER_VIEW_ATTRIBUTE;
|
||
break;
|
||
|
||
case GOPHER_ATTRIBUTE_ID_UNKNOWN:
|
||
success = FALSE;
|
||
break;
|
||
|
||
default:
|
||
success = FALSE;
|
||
break;
|
||
}
|
||
return success;
|
||
}
|
||
|
||
#endif // defined(GOPHER_ATTRIBUTE_SUPPORT)
|
||
|
||
|
||
DWORD
|
||
GetGopherNumber(
|
||
IN OUT LPSTR* pString
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Converts a 'gopher number' to a DWORD. A gopher number is the value usually
|
||
contained with angle brackets in e.g. a +VIEWS line, and is usually a
|
||
fractional number with a 'k' suffix
|
||
|
||
Arguments:
|
||
|
||
pString - pointer to pointer to string which points at the start of the
|
||
number. The number may start with a period, indicating that it
|
||
is less than one (hence the reason why we use a double).
|
||
|
||
On output, the parameter points at the character after what we
|
||
took to be the number
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
DWORD representation of the number at *pString
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
double number;
|
||
|
||
//
|
||
// the gopher number is usually inside angle brackets. Move the string
|
||
// pointer past the opening bracket, if the caller has not already
|
||
// done so
|
||
//
|
||
|
||
if (**pString == '<') {
|
||
++*pString;
|
||
}
|
||
|
||
//
|
||
// allow strtod to move the string pointer forward
|
||
//
|
||
|
||
number = StrToDbl(*pString, pString);
|
||
|
||
return (DWORD)number;
|
||
}
|
||
|
||
|
||
BOOL
|
||
ExtractDateAndTime(
|
||
IN OUT LPSTR* pString,
|
||
OUT LPFILETIME pFileTime
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Converts a 'gopher time-and-date' field to a WIN32 FILETIME structure. The
|
||
gopher date-time field is a string representation of the date and time,
|
||
contained within angle brackets and has the following format:
|
||
|
||
<YYYYMMDDhhmmss>
|
||
|
||
Where:
|
||
|
||
YYYY = year (e.g. "1995")
|
||
MM = month (1..12)
|
||
DD = day of month
|
||
hh = hour of day in 24-hour format
|
||
mm = minute of hour
|
||
ss = second of minute
|
||
|
||
Assumes: 1. On input, *pString contains entire field
|
||
|
||
Arguments:
|
||
|
||
pString - IN: points to the first character in the date-time field
|
||
OUT: points to the next character in the input stream
|
||
|
||
pFileTime - pointer to returned FILETIME structure
|
||
|
||
Return Value:
|
||
|
||
BOOL
|
||
Success - TRUE - field was converted
|
||
|
||
Failure - FALSE - a problem occurred while parsing the field
|
||
|
||
--*/
|
||
|
||
{
|
||
SYSTEMTIME systemTime;
|
||
|
||
if (**pString == '<') {
|
||
++*pString;
|
||
}
|
||
if (ExtractWord(pString, 4, &systemTime.wYear)
|
||
&& ExtractWord(pString, 2, &systemTime.wMonth)
|
||
&& ExtractWord(pString, 2, &systemTime.wDay)
|
||
&& ExtractWord(pString, 2, &systemTime.wHour)
|
||
&& ExtractWord(pString, 2, &systemTime.wMinute)
|
||
&& ExtractWord(pString, 2, &systemTime.wSecond)) {
|
||
|
||
INET_ASSERT(**pString == '>');
|
||
|
||
++*pString;
|
||
systemTime.wDayOfWeek = 0;
|
||
systemTime.wMilliseconds = 0;
|
||
return SystemTimeToFileTime(&systemTime, pFileTime);
|
||
} else {
|
||
|
||
INET_ASSERT(FALSE);
|
||
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
|
||
BOOL
|
||
ExtractView(
|
||
IN OUT LPSTR* pString,
|
||
OUT LPSTR ContentType,
|
||
IN OUT LPDWORD ContentTypeLength,
|
||
OUT LPSTR Language,
|
||
IN OUT LPDWORD LanguageLength,
|
||
OUT LPDWORD Size
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Given a pointer to a line containing a view, parse it into its constituent
|
||
parts. A view line has the following format:
|
||
|
||
"{space}{content-type}{space}[{language-id}:]<{size}><CR><LF>"
|
||
|
||
The language-id field is optional
|
||
|
||
Arguments:
|
||
|
||
pString - pointer to pointer to view line
|
||
|
||
ContentType - pointer to returned MIME content-type string
|
||
|
||
ContentTypeLength - IN: size of content-type buffer
|
||
OUT: length of content-type without terminating 0
|
||
|
||
Language - pointer to ISO-693 language-id (or NUL string)
|
||
|
||
LanguageLength - IN: size of language buffer
|
||
OUT: length of language without terminating 0
|
||
|
||
Size - pointer to returned size of view
|
||
|
||
Return Value:
|
||
|
||
BOOL
|
||
|
||
--*/
|
||
|
||
{
|
||
LPSTR string;
|
||
DWORD contLen = 0;
|
||
DWORD langLen = 0;
|
||
|
||
string = *pString;
|
||
while (*string == ' ') {
|
||
++string;
|
||
}
|
||
while (*string != ' ') {
|
||
if (contLen >= *ContentTypeLength)
|
||
return FALSE;
|
||
*ContentType++ = *string++;
|
||
contLen++;
|
||
}
|
||
*ContentType = '\0';
|
||
*ContentTypeLength = contLen;
|
||
while (*string == ' ') {
|
||
++string;
|
||
}
|
||
if (*string != '<') {
|
||
|
||
//
|
||
// must be the language field. Copy up to the terminating ':' or ' '
|
||
//
|
||
|
||
while ((*string != ' ') && (*string != ':')) {
|
||
if (langLen >= *LanguageLength)
|
||
return FALSE;
|
||
*Language++ = *string++;
|
||
langLen++;
|
||
}
|
||
|
||
//
|
||
// move the string pointer to the start of the size field
|
||
//
|
||
|
||
while (*string != '<') {
|
||
++string;
|
||
}
|
||
}
|
||
*Language = '\0';
|
||
*LanguageLength = langLen;
|
||
*Size = GetGopherNumber(&string);
|
||
*pString = string;
|
||
return TRUE;
|
||
}
|
||
|
||
#if defined(GOPHER_ATTRIBUTE_SUPPORT)
|
||
|
||
//
|
||
// manifests
|
||
//
|
||
|
||
#define SIZE_OF_GOPHER_ATTRIBUTE_FIXED_PART (2 * sizeof(DWORD))
|
||
|
||
//
|
||
// private types
|
||
//
|
||
|
||
typedef struct {
|
||
DWORD CategoryId;
|
||
DWORD AttributeId;
|
||
DWORD (*Parser)(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
|
||
DWORD NumberOfFields;
|
||
DWORD FixedSize;
|
||
} ATTRIBUTE_PARSER, *LPATTRIBUTE_PARSER;
|
||
|
||
typedef struct {
|
||
LPCSTR String;
|
||
} SINGLE_STRING_TYPE, *LPSINGLE_STRING_TYPE;
|
||
|
||
//
|
||
// private parser prototypes
|
||
//
|
||
|
||
PRIVATE DWORD ParseAdminAttribute(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
|
||
PRIVATE DWORD ParseModDateAttribute(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
|
||
PRIVATE DWORD ParseAbstractAttribute(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
|
||
PRIVATE DWORD ParseViewAttribute(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
|
||
PRIVATE DWORD ParseTreewalkAttribute(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
|
||
PRIVATE DWORD ParseIntField(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
|
||
PRIVATE DWORD ParseDwordField(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
|
||
PRIVATE DWORD ParseStringField(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
|
||
PRIVATE DWORD ParseUnknownAttribute(LPSTR*, LPDWORD, LPBYTE, LPDWORD, DWORD, DWORD);
|
||
|
||
//
|
||
// data
|
||
//
|
||
|
||
ATTRIBUTE_PARSER AttributeParsers[] = {
|
||
GOPHER_CATEGORY_ID_ADMIN,
|
||
GOPHER_ATTRIBUTE_ID_ADMIN,
|
||
ParseAdminAttribute,
|
||
2,
|
||
sizeof(GOPHER_ADMIN_ATTRIBUTE_TYPE),
|
||
|
||
GOPHER_CATEGORY_ID_ADMIN,
|
||
GOPHER_ATTRIBUTE_ID_MOD_DATE,
|
||
ParseModDateAttribute,
|
||
1,
|
||
sizeof(GOPHER_MOD_DATE_ATTRIBUTE_TYPE),
|
||
|
||
GOPHER_CATEGORY_ID_ADMIN,
|
||
GOPHER_ATTRIBUTE_ID_TTL,
|
||
ParseDwordField,
|
||
1,
|
||
sizeof(GOPHER_TTL_ATTRIBUTE_TYPE),
|
||
|
||
GOPHER_CATEGORY_ID_ADMIN,
|
||
GOPHER_ATTRIBUTE_ID_SCORE,
|
||
ParseIntField,
|
||
1,
|
||
sizeof(GOPHER_SCORE_ATTRIBUTE_TYPE),
|
||
|
||
GOPHER_CATEGORY_ID_ADMIN,
|
||
GOPHER_ATTRIBUTE_ID_RANGE,
|
||
ParseIntField,
|
||
2,
|
||
sizeof(GOPHER_SCORE_RANGE_ATTRIBUTE_TYPE),
|
||
|
||
GOPHER_CATEGORY_ID_ADMIN,
|
||
GOPHER_ATTRIBUTE_ID_SITE,
|
||
ParseStringField,
|
||
1,
|
||
sizeof(GOPHER_SITE_ATTRIBUTE_TYPE),
|
||
|
||
GOPHER_CATEGORY_ID_ADMIN,
|
||
GOPHER_ATTRIBUTE_ID_ORG,
|
||
ParseStringField,
|
||
1,
|
||
sizeof(GOPHER_ORGANIZATION_ATTRIBUTE_TYPE),
|
||
|
||
GOPHER_CATEGORY_ID_ADMIN,
|
||
GOPHER_ATTRIBUTE_ID_LOCATION,
|
||
ParseStringField,
|
||
1,
|
||
sizeof(GOPHER_LOCATION_ATTRIBUTE_TYPE),
|
||
|
||
GOPHER_CATEGORY_ID_ADMIN,
|
||
GOPHER_ATTRIBUTE_ID_GEOG,
|
||
ParseIntField,
|
||
6,
|
||
sizeof(GOPHER_GEOGRAPHICAL_LOCATION_ATTRIBUTE_TYPE),
|
||
|
||
GOPHER_CATEGORY_ID_ADMIN,
|
||
GOPHER_ATTRIBUTE_ID_TIMEZONE,
|
||
ParseIntField,
|
||
1,
|
||
sizeof(GOPHER_TIMEZONE_ATTRIBUTE_TYPE),
|
||
|
||
GOPHER_CATEGORY_ID_ADMIN,
|
||
GOPHER_ATTRIBUTE_ID_PROVIDER,
|
||
ParseStringField,
|
||
1,
|
||
sizeof(GOPHER_PROVIDER_ATTRIBUTE_TYPE),
|
||
|
||
GOPHER_CATEGORY_ID_ADMIN,
|
||
GOPHER_ATTRIBUTE_ID_VERSION,
|
||
ParseStringField,
|
||
1,
|
||
sizeof(GOPHER_VERSION_ATTRIBUTE_TYPE),
|
||
|
||
GOPHER_CATEGORY_ID_ABSTRACT,
|
||
GOPHER_ATTRIBUTE_ID_ABSTRACT,
|
||
ParseAbstractAttribute,
|
||
2,
|
||
sizeof(GOPHER_ABSTRACT_ATTRIBUTE_TYPE),
|
||
|
||
GOPHER_CATEGORY_ID_VIEWS,
|
||
GOPHER_ATTRIBUTE_ID_VIEW,
|
||
ParseViewAttribute,
|
||
3,
|
||
sizeof(GOPHER_VIEW_ATTRIBUTE_TYPE),
|
||
|
||
GOPHER_CATEGORY_ID_VERONICA,
|
||
GOPHER_ATTRIBUTE_ID_TREEWALK,
|
||
ParseTreewalkAttribute,
|
||
1,
|
||
sizeof(GOPHER_VERONICA_ATTRIBUTE_TYPE),
|
||
|
||
//
|
||
// N.B. Unknown must be the last parser in the list
|
||
//
|
||
|
||
GOPHER_CATEGORY_ID_UNKNOWN,
|
||
GOPHER_ATTRIBUTE_ID_UNKNOWN,
|
||
ParseUnknownAttribute,
|
||
1,
|
||
sizeof(GOPHER_UNKNOWN_ATTRIBUTE_TYPE)
|
||
};
|
||
|
||
#define NUMBER_OF_PARSERS ARRAY_ELEMENTS(AttributeParsers)
|
||
|
||
//
|
||
// functions
|
||
//
|
||
|
||
|
||
BOOL
|
||
FindAttribute(
|
||
IN DWORD CategoryId,
|
||
IN DWORD AttributeId,
|
||
IN LPCSTR AttributeName,
|
||
IN OUT LPSTR* Buffer,
|
||
IN OUT LPDWORD BufferLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
description-of-function.
|
||
|
||
Arguments:
|
||
|
||
CategoryId -
|
||
AttributeId -
|
||
AttributeName -
|
||
Buffer -
|
||
BufferLength -
|
||
|
||
Return Value:
|
||
|
||
BOOL
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL ok;
|
||
LPSTR categoryName;
|
||
LPSTR attributeName;
|
||
char searchName[DEFAULT_ATTRIBUTE_NAME_LENGTH + 1];
|
||
DWORD id;
|
||
int index;
|
||
int len;
|
||
|
||
if (AttributeId == GOPHER_ATTRIBUTE_ID_ALL) {
|
||
id = CategoryId;
|
||
} else {
|
||
id = AttributeId;
|
||
}
|
||
ok = MapAttributeIdToNames(id, &categoryName, &attributeName);
|
||
if (!ok) {
|
||
attributeName = (LPSTR)AttributeName;
|
||
}
|
||
if (AttributeId != GOPHER_ATTRIBUTE_ID_ALL) {
|
||
searchName[0] = ' ';
|
||
index = 1;
|
||
} else {
|
||
index = 0;
|
||
attributeName = categoryName;
|
||
}
|
||
len = strlen(attributeName);
|
||
if (len >= sizeof(searchName)) {
|
||
return FALSE;
|
||
}
|
||
strcpy(&searchName[index], attributeName);
|
||
len += index;
|
||
if (AttributeId != GOPHER_ATTRIBUTE_ID_ALL) {
|
||
searchName[len++] = ':';
|
||
searchName[len] = '\0';
|
||
}
|
||
do {
|
||
if (*BufferLength < (DWORD)len) {
|
||
return FALSE;
|
||
}
|
||
if (memcmp(*Buffer, searchName, len) == 0) {
|
||
return TRUE;
|
||
}
|
||
SkipLine(Buffer, BufferLength);
|
||
} while (*BufferLength != 0);
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
VOID
|
||
FindNextAttribute(
|
||
IN DWORD CategoryId,
|
||
IN DWORD AttributeId,
|
||
IN OUT LPSTR* Buffer,
|
||
IN OUT LPDWORD BufferLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
description-of-function.
|
||
|
||
Arguments:
|
||
|
||
CategoryId -
|
||
AttributeId -
|
||
Buffer -
|
||
BufferLength -
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL found;
|
||
|
||
INET_ASSERT((**Buffer == '+') || (**Buffer == ' '));
|
||
|
||
if (CategoryId == GOPHER_CATEGORY_ID_UNKNOWN) {
|
||
AttributeId = !GOPHER_ATTRIBUTE_ID_ALL;
|
||
}
|
||
|
||
//
|
||
// loop looking at the next line until we find:
|
||
//
|
||
// a) the end of the buffer
|
||
// b) the next section line (starts with '+')
|
||
// c) the next attribute line (starts with ' ')
|
||
//
|
||
|
||
for (found = FALSE; !found; ) {
|
||
if (SkipLine(Buffer, BufferLength)) {
|
||
if (AttributeId == GOPHER_ATTRIBUTE_ID_ALL) {
|
||
found = (BOOL)(**Buffer == '+');
|
||
} else {
|
||
found = TRUE;
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// end of buffer
|
||
//
|
||
|
||
found = TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
DWORD
|
||
EnumerateAttribute(
|
||
IN GOPHER_ATTRIBUTE_ENUMERATOR Enumerator,
|
||
IN LPSTR LinePtr,
|
||
IN DWORD LineLength,
|
||
IN LPBYTE Buffer,
|
||
IN DWORD BufferLength,
|
||
OUT LPBOOL ResumeEnumeration
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
description-of-function.
|
||
|
||
Arguments:
|
||
|
||
Enumerator -
|
||
LinePtr -
|
||
LineLength -
|
||
Buffer -
|
||
BufferLength -
|
||
ResumeEnumeration -
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD error;
|
||
char attributeName[DEFAULT_ATTRIBUTE_NAME_LENGTH + 1];
|
||
DWORD nameLength;
|
||
|
||
nameLength = sizeof(attributeName);
|
||
error = ExtractAttributeName(attributeName,
|
||
&nameLength,
|
||
&LinePtr,
|
||
&LineLength
|
||
);
|
||
if (error == ERROR_SUCCESS) {
|
||
|
||
int i;
|
||
DWORD categoryId;
|
||
DWORD attributeId;
|
||
BOOL found;
|
||
|
||
MapAttributeToIds((LPCSTR)attributeName,
|
||
&categoryId,
|
||
&attributeId
|
||
);
|
||
|
||
//
|
||
// loop, looking fot the parser to handle this particular type. If we
|
||
// don't find it, we will be conveniently left at the unknown parser
|
||
// (that's why we have (NUMBER_OF_PARSERS - 1) and the unknown parser
|
||
// at the end of the list)
|
||
//
|
||
|
||
for (i = 0; i < NUMBER_OF_PARSERS - 1; ++i) {
|
||
if ((AttributeParsers[i].CategoryId == categoryId)
|
||
&& (AttributeParsers[i].AttributeId == attributeId)) {
|
||
break;
|
||
}
|
||
}
|
||
if (BufferLength >= SIZE_OF_GOPHER_ATTRIBUTE_FIXED_PART) {
|
||
((LPGOPHER_ATTRIBUTE_TYPE)Buffer)->CategoryId = categoryId;
|
||
((LPGOPHER_ATTRIBUTE_TYPE)Buffer)->AttributeId = attributeId;
|
||
|
||
//
|
||
// remove the fixed part from the buffer size before converting
|
||
// the attribute
|
||
//
|
||
|
||
BufferLength -= SIZE_OF_GOPHER_ATTRIBUTE_FIXED_PART;
|
||
} else {
|
||
BufferLength = 0;
|
||
}
|
||
error = AttributeParsers[i].Parser(
|
||
&LinePtr,
|
||
&LineLength,
|
||
(LPBYTE)&((LPGOPHER_ATTRIBUTE_TYPE)Buffer)->AttributeType,
|
||
&BufferLength,
|
||
AttributeParsers[i].NumberOfFields,
|
||
AttributeParsers[i].FixedSize
|
||
);
|
||
|
||
//
|
||
// add back the amount of buffer space used by/required for the
|
||
// fixed part of the GOPHER_ATTRIBUTE_TYPE structure
|
||
//
|
||
*ResumeEnumeration = Enumerator((LPGOPHER_ATTRIBUTE_TYPE)Buffer, error);
|
||
}
|
||
return error;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
DWORD
|
||
ParseIntField(
|
||
IN OUT LPSTR* LinePtr,
|
||
IN OUT LPDWORD LineLength,
|
||
OUT LPBYTE Buffer,
|
||
IN OUT LPDWORD BufferLength,
|
||
IN DWORD NumberOfFields,
|
||
IN DWORD FixedSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
description-of-function.
|
||
|
||
Arguments:
|
||
|
||
LinePtr -
|
||
LineLength -
|
||
Buffer -
|
||
BufferLength -
|
||
NumberOfFields -
|
||
FixedSize -
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD error;
|
||
|
||
if (*BufferLength < FixedSize) {
|
||
error = ERROR_INSUFFICIENT_BUFFER;
|
||
} else {
|
||
error = ERROR_SUCCESS;
|
||
}
|
||
*BufferLength = FixedSize;
|
||
while ((error == ERROR_SUCCESS) && NumberOfFields--) {
|
||
if (SkipLeading(LinePtr, LineLength)) {
|
||
ExtractInt(LinePtr, 0, (LPINT)Buffer);
|
||
Buffer = (LPBYTE)((LPINT)Buffer + 1);
|
||
} else {
|
||
error = ERROR_GOPHER_DATA_ERROR;
|
||
}
|
||
}
|
||
return error;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
DWORD
|
||
ParseDwordField(
|
||
IN OUT LPSTR* LinePtr,
|
||
IN OUT LPDWORD LineLength,
|
||
OUT LPBYTE Buffer,
|
||
IN OUT LPDWORD BufferLength,
|
||
IN DWORD NumberOfFields,
|
||
IN DWORD FixedSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
description-of-function.
|
||
|
||
Arguments:
|
||
|
||
LinePtr -
|
||
LineLength -
|
||
Buffer -
|
||
BufferLength -
|
||
NumberOfFields -
|
||
FixedSize -
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD error;
|
||
|
||
if (*BufferLength < FixedSize) {
|
||
error = ERROR_INSUFFICIENT_BUFFER;
|
||
} else {
|
||
error = ERROR_SUCCESS;
|
||
}
|
||
*BufferLength = FixedSize;
|
||
while ((error == ERROR_SUCCESS) && NumberOfFields--) {
|
||
if (SkipLeading(LinePtr, LineLength)) {
|
||
ExtractDword(LinePtr, 0, (LPDWORD)Buffer);
|
||
Buffer = (LPBYTE)((LPDWORD)Buffer + 1);
|
||
} else {
|
||
error = ERROR_GOPHER_DATA_ERROR;
|
||
}
|
||
}
|
||
return error;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
DWORD
|
||
ParseStringField(
|
||
IN OUT LPSTR* LinePtr,
|
||
IN OUT LPDWORD LineLength,
|
||
OUT LPBYTE Buffer,
|
||
IN OUT LPDWORD BufferLength,
|
||
IN DWORD NumberOfFields,
|
||
IN DWORD FixedSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
description-of-function.
|
||
|
||
Arguments:
|
||
|
||
LinePtr -
|
||
LineLength -
|
||
Buffer -
|
||
BufferLength -
|
||
NumberOfFields -
|
||
FixedSize -
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD structureSize;
|
||
DWORD error;
|
||
DWORD stringLength;
|
||
LPSTR stringPtr;
|
||
LPSTR* fieldPtr;
|
||
|
||
structureSize = 0;
|
||
error = ERROR_SUCCESS;
|
||
stringPtr = (LPSTR)((LPSTR)Buffer + NumberOfFields);
|
||
fieldPtr = (LPSTR*)Buffer;
|
||
while (NumberOfFields--) {
|
||
SkipLeading(LinePtr, LineLength);
|
||
stringLength = CharacterCount(LinePtr, LineLength, "\r\n");
|
||
structureSize = sizeof(LPSTR)
|
||
+ stringLength
|
||
+ 1
|
||
;
|
||
if (*BufferLength >= structureSize) {
|
||
*fieldPtr++ = stringPtr;
|
||
CopyString(&stringPtr, *LinePtr, stringLength);
|
||
*LinePtr += stringLength - 1;
|
||
} else {
|
||
error = ERROR_INSUFFICIENT_BUFFER;
|
||
}
|
||
}
|
||
*BufferLength = structureSize;
|
||
return error;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
DWORD
|
||
ParseAdminAttribute(
|
||
IN OUT LPSTR* LinePtr,
|
||
IN OUT LPDWORD LineLength,
|
||
OUT LPBYTE Buffer,
|
||
IN OUT LPDWORD BufferLength,
|
||
IN DWORD NumberOfFields,
|
||
IN DWORD FixedSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
description-of-function.
|
||
|
||
Arguments:
|
||
|
||
LinePtr -
|
||
LineLength -
|
||
Buffer -
|
||
BufferLength -
|
||
NumberOfFields -
|
||
FixedSize -
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
|
||
--*/
|
||
|
||
{
|
||
LPSTR comment;
|
||
DWORD commentLength;
|
||
LPSTR emailAddress;
|
||
DWORD emailAddressLength;
|
||
LPSTR pstr;
|
||
DWORD len;
|
||
DWORD structureSize;
|
||
DWORD error;
|
||
|
||
SkipLeading(LinePtr, LineLength);
|
||
comment = *LinePtr;
|
||
emailAddress = strchr(*LinePtr, '<');
|
||
if (emailAddress == NULL) {
|
||
return ERROR_GOPHER_DATA_ERROR;
|
||
}
|
||
++emailAddress;
|
||
pstr = emailAddress;
|
||
emailAddressLength = 0;
|
||
len = *LineLength;
|
||
while ((*pstr != '>') && (len != 0)) {
|
||
++pstr;
|
||
++emailAddressLength;
|
||
}
|
||
commentLength = (DWORD)(emailAddress - comment);
|
||
structureSize = sizeof(GOPHER_ADMIN_ATTRIBUTE_TYPE)
|
||
+ commentLength + 1
|
||
+ emailAddressLength + 1
|
||
;
|
||
if (*BufferLength < structureSize) {
|
||
error = ERROR_INSUFFICIENT_BUFFER;
|
||
} else {
|
||
error = ERROR_SUCCESS;
|
||
}
|
||
*BufferLength = structureSize;
|
||
if (error == ERROR_SUCCESS) {
|
||
|
||
LPGOPHER_ADMIN_ATTRIBUTE_TYPE pStruct;
|
||
LPSTR stringPtr;
|
||
|
||
pStruct = (LPGOPHER_ADMIN_ATTRIBUTE_TYPE)Buffer;
|
||
stringPtr = (LPSTR)pStruct + sizeof(GOPHER_ADMIN_ATTRIBUTE_TYPE);
|
||
pStruct->Comment = (LPCSTR)stringPtr;
|
||
CopyString(&stringPtr, comment, commentLength);
|
||
pStruct->EmailAddress = (LPCSTR)stringPtr;
|
||
CopyString(&stringPtr, emailAddress, emailAddressLength);
|
||
}
|
||
return error;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
DWORD
|
||
ParseModDateAttribute(
|
||
IN OUT LPSTR* LinePtr,
|
||
IN OUT LPDWORD LineLength,
|
||
OUT LPBYTE Buffer,
|
||
IN OUT LPDWORD BufferLength,
|
||
IN DWORD NumberOfFields,
|
||
IN DWORD FixedSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
description-of-function.
|
||
|
||
Arguments:
|
||
|
||
LinePtr -
|
||
LineLength -
|
||
Buffer -
|
||
BufferLength -
|
||
NumberOfFields -
|
||
FixedSize -
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD structureSize;
|
||
DWORD error;
|
||
|
||
structureSize = sizeof(GOPHER_MOD_DATE_ATTRIBUTE_TYPE);
|
||
if (*BufferLength < structureSize) {
|
||
error = ERROR_INSUFFICIENT_BUFFER;
|
||
} else {
|
||
error = ERROR_SUCCESS;
|
||
}
|
||
*BufferLength = structureSize;
|
||
if (error == ERROR_SUCCESS) {
|
||
|
||
LPSTR dateField;
|
||
|
||
dateField = strchr(*LinePtr, '<');
|
||
if (dateField != NULL) {
|
||
ExtractDateAndTime(
|
||
&dateField,
|
||
&((LPGOPHER_MOD_DATE_ATTRIBUTE_TYPE)Buffer)->DateAndTime
|
||
);
|
||
} else {
|
||
error = ERROR_GOPHER_DATA_ERROR;
|
||
}
|
||
}
|
||
return error;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
DWORD
|
||
ParseAbstractAttribute(
|
||
IN OUT LPSTR* LinePtr,
|
||
IN OUT LPDWORD LineLength,
|
||
OUT LPBYTE Buffer,
|
||
IN OUT LPDWORD BufferLength,
|
||
IN DWORD NumberOfFields,
|
||
IN DWORD FixedSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
description-of-function.
|
||
|
||
Arguments:
|
||
|
||
LinePtr -
|
||
LineLength -
|
||
Buffer -
|
||
BufferLength -
|
||
NumberOfFields -
|
||
FixedSize -
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD error;
|
||
error = ERROR_SUCCESS;
|
||
return error;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
DWORD
|
||
ParseViewAttribute(
|
||
IN OUT LPSTR* LinePtr,
|
||
IN OUT LPDWORD LineLength,
|
||
OUT LPBYTE Buffer,
|
||
IN OUT LPDWORD BufferLength,
|
||
IN DWORD NumberOfFields,
|
||
IN DWORD FixedSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
description-of-function.
|
||
|
||
Arguments:
|
||
|
||
LinePtr -
|
||
LineLength -
|
||
Buffer -
|
||
BufferLength -
|
||
NumberOfFields -
|
||
FixedSize -
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
|
||
--*/
|
||
|
||
{
|
||
char contentType[DEFAULT_CONTENT_TYPE_NAME_LENGTH + 1];
|
||
DWORD contentTypeLength;
|
||
char language[DEFAULT_LANGUAGE_NAME_LENGTH + 1];
|
||
DWORD languageLength;
|
||
DWORD viewSize;
|
||
BOOL ok;
|
||
DWORD error;
|
||
|
||
contentTypeLength = sizeof(contentType);
|
||
languageLength = sizeof(language);
|
||
SkipLeading(LinePtr, LineLength);
|
||
ok = ExtractView(LinePtr,
|
||
contentType,
|
||
&contentTypeLength,
|
||
language,
|
||
&languageLength,
|
||
&viewSize
|
||
);
|
||
if (ok) {
|
||
|
||
DWORD structureSize;
|
||
|
||
contentTypeLength = sizeof(contentType) - contentTypeLength;
|
||
languageLength = sizeof(language) - languageLength;
|
||
structureSize = sizeof(GOPHER_VIEW_ATTRIBUTE_TYPE)
|
||
+ contentTypeLength
|
||
+ languageLength
|
||
;
|
||
if (*BufferLength >= structureSize) {
|
||
|
||
LPSTR stringPtr;
|
||
|
||
stringPtr = (LPSTR)((LPGOPHER_VIEW_ATTRIBUTE_TYPE)Buffer + 1);
|
||
((LPGOPHER_VIEW_ATTRIBUTE_TYPE)Buffer)->ContentType = stringPtr;
|
||
memcpy(stringPtr, contentType, contentTypeLength);
|
||
stringPtr += contentTypeLength;
|
||
((LPGOPHER_VIEW_ATTRIBUTE_TYPE)Buffer)->Language = stringPtr;
|
||
memcpy(stringPtr, language, languageLength);
|
||
((LPGOPHER_VIEW_ATTRIBUTE_TYPE)Buffer)->Size = viewSize;
|
||
error = ERROR_SUCCESS;
|
||
} else {
|
||
error = ERROR_INSUFFICIENT_BUFFER;
|
||
}
|
||
*BufferLength = structureSize;
|
||
} else {
|
||
error = ERROR_GOPHER_DATA_ERROR;
|
||
}
|
||
return error;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
DWORD
|
||
ParseTreewalkAttribute(
|
||
IN OUT LPSTR* LinePtr,
|
||
IN OUT LPDWORD LineLength,
|
||
OUT LPBYTE Buffer,
|
||
IN OUT LPDWORD BufferLength,
|
||
IN DWORD NumberOfFields,
|
||
IN DWORD FixedSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
description-of-function.
|
||
|
||
Arguments:
|
||
|
||
LinePtr -
|
||
LineLength -
|
||
Buffer -
|
||
BufferLength -
|
||
NumberOfFields -
|
||
FixedSize -
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD structureSize;
|
||
DWORD error;
|
||
|
||
structureSize = sizeof(GOPHER_VERONICA_ATTRIBUTE_TYPE);
|
||
if (*BufferLength < structureSize) {
|
||
error = ERROR_INSUFFICIENT_BUFFER;
|
||
} else {
|
||
error = ERROR_SUCCESS;
|
||
}
|
||
*BufferLength = structureSize;
|
||
if (error == ERROR_SUCCESS) {
|
||
SkipLeading(LinePtr, LineLength);
|
||
if (*LineLength >= 3) {
|
||
|
||
BOOL ok;
|
||
|
||
ok = (BOOL)(_strnicmp(*LinePtr, "YES", 3) == 0);
|
||
|
||
((LPGOPHER_VERONICA_ATTRIBUTE_TYPE)Buffer)->TreeWalk = ok;
|
||
} else {
|
||
error = ERROR_GOPHER_DATA_ERROR;
|
||
}
|
||
}
|
||
return error;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
DWORD
|
||
ParseUnknownAttribute(
|
||
IN OUT LPSTR* LinePtr,
|
||
IN OUT LPDWORD LineLength,
|
||
OUT LPBYTE Buffer,
|
||
IN OUT LPDWORD BufferLength,
|
||
IN DWORD NumberOfFields,
|
||
IN DWORD FixedSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
description-of-function.
|
||
|
||
Arguments:
|
||
|
||
LinePtr -
|
||
LineLength -
|
||
Buffer -
|
||
BufferLength -
|
||
NumberOfFields -
|
||
FixedSize -
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD structureSize;
|
||
DWORD error;
|
||
DWORD stringLength;
|
||
LPSTR stringPtr;
|
||
|
||
stringPtr = (LPSTR)((LPGOPHER_UNKNOWN_ATTRIBUTE_TYPE)Buffer + 1);
|
||
((LPGOPHER_UNKNOWN_ATTRIBUTE_TYPE)Buffer)->Text = stringPtr;
|
||
SkipLeading(LinePtr, LineLength);
|
||
structureSize = sizeof(GOPHER_UNKNOWN_ATTRIBUTE_TYPE)
|
||
+ *LineLength
|
||
+ 1
|
||
;
|
||
if (*BufferLength >= structureSize) {
|
||
CopyString(&stringPtr, *LinePtr, *LineLength);
|
||
} else {
|
||
error = ERROR_INSUFFICIENT_BUFFER;
|
||
}
|
||
*BufferLength = structureSize;
|
||
return error;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
DWORD
|
||
ExtractAttributeName(
|
||
OUT LPSTR AttributeName,
|
||
IN OUT LPDWORD AttributeNameLength,
|
||
IN OUT LPSTR* LinePtr,
|
||
IN OUT LPDWORD LineLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
description-of-function.
|
||
|
||
Arguments:
|
||
|
||
AttributeName -
|
||
AttributeNameLength -
|
||
LinePtr -
|
||
LineLength -
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
|
||
--*/
|
||
|
||
{
|
||
return ERROR_SUCCESS;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
DWORD
|
||
CharacterCount(
|
||
IN OUT LPSTR* LinePtr,
|
||
IN OUT LPDWORD LineLength,
|
||
IN LPSTR TerminationSet
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns the number of characters in a string, up to, but not including
|
||
the termination character. Termination character is defined as being a
|
||
member of TerminationSet
|
||
|
||
Arguments:
|
||
|
||
LinePtr - IN: Pointer to string to count characters in
|
||
OUT: Pointer to string at character found from
|
||
TerminationSet
|
||
|
||
LineLength - IN: Current length of LinePtr
|
||
OUT: Remaining length of LinePtr
|
||
|
||
TerminationSet - Pointer to string containing characters which will
|
||
terminate counting
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Number of character in LinePtr, up to, but not including the
|
||
termination character
|
||
|
||
--*/
|
||
|
||
{
|
||
char terminationChars[256];
|
||
int i;
|
||
DWORD count;
|
||
|
||
//
|
||
// zap discovery matrix, 4 bytes at a time
|
||
//
|
||
|
||
for (i = 0; i < ARRAY_ELEMENTS(terminationChars); i += sizeof(DWORD)) {
|
||
*(LPDWORD)&terminationChars[i] = 0;
|
||
}
|
||
|
||
//
|
||
// for each character that we are interested in, set its matrix entry to
|
||
// non-zero
|
||
//
|
||
|
||
for (i = 0; TerminationSet[i] != '\0'; ++i) {
|
||
terminationChars[(int)TerminationSet[i]] = 1;
|
||
}
|
||
|
||
//
|
||
// loop, looking for end-of-string (LineLength decremented to 0) or one
|
||
// of the termination characters
|
||
//
|
||
|
||
for (count = 0; *LineLength != 0; ) {
|
||
|
||
char ch;
|
||
|
||
ch = *(*LinePtr)++;
|
||
--*LineLength;
|
||
if (terminationChars[(int)ch]) {
|
||
break;
|
||
}
|
||
++count;
|
||
}
|
||
return count;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
DWORD
|
||
CountCharactersToEol(
|
||
IN OUT LPSTR* LinePtr,
|
||
IN OUT LPDWORD LineLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Special-case version of CharacterCount - knows that the termination set
|
||
comprised <CR>, <LF>
|
||
|
||
Arguments:
|
||
|
||
LinePtr - Pointer to pointer to string to count. Updated on output
|
||
|
||
LineLength - Pointer to length of string. Updated on output
|
||
|
||
Return Value:
|
||
|
||
DWORD
|
||
Length of string
|
||
|
||
--*/
|
||
|
||
{
|
||
int i;
|
||
DWORD count;
|
||
|
||
//
|
||
// loop, looking for end-of-string (LineLength decremented to 0) or one
|
||
// of the termination characters
|
||
//
|
||
|
||
for (count = 0; *LineLength != 0; ) {
|
||
|
||
char ch;
|
||
|
||
ch = *(*LinePtr)++;
|
||
--*LineLength;
|
||
if ((ch == '\r') || (ch == '\n')) {
|
||
break;
|
||
}
|
||
++count;
|
||
}
|
||
return count;
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
BOOL
|
||
SkipLeading(
|
||
IN OUT LPSTR* String,
|
||
IN OUT LPDWORD Length
|
||
)
|
||
{
|
||
while (((**String == ' ') || (**String == ':')) && (*Length != 0)) {
|
||
++*String;
|
||
--*Length;
|
||
}
|
||
return (BOOL)(*Length != 0);
|
||
}
|
||
|
||
|
||
PRIVATE
|
||
VOID
|
||
CopyString(
|
||
IN OUT LPSTR* String,
|
||
IN LPSTR Source,
|
||
IN DWORD Length
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
description-of-function.
|
||
|
||
Arguments:
|
||
|
||
String -
|
||
Source -
|
||
Length -
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
memcpy(*String, Source, Length);
|
||
*String += Length;
|
||
*((*String)++) = '\0';
|
||
}
|
||
|
||
#endif // defined(GOPHER_ATTRIBUTE_SUPPORT)
|