Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

1983 lines
52 KiB

/*
Enhanced NCSA Mosaic from Spyglass
"Guitar"
Copyright 1994 Spyglass, Inc.
All Rights Reserved
Author(s):
Jim Seidman [email protected]
Portions of this code were derived from
CERN's libwww version 2.15.
*/
#include "all.h"
#include "history.h"
#define REPEAT_PORT /* Give the port number for each file */
#define REPEAT_LISTEN /* Close each listen socket and open a new one */
#define LINE_LENGTH 256
#define COMMAND_LENGTH 256
#ifndef IPPORT_FTP
#define IPPORT_FTP 21
#endif
#define PUTS(s) (*target->isa->put_string)(target, s)
#define START(e) (*target->isa->start_element)(target, e, 0, 0)
#define END(e) (*target->isa->end_element)(target, e)
struct _HTStructured
{
CONST HTStructuredClass *isa;
/* ... */
};
static int FTP_ReadDir_Async(struct Mwin *tw, int nState, void **ppInfo);
void FTP_DisposeFTPConnection(struct _CachedConn *pCon)
{
XX_Assert((pCon->type == CONN_FTP), ("FTP_DisposeFTPConnection: connection type is %d!", pCon->type));
XX_Assert((pCon->addr != 0), ("FTP_DisposeFTPConnection: connection has no address!"));
pCon->addr = 0;
Net_Close(pCon->socket);
pCon->socket = -1;
pCon->type = CONN_NONE;
}
/******************************************************************/
/* Execute Command and get Response
** --------------------------------
**
** See the state machine illustrated in RFC959, p57. This implements
** one command/reply sequence. It also interprets lines which are to
** be continued, which are marked with a "-" immediately after the
** status code.
**
** Continuation then goes on until a line with a matching reply code
** an a space after it.
**
** On entry,
** cmd points to a command, or is NIL to just get the response.
** The command is terminated with the CRLF pair.
**
** On exit,
** returns: The first digit of the reply type,
** or negative for communication failure.
*/
struct Params_FTP_Command {
HTInputSocket * isoc;
char * cmd; /* Command to send - will be freed! */
int * pResult; /* Place to store response */
char ** ppText; /* Returns pointer to response text which must be
freed. If ppText == NULL, text isn't saved. */
/* Used internally */
int cont_resp; /* Continuation response */
int net_status; /* Network operation result */
HTChunk *pText;
int fWantFullText; /* 1 if we want a full message, 0 if we want it parsed*/
int iOffset;
BOOL bIsDash;
};
#define STATE_COMMAND_SENT (STATE_OTHER)
#define STATE_COMMAND_GOTDATA (STATE_OTHER+1)
static int FTP_Command_Async(struct Mwin *tw, int nState, void **ppInfo)
{
struct Params_FTP_Command *pParams;
char ch;
BOOL bFirstTimeInLoop;
pParams = *ppInfo;
switch (nState)
{
case STATE_INIT:
pParams->cont_resp = -1;
pParams->pText = HTChunkCreate(256);
pParams->iOffset = 0;
pParams->bIsDash = FALSE;
if ( pParams->pText == NULL )
return STATE_ABORT;
HTChunkClear(pParams->pText);
/* Send command */
if (pParams->cmd)
{
struct Params_Send *pps;
pps = GTR_CALLOC(sizeof(*pps),1);
pps->socket = pParams->isoc->input_file_number;
pps->pBuf = pParams->cmd;
pps->nBufLen = strlen(pParams->cmd);
pps->pStatus = &pParams->net_status;
Async_DoCall(Net_Send_Async, pps);
return STATE_COMMAND_SENT;
}
/* Otherwise we're just reading response.
Fall through */
case STATE_COMMAND_SENT:
if (pParams->cmd)
{
GTR_FREE(pParams->cmd);
pParams->cmd = NULL;
if (pParams->net_status < 0)
{
*pParams->pResult = -1;
return STATE_DONE;
}
}
pParams->net_status = 0;
/* fall through */
case STATE_COMMAND_GOTDATA:
if (pParams->net_status < 0)
{
*pParams->pResult = -1;
return STATE_DONE;
}
ch = 0;
bFirstTimeInLoop = TRUE;
do
{
BOOL bFirstTimeInInputLoop;
// if we don't want a full text str then,
// we clear the index so loops around
//
// we need to know that this is not the
// first time through the loop because we could
// exit, and then re-enter this loop
if ( ! bFirstTimeInLoop )
{
//initilze us as having no dash '-' in the cur line
pParams->bIsDash = FALSE;
if ( ! pParams->fWantFullText )
HTChunkClear(pParams->pText);
}
bFirstTimeInLoop = FALSE;
// init bFirstTime.. so we don't parse the third char
// on the line twice
bFirstTimeInInputLoop = TRUE;
for ( ; pParams->isoc->input_pointer < pParams->isoc->input_limit; pParams->isoc->input_pointer++)
{
ch = *pParams->isoc->input_pointer;
if (ch == CR)
continue;
else if (ch == LF)
break;
else if ( pParams->pText->size == pParams->iOffset+3 && bFirstTimeInInputLoop)
{
// PUT THE DASH AWAY, if there is one
HTChunkPutc(pParams->pText, ch);
// temp terminate it
pParams->pText->data[pParams->pText->size] = '\0';
// now get the result value
*pParams->pResult = atoi(&pParams->pText->data[pParams->iOffset]);
// mark this as the first time, to prevent doing
// this twice since we slide the size back
// could re-enter on the first condition
bFirstTimeInInputLoop = FALSE;
// if we want the full text
// parse off the 220- or if there is no dash
// we want to break since we don't want the last
// line ( ie the line without the dash )
// BUT IF .. there is no number on the line
// we don't parse it off since it may still be valid
if ( pParams->fWantFullText && *pParams->pResult != 0 )
{
pParams->pText->size = pParams->iOffset;
}
// if the result is 0 then there was no text
// in the atoi command, this means we've hit
// something like ftp.microsoft.com where there
// are not 220- on each comment line
if ( ch == '-' || *pParams->pResult == 0)
{
pParams->bIsDash = TRUE;
}
else if ( pParams->fWantFullText )
{
// we break since we don't want this
ch = LF;
pParams->bIsDash = FALSE;
// make us stop our loop
pParams->cont_resp = -1;
// munch (eat) the rest of the text to prevent
// us from us getting called back with it
pParams->isoc->input_pointer = pParams->isoc->input_limit-1;
break;
}
}
else
{
HTChunkPutc(pParams->pText, ch);
}
}
/* Step past the character we just read in the isoc */
pParams->isoc->input_pointer++;
if ( ch == LF )
{
/* Terminate this line of stuff */
if ( pParams->fWantFullText )
{
HTChunkPutc(pParams->pText, '\r');
HTChunkPutc(pParams->pText, '\n');
}
// temp terminate, then remove it
pParams->pText->data[pParams->pText->size] = '\0';
// store off the offset of where we start our loop
pParams->iOffset = pParams->pText->size;
}
/* If we didn't quit the loop because of finding an LF, get more */
// if we found a dash on the begining of a line, it means
// that we found a line that continues, that means we need
// to grab more data
if (ch != LF || ( pParams->bIsDash &&
(pParams->isoc->input_pointer >= pParams->isoc->input_limit ) ) )
{
struct Params_Isoc_Fill *pif;
pif = GTR_MALLOC(sizeof(*pif));
pif->isoc = pParams->isoc;
pif->pStatus = &pParams->net_status;
Async_DoCall(Isoc_Fill_Async, pif);
return STATE_COMMAND_GOTDATA;
}
XX_DMsg(DBG_LOAD, ("FTP: Other side sent %s\n", &pParams->pText->data[pParams->iOffset]));
if (pParams->cont_resp == -1)
{
if (pParams->bIsDash)
{
/* Start continuation */
pParams->cont_resp = *pParams->pResult;
}
}
else
{
/* Continuing */
if (pParams->cont_resp == *pParams->pResult && ch == ' ')
{
/* End of continuation */
pParams->cont_resp = -1;
}
}
} while (pParams->cont_resp != -1 &&
// confirm that we haven't looped past the end of the text
// given to us... if we have then don't party on...
pParams->isoc->input_pointer < pParams->isoc->input_limit);
if (pParams->ppText)
{
*pParams->ppText = GTR_MALLOC(pParams->pText->size + 1);
strcpy(*pParams->ppText, pParams->pText->data);
}
HTChunkFree(pParams->pText);
if (*pParams->pResult == 421)
{
/* They closed the socket on us. */
XX_DMsg(DBG_LOAD, ("FTP: 421 response\n"));
*pParams->pResult = -1;
}
else
{
*pParams->pResult /= 100;
}
return STATE_DONE;
case STATE_ABORT:
if (pParams->cmd)
{
GTR_FREE(pParams->cmd);
}
if ( pParams->pText )
HTChunkFree(pParams->pText);
*pParams->pResult = -1;
return STATE_DONE;
}
XX_Assert((0), ("Function called with illegal state: %d", nState));
return STATE_DONE;
}
/*************************************************************/
/* Start anchor element
** --------------------
**
** It is kinda convenient to have a particulr routine for
** starting an anchor element, as everything else for HTML is
** simple anyway.
*/
static void HTStartAnchor(HTStructured * obj, CONST char *name, CONST char *href, CONST char *size)
{
BOOL present[HTML_A_ATTRIBUTES];
CONST char *value[HTML_A_ATTRIBUTES];
{
int i;
for (i = 0; i < HTML_A_ATTRIBUTES; i++)
present[i] = NO;
}
if (name)
{
present[HTML_A_NAME] = YES;
value[HTML_A_NAME] = name;
}
if (href)
{
present[HTML_A_HREF] = YES;
value[HTML_A_HREF] = href;
}
/* hinting filesize */
if (size)
{
present[HTML_A_X_SIZE] = YES;
value[HTML_A_X_SIZE] = size;
}
(*obj->isa->start_element) (obj, HTML_A, present, value);
}
// HTFontCommand - generates a SGML <FONT> tag into a synthetic WWW doc
// obj - is the stream that we generate into
// size - is the size to change by current font by
static void HTFontCommand(HTStructured * obj, CONST char *size)
{
BOOL present[HTML_FONT_ATTRIBUTES];
CONST char *value[HTML_FONT_ATTRIBUTES];
{
int i;
for (i = 0; i < HTML_FONT_ATTRIBUTES ; i++)
present[i] = NO;
}
if (size)
{
present[HTML_FONT_SIZE] = YES;
value[HTML_FONT_SIZE] = size;
}
(*obj->isa->start_element) (obj, HTML_FONT, present, value);
}
// HTHRCommand - generates a SGML <HR> tag into a synthetic WWW doc
// obj - is the stream that we generate into
static void HTHRCommand(HTStructured * obj)
{
BOOL present[HTML_HR_ATTRIBUTES];
CONST char *value[HTML_HR_ATTRIBUTES];
{
int i;
for (i = 0; i < HTML_HR_ATTRIBUTES ; i++)
present[i] = NO;
}
(*obj->isa->start_element) (obj, HTML_HR, present, value);
}
/* Output one directory entry
**
*/
static void HTDirEntry(HTStructured * target, CONST char *tail, CONST char *entry)
{
char *relative;
char *escaped;
char *ptr;
char buf[64];
BOOL isFolder = FALSE;
GTR_formatmsg(RES_STRING_HTFTP_FOLDER,buf,sizeof(buf));
if(!strcmp(entry+strlen(entry)-strlen(buf),buf)) isFolder=TRUE;
/* hack, \001 placed here to delimit end of filename */
ptr = strchr(entry,'\001');
*ptr = 0;
ptr++;
escaped = HTEscape(entry, URL_XPALPHAS, '\0');
/* If empty tail, gives absolute ref below */
relative = (char *) GTR_MALLOC(strlen(tail) + strlen(escaped) + 2);
if (relative)
{
sprintf(relative, "%s/%s", tail, escaped);
HTStartAnchor(target, NULL, relative, ptr);
GTR_FREE(relative);
}
else
{
/* TODO */
}
GTR_FREE(escaped);
if (isFolder) START(HTML_B);
PUTS(entry);
if (isFolder) END(HTML_B);
END(HTML_A);
PUTS(ptr);
}
/* Output parent directory entry
**
** This gives the TITLE and H1 header, and also a link
** to the parent directory if appropriate.
*/
static void HTDirTitles(HTStructured * target, CONST char *szURL,
char *szMessage, char *szLogonMsg, char *szDirChgMsg)
{
char *path = NULL;
char *host = NULL;
char *pszHost = NULL;
char *current = NULL;
char szMsg[64];
path = HTParse(szURL, "", PARSE_PATH + PARSE_PUNCTUATION);
current = strrchr(path, '/'); /* last part or "" */
{
char *printable = NULL;
printable = GTR_strdup((current+1));
HTUnEscape(printable);
START(HTML_TITLE);
if (*printable)
{
PUTS(printable);
}
else
{
PUTS(GTR_formatmsg(RES_STRING_HTFTP_WELCOME,szMsg,sizeof(szMsg)));
host = HTParse(szURL, "", PARSE_HOST);
/* if username and/or password are here, scan past */
pszHost = strchr(host, '@');
if (!pszHost)
pszHost = host;
else
pszHost++;
PUTS(pszHost);
}
END(HTML_TITLE);
START(HTML_H2);
if (*printable)
{
PUTS(printable);
PUTS(" ");
PUTS(GTR_formatmsg(RES_STRING_HTFTP_FOLDER,szMsg,sizeof(szMsg)));
}
else
{
PUTS(GTR_formatmsg(RES_STRING_HTFTP_WELCOME,szMsg,sizeof(szMsg)));
PUTS(pszHost);
}
END(HTML_H2);
// we reduce the size of the Message text, since
// it looks stupid for it to overflow, and be big
HTFontCommand(target, "-1");
START(HTML_PRE);
//
// Note: We check the following strings at offset 2.
// this is to check for "\r\n\0" strings which
// are really the parsers way of saying "its blank"
//
// check for a greeting message, print if we have one
if (!*printable && szMessage && szMessage[2] != '\0' )
{
HTHRCommand(target);
PUTS(szMessage);
}
// check for a logon message, print if we have one
if (!*printable && szLogonMsg && szLogonMsg[2] != '\0' )
{
HTHRCommand(target);
PUTS(szLogonMsg);
}
// check if we have a directory specific message
if (szDirChgMsg && szDirChgMsg[2] != '\0' )
{
HTHRCommand(target);
PUTS(szDirChgMsg);
}
HTHRCommand(target);
END(HTML_PRE);
END(HTML_FONT);
GTR_FREE(printable);
}
/* Make link back to parent directory
*/
if (current && current[1])
{ /* was a slash AND something else too */
char *parent;
char *relative;
*current++ = 0;
parent = strrchr(path, '/'); /* penultimate slash */
relative = (char *) GTR_MALLOC(strlen(current) + 4);
if (relative)
{
sprintf(relative, "%s/..", current);
HTStartAnchor(target, NULL, relative, NULL);
GTR_FREE(relative);
}
else
{
/* TODO */
}
PUTS(GTR_formatmsg(RES_STRING_HTFTP_UP,szMsg,sizeof(szMsg)));
/* We only put the string 'Up one level' on screen
if (parent)
{
char *printable = NULL;
printable = GTR_strdup((parent+1));
HTUnEscape(printable);
PUTS(printable);
GTR_FREE(printable);
}
else
{
PUTS("/");
}
*/
END(HTML_A);
}
if (path)
GTR_FREE(path);
if (host)
GTR_FREE(host);
}
struct Params_FTP_ReadDir {
HTRequest * request;
int * pStatus;
int data_soc; /* Data socket */
/* Used internally */
HTStructured * target;
char lastpath[255 + 1];
HTBTree * bt;
HTChunk * chunk;
HTInputSocket * isoc;
BOOL fFromDCache;
FILE * fpDc;
char * pszDcFile;
char * pGreeting;
char * pDirChg;
char * pLogonMsg;
};
#define STATE_READDIR_GOTDATA (STATE_OTHER)
const char cszFtp[]="ftp";
#define HTFtpFormat() HTAtom_for(cszFtp)
BOOL FFtpFormat(HTFormat format)
{
return (format == HTAtom_for(cszFtp));
}
int DoFtpDCache(struct Mwin *tw, struct Data_LoadFileCache *pData, HTFormat format)
{
struct Params_FTP_ReadDir *pfrd;
int state;
if (!(pfrd = GTR_MALLOC(sizeof(*pfrd))))
goto LErr;
pfrd->request = pData->request;
pfrd->pStatus = pData->pStatus;
pfrd->data_soc = 0; /* Don't need it for cached data */
pfrd->fFromDCache = TRUE;
pfrd->fpDc = pData->fp;
pfrd->pGreeting = NULL;
pfrd->pLogonMsg = NULL;
pfrd->pDirChg = NULL;
state = FTP_ReadDir_Async(tw, STATE_INIT, &pfrd);
GTR_FREE(pfrd);
return state;
LErr:
*pData->pStatus = -1;
return STATE_DONE;
}
static int FTP_ReadDir_Async(struct Mwin *tw, int nState, void **ppInfo)
{
struct Params_FTP_ReadDir *pParams;
char *address;
char *filename;
char itemsize[20];
char itemtype;
int itemdate;
char buf[128];
int ret;
char *ptr;
char *pNext;
HTBTElement *ele;
char ch;
int cb; /*count of bytes read in from dcache */
if (tw) tw->bSilent = FALSE;
pParams = *ppInfo;
switch (nState)
{
case STATE_INIT:
/* We parse out the filename again instead of using
the one from the FTP load because now we want it
escaped. */
pParams->request = HTRequest_validate(pParams->request);
if (pParams->request == NULL) return STATE_DONE;
address = pParams->request->destination->szActualURL;
filename = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION);
if (!filename || *filename == 0)
{ /* Empty filename : use root */
strcpy(pParams->lastpath, "/");
}
else
{
char *p;
p = strrchr(filename, '/'); /* find lastslash */
if (p)
strcpy(pParams->lastpath, p + 1); /* take slash off the beginning */
else
strcpy(pParams->lastpath, "/"); /* probably an error */
}
if (filename)
GTR_FREE(filename);
pParams->target = HTML_new(tw, pParams->request, NULL, WWW_HTML, pParams->request->output_format, pParams->request->output_stream);
HTDirTitles(pParams->target, pParams->request->destination->szActualURL,
pParams->pGreeting, pParams->pLogonMsg, pParams->pDirChg);
pParams->bt = HTBTree_new((HTComparer) strcasecomp);
pParams->chunk = HTChunkCreate(128);
(*pParams->target->isa->start_element)(pParams->target, HTML_DIR, 0, 0);
(*pParams->target->isa->start_element)(pParams->target, HTML_PRE, 0, 0);
(*pParams->target->isa->start_element)(pParams->target, HTML_LI, 0, 0);
(*pParams->target->isa->put_string)(pParams->target, GTR_formatmsg(RES_STRING_HTFTP_DIRHEADER,buf,sizeof(buf)));
(*pParams->target->isa->start_element)(pParams->target, HTML_BR, 0, 0);
(*pParams->target->isa->start_element)(pParams->target, HTML_BR, 0, 0);
pParams->isoc = HTInputSocket_new(pParams->data_soc);
if (!pParams->fFromDCache)
{
struct Params_Isoc_Fill *pif;
#ifdef FEATURE_INTL
SetFileDCache(tw->w3doc, pParams->request->destination->szActualURL, ENCODING_BINARY, &pParams->fpDc, &pParams->pszDcFile, HTFtpFormat());
#else
SetFileDCache(pParams->request->destination->szActualURL, ENCODING_BINARY, &pParams->fpDc, &pParams->pszDcFile, HTFtpFormat());
#endif
pif = GTR_MALLOC(sizeof(*pif));
pif->isoc = pParams->isoc;
pif->pStatus = pParams->pStatus;
Async_DoCall(Isoc_Fill_Async, pif);
return STATE_READDIR_GOTDATA;
}
else
goto LDataFromDCache;
case STATE_READDIR_GOTDATA:
if (*pParams->pStatus > 0)
{
LDataFromDCache:
while (1)
{
if (!pParams->fFromDCache)
{
/* Getting data from the net */
if (pParams->fpDc)
{
/* save it to dcache */
fwrite( pParams->isoc->input_buffer,
1,
pParams->isoc->input_limit - pParams->isoc->input_buffer,
pParams->fpDc);
}
}
else
{
/* getting data from dcache, read it */
XX_Assert(pParams->fpDc, (""));
cb = fread(pParams->isoc->input_buffer, 1, INPUT_BUFFER_SIZE - 1, pParams->fpDc);
if (cb == 0)
{ /* error */
if (ferror(pParams->fpDc) != 0)
goto LState_Abort;
/* EOF: break out of while loop */
break;
}
pParams->isoc->input_limit = pParams->isoc->input_buffer + cb;
}
for (pNext = pParams->isoc->input_buffer; pNext < pParams->isoc->input_limit; pNext++)
{
ch = *pNext;
if (ch == EOF)
{
break;
}
else if (ch == CR | ch == LF)
{
if (pParams->chunk->size > 0)
{
filename = NULL;
HTChunkTerminate(pParams->chunk);
ret=sscanf(pParams->chunk->data,"%c%*9s%*d %*s %*s %s%n", &itemtype, itemsize, &itemdate);
if (ret!=2)
{
/* BUGBUG consider displaying an error msg */
HTChunkClear(pParams->chunk);
continue;
}
ptr = strrchr(pParams->chunk->data,' ');
if (ptr == NULL)
{
/* BUGBUG what does this really do? clearly
if sscanf() above succeeds then this must succeed
*/
HTChunkClear(pParams->chunk);
continue;
}
ptr++;
if (!strcmp(ptr,".") || !strcmp(ptr,".."))
{
HTChunkClear(pParams->chunk);
continue;
}
/* i can only parse unix-style dir listings */
if (itemtype!='d' && itemtype!='-' && itemtype!='l')
{
/* BUGBUG consider displaying an error msg */
HTChunkClear(pParams->chunk);
continue;
}
/* if you screw with formatting here, then you must
screw with formatting in HTDirEntry & HTDirTitle
*/
/* approximate size of on-screen text: filename+size+3+date+3+type */
ret=strlen(ptr);
if (ret > 24)
ret += 8+3+12+3+64;
else
ret = 24+8+3+12+3+64;
filename = GTR_MALLOC(ret);
if (itemtype=='d')
{
*(ptr-1) = 0; /* terminate Date field in original */
sprintf (filename,"%-24s %.12s ",ptr,pParams->chunk->data+itemdate+1);
strcat (filename, GTR_formatmsg(RES_STRING_HTFTP_FOLDER,buf,sizeof(buf)));
}
else if (itemtype=='-')
{
ret = atoi(itemsize);
/* terminate Date Modified field in original */
*(ptr-1) = 0;
sprintf (filename, "%-24s %6dKB %.12s ",ptr,(ret+1023)/1024,pParams->chunk->data+itemdate+1);
strcat (filename, GTR_formatmsg(RES_STRING_HTFTP_FILE,buf,sizeof(buf)));
}
else
{
/* depend on exact link format of "file -> /path/file" */
*(ptr-4) = 0;
ptr = strrchr(pParams->chunk->data,' ');
ptr++;
sprintf (filename, "%-24s ",ptr);
strcat (filename, GTR_formatmsg(RES_STRING_HTFTP_SHORTCUT,buf,sizeof(buf)));
}
filename[strlen(ptr)] = '\001';
HTBTree_add(pParams->bt, filename);
HTChunkClear(pParams->chunk);
}
}
else
{
HTChunkPutc(pParams->chunk, ch);
}
}
if (ch != EOF)
{
struct Params_Isoc_Fill *pif;
/* Update display */
(*pParams->target->isa->block_done)(pParams->target);
if (!pParams->fFromDCache)
{
/* Get next block of data */
pif = GTR_MALLOC(sizeof(*pif));
pif->isoc = pParams->isoc;
pif->pStatus = pParams->pStatus;
Async_DoCall(Isoc_Fill_Async, pif);
return STATE_READDIR_GOTDATA;
}
}
else /* ch == EOF */
break;
} /* while (1) */
} /* if (*pParams->pStatus > 0) */
/* Now add entries in sorted order. */
for (ele = HTBTree_next(pParams->bt, NULL);
ele != NULL;
ele = HTBTree_next(pParams->bt, ele))
{
(*pParams->target->isa->start_element)(pParams->target, HTML_LI, 0, 0);
HTDirEntry(pParams->target, pParams->lastpath, (char *) HTBTree_object(ele));
}
/* Fall through */
case STATE_ABORT:
LState_Abort:
pParams->request = HTRequest_validate(pParams->request);
if ((!pParams->fFromDCache) && pParams->request)
{
DCACHETIME dctNever = {DCACHETIME_EXPIRE_NEVER,DCACHETIME_EXPIRE_NEVER};
DCACHETIME dct = {0,0};
UpdateFileDCache( pParams->request->destination->szActualURL, /* szActualURL */
&pParams->fpDc,
&pParams->pszDcFile,
HTFtpFormat(),
dctNever,
dct,
/*fAbort=*/nState == STATE_ABORT,
FALSE,
tw);
}
else
*pParams->pStatus = (nState == STATE_ABORT ? -1 : HT_LOADED);
if (*pParams->pStatus == HT_LOADED && pParams->request && tw)
{
GHist_Add(pParams->request->destination->szActualURL, NULL, time(NULL), TRUE);
if (tw->w3doc)
W3Doc_CheckAnchorVisitations(tw->w3doc, tw);
}
(*pParams->target->isa->end_element)(pParams->target, HTML_PRE);
(*pParams->target->isa->end_element)(pParams->target, HTML_DIR);
(*pParams->target->isa->free)(pParams->target);
HTChunkFree(pParams->chunk);
HTBTreeAndObject_free(pParams->bt);
return STATE_DONE;
}
XX_Assert((0), ("Function called with illegal state: %d", nState));
return STATE_DONE;
}
/*************************************************************/
struct Data_LoadFTP {
HTRequest * request;
int * pStatus;
BOOL bWaiting;
const char * arg;
char * pszHost;
char * username;
char * password;
struct _CachedConn *pCon;
struct MultiAddress address;
unsigned short port;
SockA data_addr;
BOOL PASV;
unsigned long where; /* Where we connected to */
int s; /* socket for control connection */
int data_soc; /* socket for data connection */
int response; /* ftp response status */
char * pResText; /* response text */
char * pGreeting; /* greeting text used to give messages */
char * pLogonMsg;
char * pDirChg; /* message that we get when changing dirs */
HTInputSocket *isoc; /* buffer for control connection */
char * filename;
HTFormat format;
int net_status;
};
#define STATE_FTP_GOTHOST (STATE_OTHER + 0)
#define STATE_FTP_CONNECTED (STATE_OTHER + 1)
#define STATE_FTP_GOTGREETING (STATE_OTHER + 2)
#define STATE_FTP_SENTUSER (STATE_OTHER + 3)
#define STATE_FTP_SENTPASS (STATE_OTHER + 4)
#define STATE_FTP_SENTACCT (STATE_OTHER + 5)
#define STATE_FTP_LOGGEDIN (STATE_OTHER + 6)
#define STATE_FTP_SENTPASV (STATE_OTHER + 7)
#define STATE_FTP_DOPORT (STATE_OTHER + 8)
#define STATE_FTP_SENTPORT (STATE_OTHER + 10)
#define STATE_FTP_GOTDATACONN (STATE_OTHER + 11)
#define STATE_FTP_SENTTYPE (STATE_OTHER + 12)
#define STATE_FTP_SENTRETR (STATE_OTHER + 13)
#define STATE_FTP_SENTCWD (STATE_OTHER + 14)
#define STATE_FTP_SENTNLST (STATE_OTHER + 15)
#define STATE_FTP_GOTDATA (STATE_OTHER + 16)
static void FTP_CleanUp(struct Mwin *tw, struct Data_LoadFTP *pData)
{
if (pData->bWaiting && tw)
{
WAIT_Pop(tw);
}
if (pData->pszHost)
GTR_FREE(pData->pszHost);
if (pData->username)
GTR_FREE(pData->username);
if (pData->password)
GTR_FREE(pData->password);
if (pData->pResText)
GTR_FREE(pData->pResText);
if (pData->filename)
GTR_FREE(pData->filename);
if (pData->pGreeting)
GTR_FREE(pData->pGreeting);
if (pData->pLogonMsg)
GTR_FREE(pData->pLogonMsg);
if (pData->pDirChg)
GTR_FREE(pData->pDirChg);
if (pData->data_soc > 0)
Net_Close(pData->data_soc);
if (pData->isoc)
HTInputSocket_free(pData->isoc);
}
static int FTP_Abort(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
pData = *ppInfo;
pData->request = HTRequest_validate(pData->request);
FTP_CleanUp(tw, pData);
/* The connection might now be in an invalid state */
if (tw) TW_DisposeConnection(&tw->cached_conn);
*pData->pStatus = -1;
return STATE_DONE;
}
static int FTP_DoInit(struct Mwin *tw, void **ppInfo)
{
struct Params_LoadAsync *pParams;
struct Data_LoadFTP *pData;
struct Params_MultiParseInet *ppi;
char *name;
char buf[MAX_URL_STRING];
pParams = *ppInfo;
pParams->request = HTRequest_validate(pParams->request);
if (pParams->request == NULL || (!(name = pParams->request->destination->szActualURL)) || !*name)
{
*pParams->pStatus = -2;
return STATE_DONE;
}
/* It's common for someone to enter an invalid directory URL which
ends in a slash ("ftp://foo.com/pub/mac/"), so make sure this
isn't malformed like that. */
{
char *filename;
char *p = NULL;
filename = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
if (filename)
p = strrchr(filename, '/');
if (p && !*(p+1) && p != filename)
{
/* The URL was, in fact, malformed. I told you it was common. */
/* Fix up the URL and try again. */
p = strrchr(name, '/');
strncpy(buf, name, p - name);
buf[p - name] = '\0';
Dest_UpdateActual(pParams->request->destination, buf,FALSE);
*pParams->pStatus = HT_REDIRECTION_ON_FLY;
GTR_FREE(filename);
return STATE_DONE;
}
GTR_FREE(filename);
}
/* Copy the parameters we were passed into our own, larger structure. */
pData = GTR_MALLOC(sizeof(struct Data_LoadFTP));
memset(pData, 0, sizeof(*pData));
pData->request = pParams->request;
pData->pStatus = pParams->pStatus;
GTR_FREE(pParams);
*ppInfo = pData;
/* BUGBUG hack by tr for guessing FTP filesize */
pData->request->content_length = pData->request->content_length_hint;
pData->arg = name;
/* Parse host, username and password out of URL */
{
char *p1 = HTParse(pData->arg, "", PARSE_HOST);
char *p2 = strrchr(p1, '@'); /* user? */
char *username = NULL;
char *password = NULL;
char *pw;
char *freeme;
freeme = p1;
if (p2)
{
username = p1;
*p2 = 0; /* terminate */
p1 = p2 + 1; /* point to host */
pw = strchr(username, ':');
if (pw)
{
*pw++ = 0;
password = pw;
}
}
if (pData->pszHost) /* TODO is this needed? */
{
GTR_FREE(pData->pszHost);
}
pData->pszHost = GTR_strdup(p1);
if (username)
{
if (pData->username) /* TODO is this needed? */
{
GTR_FREE(pData->username);
}
pData->username = GTR_strdup(username);
}
if (password)
{
if (pData->password) /* TODO is this needed? */
{
GTR_FREE(pData->password);
}
pData->password = GTR_strdup(password);
}
GTR_FREE(freeme);
}
/* Figure out the address for the target system. */
/* Set up defaults */
pData->port = WS_HTONS(IPPORT_FTP);
/* Get node name and optional port number */
ppi = GTR_MALLOC(sizeof(*ppi));
ppi->pAddress = &pData->address;
ppi->pPort = &pData->port;
ppi->str = pData->pszHost;
ppi->pStatus = &pData->net_status;
ppi->request = pData->request;
Async_DoCall(Net_MultiParse_Async, ppi);
return STATE_SECURITY_CHECK;
}
static int FTP_DoGotHost(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
char szStatus[64];
pData = *ppInfo;
GTR_FREE(pData->pszHost);
pData->pszHost = NULL;
if ( pData->net_status < 0
|| pData->net_status == HT_REDIRECTION_DCACHE_TIMEOUT)
{
XX_DMsg(DBG_LOAD, ("Net_Parse_Async returned %d\n", pData->net_status));
*pData->pStatus = (pData->net_status == HT_REDIRECTION_DCACHE_TIMEOUT ? HT_REDIRECTION_DCACHE_TIMEOUT : -1);
FTP_CleanUp(tw, pData);
return STATE_DONE;
}
/* See if our cached connection is for this site
and user. (We cache one ftp connection per window */
pData->pCon = &tw->cached_conn;
if (pData->pCon->type == CONN_FTP && Net_CompareAddresses(pData->pCon->addr, &pData->address))
{
/* The address is correct. Confirm that the socket
is still open. */
if (!Net_FlushSocket(pData->pCon->socket))
{
XX_DMsg(DBG_LOAD, ("FTP: Using cached connection for %s\n", pData->pszHost));
pData->s = pData->pCon->socket;
pData->isoc = HTInputSocket_new(pData->s);
return STATE_FTP_LOGGEDIN;
}
else
{
/* The other side closed the connection on us. */
XX_DMsg(DBG_LOAD, ("FTP: Cached connection for %s closed by other side\n", pData->pszHost));
}
}
/* The cached connection wasn't useful. Get rid of it. */
TW_DisposeConnection(pData->pCon);
/* Try to establish a new control connection */
WAIT_Push(tw, waitSameInteract, GTR_formatmsg(RES_STRING_HTFTP1,szStatus,sizeof(szStatus)));
WAIT_SetStatusBarIcon( tw, SBI_FindingIcon );
pData->bWaiting = TRUE;
{
/* Do connect call */
struct Params_MultiConnect *ppc;
ppc = GTR_MALLOC(sizeof(*ppc));
#ifdef FEATURE_KEEPALIVE
ppc->pszHost = NULL;
#endif
ppc->pSocket = &pData->s;
ppc->pAddress = &pData->address;
ppc->nPort = pData->port;
ppc->pWhere = &pData->where;
ppc->pStatus = &pData->net_status;
#ifdef HTTPS_ACCESS_TYPE
ppc->paramsConnectBase.dwSslFlags = 0;
#endif
Async_DoCall(Net_MultiConnect_Async, ppc);
}
return STATE_FTP_CONNECTED;
}
static int FTP_DoConnected(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
struct Params_FTP_Command *pfc;
char szStatus[64];
pData = *ppInfo;
if ( pData->net_status < 0
|| pData->net_status == HT_REDIRECTION_DCACHE_TIMEOUT)
{
WAIT_Pop(tw);
pData->bWaiting = FALSE;
XX_DMsg(DBG_LOAD | DBG_WWW, ("Unable to connect to remote host for %s (errno = %d)\n", pData->arg, errno));
*pData->pStatus = (pData->net_status == HT_REDIRECTION_DCACHE_TIMEOUT ? HT_REDIRECTION_DCACHE_TIMEOUT : -1);
FTP_CleanUp(tw, pData);
return STATE_DONE;
}
WAIT_Update(tw,
waitSameInteract, GTR_formatmsg(RES_STRING_HTFTP2,szStatus,sizeof(szStatus)));
pData->isoc = HTInputSocket_new(pData->s);
/* Get greeting. */
pfc = GTR_MALLOC(sizeof(*pfc));
pfc->isoc = pData->isoc;
pfc->cmd = NULL;
pfc->ppText = &pData->pGreeting;
pfc->pResult = &pData->response;
pfc->fWantFullText = 1; // we want a greeting message
Async_DoCall(FTP_Command_Async, pfc);
return STATE_FTP_GOTGREETING;
}
static int FTP_DoGotGreeting(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
struct Params_FTP_Command *pfc;
char *command;
pData = *ppInfo;
if (pData->response != 2)
{
/* Illegal response! */
*pData->pStatus = -1;
FTP_CleanUp(tw, pData);
return STATE_DONE;
}
/* Send username */
if (pData->username)
{
command = GTR_MALLOC(10 + strlen(pData->username));
sprintf(command, "USER %s\015\012", pData->username);
GTR_FREE(pData->username);
pData->username = NULL;
}
else
{
command = GTR_MALLOC(25);
strcpy(command, "USER anonymous\015\012");
}
pfc = GTR_MALLOC(sizeof(*pfc));
pfc->isoc = pData->isoc;
pfc->cmd = command;
pfc->pResult = &pData->response;
pfc->ppText = &pData->pLogonMsg;
pfc->fWantFullText = 1; // we want a greeting message
Async_DoCall(FTP_Command_Async, pfc);
return STATE_FTP_SENTUSER;
}
static int FTP_DoSentUser(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
struct Params_FTP_Command *pfc;
char *command;
pData = *ppInfo;
if (pData->response == 2)
{
/* System didn't require a password. We're logged in. */
return STATE_FTP_LOGGEDIN;
}
if (pData->response != 3)
{
/* Illegal response! */
*pData->pStatus = -1;
FTP_CleanUp(tw, pData);
return STATE_DONE;
}
/* Send password */
if (pData->password)
{
command = GTR_MALLOC(10 + strlen(pData->password));
sprintf(command, "PASS %s\015\012", pData->password);
GTR_FREE(pData->password);
pData->password = NULL;
}
else
{
char *user = NULL;
CONST char *host = HTHostName();
#ifdef UNIX
user = getenv("USER");
#endif
/*
TODO get a user name from prefs
*/
if (!user)
user = "WWWuser";
/* If not fully qualified, suppress it as ftp.uu.net
prefers a blank to a bad name */
if (!strchr(host, '.'))
host = "";
command = (char *) GTR_MALLOC(11 + strlen(host) + strlen(user));
if (command)
{
sprintf(command, "PASS %s@%s\015\012", user, host);
}
}
pfc = GTR_MALLOC(sizeof(*pfc));
if (pfc)
{
pfc->isoc = pData->isoc;
pfc->cmd = command;
pfc->pResult = &pData->response;
if ( pData->pLogonMsg )
GTR_FREE( pData->pLogonMsg );
pfc->ppText = &pData->pLogonMsg;
pfc->fWantFullText = 1; // we want a greeting message
Async_DoCall(FTP_Command_Async, pfc);
}
else
{
/* TODO */
}
return STATE_FTP_SENTPASS;
}
static int FTP_DoSentPass(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
struct Params_FTP_Command *pfc;
char *command;
pData = *ppInfo;
if (pData->response == 2)
{
/* System didn't require an account. We're logged in. */
return STATE_FTP_LOGGEDIN;
}
if (pData->response != 3)
{
/* Illegal response! */
*pData->pStatus = -1;
FTP_CleanUp(tw, pData);
return STATE_DONE;
}
/* Send account */
command = GTR_MALLOC(25);
strcpy(command, "ACCT noaccount\015\012");
pfc = GTR_MALLOC(sizeof(*pfc));
pfc->isoc = pData->isoc;
pfc->cmd = command;
pfc->pResult = &pData->response;
if ( pData->pLogonMsg )
GTR_FREE( pData->pLogonMsg );
pfc->ppText = &pData->pLogonMsg;
pfc->fWantFullText = 1; // we want a greeting message
Async_DoCall(FTP_Command_Async, pfc);
return STATE_FTP_SENTACCT;
}
static int FTP_DoSentAcct(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
pData = *ppInfo;
if (pData->response == 2)
{
return STATE_FTP_LOGGEDIN;
}
else {
/* Illegal response! */
*pData->pStatus = -1;
FTP_CleanUp(tw, pData);
return STATE_DONE;
}
}
static int FTP_DoLoggedIn(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
struct Params_FTP_Command *pfc;
char *command;
char szStatus[64];
pData = *ppInfo;
/* We may already have a wait frame pushed, depending on whether
we had to log in again. */
if (pData->bWaiting)
{
WAIT_Update(tw, waitSameInteract, GTR_formatmsg(RES_STRING_HTFTP3,szStatus,sizeof(szStatus)));
}
else
{
WAIT_Push(tw,
waitSameInteract, GTR_formatmsg(RES_STRING_HTFTP3,szStatus,sizeof(szStatus)));
pData->bWaiting = TRUE;
}
/* Fill in connection information */
if (pData->pCon->type != CONN_FTP)
{
pData->pCon->addr = pData->where;
pData->pCon->socket = pData->s;
pData->pCon->type = CONN_FTP;
}
/* Ask the server to use passive mode */
command = GTR_MALLOC(10);
strcpy(command, "PASV\015\012");
pfc = GTR_MALLOC(sizeof(*pfc));
pfc->isoc = pData->isoc;
pfc->cmd = command;
pfc->ppText = &pData->pResText;
pfc->pResult = &pData->response;
pfc->fWantFullText = 0;
Async_DoCall(FTP_Command_Async, pfc);
return STATE_FTP_SENTPASV;
}
static int FTP_DoSentPasv(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
char *p;
int count, reply, h0, h1, h2, h3, p0, p1; /* Parts of reply */
int port;
long ltmp;
pData = *ppInfo;
if (pData->response < 0)
{
FTP_CleanUp(tw, pData);
*pData->pStatus = -1;
return STATE_DONE;
}
XX_DMsg(DBG_LOAD, ("FTP: reply to PASV was: %s", pData->pResText));
if (pData->response != 2)
{
pData->PASV = FALSE;
GTR_FREE(pData->pResText);
return STATE_FTP_DOPORT;
#if 0
ERR_ReportError(tw, errPasvNotSupported, NULL, NULL);
FTP_CleanUp(tw, pData);
/* The connection might now be in an invalid state */
TW_DisposeConnection(&tw->cached_conn);
*pData->pStatus = -1;
return STATE_DONE;
#endif
}
pData->PASV = TRUE;
for (p = pData->pResText; *p; p++)
{
if ((*p < '0') || (*p > '9'))
*p = ' ';
}
count = sscanf(pData->pResText, "%d%d%d%d%d%d%d", &reply, &h0, &h1, &h2, &h3, &p0, &p1);
GTR_FREE(pData->pResText);
pData->pResText = NULL;
if (count < 7)
{
ERR_ReportError(tw, errPasvNotSupported, NULL, NULL);
FTP_CleanUp(tw, pData);
/* The connection might now be in an invalid state */
TW_DisposeConnection(&tw->cached_conn);
*pData->pStatus = -1;
return STATE_DONE;
}
port = (p0 << 8) + p1;
/* Put together the address for the data connection */
pData->data_addr.sin_family = AF_INET;
ltmp = (h0 << 24) | (h1 << 16) | (h2 << 8) | h3;
pData->data_addr.sin_addr.s_addr = WS_HTONL(ltmp);
pData->data_addr.sin_port = WS_HTONS(port);
pData->data_soc = Net_Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (pData->data_soc < 0)
{
FTP_CleanUp(tw, pData);
/* The connection might now be in an invalid state */
TW_DisposeConnection(&tw->cached_conn);
*pData->pStatus = -1;
return STATE_DONE;
}
{
/* Do connect call */
struct Params_Connect *ppc;
ppc = GTR_MALLOC(sizeof(*ppc));
ppc->socket = pData->data_soc;
memcpy(&ppc->address, &pData->data_addr, sizeof(ppc->address));
ppc->pStatus = &pData->net_status;
#ifdef HTTPS_ACCESS_TYPE
ppc->paramsConnectBase.dwSslFlags = 0;
#endif
Async_DoCall(Net_Connect_Async, ppc);
}
return STATE_FTP_GOTDATACONN;
}
static int FTP_DoPort(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
struct Params_FTP_Command *pfc;
int result;
struct sockaddr_in data_addr, comm_addr;
int data_addrlen, comm_addrlen;
char *command;
pData = *ppInfo;
/* Put together the address for the data connection */
pData->data_soc = Net_Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
#if 0 // BUGBUG: could use better error checking -tr
if (pData->data_soc < 0)
{
FTP_CleanUp(tw, pData);
/* The connection might now be in an invalid state */
TW_DisposeConnection(&tw->cached_conn);
*pData->pStatus = -1;
return STATE_DONE;
}
#endif
data_addrlen = comm_addrlen = sizeof(data_addr);
data_addr.sin_family = AF_INET;
data_addr.sin_addr.s_addr = INADDR_ANY;
data_addr.sin_port = 0;
result = WS_BIND(pData->data_soc, (LPSOCKADDR)&data_addr, data_addrlen);
result = WS_GETSOCKNAME(pData->data_soc, (LPSOCKADDR)&data_addr, &data_addrlen);
result = WS_GETSOCKNAME(pData->s, (LPSOCKADDR)&comm_addr, &comm_addrlen);
WS_WSAASYNCSELECT(pData->data_soc, wg.hWndHidden, SOCKET_MESSAGE, FD_CONNECT);
result = WS_LISTEN(pData->data_soc, 1);
if (result)
{
FTP_CleanUp(tw, pData);
/* The connection might now be in an invalid state */
TW_DisposeConnection(&tw->cached_conn);
*pData->pStatus = -1;
return STATE_DONE;
}
/* Ask the server to use specific port */
command = GTR_MALLOC(80);
sprintf (command, "PORT %d,%d,%d,%d,%d,%d\015\012",
comm_addr.sin_addr.S_un.S_un_b.s_b1,
comm_addr.sin_addr.S_un.S_un_b.s_b2,
comm_addr.sin_addr.S_un.S_un_b.s_b3,
comm_addr.sin_addr.S_un.S_un_b.s_b4,
data_addr.sin_port & 0x00ff,
data_addr.sin_port >> 8
);
pfc = GTR_MALLOC(sizeof(*pfc));
pfc->isoc = pData->isoc;
pfc->cmd = command;
pfc->ppText = NULL;
pfc->pResult = &pData->response;
pfc->fWantFullText = 0;
Async_DoCall(FTP_Command_Async, pfc);
return STATE_FTP_SENTPORT;
}
static int FTP_DoSentPort(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
pData = *ppInfo;
if (pData->response < 0)
{
FTP_CleanUp(tw, pData);
*pData->pStatus = -1;
return STATE_DONE;
}
XX_DMsg(DBG_LOAD, ("FTP: reply to PORT was: %s", pData->pResText));
if (pData->response != 2)
{
FTP_CleanUp(tw, pData);
/* The connection might now be in an invalid state */
TW_DisposeConnection(&tw->cached_conn);
*pData->pStatus = -1;
return STATE_DONE;
}
return STATE_FTP_GOTDATACONN;
}
static int FTP_DoGotDataConn(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
struct Params_FTP_Command *pfc;
char *command;
BOOL bBinary;
char szStatus[64];
pData = *ppInfo;
if (pData->net_status < 0)
{
XX_DMsg(DBG_LOAD, ("FTP: Couldn't open data connection!\n"));
FTP_CleanUp(tw, pData);
/* The connection might now be in an invalid state */
TW_DisposeConnection(&tw->cached_conn);
*pData->pStatus = -1;
return STATE_DONE;
}
WAIT_Update(tw, waitSameInteract,
GTR_formatmsg(RES_STRING_HTFTP4,szStatus,sizeof(szStatus)));
/* Figure out what file we're getting, and whether it's
text or binary */
pData->filename = HTParse(pData->arg, "", PARSE_PATH + PARSE_PUNCTUATION);
if (!*pData->filename)
{
pData->filename = GTR_strdup("/");
}
HTUnEscape(pData->filename);
pData->format = HTFileFormat(pData->filename,
&pData->request->content_encoding,
&pData->request->content_language);
bBinary = (pData->request->content_encoding != ENCODING_8BIT &&
pData->request->content_encoding != ENCODING_7BIT);
command = GTR_MALLOC(10);
sprintf(command, "TYPE %c\015\012", bBinary ? 'I' : 'A');
pfc = GTR_MALLOC(sizeof(*pfc));
pfc->isoc = pData->isoc;
pfc->cmd = command;
pfc->ppText = NULL;
pfc->pResult = &pData->response;
pfc->fWantFullText = 0;
Async_DoCall(FTP_Command_Async, pfc);
return STATE_FTP_SENTTYPE;
}
static int FTP_DoSentType(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
struct Params_FTP_Command *pfc;
char *command;
pData = *ppInfo;
if (pData->response == 2)
{
/* We successfully changed the mode. Request the file */
command = GTR_MALLOC(strlen(pData->filename) + 10);
sprintf(command, "RETR %s\015\012", pData->filename);
pfc = GTR_MALLOC(sizeof(*pfc));
pfc->isoc = pData->isoc;
pfc->cmd = command;
pfc->ppText = &pData->pResText;
pfc->pResult = &pData->response;
pfc->fWantFullText = 0;
Async_DoCall(FTP_Command_Async, pfc);
return STATE_FTP_SENTRETR;
}
else {
/* Illegal response! */
*pData->pStatus = -1;
FTP_CleanUp(tw, pData);
/* The connection might now be in an invalid state */
TW_DisposeConnection(&tw->cached_conn);
return STATE_DONE;
}
}
static int FTP_DoSentRetr(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
struct Params_FTP_Command *pfc;
struct Params_HTParseSocket *pps;
char *command;
char *pSize;
int result;
struct sockaddr_in serv_addr;
int serv_addrlen;
pData = *ppInfo;
if (pData->response == 1)
{
if (pData->pResText)
{
/* Parse out file size */
pSize = strrchr(pData->pResText, '(');
if (pSize)
{
char *pszKChar = NULL;
char *pszkChar = NULL;
char *pszTerm;
pszTerm = strchr(pSize,')');
if ( pszTerm )
{
*pszTerm = '\0';
pszKChar = strchr(pSize,'K');
pszkChar = strchr(pSize,'k');
*pszTerm = ')';
}
pData->request->content_length = atoi(pSize + 1);
// if its in KB instead of bytes, we convert to bytes from KiloBytes
if ( pszKChar || pszkChar)
pData->request->content_length *= 1024;
}
GTR_FREE(pData->pResText);
pData->pResText = NULL;
}
if (!pData->PASV)
{
serv_addrlen = sizeof(serv_addr);
result = WS_ACCEPT (pData->data_soc, (LPSOCKADDR)&serv_addr, &serv_addrlen);
if (result == INVALID_SOCKET)
{
/* The server isn't sending us data */
ERR_ReportError(tw, errPasvNotSupported, NULL, NULL);
*pData->pStatus = -1;
FTP_CleanUp(tw, pData);
/* The connection might now be in an invalid state */
TW_DisposeConnection(&tw->cached_conn);
return STATE_DONE;
}
Net_Close(pData->data_soc);
pData->data_soc = result;
}
pps = GTR_MALLOC(sizeof(*pps));
pps->format_in = pData->format;
pps->file_number = pData->data_soc;
pps->request = pData->request;
pps->pStatus = &pData->net_status;
Async_DoCall(HTParseSocket_Async, pps);
return STATE_FTP_GOTDATA;
}
else
{
/* We failed. It might be a directory. */
if (pData->pResText)
{
GTR_FREE(pData->pResText);
pData->pResText = NULL;
}
command = GTR_MALLOC(strlen(pData->filename) + 10);
sprintf(command, "CWD %s\015\012", pData->filename);
pfc = GTR_MALLOC(sizeof(*pfc));
pfc->isoc = pData->isoc;
pfc->cmd = command;
pfc->ppText = &pData->pDirChg;
pfc->pResult = &pData->response;
pfc->fWantFullText = 1;
Async_DoCall(FTP_Command_Async, pfc);
return STATE_FTP_SENTCWD;
}
}
static int FTP_DoSentCwd(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
struct Params_FTP_Command *pfc;
char *command;
pData = *ppInfo;
if (pData->response != 2)
{
*pData->pStatus = -1;
FTP_CleanUp(tw, pData);
/* The connection might now be in an invalid state */
TW_DisposeConnection(&tw->cached_conn);
return STATE_DONE;
}
command = GTR_MALLOC(10);
strcpy(command, "LIST\015\012");
pfc = GTR_MALLOC(sizeof(*pfc));
pfc->isoc = pData->isoc;
pfc->cmd = command;
pfc->ppText = NULL;
pfc->pResult = &pData->response;
pfc->fWantFullText = 0;
Async_DoCall(FTP_Command_Async, pfc);
return STATE_FTP_SENTNLST;
}
static int FTP_DoSentNlst(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
struct Params_FTP_ReadDir *pfrd;
char szStatus[64];
int result;
struct sockaddr_in serv_addr;
int serv_addrlen;
pData = *ppInfo;
if (pData->response != 1)
{
/* The server isn't sending us data */
*pData->pStatus = -1;
FTP_CleanUp(tw, pData);
/* The connection might now be in an invalid state */
TW_DisposeConnection(&tw->cached_conn);
return STATE_DONE;
}
WAIT_Update(tw,
waitSameInteract, GTR_formatmsg(RES_STRING_HTFTP5,szStatus,sizeof(szStatus)));
if (!pData->PASV)
{
serv_addrlen = sizeof(serv_addr);
result = WS_ACCEPT (pData->data_soc, (LPSOCKADDR)&serv_addr, &serv_addrlen);
if (result == INVALID_SOCKET)
{
/* The server isn't sending us data */
ERR_ReportError(tw, errPasvNotSupported, NULL, NULL);
*pData->pStatus = -1;
FTP_CleanUp(tw, pData);
/* The connection might now be in an invalid state */
TW_DisposeConnection(&tw->cached_conn);
return STATE_DONE;
}
Net_Close(pData->data_soc);
pData->data_soc = result;
}
/* Read in the directory listing */
pfrd = GTR_MALLOC(sizeof(*pfrd));
pfrd->request = pData->request;
pfrd->pStatus = &pData->net_status;
pfrd->data_soc = pData->data_soc;
pfrd->fFromDCache = FALSE;
pfrd->fpDc = NULL;
pfrd->pGreeting = pData->pGreeting;
pfrd->pLogonMsg = pData->pLogonMsg;
pfrd->pDirChg = pData->pDirChg;
Async_DoCall(FTP_ReadDir_Async, pfrd);
return STATE_FTP_GOTDATA;
}
static int FTP_DoGotData(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
pData = *ppInfo;
if (pData->net_status < 0)
{
/* The connection might now be in an invalid state */
TW_DisposeConnection(&tw->cached_conn);
*pData->pStatus = -1;
}
else
{
*pData->pStatus = HT_LOADED;
}
FTP_CleanUp(tw, pData);
return STATE_DONE;
}
static int HTFTPLoad_Async(struct Mwin *tw, int nState, void **ppInfo)
{
// I believe that htfwrite.c will do this for us, where necessary
// if (tw) tw->bSilent = TRUE;
switch (nState)
{
case STATE_INIT:
return FTP_DoInit(tw, ppInfo);
case STATE_SECURITY_CHECK:
return SecurityCheck(tw, ((struct Data_LoadFTP*) (*ppInfo))->request, FALSE, FALSE, STATE_FTP_GOTHOST);
case STATE_FTP_GOTHOST:
return FTP_DoGotHost(tw, ppInfo);
case STATE_FTP_CONNECTED:
return FTP_DoConnected(tw, ppInfo);
case STATE_FTP_GOTGREETING:
return FTP_DoGotGreeting(tw, ppInfo);
case STATE_FTP_SENTUSER:
return FTP_DoSentUser(tw, ppInfo);
case STATE_FTP_SENTPASS:
return FTP_DoSentPass(tw, ppInfo);
case STATE_FTP_SENTACCT:
return FTP_DoSentAcct(tw, ppInfo);
case STATE_FTP_LOGGEDIN:
return FTP_DoLoggedIn(tw, ppInfo);
case STATE_FTP_SENTPASV:
return FTP_DoSentPasv(tw, ppInfo);
case STATE_FTP_DOPORT:
return FTP_DoPort(tw, ppInfo);
case STATE_FTP_SENTPORT:
return FTP_DoSentPort(tw, ppInfo);
case STATE_FTP_GOTDATACONN:
return FTP_DoGotDataConn(tw, ppInfo);
case STATE_FTP_SENTTYPE:
return FTP_DoSentType(tw, ppInfo);
case STATE_FTP_SENTRETR:
return FTP_DoSentRetr(tw, ppInfo);
case STATE_FTP_SENTCWD:
return FTP_DoSentCwd(tw, ppInfo);
case STATE_FTP_SENTNLST:
return FTP_DoSentNlst(tw, ppInfo);
case STATE_FTP_GOTDATA:
return FTP_DoGotData(tw, ppInfo);
case STATE_ABORT:
return FTP_Abort(tw, ppInfo);
}
XX_Assert((0), ("Function called with illegal state: %d", nState));
return STATE_DONE;
}
GLOBALDEF PUBLIC HTProtocol HTFTP ={"ftp", NULL, HTFTPLoad_Async};