mirror of https://github.com/lianthony/NT4.0
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.
576 lines
14 KiB
576 lines
14 KiB
/*
|
|
This file was derived from the libwww code, version 2.15, from CERN.
|
|
A number of modifications have been made by Spyglass.
|
|
|
|
[email protected]
|
|
*/
|
|
|
|
#include "all.h"
|
|
#include <shellapi.h>
|
|
#include "mime.h"
|
|
#ifdef FEATURE_INTL
|
|
#define IEXPLORE
|
|
#include <fechrcnv.h>
|
|
#endif
|
|
#include "history.h"
|
|
|
|
struct _HTStructured
|
|
{
|
|
CONST HTStructuredClass *isa;
|
|
/* ... */
|
|
};
|
|
|
|
struct _HTStream
|
|
{
|
|
CONST HTStreamClass *isa;
|
|
/* ... */
|
|
};
|
|
|
|
/* Controlling globals
|
|
**
|
|
*/
|
|
|
|
PUBLIC HTFormat HTFileFormat(CONST char *filename, PENCODING pencoding, HTAtom *planguage)
|
|
{
|
|
char *p;
|
|
CONST char *pslash;
|
|
|
|
if (planguage)
|
|
*planguage = 0; /* note that this isn't supported at all yet */
|
|
|
|
pslash = strrchr(filename, '/');
|
|
if (pslash)
|
|
pslash++;
|
|
/* the filename passed in was a URL. pslash now points to the basename */
|
|
else
|
|
pslash = filename;
|
|
|
|
p = strrchr(pslash, '.');
|
|
if (p)
|
|
{
|
|
HTAtom atomMIMEType;
|
|
|
|
XX_DMsg(DBG_WWW, ("HTFileFormat: Looking for extension %s... ", p));
|
|
|
|
if (MIME_GetMIMEAtomFromExtension(pslash, &atomMIMEType))
|
|
{
|
|
XX_DMsg(DBG_WWW, ("found! MIME=%s\n", HTAtom_name(atomMIMEType)));
|
|
|
|
if (pencoding)
|
|
*pencoding = MIME_GetEncoding(atomMIMEType);
|
|
|
|
return atomMIMEType;
|
|
}
|
|
|
|
XX_DMsg(DBG_WWW, ("Suffix not found.\n"));
|
|
if (pencoding)
|
|
*pencoding = ENCODING_BINARY;
|
|
return HTAtom_for("application/octet-stream");
|
|
}
|
|
else
|
|
{
|
|
if (pencoding)
|
|
*pencoding = 0;
|
|
return HTAtom_for("text/plain");
|
|
}
|
|
}
|
|
|
|
/* Convert filenames between local and WWW formats
|
|
** -----------------------------------------------
|
|
** Make up a suitable name for saving the node in
|
|
**
|
|
** E.g. $(HOME)/WWW/news/[email protected]
|
|
** $(HOME)/WWW/http/crnvmc/FIND/xx.xxx.xx
|
|
**
|
|
** On exit,
|
|
** returns a GTR_MALLOC'ed string which must be freed by the caller.
|
|
*/
|
|
PUBLIC char *HTLocalName(CONST char *name)
|
|
{
|
|
char *host = HTParse(name, "", PARSE_HOST);
|
|
int iLoc;
|
|
#ifdef WIN32
|
|
int iSlash;
|
|
#endif
|
|
|
|
if (!host || !*host || (0 == strcasecomp(host, HTHostName())) ||
|
|
(0 == strcasecomp(host, "localhost")))
|
|
{
|
|
char *path;
|
|
|
|
if (host)
|
|
GTR_FREE(host);
|
|
|
|
path = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
|
|
|
|
#ifdef MAC
|
|
#define SLASH ':'
|
|
/* Convert slashes to colons before we unescape */
|
|
for (iLoc = 0; path[iLoc]; iLoc++)
|
|
{
|
|
if (path[iLoc] == '/')
|
|
path[iLoc] = SLASH;
|
|
}
|
|
#endif
|
|
|
|
HTUnEscape(path); /* Interpret % signs */
|
|
|
|
/*
|
|
BEGIN NCSA Copy-Paste
|
|
*/
|
|
#ifdef WIN32
|
|
if ( (path[0] == '/') && (strchr(path, ':') || strchr(path, '|')) )
|
|
{
|
|
char *shuffle = path; // We have drive spec - strip leading slash
|
|
|
|
while (*shuffle && (*(shuffle + 1)))
|
|
{
|
|
*shuffle = *(shuffle + 1);
|
|
shuffle++;
|
|
}
|
|
*shuffle = 0;
|
|
}
|
|
#endif
|
|
#ifdef WIN32
|
|
#define SLASH '\\'
|
|
iSlash = 0;
|
|
for (iLoc = 0; iLoc < strlen(path); iLoc++)
|
|
{
|
|
if (path[iLoc] == '|')
|
|
path[iLoc] = ':';
|
|
|
|
else if (path[iLoc] == '/')
|
|
{
|
|
path[iLoc] = SLASH;
|
|
iSlash++;
|
|
}
|
|
else if (path[iLoc] == SLASH)
|
|
iSlash++;
|
|
}
|
|
if ((path[strlen(path) - 1] == SLASH) && (iSlash > 1))
|
|
path[strlen(path) - 1] = 0;
|
|
#endif
|
|
|
|
#ifdef MAC
|
|
/* Remove initial colon. Note that this could cause a problem with a
|
|
relative path name, if a person were so foolish as to enter one. */
|
|
if (path[0] == ':')
|
|
memmove(path, path + 1, strlen(path));
|
|
#endif
|
|
/*
|
|
END NCSA Copy-Paste
|
|
*/
|
|
XX_DMsg(DBG_WWW, ("Node `%s' means path `%s'\n", name, path));
|
|
return (path);
|
|
}
|
|
else
|
|
{
|
|
GTR_FREE(host);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Adjust local file name -- if it's relative to the document
|
|
// it's embedded in (and that doc is a 'file:' itself),
|
|
// adjust it to be an absolute reference
|
|
//
|
|
// Note: this may return a newly allocate a new string and free the old one
|
|
// so the input string must have been malloc'ed
|
|
//
|
|
static char *AdjustLocalNameIfRelative( struct Mwin *tw, char *localname )
|
|
{
|
|
char *retval = localname;
|
|
|
|
if ( tw && tw->w3doc && tw->w3doc->szActualURL )
|
|
{
|
|
char *p = tw->w3doc->szActualURL;
|
|
|
|
if ( _strnicmp( p, "file:", 5 ) == 0 )
|
|
{
|
|
char *s;
|
|
|
|
p += 5; // move past 'file:'
|
|
if ( s = strrchr( p, '\\' ) )
|
|
{
|
|
s++; // move past the last slash
|
|
if ( localname[0] && localname[0] != '\\' && localname[1] != ':' ) // is it relative?
|
|
{
|
|
char *newlocalname = (char *) GTR_MALLOC( (s-p) + strlen(localname) + 1);
|
|
|
|
if ( newlocalname )
|
|
{
|
|
//
|
|
// Build absolute local name
|
|
//
|
|
strncpy( newlocalname, p, (s-p) );
|
|
strcpy( newlocalname + (s-p), localname );
|
|
GTR_FREE( localname );
|
|
|
|
retval = newlocalname;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
struct Data_LoadFile {
|
|
HTRequest * request;
|
|
int * pStatus; /* Where to store the status return */
|
|
FILE * fp;
|
|
HTStream * stream;
|
|
BOOL ref_filename_accepted;
|
|
};
|
|
|
|
#define STATE_FILE_STREAMINIT (STATE_OTHER + 1)
|
|
#define STATE_FILE_COPYING (STATE_OTHER + 2)
|
|
|
|
/* This is a pretty pathetic async version - once it starts copying it
|
|
just keeps going. */
|
|
PRIVATE int HTLoadFile_Async(struct Mwin *tw, int nState, void **ppInfo)
|
|
{
|
|
CONST char *addr;
|
|
char *filename;
|
|
char *access;
|
|
HTFormat format;
|
|
char *newname = 0; /* Simplified name of file */
|
|
ENCODING encoding;
|
|
HTAtom language;
|
|
char *localname;
|
|
struct Params_LoadAsync *pParams;
|
|
struct Data_LoadFile *pData;
|
|
|
|
pData = *ppInfo;
|
|
switch (nState)
|
|
{
|
|
case STATE_INIT:
|
|
pParams = *ppInfo;
|
|
pData = GTR_MALLOC(sizeof(*pData));
|
|
memset(pData, 0, sizeof(*pData));
|
|
pData->request = HTRequest_validate(pParams->request);
|
|
pData->pStatus = pParams->pStatus;
|
|
pData->ref_filename_accepted = FALSE;
|
|
*ppInfo = pData;
|
|
GTR_FREE(pParams);
|
|
|
|
if (!pData->request)
|
|
{
|
|
*pData->pStatus = HT_INTERNAL;
|
|
return STATE_DONE;
|
|
}
|
|
|
|
pData->request->content_length = 0;
|
|
|
|
/* Reduce the filename to a basic form (hopefully unique!)
|
|
*/
|
|
addr = pData->request->destination->szActualURL;
|
|
newname = GTR_strdup(addr);
|
|
filename = HTParse(newname, "", PARSE_PATH | PARSE_PUNCTUATION);
|
|
|
|
/* If access is ftp => go directly to ftp code (henrik 27/02-94) */
|
|
access = HTParse(newname, "", PARSE_ACCESS);
|
|
if (!strcmp("ftp", access))
|
|
{
|
|
if (newname)
|
|
{
|
|
GTR_FREE(newname);
|
|
newname = NULL;
|
|
}
|
|
if (access)
|
|
{
|
|
GTR_FREE(access);
|
|
access = NULL;
|
|
}
|
|
if (filename)
|
|
{
|
|
GTR_FREE(filename); /* Not used anymore */
|
|
filename = NULL;
|
|
}
|
|
goto try_ftp;
|
|
}
|
|
else
|
|
{
|
|
if (newname)
|
|
{
|
|
GTR_FREE(newname);
|
|
newname = NULL;
|
|
}
|
|
if (access)
|
|
{
|
|
GTR_FREE(access);
|
|
access = NULL;
|
|
}
|
|
}
|
|
|
|
format = HTFileFormat(filename, &encoding, &language);
|
|
pData->request->content_type = format;
|
|
pData->request->content_encoding = encoding;
|
|
pData->request->content_language = language;
|
|
pData->request->szLocalFileName = NULL;
|
|
|
|
if (filename)
|
|
{
|
|
GTR_FREE(filename);
|
|
filename = NULL;
|
|
}
|
|
|
|
localname = HTLocalName(addr);
|
|
|
|
|
|
/* Need protection here for telnet server but not httpd server */
|
|
if (localname)
|
|
{ /* try local file system */
|
|
localname = AdjustLocalNameIfRelative( tw, localname );
|
|
|
|
//
|
|
// Now adjust the doc's ActualURL to an absolute form so that
|
|
// subsequent relative paths will work
|
|
//
|
|
{
|
|
char *filename_part;
|
|
char *source_to_use = localname;
|
|
char temp_fpn[MAX_PATH+1];
|
|
|
|
//
|
|
// First, get canonical name (removes embedded ..'s)
|
|
//
|
|
if (GetFullPathName(localname, MAX_PATH + 1, temp_fpn,
|
|
&filename_part))
|
|
source_to_use = temp_fpn;
|
|
|
|
GTR_FREE(pData->request->destination->szActualURL);
|
|
pData->request->destination->szActualURL =
|
|
(char *) GTR_MALLOC( 5 + strlen(source_to_use) + 1 );
|
|
strcpy( pData->request->destination->szActualURL, "file:" );
|
|
strcat( pData->request->destination->szActualURL, source_to_use );
|
|
pData->request->szLocalFileName = GTR_strdup(localname);
|
|
}
|
|
|
|
pData->fp = fopen(localname, "rb");
|
|
|
|
|
|
if (pData->fp)
|
|
{ /* Good! */
|
|
if (0 == fseek(pData->fp, 0, SEEK_END))
|
|
{
|
|
pData->request->content_length = ftell(pData->fp);
|
|
if (pData->request->content_length < 0)
|
|
pData->request->content_length = 0;
|
|
fseek(pData->fp, 0, SEEK_SET);
|
|
}
|
|
|
|
// SNIFF the data, to see if we think we can second guess the file type
|
|
{
|
|
char input_buffer[1024];
|
|
int bytes = pData->request->content_length;
|
|
BOOL bEOF = TRUE;
|
|
|
|
if (bytes > 1024)
|
|
{
|
|
bytes = 1024;
|
|
bEOF = FALSE;
|
|
}
|
|
bytes = fread(input_buffer, 1, bytes, pData->fp);
|
|
if (bytes > 0)
|
|
{
|
|
HTRecognizeMimeData(input_buffer,
|
|
bytes,
|
|
&format,
|
|
pData->request,
|
|
bEOF);
|
|
}
|
|
fseek(pData->fp, 0, SEEK_SET);
|
|
}
|
|
|
|
|
|
pData->stream = HTStreamStack(tw, format, pData->request);
|
|
if (!pData->stream)
|
|
{
|
|
if ( pData->request->iFlags & HTREQ_NULL_STREAM_IS_OK )
|
|
{
|
|
pData->request->iFlags &= ~HTREQ_NULL_STREAM_IS_OK;
|
|
*pData->pStatus = HT_LOADED;
|
|
}
|
|
else
|
|
*pData->pStatus = -501;
|
|
|
|
GTR_FREE(localname);
|
|
return STATE_DONE;
|
|
}
|
|
else
|
|
{
|
|
/* Ignore CRLF if necessary
|
|
*/
|
|
if (! (pData->request->iFlags & HTREQ_BINARY) &&
|
|
(pData->request->content_encoding == ENCODING_7BIT ||
|
|
pData->request->content_encoding == ENCODING_8BIT))
|
|
pData->stream = HTNetToText(pData->stream);
|
|
|
|
HTSetStreamStatus(tw, pData->stream, pData->request);
|
|
|
|
if (pData->stream->isa->io_control)
|
|
pData->ref_filename_accepted =
|
|
(*pData->stream->isa->io_control)(
|
|
pData->stream,
|
|
HTS_IOCTL_FILE_BY_REF,
|
|
localname);
|
|
GTR_FREE(localname);
|
|
|
|
if (pData->stream->isa->init_Async)
|
|
{
|
|
/* The stream has an async initialization function that needs to be called
|
|
(probably to put up a dialog) before we continue. */
|
|
struct Params_InitStream *pis;
|
|
|
|
pis = GTR_MALLOC(sizeof(*pis));
|
|
pis->me = pData->stream;
|
|
pis->request = pData->request;
|
|
pis->pResult = pData->pStatus;
|
|
pis->fDCache = FALSE; //Don't cache to disk
|
|
Async_DoCall(pData->stream->isa->init_Async, pis);
|
|
return STATE_FILE_STREAMINIT;
|
|
}
|
|
else
|
|
return STATE_FILE_COPYING;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If we can't open it, it either doesn't exist, or is a
|
|
// directory, or is opened exclusive by someone else.
|
|
//
|
|
if ( FExistsDir(localname, FALSE, FALSE) ) {
|
|
// It's a directory, so let's ShellExecute it...
|
|
HINSTANCE shellex_result
|
|
= ShellExecute( NULL, NULL, localname, NULL, NULL, SW_SHOW );
|
|
|
|
if ( (UINT) shellex_result > 32 )
|
|
{
|
|
*pData->pStatus = HT_LOADED;
|
|
GTR_FREE(localname);
|
|
return STATE_DONE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
try_ftp:
|
|
/* Now, as transparently mounted access has failed, we try FTP.
|
|
*/
|
|
{
|
|
char *newname = NULL;
|
|
char *myhost;
|
|
BOOL bTryFTP;
|
|
|
|
bTryFTP = FALSE;
|
|
myhost = HTParse(addr, "", PARSE_HOST);
|
|
if (myhost && *myhost)
|
|
{
|
|
bTryFTP = TRUE;
|
|
}
|
|
if (myhost)
|
|
{
|
|
GTR_FREE(myhost);
|
|
}
|
|
|
|
if (bTryFTP)
|
|
{
|
|
newname = GTR_MALLOC(strlen(addr));
|
|
strcpy(newname, "ftp:");
|
|
strcat(newname, addr + 5);
|
|
Dest_UpdateActual(pData->request->destination, newname,FALSE);
|
|
GTR_FREE(newname);
|
|
*pData->pStatus = HT_REDIRECTION_ON_FLY;
|
|
return STATE_DONE;
|
|
}
|
|
}
|
|
|
|
/* All attempts have failed.
|
|
*/
|
|
*pData->pStatus = -403;
|
|
// <CMF> I don't think this should be reported here
|
|
// ERR_ReportError(tw, errFileURLNotFound, pData->request->destination->szActualURL, "");
|
|
return STATE_DONE;
|
|
|
|
case STATE_FILE_STREAMINIT:
|
|
if (*pData->pStatus < 0)
|
|
{
|
|
(*pData->stream->isa->abort)(pData->stream, 0);
|
|
return STATE_DONE;
|
|
}
|
|
/* else fall through to STATE_FILE_COPYING */
|
|
case STATE_FILE_COPYING:
|
|
{
|
|
int bytes = 0;
|
|
int status = 0;
|
|
char input_buffer[INPUT_BUFFER_SIZE];
|
|
#ifdef FEATURE_INTL
|
|
// BUGBUG:I have to revisit this to make sure
|
|
//every case is covered here.
|
|
// _BUGBUG Perf: should load fechrcnv.dll on demand.
|
|
|
|
if ((pData->request != NULL && pData->request->iMimeCharSet != -1 && aMimeCharSet[pData->request->iMimeCharSet].iChrCnv)
|
|
|| (tw != NULL && aMimeCharSet[tw->iMimeCharSet].iChrCnv))
|
|
FCC_Init();
|
|
#endif
|
|
|
|
if ( (pData->request->iFlags & HTREQ_DOING_SAVE_FILE)
|
|
|| !pData->ref_filename_accepted )
|
|
{
|
|
while (1)
|
|
{
|
|
status = fread(input_buffer, 1, INPUT_BUFFER_SIZE, pData->fp);
|
|
if (status == 0)
|
|
{ /* EOF or error */
|
|
if (ferror(pData->fp) != 0)
|
|
status = -1;
|
|
break;
|
|
}
|
|
bytes += status;
|
|
if (pData->request->content_length)
|
|
WAIT_SetTherm(tw, bytes);
|
|
(*pData->stream->isa->put_block)(pData->stream, input_buffer, status, FALSE);
|
|
}
|
|
}
|
|
fclose(pData->fp);
|
|
pData->fp = NULL;
|
|
if (status < 0)
|
|
{
|
|
(*pData->stream->isa->abort)(pData->stream, 0);
|
|
pData->stream = NULL;
|
|
*pData->pStatus = -1;
|
|
}
|
|
else
|
|
{
|
|
DCACHETIME dct={0,0};
|
|
|
|
(*pData->stream->isa->free)(pData->stream, dct, dct);
|
|
pData->stream = NULL;
|
|
*pData->pStatus = HT_LOADED;
|
|
}
|
|
return STATE_DONE;
|
|
}
|
|
|
|
case STATE_ABORT:
|
|
pData->request = HTRequest_validate(pData->request);
|
|
if (pData->stream)
|
|
{
|
|
(*pData->stream->isa->abort)(pData->stream, HTERROR_CANCELLED);
|
|
}
|
|
if (pData->fp)
|
|
{
|
|
fclose(pData->fp);
|
|
}
|
|
*pData->pStatus = -1;
|
|
return STATE_DONE;
|
|
}
|
|
XX_Assert((0), ("Function called with illegal state: %d", nState));
|
|
return STATE_DONE;
|
|
}
|
|
|
|
/* Protocol descriptors
|
|
*/
|
|
GLOBALDEF PUBLIC HTProtocol HTFile ={"file", NULL, HTLoadFile_Async};
|