You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1065 lines
29 KiB
1065 lines
29 KiB
/*++
|
|
|
|
Copyright (c) 1994 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
gfrapiu.cxx
|
|
|
|
Abstract:
|
|
|
|
Common sub-API level functions
|
|
|
|
Contents:
|
|
TestLocatorType
|
|
GetAttributes
|
|
MakeAttributeRequest
|
|
ParseGopherUrl
|
|
GopherLocatorToUrl
|
|
|
|
Author:
|
|
|
|
Richard L Firth (rfirth) 19-Nov-1994
|
|
|
|
Environment:
|
|
|
|
Win32 user-level DLL
|
|
|
|
Revision History:
|
|
|
|
19-Nov-1994
|
|
Created
|
|
|
|
--*/
|
|
|
|
#include <wininetp.h>
|
|
#include "gfrapih.h"
|
|
|
|
// because wininet doesnt know IStream
|
|
#define NO_SHLWAPI_STREAM
|
|
#include <shlwapi.h>
|
|
#include <shlwapip.h>
|
|
|
|
#define INTERNET_DEFAULT_CSO_PORT 105
|
|
#define INTERNET_MAX_WELL_KNOWN_PORT 1023
|
|
//
|
|
// functions
|
|
//
|
|
|
|
BOOL IsInappropriateGopherPort (INTERNET_PORT port)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Gopher URLs can encode arbitrary data to arbitrary ports. This characteristic
|
|
enables malicious web pages to redirect IE to exploit security holes, for
|
|
example, to spoof a mailer daemon inside a firewall. Based on experimentation,
|
|
Netscape apparently disables gopher on ports 1 and 7 though 25 odd. That range
|
|
covers many of the well-known ports catalogued by IANA but misses many others
|
|
like 137 through 139, assigned for netbios over tcp/ip . Since gopher is
|
|
becoming increasingly irrelevant, we prefer to be stricter . IE3 now disables
|
|
gopher on ports less than 1024, except for 70, the standard gopher port, and
|
|
105, typically used for CSO name searches.
|
|
|
|
Arguments: Port number
|
|
|
|
Return Value: TRUE for success, FALSE for failure
|
|
|
|
--*/
|
|
{
|
|
if (port > INTERNET_MAX_WELL_KNOWN_PORT)
|
|
return FALSE;
|
|
switch (port) {
|
|
case INTERNET_INVALID_PORT_NUMBER:
|
|
case INTERNET_DEFAULT_GOPHER_PORT:
|
|
case INTERNET_DEFAULT_CSO_PORT:
|
|
return FALSE;
|
|
default:
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
TestLocatorType(
|
|
IN LPCSTR Locator,
|
|
IN DWORD TypeMask
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks that Locator is valid and checks if it is of the specified type.
|
|
This function is mainly for use by GfrIsXxxx APIs
|
|
|
|
Arguments:
|
|
|
|
Locator - pointer to app-supplied locator string
|
|
|
|
TypeMask - gopher type mask to check for
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
Locator is good and of the specified type
|
|
|
|
Failure - ERROR_INVALID_PARAMETER
|
|
Locator is bad
|
|
|
|
ERROR_INVALID_FUNCTION
|
|
Locator is good, but not of the specified type
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD error;
|
|
BOOL success = FALSE;
|
|
|
|
//
|
|
// BUGBUG - 1. Do we really want to test this parameter?
|
|
// 2. If so, is the length sufficient?
|
|
//
|
|
|
|
if (IsBadStringPtr(Locator, MAX_GOPHER_LOCATOR_LENGTH)) {
|
|
error = ERROR_INVALID_PARAMETER;
|
|
} else {
|
|
|
|
DWORD gopherType;
|
|
|
|
gopherType = GopherCharToType(*Locator);
|
|
if (gopherType == INVALID_GOPHER_TYPE) {
|
|
|
|
//
|
|
// not a recognizable type - Locator is bogus
|
|
//
|
|
|
|
error = ERROR_INVALID_PARAMETER;
|
|
} else if (gopherType & TypeMask) {
|
|
error = ERROR_SUCCESS;
|
|
} else {
|
|
|
|
//
|
|
// slight bogosity - need an error code to differentiate matched
|
|
// vs. not-matched: INVALID_FUNCTION will do
|
|
//
|
|
|
|
error = ERROR_INVALID_FUNCTION;
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
#if defined(GOPHER_ATTRIBUTE_SUPPORT)
|
|
|
|
|
|
DWORD
|
|
GetAttributes(
|
|
IN GOPHER_ATTRIBUTE_ENUMERATOR Enumerator,
|
|
IN DWORD CategoryId,
|
|
IN DWORD AttributeId,
|
|
IN LPCSTR AttributeName,
|
|
IN LPSTR InBuffer,
|
|
IN DWORD InBufferLength,
|
|
OUT LPBYTE OutBuffer,
|
|
IN DWORD OutBufferLength,
|
|
OUT LPDWORD CharactersReturned
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Pulls attributes out of a buffer and puts them in the caller's buffer or
|
|
enumerates them (if Enumerator supplied)
|
|
|
|
Arguments:
|
|
|
|
Enumerator - address of caller's enumerator function
|
|
|
|
CategoryId - category of attribute(s)
|
|
|
|
AttributeId - the attribute to return
|
|
|
|
AttributeName - name of the attribute if not a known attribute
|
|
|
|
InBuffer - pointer to buffer containing gopher+ attributes
|
|
|
|
InBufferLength - length of attribute buffer
|
|
|
|
OutBuffer - pointer to caller's buffer where attributes returned
|
|
|
|
OutBufferLength - length of caller's buffer
|
|
|
|
CharactersReturned - pointer to returned buffer length
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_GOPHER_ATTRIBUTE_NOT_FOUND
|
|
We couldn't find the requested attribute/category
|
|
|
|
ERROR_INSUFFICIENT_BUFFER
|
|
The caller's buffer isn't large enough to contain the
|
|
attributes. *lpdwCharactersReturned will contain the
|
|
required length
|
|
|
|
ERROR_GOPHER_DATA_ERROR
|
|
We couldn't parse the attributes for some reason
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD error;
|
|
|
|
*CharactersReturned = 0;
|
|
|
|
//
|
|
// the buffer starts with the "+INFO:" attribute describing the locator. We
|
|
// don't return this as an attribute
|
|
//
|
|
|
|
if (SkipLine(&InBuffer, &InBufferLength)) {
|
|
|
|
LPSTR endSection;
|
|
BOOL done;
|
|
BOOL found;
|
|
BOOL more;
|
|
|
|
if (CategoryId != GOPHER_CATEGORY_ID_ALL) {
|
|
|
|
//
|
|
// advance InBuffer to the line that contains the requested
|
|
// attribute
|
|
//
|
|
|
|
found = FindAttribute(CategoryId,
|
|
AttributeId,
|
|
AttributeName,
|
|
&InBuffer,
|
|
&InBufferLength
|
|
);
|
|
if (found) {
|
|
|
|
//
|
|
// if the caller requested that we return all attributes in a
|
|
// section, then skip the line containing the category name
|
|
//
|
|
|
|
if (AttributeId == GOPHER_ATTRIBUTE_ID_ALL) {
|
|
found = SkipLine(&InBuffer, &InBufferLength);
|
|
}
|
|
if (found) {
|
|
|
|
DWORD bufferLeft;
|
|
|
|
//
|
|
// get the end of the section or line in endSection
|
|
//
|
|
|
|
endSection = InBuffer;
|
|
bufferLeft = InBufferLength;
|
|
FindNextAttribute(CategoryId,
|
|
AttributeId,
|
|
&endSection,
|
|
&bufferLeft
|
|
);
|
|
}
|
|
}
|
|
error = found ? ERROR_SUCCESS : ERROR_GOPHER_ATTRIBUTE_NOT_FOUND;
|
|
} else {
|
|
endSection = InBuffer + InBufferLength;
|
|
}
|
|
|
|
more = TRUE;
|
|
done = FALSE;
|
|
|
|
while ((error == ERROR_SUCCESS) && (InBuffer < endSection) && more) {
|
|
|
|
LPSTR linePtr;
|
|
char lineBuffer[256]; // arbitrary
|
|
DWORD lineLength;
|
|
BOOL ok;
|
|
|
|
linePtr = lineBuffer;
|
|
lineLength = sizeof(lineBuffer);
|
|
ok = CopyToEol(&linePtr,
|
|
&lineLength,
|
|
&InBuffer,
|
|
&InBufferLength
|
|
);
|
|
if (ok) {
|
|
if (Enumerator != NULL) {
|
|
|
|
//
|
|
// if the line starts with a '+' then (we assume) we are
|
|
// enumerating all attributes, in which case this line
|
|
// just serves to identify the next attribute section. We
|
|
// don't return any info
|
|
//
|
|
|
|
if (*linePtr == '+') {
|
|
|
|
char newCategory[32]; // arbitrary
|
|
int i;
|
|
|
|
for (i = 0; i < sizeof(newCategory); ++i) {
|
|
|
|
char ch;
|
|
|
|
ch = linePtr[i];
|
|
if ((ch == '\r') || (ch == '\n') || (ch == ' ') || (ch == ':')) {
|
|
break;
|
|
}
|
|
newCategory[i] = ch;
|
|
}
|
|
newCategory[i] = '\0';
|
|
MapAttributeToIds((LPCSTR)newCategory,
|
|
&CategoryId,
|
|
&AttributeId
|
|
);
|
|
if (CategoryId == GOPHER_CATEGORY_ID_ABSTRACT) {
|
|
|
|
//
|
|
// BUGBUG - the remainder of this line may contain
|
|
// a locator identifying the location of
|
|
// a file containing the abstract
|
|
//
|
|
|
|
}
|
|
} else {
|
|
error = EnumerateAttribute(Enumerator,
|
|
linePtr,
|
|
lineLength,
|
|
OutBuffer,
|
|
OutBufferLength,
|
|
&more
|
|
);
|
|
done = TRUE;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// get the length of the line in lineLength. N.B. We have
|
|
// to subtract an extra 1 because CopyToEol adds a '\0'
|
|
//
|
|
|
|
lineLength = sizeof(lineBuffer) - lineLength - 1;
|
|
if (OutBufferLength >= lineLength) {
|
|
memcpy(OutBuffer, lineBuffer, lineLength);
|
|
OutBuffer += lineLength;
|
|
OutBufferLength -= lineLength;
|
|
done = TRUE;
|
|
} else {
|
|
error = ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
//
|
|
// always update the characters copied/required parameter
|
|
//
|
|
|
|
*CharactersReturned += lineLength;
|
|
}
|
|
} else {
|
|
error = ERROR_GOPHER_DATA_ERROR;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if nothing was copied or enumerated then the attribute was not found
|
|
//
|
|
|
|
if (!done && (error == ERROR_SUCCESS)) {
|
|
error = ERROR_GOPHER_ATTRIBUTE_NOT_FOUND;
|
|
}
|
|
} else {
|
|
error = ERROR_GOPHER_DATA_ERROR;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
|
|
LPSTR
|
|
MakeAttributeRequest(
|
|
IN LPSTR Selector,
|
|
IN LPSTR Attribute
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts a gopher+ request into a request for attributes. E.g. turns
|
|
"0Foo" into "0Foo\t!+ADMIN"
|
|
|
|
Arguments:
|
|
|
|
Selector - pointer to identifier of gopher+ item to get attributes for
|
|
|
|
Attribute - pointer to name of attribute(s) to retrieve
|
|
|
|
Return Value:
|
|
|
|
LPSTR
|
|
Success - pointer to allocated memory containing attribute requester
|
|
|
|
Failure - NULL
|
|
|
|
--*/
|
|
|
|
{
|
|
INT selectorLength;
|
|
INT attributeLength;
|
|
LPSTR request;
|
|
|
|
selectorLength = (Selector != NULL) ? strlen(Selector) : 0;
|
|
attributeLength = (Attribute != NULL) ? strlen(Attribute) : 0;
|
|
request = NEW_MEMORY(selectorLength
|
|
|
|
//
|
|
// sizeof(GOPHER_PLUS_INFO_REQUEST) includes 2 for
|
|
// <CR><LF> and 1 for terminator
|
|
//
|
|
|
|
+ sizeof(GOPHER_PLUS_INFO_REQUEST)
|
|
+ attributeLength,
|
|
CHAR
|
|
);
|
|
if (request != NULL) {
|
|
if (Selector != NULL) {
|
|
memcpy(request, Selector, selectorLength);
|
|
}
|
|
memcpy(&request[selectorLength],
|
|
GOPHER_PLUS_ITEM_INFO,
|
|
sizeof(GOPHER_PLUS_ITEM_INFO) - 1
|
|
);
|
|
selectorLength += sizeof(GOPHER_PLUS_ITEM_INFO) - 1;
|
|
if (Attribute != NULL) {
|
|
memcpy(&request[selectorLength], Attribute, attributeLength);
|
|
selectorLength += attributeLength;
|
|
}
|
|
memcpy(&request[selectorLength],
|
|
GOPHER_REQUEST_TERMINATOR,
|
|
sizeof(GOPHER_REQUEST_TERMINATOR)
|
|
);
|
|
}
|
|
return request;
|
|
}
|
|
|
|
#endif // defined(GOPHER_ATTRIBUTE_SUPPORT)
|
|
|
|
|
|
DWORD
|
|
ParseGopherUrl(
|
|
IN OUT LPHINTERNET hInternet,
|
|
IN LPSTR Url,
|
|
IN DWORD SchemeLength,
|
|
IN LPSTR Headers,
|
|
IN DWORD HeadersLength,
|
|
IN DWORD OpenFlags,
|
|
IN DWORD_PTR Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
URL parser for gopher URLs. Support function for InternetOpenUrl() and
|
|
ParseUrl().
|
|
|
|
This is a macro function that just cracks the URL and calls gopher APIs to
|
|
do the work
|
|
|
|
Arguments:
|
|
|
|
hInternet - IN: Internet gateway handle
|
|
OUT: if successful handle of opened item, else undefined
|
|
|
|
Url - pointer to string containing gopher URL to open
|
|
|
|
SchemeLength - length of the URL scheme, exluding "://"
|
|
|
|
Headers - unused for gopher
|
|
|
|
HeadersLength - unused for gopher
|
|
|
|
OpenFlags - optional flags for opening a file (cache/no-cache, etc.)
|
|
|
|
Context - app-supplied context value for call-backs
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INTERNET_INVALID_URL
|
|
The URL passed in could not be parsed
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_GOPHER,
|
|
Dword,
|
|
"ParseGopherUrl",
|
|
"%#x [%#x], %q, %d, %#x, %d, %#x, %#x",
|
|
hInternet,
|
|
*hInternet,
|
|
Url,
|
|
SchemeLength,
|
|
Headers,
|
|
HeadersLength,
|
|
OpenFlags,
|
|
Context
|
|
));
|
|
|
|
DWORD error;
|
|
DWORD gopherType;
|
|
LPSTR selector;
|
|
LPSTR searchString;
|
|
HINTERNET hMapped = NULL;
|
|
|
|
UNREFERENCED_PARAMETER(Headers);
|
|
UNREFERENCED_PARAMETER(HeadersLength);
|
|
|
|
//
|
|
// extract the address information - no user name or password
|
|
//
|
|
|
|
DWORD urlLength;
|
|
LPSTR pHostName;
|
|
DWORD hostNameLength;
|
|
INTERNET_PORT port;
|
|
LPSTR lpszUrl = Url;
|
|
|
|
Url += SchemeLength + sizeof("://") - 1;
|
|
error = GetUrlAddress(&Url,
|
|
&urlLength,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&pHostName,
|
|
&hostNameLength,
|
|
&port,
|
|
NULL
|
|
);
|
|
if (error != ERROR_SUCCESS) {
|
|
goto quit;
|
|
}
|
|
|
|
if (IsInappropriateGopherPort(port)) {
|
|
error = ERROR_INTERNET_INVALID_URL;
|
|
goto quit;
|
|
}
|
|
|
|
//
|
|
// a '/' between address and url-path is not significant to gopher
|
|
//
|
|
|
|
if (*Url == '/') {
|
|
++Url;
|
|
--urlLength;
|
|
|
|
//
|
|
// the fact that we can ignore the '/' between address and url-path
|
|
// means that it is okay to write a '\0' at the end of the host name
|
|
//
|
|
|
|
pHostName[hostNameLength] = '\0';
|
|
}
|
|
|
|
//
|
|
// if the URL just consisted of gopher://host[:port] then by default we are
|
|
// referencing the root gopher directory
|
|
//
|
|
|
|
if (*Url != '\0') {
|
|
|
|
//
|
|
// Before decoding, convert '?' to tab and thereafter any '+' to ' '
|
|
//
|
|
|
|
LPSTR lpszScan = strchr (Url, '?');
|
|
if (lpszScan)
|
|
{
|
|
*lpszScan++ = '\t';
|
|
while (*lpszScan)
|
|
{
|
|
INET_ASSERT (*lpszScan != '?'); // should be encoded
|
|
if (*lpszScan == '+')
|
|
*lpszScan = ' ';
|
|
lpszScan++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// we need to convert the url-path before checking it for the search
|
|
// string and gopher+ fields because we need to search for '\t' which
|
|
// is currently encoded
|
|
//
|
|
|
|
if(FAILED(UrlUnescapeInPlace(Url, 0))){
|
|
goto quit;
|
|
}
|
|
urlLength = lstrlen(Url);
|
|
|
|
//
|
|
// find the type of the gopher resource; if unknown, treat as a file
|
|
//
|
|
|
|
gopherType = GopherCharToType(Url[0]);
|
|
selector = &Url[1];
|
|
|
|
//
|
|
// urlLength is now the length of the converted selector
|
|
//
|
|
|
|
--urlLength;
|
|
searchString = (LPSTR)memchr((LPVOID)selector, '\t', urlLength);
|
|
if (searchString != NULL) {
|
|
|
|
LPSTR plusString;
|
|
|
|
//
|
|
// zero-terminate the search string, then check if for a gopher+
|
|
// component
|
|
//
|
|
|
|
*searchString++ = '\0';
|
|
plusString = (LPSTR)memchr((LPVOID)searchString,
|
|
'\t',
|
|
urlLength - (DWORD) (searchString - selector)
|
|
);
|
|
if (plusString != NULL) {
|
|
*plusString++ = '\0';
|
|
gopherType |= GOPHER_TYPE_GOPHER_PLUS;
|
|
|
|
//
|
|
// if the URL defines a file then we may have a view type
|
|
//
|
|
|
|
//
|
|
// BUGBUG - need to handle:
|
|
//
|
|
// - alternate file views
|
|
// - attribute requests (?)
|
|
// - ASK forms
|
|
//
|
|
}
|
|
}
|
|
} else {
|
|
gopherType = GOPHER_TYPE_DIRECTORY;
|
|
selector = NULL;
|
|
searchString = NULL;
|
|
}
|
|
|
|
HINTERNET hConnect;
|
|
|
|
//
|
|
// initialize in case of error
|
|
//
|
|
|
|
hConnect = NULL;
|
|
|
|
//
|
|
// get the offline state
|
|
//
|
|
|
|
BOOL bOffline;
|
|
DWORD dwFlags;
|
|
|
|
bOffline = IsOffline();
|
|
if (bOffline || (OpenFlags & INTERNET_FLAG_OFFLINE)) {
|
|
dwFlags = INTERNET_FLAG_OFFLINE;
|
|
} else {
|
|
dwFlags = 0;
|
|
}
|
|
|
|
//
|
|
// try to create a locator from the various parts
|
|
//
|
|
|
|
char locator[MAX_GOPHER_LOCATOR_LENGTH + 1];
|
|
DWORD locatorLength;
|
|
|
|
locatorLength = sizeof(locator);
|
|
if (GopherCreateLocator(pHostName,
|
|
port,
|
|
NULL,
|
|
selector,
|
|
gopherType,
|
|
locator,
|
|
&locatorLength
|
|
)) {
|
|
|
|
//
|
|
// ok, all parts present and correct; open a handle to the gopher
|
|
// resource
|
|
//
|
|
|
|
hConnect = InternetConnect(*hInternet,
|
|
pHostName,
|
|
port,
|
|
NULL, // lpszUserName
|
|
NULL, // lpszPassword
|
|
INTERNET_SERVICE_GOPHER,
|
|
dwFlags,
|
|
|
|
//
|
|
// we are creating a "hidden" handle - don't
|
|
// tell the app about it
|
|
//
|
|
|
|
INTERNET_NO_CALLBACK
|
|
);
|
|
|
|
try_again:
|
|
|
|
if (hConnect != NULL) {
|
|
|
|
HINTERNET handle;
|
|
|
|
if ( hMapped == NULL )
|
|
{
|
|
error = MapHandleToAddress(hConnect, (LPVOID *)&hMapped, FALSE);
|
|
|
|
if ( (error != ERROR_SUCCESS) && (hMapped == NULL) )
|
|
{
|
|
goto error_quit;
|
|
}
|
|
|
|
error = ERROR_SUCCESS;
|
|
}
|
|
|
|
INET_ASSERT(hMapped != NULL);
|
|
|
|
((INTERNET_CONNECT_HANDLE_OBJECT *)hMapped)->SetURL(lpszUrl);
|
|
|
|
if ( IS_GOPHER_DIRECTORY(gopherType)
|
|
|| IS_GOPHER_SEARCH_SERVER(gopherType)) {
|
|
|
|
// set htmlfind only if RAW is not asked
|
|
|
|
if (!(OpenFlags & INTERNET_FLAG_RAW_DATA)) {
|
|
|
|
((INTERNET_CONNECT_HANDLE_OBJECT *)hMapped)->SetHtmlFind(TRUE);
|
|
|
|
//
|
|
// BUGBUG: we don't have time to handle CSO searches
|
|
//
|
|
if (IS_GOPHER_PHONE_SERVER (gopherType))
|
|
goto cso_hack;
|
|
|
|
if ( IS_GOPHER_SEARCH_SERVER(gopherType)
|
|
&& (!searchString || !searchString[0])) {
|
|
|
|
cso_hack:
|
|
handle = NULL;
|
|
|
|
if (ERROR_SUCCESS == RMakeGfrFixedObjectHandle
|
|
(hMapped, &handle, gopherType)) {
|
|
handle = ((HANDLE_OBJECT *)handle)->GetPseudoHandle();
|
|
}
|
|
|
|
DereferenceObject((LPVOID)hMapped);
|
|
goto got_handle;
|
|
}
|
|
|
|
}
|
|
|
|
handle = GopherFindFirstFile(hConnect,
|
|
locator,
|
|
searchString,
|
|
NULL,
|
|
OpenFlags | dwFlags,
|
|
Context
|
|
);
|
|
|
|
|
|
} else {
|
|
|
|
handle = GopherOpenFile(hConnect,
|
|
locator,
|
|
NULL,
|
|
OpenFlags | dwFlags,
|
|
Context
|
|
);
|
|
}
|
|
|
|
got_handle:
|
|
|
|
if (handle != NULL) {
|
|
|
|
//
|
|
// map the handles
|
|
//
|
|
|
|
HINTERNET hRequestMapped;
|
|
error = MapHandleToAddress(handle, (LPVOID *)&hRequestMapped, FALSE);
|
|
INET_ASSERT(error == ERROR_SUCCESS);
|
|
|
|
HINTERNET hConnectMapped;
|
|
error = MapHandleToAddress(hConnect, (LPVOID *)&hConnectMapped, FALSE);
|
|
INET_ASSERT(error == ERROR_SUCCESS);
|
|
|
|
//
|
|
// link the request and connect handles so that the connect handle
|
|
// object will be deleted when the request handle is closed
|
|
//
|
|
|
|
RSetParentHandle(hRequestMapped, hConnectMapped, TRUE);
|
|
|
|
//
|
|
// reduce the reference counts incremented by MapHandleToAddress()
|
|
//
|
|
|
|
if (hRequestMapped != NULL) {
|
|
DereferenceObject((LPVOID)hRequestMapped);
|
|
}
|
|
if (hConnectMapped != NULL) {
|
|
DereferenceObject((LPVOID)hConnectMapped);
|
|
}
|
|
|
|
//
|
|
// return the request handle to the caller
|
|
//
|
|
|
|
*hInternet = handle;
|
|
|
|
error = ERROR_SUCCESS;
|
|
goto quit;
|
|
} else if (!bOffline && IsOffline() && !(dwFlags & INTERNET_FLAG_OFFLINE)) {
|
|
|
|
//
|
|
// we went offline during the request. Try again, this time
|
|
// from cache
|
|
//
|
|
|
|
dwFlags = INTERNET_FLAG_OFFLINE;
|
|
goto try_again;
|
|
}
|
|
}
|
|
}
|
|
|
|
error_quit:
|
|
|
|
if ( hMapped != NULL )
|
|
{
|
|
DereferenceObject((LPVOID)hMapped);
|
|
hMapped = NULL;
|
|
}
|
|
|
|
|
|
error = GetLastError();
|
|
if (hConnect != NULL) {
|
|
|
|
//
|
|
// BUGBUG - this should close the item handle also (if open)
|
|
//
|
|
|
|
_InternetCloseHandle(hConnect);
|
|
}
|
|
|
|
INET_ASSERT(error != ERROR_SUCCESS);
|
|
|
|
quit:
|
|
|
|
|
|
DEBUG_LEAVE(error);
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
GopherLocatorToUrl(
|
|
IN LPSTR Locator,
|
|
OUT LPSTR Buffer,
|
|
IN DWORD BufferLength,
|
|
OUT LPDWORD UrlLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts a gopher locator to a gopher URL. E.g. converts:
|
|
|
|
1foo\tFoo Directory\tfoo.host\t77\t+
|
|
|
|
to the URL:
|
|
|
|
gopher://foo.host:77/1Foo%20Directory%09%09%2B
|
|
|
|
Arguments:
|
|
|
|
Locator - pointer to gopher locator to convert
|
|
|
|
Buffer - pointer to buffer where URL is written
|
|
|
|
BufferLength - size of Buffer in bytes
|
|
|
|
UrlLength - number of bytes written to Buffer
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INTERNET_INTERNAL_ERROR
|
|
We blew an internal buffer limit
|
|
|
|
ERROR_INSUFFICIENT_BUFFER
|
|
Buffer is not large enough to hold the converted URL
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD gopherType;
|
|
char selector[MAX_GOPHER_SELECTOR_TEXT + 1];
|
|
DWORD selectorLength;
|
|
char hostName[MAX_GOPHER_HOST_NAME + 1];
|
|
DWORD hostNameLength;
|
|
DWORD gopherPort;
|
|
LPSTR gopherPlus;
|
|
char urlBuf[INTERNET_MAX_URL_LENGTH];
|
|
DWORD urlBufferLength;
|
|
LPSTR urlBuffer;
|
|
DWORD error;
|
|
DWORD bufLen;
|
|
|
|
urlBufferLength = sizeof(urlBuf);
|
|
urlBuffer = urlBuf;
|
|
bufLen = BufferLength;
|
|
|
|
//
|
|
// start with the gopher protocol specifier
|
|
//
|
|
|
|
if (bufLen > sizeof("gopher://")) {
|
|
memcpy(Buffer, "gopher://", sizeof("gopher://") - 1);
|
|
Buffer += sizeof("gopher://") - 1;
|
|
bufLen -= sizeof("gopher://") - 1;
|
|
} else {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
//
|
|
// use CrackLocator() to get the individual parts of the locator
|
|
//
|
|
|
|
selectorLength = sizeof(selector);
|
|
hostNameLength = sizeof(hostName);
|
|
if (!CrackLocator(Locator,
|
|
&gopherType,
|
|
NULL, // DisplayString - we don't care about this in the URL
|
|
NULL, // DisplayStringLength
|
|
selector,
|
|
&selectorLength,
|
|
hostName,
|
|
&hostNameLength,
|
|
&gopherPort,
|
|
&gopherPlus
|
|
)) {
|
|
|
|
//
|
|
// most likely we bust a limit!
|
|
//
|
|
|
|
return ERROR_INTERNET_INTERNAL_ERROR;
|
|
}
|
|
|
|
//
|
|
// add in the host name
|
|
//
|
|
|
|
if (bufLen > hostNameLength) {
|
|
memcpy(Buffer, hostName, hostNameLength);
|
|
Buffer += hostNameLength;
|
|
bufLen -= hostNameLength;
|
|
} else {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
//
|
|
// add the port, but only if it is not the default (70)
|
|
//
|
|
|
|
if (gopherPort != INTERNET_DEFAULT_GOPHER_PORT) {
|
|
if (bufLen > 1 + INTERNET_MAX_PORT_NUMBER_LENGTH) {
|
|
|
|
int n;
|
|
|
|
n = wsprintf(Buffer, ":%u", gopherPort);
|
|
Buffer += n;
|
|
bufLen -= (DWORD)n;
|
|
} else {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
|
|
//
|
|
// add the URL-path separator and the locator type character
|
|
//
|
|
|
|
if (bufLen > 2) {
|
|
*Buffer++ = '/';
|
|
*Buffer++ = *Locator;
|
|
bufLen -= 2;
|
|
}
|
|
|
|
//
|
|
// copy the selector string, and any gopher+ addenda to a separater buffer
|
|
//
|
|
|
|
if (urlBufferLength > selectorLength) {
|
|
memcpy(urlBuffer, selector, selectorLength);
|
|
urlBuffer += selectorLength;
|
|
urlBufferLength -= selectorLength;
|
|
}
|
|
|
|
//
|
|
// if the locator specifies a gopher+ item then add the gopher+ indicator
|
|
//
|
|
|
|
if (gopherPlus != NULL) {
|
|
if (urlBufferLength > 3) {
|
|
memcpy(urlBuffer, "\t\t+", 3);
|
|
urlBufferLength -= 3;
|
|
urlBuffer += 3;
|
|
}
|
|
}
|
|
|
|
//
|
|
// finally terminate the URL
|
|
//
|
|
|
|
if (urlBufferLength >= 1) {
|
|
*urlBuffer++ = '\0';
|
|
--urlBufferLength;
|
|
} else {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
//
|
|
// now escape any special characters (e.g. space, tab, etc.) in the url-path
|
|
//
|
|
|
|
*UrlLength = bufLen;
|
|
|
|
error = EncodeUrlPath(NO_ENCODE_PATH_SEP,
|
|
SCHEME_GOPHER,
|
|
urlBuf,
|
|
sizeof(urlBuf) - urlBufferLength - 1,
|
|
Buffer,
|
|
UrlLength
|
|
);
|
|
if (error == ERROR_SUCCESS) {
|
|
*UrlLength += BufferLength - bufLen;
|
|
}
|
|
return error;
|
|
}
|