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.
 
 
 
 
 
 

1443 lines
40 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"
#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 PUTC(c) (*targetClass.put_character)(target, c)
#define PUTS(s) (*targetClass.put_string)(target, s)
#define START(e) (*targetClass.start_element)(target, e, 0, 0)
#define END(e) (*targetClass.end_element)(target, e)
#define FREE_TARGET (*targetClass.free)(target)
struct _HTStructured
{
CONST HTStructuredClass *isa;
/* ... */
};
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 */
char text[LINE_LENGTH + 1];
int index; /* index into text[] */
};
#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;
signed char ch;
pParams = *ppInfo;
switch (nState)
{
case STATE_INIT:
pParams->index = 0;
pParams->cont_resp = -1;
/* Send command */
if (pParams->cmd)
{
struct Params_Send *pps;
pps = GTR_CALLOC(sizeof(*pps), 1);
if (pps)
{
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;
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
/* 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;
do
{
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->index < LINE_LENGTH)
{
pParams->text[pParams->index++] = ch;
}
}
/* Step past the character we just read in the isoc */
pParams->isoc->input_pointer++;
/* If we didn't quit the loop because of finding an LF, get more */
if (ch != LF)
{
struct Params_Isoc_Fill *pif;
pif = GTR_CALLOC(sizeof(*pif), 1);
if (pif)
{
pif->isoc = pParams->isoc;
pif->pStatus = &pParams->net_status;
Async_DoCall(Isoc_Fill_Async, pif);
return STATE_COMMAND_GOTDATA;
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
/* Terminate this line of stuff */
pParams->text[pParams->index] = '\0';
XX_DMsg(DBG_LOAD, ("FTP: Other side sent %s\n", pParams->text));
*pParams->pResult = atoi(pParams->text);
ch = pParams->text[3];
if (pParams->cont_resp == -1)
{
if (ch == '-')
{
/* Start continuation */
pParams->cont_resp = *pParams->pResult;
pParams->index = 0;
}
}
else
{
/* Continuing */
if (pParams->cont_resp == *pParams->pResult && ch == ' ')
{
/* End of continuation */
pParams->cont_resp = -1;
}
}
pParams->index = 0;
} while (pParams->cont_resp != -1);
if (pParams->ppText)
{
*pParams->ppText = GTR_MALLOC(strlen(pParams->text) + 1);
if (*pParams->ppText)
{
strcpy(*pParams->ppText, pParams->text);
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
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);
}
*pParams->pResult = -1;
return STATE_DONE;
}
XX_Assert((0), ("Function called with illegal state: %d", nState));
return STATE_DONE;
}
/*************************************************************/
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;
};
#define STATE_READDIR_GOTDATA (STATE_OTHER)
static int FTP_ReadDir_Async(struct Mwin *tw, int nState, void **ppInfo)
{
struct Params_FTP_ReadDir *pParams;
char *address;
char *filename;
char *pNext;
signed char ch;
HTBTElement *ele;
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. */
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, 0);
pParams->bt = HTBTree_new((HTComparer) strcasecomp);
pParams->chunk = HTChunkCreate(128);
(*pParams->target->isa->start_element)(pParams->target, HTML_DIR, 0, 0);
pParams->isoc = HTInputSocket_new(pParams->data_soc);
{
struct Params_Isoc_Fill *pif;
pif = GTR_CALLOC(sizeof(*pif), 1);
if (pif)
{
pif->isoc = pParams->isoc;
pif->pStatus = pParams->pStatus;
Async_DoCall(Isoc_Fill_Async, pif);
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
return STATE_READDIR_GOTDATA;
case STATE_READDIR_GOTDATA:
if (*pParams->pStatus > 0)
{
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);
filename = GTR_strdup(pParams->chunk->data);
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);
/* Get next block of data */
pif = GTR_CALLOC(sizeof(*pif), 1);
if (pif)
{
pif->isoc = pParams->isoc;
pif->pStatus = pParams->pStatus;
Async_DoCall(Isoc_Fill_Async, pif);
return STATE_READDIR_GOTDATA;
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
}
/* 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), 0);
}
/* Fall through */
case STATE_ABORT:
(*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;
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 */
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_GOTDATACONN (STATE_OTHER + 8)
#define STATE_FTP_SENTTYPE (STATE_OTHER + 9)
#define STATE_FTP_SENTRETR (STATE_OTHER + 10)
#define STATE_FTP_SENTCWD (STATE_OTHER + 11)
#define STATE_FTP_SENTNLST (STATE_OTHER + 12)
#define STATE_FTP_GOTDATA (STATE_OTHER + 13)
static void FTP_CleanUp(struct Mwin *tw, struct Data_LoadFTP *pData)
{
if (pData->bWaiting)
{
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->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;
FTP_CleanUp(tw, pData);
/* The connection might now be in an invalid state */
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;
#ifndef _GIBRALTAR
char buf[MAX_URL_STRING];
#endif // _GIBRALTAR
pParams = *ppInfo;
name = pParams->request->destination->szActualURL;
if (!name || !*name)
{
*pParams->pStatus = -2;
return STATE_DONE;
}
#ifndef _GIBRALTAR
/* 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);
*pParams->pStatus = HT_REDIRECTION_ON_FLY;
GTR_FREE(filename);
return STATE_DONE;
}
GTR_FREE(filename);
}
#endif // _GIBRALTAR
/* Copy the parameters we were passed into our own, larger structure. */
pData = GTR_CALLOC(sizeof(struct Data_LoadFTP), 1);
if (pData)
{
memset(pData, 0, sizeof(*pData));
pData->request = pParams->request;
pData->pStatus = pParams->pStatus;
GTR_FREE(pParams);
*ppInfo = pData;
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
pData->request->content_length = 0;
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_CALLOC(sizeof(*ppi), 1);
if (ppi)
{
ppi->pAddress = &pData->address;
ppi->pPort = &pData->port;
ppi->str = pData->pszHost;
ppi->pStatus = &pData->net_status;
Async_DoCall(Net_MultiParse_Async, ppi);
return STATE_FTP_GOTHOST;
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
static int FTP_DoGotHost(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
pData = *ppInfo;
GTR_FREE(pData->pszHost);
pData->pszHost = NULL;
if (pData->net_status < 0)
{
XX_DMsg(DBG_LOAD, ("Net_Parse_Async returned %d\n", pData->net_status));
*pData->pStatus = -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->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 ? 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_GetString(SID_INF_CONNECTING_TO_FTP_SERVER));
pData->bWaiting = TRUE;
{
/* Do connect call */
struct Params_MultiConnect *ppc;
ppc = GTR_CALLOC(sizeof(*ppc), 1);
if (ppc)
{
ppc->pSocket = &pData->s;
ppc->pAddress = &pData->address;
ppc->nPort = pData->port;
ppc->pWhere = &pData->where;
ppc->pStatus = &pData->net_status;
#ifdef FEATURE_SOCKS_LOW_LEVEL
ppc->bUseSocksProxy = pData->request->destination->bUseSocksProxy;
#endif
Async_DoCall(Net_MultiConnect_Async, ppc);
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
return STATE_FTP_CONNECTED;
}
static int FTP_DoConnected(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
struct Params_FTP_Command *pfc;
pData = *ppInfo;
if (pData->net_status < 0)
{
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 = -1;
FTP_CleanUp(tw, pData);
return STATE_DONE;
}
WAIT_Update(tw, waitSameInteract, GTR_GetString(SID_INF_LOGGING_INTO_FTP_SERVER));
pData->isoc = HTInputSocket_new(pData->s);
/* Get greeting. */
pfc = GTR_CALLOC(sizeof(*pfc), 1);
if (pfc)
{
pfc->isoc = pData->isoc;
pfc->cmd = NULL;
pfc->ppText = NULL;
pfc->pResult = &pData->response;
Async_DoCall(FTP_Command_Async, pfc);
return STATE_FTP_GOTGREETING;
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
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));
if (command)
{
sprintf(command, "USER %s\015\012", pData->username);
GTR_FREE(pData->username);
pData->username = NULL;
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
else
{
command = GTR_MALLOC(25);
if (command)
{
strcpy(command, "USER anonymous\015\012");
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
pfc = GTR_CALLOC(sizeof(*pfc), 1);
if (pfc)
{
pfc->isoc = pData->isoc;
pfc->cmd = command;
pfc->ppText = NULL;
pfc->pResult = &pData->response;
Async_DoCall(FTP_Command_Async, pfc);
return STATE_FTP_SENTUSER;
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
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));
if (command)
{
sprintf(command, "PASS %s\015\012", pData->password);
GTR_FREE(pData->password);
pData->password = NULL;
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
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);
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
pfc = GTR_CALLOC(sizeof(*pfc), 1);
if (pfc)
{
pfc->isoc = pData->isoc;
pfc->cmd = command;
pfc->ppText = NULL;
pfc->pResult = &pData->response;
Async_DoCall(FTP_Command_Async, pfc);
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
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);
if (command)
{
strcpy(command, "ACCT noaccount\015\012");
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
pfc = GTR_CALLOC(sizeof(*pfc), 1);
if (pfc)
{
pfc->isoc = pData->isoc;
pfc->cmd = command;
pfc->ppText = NULL;
pfc->pResult = &pData->response;
Async_DoCall(FTP_Command_Async, pfc);
return STATE_FTP_SENTACCT;
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
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;
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_GetString(SID_INF_ESTABLISHING_FTP_CONNECTION));
}
else
{
WAIT_Push(tw, waitSameInteract, GTR_GetString(SID_INF_ESTABLISHING_FTP_CONNECTION));
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);
if (command)
{
strcpy(command, "PASV\015\012");
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
pfc = GTR_CALLOC(sizeof(*pfc), 1);
if (pfc)
{
pfc->isoc = pData->isoc;
pfc->cmd = command;
pfc->ppText = &pData->pResText;
pfc->pResult = &pData->response;
Async_DoCall(FTP_Command_Async, pfc);
return STATE_FTP_SENTPASV;
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
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)
{
ERR_ReportError(tw, SID_ERR_PASSIVE_MODE_NOT_SUPPORTED, 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;
}
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, SID_ERR_PASSIVE_MODE_NOT_SUPPORTED, 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_CALLOC(sizeof(*ppc), 1);
if (ppc)
{
ppc->socket = pData->data_soc;
memcpy(&ppc->address, &pData->data_addr, sizeof(ppc->address));
ppc->pStatus = &pData->net_status;
#ifdef FEATURE_SOCKS_LOW_LEVEL
ppc->bUseSocksProxy = pData->request->destination->bUseSocksProxy;
#endif
Async_DoCall(Net_Connect_Async, ppc);
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
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;
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_GetString(SID_INF_SENDING_FTP_COMMANDS));
/* 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 != HTAtom_for("8bit") &&
pData->request->content_encoding != HTAtom_for("7bit"));
command = GTR_MALLOC(10);
if (command)
{
sprintf(command, "TYPE %c\015\012", bBinary ? 'I' : 'A');
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
pfc = GTR_CALLOC(sizeof(*pfc), 1);
if (pfc)
{
pfc->isoc = pData->isoc;
pfc->cmd = command;
pfc->ppText = NULL;
pfc->pResult = &pData->response;
Async_DoCall(FTP_Command_Async, pfc);
return STATE_FTP_SENTTYPE;
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
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);
if (command)
{
sprintf(command, "RETR %s\015\012", pData->filename);
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
pfc = GTR_CALLOC(sizeof(*pfc), 1);
if (pfc)
{
pfc->isoc = pData->isoc;
pfc->cmd = command;
pfc->ppText = &pData->pResText;
pfc->pResult = &pData->response;
Async_DoCall(FTP_Command_Async, pfc);
return STATE_FTP_SENTRETR;
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
else {
/* Illegal response! */
*pData->pStatus = -1;
/* The connection might now be in an invalid state */
TW_DisposeConnection(&tw->cached_conn);
FTP_CleanUp(tw, pData);
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;
pData = *ppInfo;
if (pData->response == 1)
{
if (pData->pResText)
{
/* Parse out file size */
pSize = strrchr(pData->pResText, '(');
if (pSize)
{
pData->request->content_length = atoi(pSize + 1);
}
GTR_FREE(pData->pResText);
pData->pResText = NULL;
}
pps = GTR_CALLOC(sizeof(*pps), 1);
if (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
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
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);
if (command)
{
#if 0
if (!lstrcmpi(pData->filename, "/"))
{
strcpy(command, "CWD \015\012");
}
else
{
sprintf(command, "CWD %s\015\012", pData->filename);
}
#else
sprintf(command, "CWD %s\015\012", pData->filename);
#endif // _GIBRALTAR
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
pfc = GTR_CALLOC(sizeof(*pfc), 1);
if (pfc)
{
pfc->isoc = pData->isoc;
pfc->cmd = command;
pfc->ppText = NULL;
pfc->pResult = &pData->response;
Async_DoCall(FTP_Command_Async, pfc);
return STATE_FTP_SENTCWD;
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
}
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;
/* The connection might now be in an invalid state */
TW_DisposeConnection(&tw->cached_conn);
FTP_CleanUp(tw, pData);
return STATE_DONE;
}
command = GTR_MALLOC(10);
if (command)
{
strcpy(command, "NLST\015\012");
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
pfc = GTR_CALLOC(sizeof(*pfc), 1);
if (pfc)
{
pfc->isoc = pData->isoc;
pfc->cmd = command;
pfc->ppText = NULL;
pfc->pResult = &pData->response;
Async_DoCall(FTP_Command_Async, pfc);
return STATE_FTP_SENTNLST;
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
static int FTP_DoSentNlst(struct Mwin *tw, void **ppInfo)
{
struct Data_LoadFTP *pData;
struct Params_FTP_ReadDir *pfrd;
pData = *ppInfo;
if (pData->response != 1)
{
/* The server isn't sending us data */
*pData->pStatus = -1;
/* The connection might now be in an invalid state */
TW_DisposeConnection(&tw->cached_conn);
FTP_CleanUp(tw, pData);
return STATE_DONE;
}
WAIT_Update(tw, waitSameInteract, GTR_GetString(SID_INF_RECEIVING_FTP_DIRECTORY_LISTING));
/* Read in the directory listing */
pfrd = GTR_MALLOC(sizeof(*pfrd));
if (pfrd)
{
pfrd->request = pData->request;
pfrd->pStatus = &pData->net_status;
pfrd->data_soc = pData->data_soc;
Async_DoCall(FTP_ReadDir_Async, pfrd);
return STATE_FTP_GOTDATA;
}
else
{
ERR_ReportError(tw, SID_ERR_OUT_OF_MEMORY, NULL, NULL);
return STATE_ABORT;
}
}
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)
{
switch (nState)
{
case STATE_INIT:
return FTP_DoInit(tw, ppInfo);
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_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};