1066 lines
29 KiB
C++
1066 lines
29 KiB
C++
/*++
|
||
|
||
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;
|
||
}
|