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.
1443 lines
40 KiB
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};
|