mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-23 20:32:43 +00:00
95948b35ec
cl_netfps has also had some work. Added polling of http-based master servers (gameaholic). Lots of darkplaces compatability things added, for nexuiz. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@1054 fc73d0e0-1445-4013-8a0c-d673dee63da5
900 lines
20 KiB
C
900 lines
20 KiB
C
#include "quakedef.h"
|
|
|
|
#ifdef WEBCLIENT
|
|
|
|
#include "iweb.h"
|
|
|
|
#ifdef _WIN32
|
|
#define EWOULDBLOCK WSAEWOULDBLOCK
|
|
#define EMSGSIZE WSAEMSGSIZE
|
|
#define ECONNRESET WSAECONNRESET
|
|
#define ECONNABORTED WSAECONNABORTED
|
|
#define ECONNREFUSED WSAECONNREFUSED
|
|
#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
|
|
|
|
#define qerrno WSAGetLastError()
|
|
#else
|
|
#define qerrno errno
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#include <sys/param.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/uio.h>
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#ifdef sun
|
|
#include <sys/filio.h>
|
|
#endif
|
|
|
|
#ifdef NeXT
|
|
#include <libc.h>
|
|
#endif
|
|
|
|
#define closesocket close
|
|
#define ioctlsocket ioctl
|
|
#endif
|
|
|
|
|
|
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;
|
|
|
|
IWEBFILE *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;
|
|
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\nPORT %s\r\n", con->pathprefix, con->path, 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 = con->f->length;
|
|
}
|
|
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
|
|
{
|
|
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, false);
|
|
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;
|
|
}
|
|
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));
|
|
|
|
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
|