1
0
Fork 0
forked from fte/fteqw
fteqw/engine/http/ftpclient.c
Spoike 503eff6421 Reworked the filesystem. We now support a virtual filesystem. Many places accept stream usage, although many formats do not support this.
I'm not sure if this will break anything. It shouldn't do, but it might.

Not everything is ported over yet. Ideally there would be no more use of fopen anywhere else in the engine, and com_gamedir would be made static to fs.c
There are a couple of other changes too.

http/ftp stuff is currently disabled.


git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@1728 fc73d0e0-1445-4013-8a0c-d673dee63da5
2005-12-21 03:07:33 +00:00

894 lines
20 KiB
C

#include "quakedef.h"
#ifdef WEBCLIENT
#include "iweb.h"
#include "netinc.h"
typedef struct FTPclientconn_s{
char server[256];
char name[64];
char pwd[64];
char path[256];
char pathprefix[256]; //Urhum.. Without this we can browse various entire hard drives too easily.
char file[64];
char localfile[MAX_QPATH];
int transfersize;
int transfered;
int controlsock;
int datasock; //FTP only allows one transfer per connection.
enum {ftp_control, ftp_listing, ftp_getting, ftp_putting} type;
int stage;
vfsfile_t *f;
struct FTPclientconn_s *next;
void (*NotifyFunction)(char *localfile, qboolean sucess); //called when failed or succeeded, and only if it got a connection in the first place.
//ftp doesn't guarentee it for anything other than getting though. :(
} FTPclientconn_t;
FTPclientconn_t *FTPclientconn;
FTPclientconn_t *FTP_CreateConnection(char *addy)
{
unsigned long _true = true;
struct sockaddr_qstorage from;
FTPclientconn_t *con;
con = IWebMalloc(sizeof(FTPclientconn_t));
if ((con->controlsock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
{
Sys_Error ("FTP_UDP_OpenSocket: socket: %s\n", strerror(qerrno));
}
{//quake routines using dns and stuff (Really, I wanna keep quake and ftp fairly seperate)
netadr_t qaddy;
NET_StringToAdr (addy, &qaddy);
if (!qaddy.port)
qaddy.port = htons(21);
NetadrToSockadr(&qaddy, &from);
}
//not yet blocking.
if (connect(con->controlsock, (struct sockaddr *)&from, sizeof(from)) == -1)
{
IWebWarnPrintf ("FTP_TCP_OpenSocket: connect: %i %s\n", qerrno, strerror(qerrno));
closesocket(con->controlsock);
IWebFree(con);
return NULL;
}
if (ioctlsocket (con->controlsock, FIONBIO, &_true) == -1) //now make it non blocking.
{
Sys_Error ("FTP_TCP_OpenSocket: ioctl FIONBIO: %s\n", strerror(qerrno));
}
Q_strncpyz(con->server, addy, sizeof(con->server));
strcpy(con->name, "anonymous");
con->next = FTPclientconn;
FTPclientconn = con;
con->stage = 1;
con->type = ftp_control;
strcpy(con->path, "/");
con->datasock = INVALID_SOCKET;
con->transfersize = -1;
con->transfered = 0;
return FTPclientconn;
}
//duplicate a connection to get multiple data channels with a server.
FTPclientconn_t *FTP_DuplicateConnection(FTPclientconn_t *old)
{
FTPclientconn_t *new;
new = FTP_CreateConnection(old->server);
*new->server = '\0'; //mark it as non control
strcpy(new->name, old->name);
strcpy(new->pwd, old->pwd);
strcpy(new->path, old->path);
strcpy(new->pathprefix, old->pathprefix);
return new;
}
int FTP_CL_makelistensocket(void)
{
char name[256];
unsigned long _true = true;
int sock;
struct hostent *hent;
struct sockaddr_in address;
// int fromlen;
address.sin_family = AF_INET;
if (gethostname(name, sizeof(name)) == -1)
return INVALID_SOCKET;
hent = gethostbyname(name);
if (!hent)
return INVALID_SOCKET;
address.sin_addr.s_addr = *(int *)(hent->h_addr_list[0]);
address.sin_port = 0;
if ((sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
{
Sys_Error ("FTP_TCP_OpenSocket: socket:", strerror(qerrno));
}
if (ioctlsocket (sock, FIONBIO, &_true) == -1)
{
Sys_Error ("FTP_TCP_OpenSocket: ioctl FIONBIO:", strerror(qerrno));
}
if( bind (sock, (void *)&address, sizeof(address)) == -1)
{
closesocket(sock);
return INVALID_SOCKET;
}
listen(sock, 1);
return sock;
}
int FTP_CL_makeconnectsocket(char *ftpdest)
{
unsigned long _true = true;
int sock;
struct sockaddr_in address;
if (!ftpdest)
return 0;
if (*ftpdest == '(')
ftpdest++;
if ((sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
{
IWebWarnPrintf ("FTP_UDP_OpenSocket: socket:", strerror(qerrno));
return INVALID_SOCKET;
}
if (ioctlsocket (sock, FIONBIO, &_true) == -1)
{
closesocket(sock);
IWebWarnPrintf ("FTTP_UDP_OpenSocket: ioctl FIONBIO:", strerror(qerrno));
return INVALID_SOCKET;
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = 0;
if( bind (sock, (void *)&address, sizeof(address)) == -1)
{
closesocket(sock);
IWebWarnPrintf ("FTTP_UDP_OpenSocket: bind:", strerror(qerrno));
return INVALID_SOCKET;
}
FTP_StringToAdr(ftpdest, (qbyte *)&address.sin_addr, (qbyte *)&address.sin_port);
//this is commented out because connect always reports would_block, no matter what happens. So why check?
//if (
connect(sock, (struct sockaddr *)&address, sizeof(address));// == -1)
/* {
closesocket(sock);
Con_Printf ("FTTP_UDP_OpenSocket: ioctl FIONBIO:", strerror(qerrno));
return INVALID_SOCKET;
}
*/
return sock;
}
iwboolean FTP_SocketToString (int socket, char *s)
{
struct sockaddr_in addr;
int adrlen = sizeof(addr);
if (getsockname(socket, (struct sockaddr*)&addr, &adrlen) == -1)
return false;
sprintf(s, "%i,%i,%i,%i,%i,%i", ((qbyte*)&addr.sin_addr)[0], ((qbyte*)&addr.sin_addr)[1], ((qbyte*)&addr.sin_addr)[2], ((qbyte*)&addr.sin_addr)[3], ((qbyte *)&addr.sin_port)[0], ((qbyte *)&addr.sin_port)[1]);
return true;
}
iwboolean FTP_ClientConnThink (FTPclientconn_t *con) //true to kill con
{
char *line, *msg;
int ret;
char readdata[8192];
char tempbuff[8192];
if (con->stage == 6)
{
int len;
if (con->type == ftp_getting)
{
if (!cls.downloadmethod || (cls.downloadmethod == DL_FTP && !strcmp(cls.downloadname, con->localfile)))
{
strcpy(cls.downloadname, con->localfile);
cls.downloadmethod = DL_FTP;
if (con->transfersize == -1)
cls.downloadpercent=50;
else
cls.downloadpercent = con->transfered*100.0f/con->transfersize;
}
while((len = recv(con->datasock, readdata, sizeof(readdata), 0)) >0 )
{
IWebFWrite(readdata, len, 1, con->f);
con->transfered += len;
}
if (len == 0)
{
closesocket(con->datasock);
con->datasock = INVALID_SOCKET;
}
}
else if (con->type == ftp_putting)
{
int pos, sent;
int ammount, wanted = sizeof(readdata);
pos = IWebFTell(con->f);
ammount = IWebFRead(readdata, 1, wanted, con->f);
sent = send(con->datasock, readdata, ammount, 0);
if (sent == -1)
IWebFSeek(con->f, pos, SEEK_SET); //go back. Too much data
else
{
IWebFSeek(con->f, pos + sent, SEEK_SET); //written this much
if (!ammount) //file is over
{
closesocket(con->datasock);
con->datasock = INVALID_SOCKET;
// msg = "226 Transfer complete.\r\n";
// send (con->controlsock, msg, strlen(msg), 0);
}
}
}
}
ret = recv(con->controlsock, (char *)readdata, sizeof(readdata)-1, 0);
if (ret == -1)
{
if (qerrno == EWOULDBLOCK)
return false;
if (qerrno == ECONNABORTED || qerrno == ECONNRESET)
{
Con_TPrintf (TL_CONNECTIONLOSTORABORTED);
return true;
}
// Con_TPrintf (TL_NETGETPACKETERROR, strerror(qerrno));
return true;
}
readdata[ret] = '\0'; //null terminate. (it's a string)
//we now have a message.
//We've got to work out what has happened already.
//a server can send many lines of text for one reply
//220-hello
// this
//220-is
// 220-all
//220 one reply
//so we only read lines that contain number space words, without any leading space
line = readdata;
while (1)
{
msg = line;
while (*line)
{
if (*line == '\n')
break;
if (*line == '\r')
break;
line++;
}
if (!*line) //broken message
break;
*line = '\0';
line++;
if (*con->server)
IWebDPrintf("^2FTP: %s\n", COM_TrimString(msg));
if (*msg < '0' || *msg > '9') //make sure it starts with number
continue;
ret = atoi(msg);
while(*msg >= '0' && *msg <= '9') //find next non number
msg++;
if (*msg != ' ') //must be a space (definatly not a '-')
continue;
msg++;
if (ret == 220)
{
sprintf(tempbuff, "USER %s\r\n", con->name);
send(con->controlsock, tempbuff, strlen(tempbuff), 0);
con->stage = 1;
}
else if (ret == 331)
{
if (con->type == ftp_control)
sprintf(tempbuff, "PASS %s\r\nPWD %s\r\n", con->pwd, con->path);
else
sprintf(tempbuff, "PASS %s\r\n", con->pwd);
send(con->controlsock, tempbuff, strlen(tempbuff), 0);
con->stage = 2;
}
else if (ret == 230) //we must now do something useful
{
char adr[64];
if (con->type == ftp_control) //control is for browsing and duplicating
continue;
con->datasock = FTP_CL_makelistensocket();
if (!con->datasock || !FTP_SocketToString(con->datasock, adr))
{
return true;
}
sprintf(tempbuff, "CWD %s%s\r\n", con->pathprefix, con->path);
send(con->controlsock, tempbuff, strlen(tempbuff), 0);
goto usepasv;
sprintf(tempbuff, "PORT %s\r\n", adr);
send(con->controlsock, tempbuff, strlen(tempbuff), 0);
con->stage = 3;
}
else if (ret == 200)
{
struct sockaddr addr;
int addrlen = sizeof(addr);
int temp;
if (con->type == ftp_control)
continue;
if (con->stage == 3)
{
temp = accept(con->datasock, &addr, &addrlen);
closesocket(con->datasock);
con->datasock = temp;
if (temp != INVALID_SOCKET)
{
con->stage = 6;
if (con->type == ftp_getting)
{
con->f = IWebFOpenWrite(con->localfile, false);
if (con->f)
{
sprintf(tempbuff, "RETR %s\r\n", con->file);
con->stage = 6;
con->transfered = 0;
con->transfersize = -1;
}
else
{
sprintf(tempbuff, "QUIT\r\n");
con->stage = 7;
}
}
else if (con->type == ftp_putting)
{
con->f = IWebFOpenRead(con->localfile);
if (con->f)
{
sprintf(tempbuff, "STOR %s\r\n", con->file);
con->stage = 6;
con->transfered = 0;
con->transfersize = VFS_GETLEN(con->f);
}
else
{
sprintf(tempbuff, "QUIT\r\n");
con->stage = 7;
}
}
else
sprintf(tempbuff, "LIST %s\r\n", con->pwd);
send(con->controlsock, tempbuff, strlen(tempbuff), 0);
}
else
{
usepasv:
Con_Printf("FTP: Trying passive server mode\n");
msg = va("PASV\r\n");
send(con->controlsock, msg, strlen(msg), 0);
con->stage = 4;
}
}
}
else if (ret == 213)
{
con->transfersize = atoi(msg);
msg = va("RETR %s\r\n", con->file);
con->stage = 6;
con->transfered = 0;
send(con->controlsock, msg, strlen(msg), 0);
}
else if (ret == 125) //begining transfer
{
if (con->type == ftp_getting)
{
COM_StripExtension(con->localfile, msg);
strcat(msg, ".tmp");
con->f = IWebFOpenWrite(msg, false);
if (!con->f)
{
msg = va("ABOR\r\nQUIT\r\n"); //bummer. we couldn't open this file to output to.
send(con->controlsock, msg, strlen(msg), 0);
con->stage = 7;
return true;
}
}
// msg = va("LIST\r\n");
// send(con->controlsock, msg, strlen(msg), 0);
con->stage = 6;
}
else if (ret == 226) //transfer complete
{
int len;
char data[1024];
if (con->f)
{
if (con->type == ftp_getting)
{
while(1) //this is potentially dodgy.
{
len = recv(con->datasock, data, sizeof(data), 0);
if (len == 0)
break;
if (len == -1)
{
if (qerrno != EWOULDBLOCK)
break;
continue;
}
con->transfered+=len;
data[len] = 0;
IWebFWrite(data, len, 1, con->f);
}
}
IWebFClose(con->f);
con->f = NULL;
closesocket(con->datasock);
con->datasock = INVALID_SOCKET;
if (con->NotifyFunction)
{
con->NotifyFunction(con->localfile, true);
con->NotifyFunction = NULL;
}
if (con->transfersize != -1 && con->transfered != con->transfersize)
{
IWebPrintf("Transfer corrupt\nTransfered %i of %i bytes\n", con->transfered, con->transfersize);
}
else
IWebPrintf("Transfer compleate\n");
}
else
{
while((len = recv(con->datasock, data, sizeof(data), 0)) >0 )
{
data[len] = 0;
if (strchr(data, '\r'))
{
line = data;
for(;;)
{
msg = strchr(line, '\r');
if (!msg)
break;
*msg = '\0';
Con_Printf("%s", line);
line = msg+1;
}
Con_Printf("%s", line);
}
else
Con_Printf("%s", data);
}
closesocket(con->datasock);
con->datasock = INVALID_SOCKET;
}
msg = va("QUIT\r\n");
send(con->controlsock, msg, strlen(msg), 0);
con->stage = 7;
}
else if (ret == 227)
{
// Con_Printf("FTP: Got passive server mode\n");
if (con->datasock != INVALID_SOCKET)
closesocket(con->datasock);
con->datasock = INVALID_SOCKET;
con->datasock = FTP_CL_makeconnectsocket(strchr(msg, '('));
if (con->datasock != INVALID_SOCKET)
{
if (con->type == ftp_getting)
{
con->f = IWebFOpenWrite(con->localfile, false);
if (con->f)
{
con->stage = 8;
msg = va("TYPE I\r\nSIZE %s\r\n", con->file);
con->transfersize = -1;
/*
msg = va("RETR %s\r\n", con->file);
con->stage = 6;
con->transfered = 0;
*/
}
else
{
msg = va("QUIT\r\n");
con->stage = 7;
Con_Printf("FTP: Failed to open local file %s\n", con->localfile);
}
}
else if (con->type == ftp_putting)
{
con->f = IWebFOpenRead(con->localfile);
if (con->f)
{
msg = va("STOR %s\r\n", con->file);
con->stage = 6;
con->transfered = 0;
con->transfersize = con->f->length;
}
else
{
msg = va("QUIT\r\n");
con->stage = 7;
Con_Printf("FTP: Failed to open local file %s\n", con->localfile);
}
}
else
{
msg = "LIST\r\n";
con->stage = 6;
}
}
else
{
msg = "QUIT\r\n";
con->stage = 7;
Con_Printf("FTP: Didn't connect\n");
}
send (con->controlsock, msg, strlen(msg), 0);
}
else if (ret == 250)
{
Con_Printf("FTP: %i %s\n", ret, msg);
}
else if (ret == 257)
{ //stick it on the beginning.
Con_Printf("FTP: %i %s\n", ret, msg);
msg = strchr(msg, '"');
if (msg)
{
Q_strncpyz(con->pathprefix, msg+1, sizeof(con->pathprefix));
msg = strchr(con->pathprefix, '"');
if (msg)
*msg = '\0';
}
else
Q_strcpyline(con->pathprefix, msg+4, sizeof(con->pathprefix)-1);
}
else
{
if (ret < 200)
continue;
if (con->stage == 5)
{
Con_DPrintf("FTP: Trying passive server mode\n");
msg = va("PASV\r\n");
send(con->controlsock, msg, strlen(msg), 0);
con->stage = 4;
continue;
}
if (ret != 221)
Con_Printf("^1FTP: %i %s\n", ret, msg);
return true;
}
continue;
}
return false;
}
void FTP_ClientThink (void)
{
FTPclientconn_t *con, *old=NULL;
for (con = FTPclientconn; con; con = con->next)
{
if (FTP_ClientConnThink(con))
{
if (con->NotifyFunction)
con->NotifyFunction(con->localfile, false);
if (cls.downloadmethod == DL_FTP && !strcmp(cls.downloadname, con->localfile))
{ //this was us
cls.downloadmethod = DL_NONE;
}
if (con->f)
IWebFClose(con->f);
if (con->controlsock != INVALID_SOCKET)
closesocket(con->controlsock);
if (con->datasock != INVALID_SOCKET)
closesocket(con->datasock);
if (!old)
{
FTPclientconn = con->next;
IWebFree(con);
break;
}
else
{
old->next = con->next;
IWebFree(con);
break;
}
}
old = con;
}
}
FTPclientconn_t *FTP_FindControl(void)
{
FTPclientconn_t *con;
for (con = FTPclientconn; con; con = con->next)
{
if (*con->server)
return con;
}
return NULL;
}
void FTP_FixupPath(char *out)
{ //convert \[ to [
char *in;
in = out;
while (*in)
{
if (*in == '\\')
{
in++;
if (*in == '\0')
break;
}
*out++ = *in++;
}
*out++ = '\0';
}
qboolean FTP_Client_Command (char *cmd, void (*NotifyFunction)(char *localfile, qboolean sucess))
{
char command[64];
char server[MAX_OSPATH];
FTPclientconn_t *con;
cmd = COM_ParseOut(cmd, command, sizeof(command));
if (!stricmp(command, "open"))
{
if (FTP_FindControl())
Con_Printf("You are already connected\n");
else
{
cmd = COM_ParseOut(cmd, server, sizeof(server));
if ((con = FTP_CreateConnection(server)))
{
Con_Printf("FTP connect succeded\n");
cmd = COM_ParseOut(cmd, command, sizeof(command));
if (cmd)
{
Q_strncpyz(con->name, command, sizeof(con->name));
cmd = COM_ParseOut(cmd, command, sizeof(command));
if (cmd)
Q_strncpyz(con->pwd, command, sizeof(con->pwd));
}
return true;
}
else
Con_Printf("FTP connect failed\n");
}
return false;
}
else if (!stricmp(command, "download"))
{
cmd = COM_ParseOut(cmd, server, sizeof(server));
con = FTP_CreateConnection(server);
if (!con)
{
Con_Printf("FTP: Couldn't connect\n");
return false;
}
con->NotifyFunction = NotifyFunction;
*con->server = '\0';
con->type = ftp_getting;
cmd = COM_ParseOut(cmd, server, sizeof(server));
Q_strncpyz(con->file, server, sizeof(con->file));
Q_strncpyz(con->localfile, server, sizeof(con->localfile));
if ((cmd = COM_ParseOut(cmd, server, sizeof(server))))
Q_strncpyz(con->localfile, server, sizeof(con->localfile));
FTP_FixupPath(con->file);
FTP_FixupPath(con->localfile);
return true;
}
else if (!stricmp(command, "quit"))
{
con = FTP_FindControl();
if (con)
{
char *msg;
msg = va("QUIT\r\n");
send(con->controlsock, msg, strlen(msg), 0);
// if (con->datasock)
// closesocket(con->datasock);
// closesocket(con->controlsock);
}
else
Con_Printf("No main FTP connection\n");
return true;
}
else if (!stricmp(command, "list"))
{
FTPclientconn_t *new, *con = FTP_FindControl();
if (!con)
{
Con_Printf("Not connected\n");
return false;
}
new = FTP_DuplicateConnection(con);
if (!new)
{
Con_Printf("Failed duplicate connection\n");
return false;
}
new->type = ftp_listing;
new->NotifyFunction = NotifyFunction;
return true;
}
else if (!stricmp(command, "get"))
{
FTPclientconn_t *new, *con = FTP_FindControl();
if (!con)
{
Con_Printf("Not connected\n");
return false;
}
cmd = COM_ParseOut(cmd, command, sizeof(command));
if (!cmd)
{
Con_Printf("No file specified\n");
return false;
}
new = FTP_DuplicateConnection(con);
if (!new)
{
Con_Printf("Failed duplicate connection\n");
return false;
}
new->NotifyFunction = NotifyFunction;
new->type = ftp_getting;
sprintf(new->file, command);
sprintf(new->localfile, "%s%s", new->path, command);
return true;
}
else if (!stricmp(command, "put"))
{
FTPclientconn_t *new, *con = FTP_FindControl();
if (!con)
{
Con_Printf("Not connected\n");
return false;
}
cmd = COM_ParseOut(cmd, command, sizeof(command));
if (!cmd)
{
Con_Printf("No file specified\n");
return false;
}
new = FTP_DuplicateConnection(con);
if (!new)
{
Con_Printf("Failed duplicate connection\n");
return false;
}
new->NotifyFunction = NotifyFunction;
new->type = ftp_putting;
sprintf(new->file, command);
sprintf(new->localfile, "%s%s", new->path, command);
return true;
}
else if (!stricmp(command, "cwd"))
{
FTPclientconn_t *con = FTP_FindControl();
if (!con)
{
Con_Printf("Not connected\n");
return false;
}
Con_Printf("%s\n", con->path);
return true;
}
else if (!stricmp(command, "cd"))
{
char *msg;
FTPclientconn_t *con = FTP_FindControl();
if (!con)
{
Con_Printf("Not connected\n");
return false;
}
cmd = COM_ParseOut(cmd, command, sizeof(command));
if (*command == '/') //absolute
Q_strncpyz(con->path, command, sizeof(con->path));
else //bung it on the end
{
strncat(con->path, "/", sizeof(con->path)-1);
strncat(con->path, command, sizeof(con->path)-1);
}
msg = va("CWD %s%s\r\n", con->pathprefix, con->path);
if (send(con->controlsock, msg, strlen(msg), 0)==strlen(msg))
return true;
}
else
Con_Printf("Unrecognised FTP command\n");
/*
com = COM_ParseOut(com, command, sizeof(command));
com = COM_ParseOut(com, command, sizeof(command));
com = COM_ParseOut(com, command, sizeof(command));
*/
return false;
}
#endif