fteqw/engine/http/ftpserver.c
Spoike ffc2a08589 pass network addresses around as a pointer rather than as a struct. They've grown quite a bit from vanilla code and can now be quite large. this should give more efficient network filtering+matching.
Added version+time+date to segfault lots.
try to use vbo+vao as needed.
added a manifest file in order to disable uac emulation and its virtual store lies.
particles now support a sort of namespace. eg: an effect called "cfg.effect" will load up the 'cfg' particle config and use its 'effect' effect (but not replace any explicit effects). You can still create particle effects called 'cfg.effect' with no issue.
Added support for fsarchive plugins.
Added a sys_register_file_associations command. .bsp not yet handled, but demo playback should work fine.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4324 fc73d0e0-1445-4013-8a0c-d673dee63da5
2013-05-03 04:28:08 +00:00

999 lines
23 KiB
C

#include "quakedef.h"
#ifdef WEBSVONLY
#undef vsnprintf
#undef _vsnprintf
#ifdef _WIN32
#define vsnprintf _vsnprintf
#endif
#endif
#ifdef WEBSERVER
#include "iweb.h"
//hows this as a bug.
//TCP data can travel at different speeds.
//If the later bits of a data channel arrive after the message saying that a transfer was compleate,
//the later bits of the file may not arrive before the client closes the conenction.
//this is a major bug and can prevent the server from giving files at a high pl/ping
#include "netinc.h"
static iwboolean ftpserverinitied = false;
static int ftpserversocket = INVALID_SOCKET;
qboolean ftpserverfailed;
typedef struct FTPclient_s{
char name[64];
char pwd[64];
int auth; //has it got auth?
char path[256];
char commandbuffer[256];
char messagebuffer[256];
int cmdbuflen;
int msgbuflen;
int controlsock;
int datasock; //FTP only allows one transfer per connection.
int dataislisten;
int datadir; //0 no data, 1 reading, 2 writing
vfsfile_t *file;
unsigned long blocking;
struct FTPclient_s *next;
} FTPclient_t;
FTPclient_t *FTPclient;
int FTP_BeginListening(int aftype, int port)
{
struct sockaddr_qstorage address;
unsigned long _true = true;
unsigned long _false = false;
int i;
int sock;
#ifdef IPPROTO_IPV6
if ((sock = socket ((aftype!=1)?PF_INET6:PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
#else
if ((sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
#endif
{
IWebPrintf ("FTP_BeginListening: socket: %s\n", strerror(qerrno));
return INVALID_SOCKET;
}
if (ioctlsocket (sock, FIONBIO, &_true) == -1)
{
IWebPrintf ("FTP_BeginListening: ioctl FIONBIO: %s", strerror(qerrno));
return INVALID_SOCKET;
}
#ifdef IPPROTO_IPV6
if (aftype != 1)
{
//0=ipv4+ipv6
//2=ipv6 only
if (aftype == 0)
{
if (0 > setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&_false, sizeof(_false)))
{
//abort and do ipv4 only if hybrid sockets don't work.
closesocket(sock);
return FTP_BeginListening(1, port);
}
}
memset(&address, 0, sizeof(address));
((struct sockaddr_in6*)&address)->sin6_family = AF_INET6;
if (port == PORT_ANY)
((struct sockaddr_in6*)&address)->sin6_port = 0;
else
((struct sockaddr_in6*)&address)->sin6_port = htons((short)port);
}
else
#endif
{
//1=ipv4 only
((struct sockaddr_in*)&address)->sin_family = AF_INET;
//ZOID -- check for interface binding option
if ((i = COM_CheckParm("-ip")) != 0 && i < com_argc) {
((struct sockaddr_in*)&address)->sin_addr.s_addr = inet_addr(com_argv[i+1]);
Con_TPrintf(TL_NETBINDINTERFACE,
inet_ntoa(((struct sockaddr_in*)&address)->sin_addr));
} else
((struct sockaddr_in*)&address)->sin_addr.s_addr = INADDR_ANY;
if (port == PORT_ANY)
((struct sockaddr_in*)&address)->sin_port = 0;
else
((struct sockaddr_in*)&address)->sin_port = htons((short)port);
}
if( bind (sock, (void *)&address, sizeof(address)) == -1)
{
IWebPrintf("FTP_BeginListening: failed to bind socket\n");
closesocket(ftpserversocket);
return INVALID_SOCKET;
}
listen(sock, 3);
return sock;
}
void FTP_ServerShutdown(void)
{
closesocket(ftpserversocket);
ftpserverinitied = false;
IWebPrintf("FTP server is deactivated\n");
}
//we ought to filter this to remove duplicates.
static int QDECL SendFileNameTo(const char *rawname, int size, void *param, struct searchpath_s *spath)
{
int socket = *(int*)param;
// int i;
char buffer[256+1];
char *slash;
char nondirname[MAX_QPATH];
int isdir = rawname[strlen(rawname)-1] == '/';
char *fname;
#ifndef WEBSVONLY //copy protection of the like that QWSV normally has.
if (!isdir)
if (!SV_AllowDownload(rawname)) //don't advertise if we're going to disallow it
return true;
#endif
Q_strncpyz(nondirname, rawname, sizeof(nondirname));
if (isdir)
nondirname[strlen(nondirname)-1] = '\0';
fname = nondirname;
while((slash = strchr(fname, '/')))
fname = slash+1;
if (isdir)
sprintf(buffer, "drw-r--r--\t1\troot\troot\t%8u Jan 1 12:00 %s\r\n", size, fname);
else
sprintf(buffer, "-rw-r--r--\t1\troot\troot\t%8u Jan 1 12:00 %s\r\n", size, fname);
// strcpy(buffer, fname);
// for (i = strlen(buffer); i < 40; i+=8)
// strcat(buffer, "\t");
send(socket, buffer, strlen(buffer), 0);
return true;
}
int FTP_SV_makelistensocket(unsigned long nblocking)
{
char name[256];
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: %s", strerror(qerrno));
}
if (ioctlsocket (sock, FIONBIO, &nblocking) == -1)
{
Sys_Error ("FTP_TCP_OpenSocket: ioctl FIONBIO: %s", strerror(qerrno));
}
if( bind (sock, (void *)&address, sizeof(address)) == -1)
{
closesocket(sock);
return INVALID_SOCKET;
}
listen(sock, 2);
return sock;
}
iwboolean FTP_SVSocketPortToString (int socket, char *s)
{
struct sockaddr_qstorage addr;
int adrlen = sizeof(addr);
if (getsockname(socket, (struct sockaddr*)&addr, &adrlen) == -1)
return false;
if (((struct sockaddr_in*)&addr)->sin_family == AF_INET6)
sprintf(s, "%i", ntohs(((struct sockaddr_in6*)&addr)->sin6_port));
else
sprintf(s, "%i", ntohs(((struct sockaddr_in*)&addr)->sin_port));
return true;
}
//only to be used for ipv4 sockets.
iwboolean FTP_SVSocketToString (int socket, char *s)
{
struct sockaddr_in addr;
qbyte *baddr;
int adrlen = sizeof(addr);
char name[256];
//get the port.
if (getsockname(socket, (struct sockaddr*)&addr, &adrlen) == -1)
return false;
baddr = (qbyte *)&addr.sin_addr;
if (gethostname(name, sizeof(name)) != -1)
{
struct hostent *hent = gethostbyname(name);
if (hent)
baddr = hent->h_addr_list[0];
}
sprintf(s, "%i,%i,%i,%i,%i,%i", baddr[0], baddr[1], baddr[2], baddr[3], ((qbyte *)&addr.sin_port)[0], ((qbyte *)&addr.sin_port)[1]);
return true;
}
iwboolean FTP_SVRemoteSocketToString (int socket, char *s, int slen)
{
struct sockaddr_qstorage addr;
netadr_t na;
int adrlen = sizeof(addr);
// addr.sin_family = AF_INET;
if (getpeername(socket, (struct sockaddr*)&addr, &adrlen) == -1)
{
*s = 0;
return false;
}
SockadrToNetadr(&addr, &na);
NET_AdrToString(s, slen, &na);
// if (((struct sockaddr_in*)&addr)->sin_family == AF_INET6)
// {
// }
// else
// 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;
}
/*
* Responsable for sending all control server -> client messages.
* Queues the message if it cannot send now.
* Kicks if too big a queue.
*/
void QueueMessage(FTPclient_t *cl, char *msg)
{
if (send (cl->controlsock, msg, strlen(msg), 0) == -1)
{ //wasn't sent
if (strlen(msg) + strlen(cl->messagebuffer) >= sizeof(cl->messagebuffer)-1)
closesocket(cl->controlsock); //but don't mark it as closed, so we get errors later (for this is how we shall tell).
strcat(cl->messagebuffer, msg);
}
}
void VARGS QueueMessageva(FTPclient_t *cl, char *fmt, ...)
{
va_list argptr;
char msg[1024];
va_start (argptr, fmt);
vsnprintf (msg,sizeof(msg)-1, fmt,argptr);
msg[sizeof(msg)-1] = 0;
va_end (argptr);
if (send (cl->controlsock, msg, strlen(msg), 0) == -1)
{ //wasn't sent
if (strlen(msg) + strlen(cl->messagebuffer) >= sizeof(cl->messagebuffer)-1)
closesocket(cl->controlsock); //but don't mark it as closed, so we get errors later (for this is how we shall tell).
strcat(cl->messagebuffer, msg);
}
}
iwboolean FTP_ServerThinkForConnection(FTPclient_t *cl)
{
int ret;
struct sockaddr_in from;
int fromlen;
char *msg, *line;
char mode[64];
char resource[8192];
int _true = true;
if (cl->datadir == 1)
{
int pos, sent;
int ammount, wanted = sizeof(resource);
pos = VFS_TELL(cl->file);
ammount = VFS_READ(cl->file, resource, wanted);
sent = send(cl->datasock, resource, ammount, 0);
if (sent == -1)
{
VFS_SEEK(cl->file, pos);
if (qerrno != EWOULDBLOCK)
{
closesocket(cl->datasock);
cl->datasock = INVALID_SOCKET;
VFS_CLOSE(cl->file);
cl->file = NULL;
QueueMessage (cl, "226 Transfer complete .\r\n");
cl->datadir = 0;
}
}
else
{
if (sent != ammount)
VFS_SEEK(cl->file, pos + sent);
if (ammount != wanted && sent == ammount) //file is over
{
send(cl->datasock, resource, 0, 0);
send(cl->datasock, resource, 0, 0);
send(cl->datasock, resource, 0, 0);
closesocket(cl->datasock);
cl->datasock = INVALID_SOCKET;
VFS_CLOSE(cl->file);
cl->file = NULL;
QueueMessage (cl, "226 Transfer complete .\r\n");
cl->datadir = 0;
}
}
pos = cl->datadir?1:!cl->blocking;
if (ioctlsocket (cl->controlsock, FIONBIO, (u_long *)&pos) == -1)
{
IWebPrintf ("FTP_ServerRun: blocking error: %s\n", strerror(qerrno));
return 0;
}
}
else if (cl->datadir == 2)
{
int len;
while((len = recv(cl->datasock, resource, sizeof(resource), 0)) >0 )
{
VFS_WRITE(cl->file, resource, len);
}
if (len == -1)
{
if (qerrno != EWOULDBLOCK)
{
closesocket(cl->datasock);
cl->datasock = INVALID_SOCKET;
if (cl->file)
VFS_CLOSE(cl->file);
cl->file = NULL;
QueueMessage (cl, "226 Transfer complete .\r\n");
cl->datadir = 0;
}
}
if (len == 0)
{
QueueMessage (cl, "226 Transfer complete .\r\n");
VFS_CLOSE(cl->file);
cl->file = NULL;
cl->datadir = 0;
}
}
ret = recv(cl->controlsock, cl->commandbuffer+cl->cmdbuflen, sizeof(cl->commandbuffer)-1 - cl->cmdbuflen, 0);
if (ret == -1)
{
if (qerrno == EWOULDBLOCK)
return false; //remove
if (qerrno == ECONNABORTED || qerrno == ECONNRESET)
return true;
Con_TPrintf (TL_NETGETPACKETERROR, strerror(qerrno));
return true;
}
if (*cl->messagebuffer)
{
if (send (cl->controlsock, cl->messagebuffer, strlen(cl->messagebuffer), 0) != -1)
*cl->messagebuffer = '\0'; //YAY! It went!
}
if (ret == 0)
return false;
cl->cmdbuflen += ret;
cl->commandbuffer[cl->cmdbuflen] = 0;
line = cl->commandbuffer;
while (1)
{
msg = line;
while (*line)
{
if (*line == '\r')
*line = ' ';
if (*line == '\n')
break;
line++;
}
if (!*line) //broken client
{
memmove(cl->commandbuffer, line, strlen(line)+1);
cl->cmdbuflen = strlen(line);
break;
}
*line = '\0';
line++;
IWebPrintf("FTP: %s\n", msg);
msg = COM_ParseOut(msg, mode, sizeof(mode));
if (!stricmp(mode, "SYST"))
{
QueueMessage (cl, "215 UNIX Type: L8.\r\n"); //some browsers can be wierd about things.
}
else if (!stricmp(mode, "user"))
{
msg = COM_ParseOut(msg, cl->name, sizeof(cl->name));
QueueMessage (cl, "331 User name received, will be checked with password.\r\n");
}
else if (!stricmp(mode, "pass"))
{
msg = COM_ParseOut(msg, cl->pwd, sizeof(cl->pwd));
cl->auth = IWebAuthorize(cl->name, cl->pwd);
if (cl->auth)
QueueMessage (cl, "230 User logged in.\r\n");
else
QueueMessage (cl, "530 Username or Password was incorrect or otherwise invalid.\r\n");
}
else if (!stricmp(mode, "TYPE"))
{
if (!cl->auth)
{
QueueMessage (cl, "530 Not logged in.\r\n");
continue;
}
msg = COM_ParseOut(msg, resource, sizeof(resource));
if (!stricmp(resource, "A")) //ascii
{
QueueMessage (cl, "200 asci selected.\r\n");
}
else if (!stricmp(resource, "I")) //binary
{
QueueMessage (cl, "200 binary selected.\r\n");
}
else
{
QueueMessage (cl, "200 asci selected.\r\n");
}
}
else if (!stricmp(mode, "PWD"))
{
if (!cl->auth)
{
QueueMessage (cl, "530 Not logged in.\r\n");
continue;
}
QueueMessageva (cl, "257 \"%s\"\r\n", cl->path);
}
else if (!stricmp(mode, "CWD"))
{
char *p;
if (!cl->auth)
{
QueueMessage (cl, "530 Not logged in.\r\n");
continue;
}
Q_strcpyline(cl->path, msg+1, sizeof(cl->path));//path starts after cmd and single space
for (p = cl->path+strlen(cl->path)-1; *p == ' ' && p >= cl->path; p--)
*p = '\0';
QueueMessage (cl, "200 directory changed.\r\n");
}
else if (!stricmp(mode, "EPSV"))
{
int aftype = 0;
//one argument, "1"=ipv4, "2"=ipv6. if not present, use same as control connection
//reply: "229 Entering Extended Passive Mode (|||$PORTNUM|)\r\n"
if (!cl->auth)
{
QueueMessage (cl, "530 Not logged in.\r\n");
continue;
}
if (cl->datasock != INVALID_SOCKET)
{
closesocket(cl->datasock);
cl->datasock = INVALID_SOCKET;
}
cl->datasock = FTP_BeginListening(aftype, 0);
if (cl->datasock == INVALID_SOCKET)
QueueMessage (cl, "425 server was unable to make a listen socket\r\n");
else
{
FTP_SVSocketPortToString(cl->datasock, resource);
QueueMessageva (cl, "229 Entering Extended Passive Mode (|||%s|).\r\n", resource);
}
cl->dataislisten = true;
}
else if (!stricmp(mode, "PASV"))
{
if (!cl->auth)
{
QueueMessage (cl, "530 Not logged in.\r\n");
continue;
}
if (cl->datasock != INVALID_SOCKET)
{
closesocket(cl->datasock);
cl->datasock = INVALID_SOCKET;
}
cl->datasock = FTP_BeginListening(1, 0);
if (cl->datasock == INVALID_SOCKET)
QueueMessage (cl, "425 server was unable to make a listen socket\r\n");
else
{
FTP_SVSocketToString(cl->datasock, resource);
QueueMessageva (cl, "227 Entering Passive Mode (%s).\r\n", resource);
}
cl->dataislisten = true;
}
// else if (!stricmp(mode, "EPRT"))
// {
//eg: one of:
//EPRT |1|132.235.1.2|6275|
//EPRT |2|1080::8:800:200C:417A|5282|
//reply: 522 Network protocol not supported, use (1,2)
// }
else if (!stricmp(mode, "PORT"))
{
if (!cl->auth)
{
QueueMessage (cl, "530 Not logged in.\r\n");
continue;
}
if (cl->datasock != INVALID_SOCKET)
{
closesocket(cl->datasock);
cl->datasock = INVALID_SOCKET;
}
msg = COM_ParseOut(msg, resource, sizeof(resource));
cl->dataislisten = false;
if ((cl->datasock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
{
Sys_Error ("FTP_ServerThinkForConnection: socket: %s", strerror(qerrno));
}
if (ioctlsocket (cl->datasock, FIONBIO, (u_long *)&_true) == -1)
{
Sys_Error ("FTP_ServerThinkForConnection: ioctl FIONBIO: %s", strerror(qerrno));
}
from.sin_family = AF_INET;
from.sin_addr.s_addr = INADDR_ANY;
from.sin_port = 0;
if( bind (cl->datasock, (void *)&from, sizeof(from)) == -1)
{
closesocket(cl->datasock);
cl->datasock=INVALID_SOCKET;
QueueMessage (cl, "425 server bind error.\r\n");
continue;
}
fromlen = sizeof(from);
FTP_StringToAdr(resource, (qbyte *)&from.sin_addr, (qbyte *)&from.sin_port);
connect(cl->datasock, (struct sockaddr *)&from, fromlen);
QueueMessage (cl, "200 Opened data channel.\r\n");
}
else if (!stricmp(mode, "LIST"))
{
char buffer[256];
if (!cl->auth)
{
QueueMessage (cl, "530 Not logged in.\r\n");
continue;
}
if (cl->dataislisten) //accept a connect.
{
int err;
int _true = true;
int temp;
struct sockaddr_qstorage adr;
int adrlen = sizeof(adr);
temp = accept(cl->datasock, (struct sockaddr *)&adr, &adrlen);
err = qerrno;
closesocket(cl->datasock);
cl->datasock = temp;
cl->dataislisten = false;
if (cl->datasock == INVALID_SOCKET)
{
QueueMessageva (cl, "425 Can't accept pasv data connection - %i.\r\n", err);
continue;
}
else
ioctlsocket(cl->datasock, FIONBIO, (u_long *)&_true);
}
if (cl->datasock == INVALID_SOCKET)
{
QueueMessage (cl, "503 Bad sequence of commands.\r\n");
continue;
}
if (*cl->path == '/')
strcpy(buffer, cl->path+1);
else
strcpy(buffer, cl->path);
if (*buffer) //last character should be a /
if (buffer[strlen(buffer)-1] != '/')
strcat(buffer, "/");
strcat(buffer, "*");
QueueMessage (cl, "125 Opening FAKE ASCII mode data connection for file.\r\n");
COM_EnumerateFiles(buffer, SendFileNameTo, &cl->datasock);
QueueMessage (cl, "226 Transfer complete.\r\n");
closesocket(cl->datasock);
cl->datasock = INVALID_SOCKET;
}
// else if (!stricmp(mode, "SIZE")) //why IE can't use the list command to find file length, I've no idea.
// {
// msg = COM_ParseOut(msg, resource, sizeof(resource));
// }
else if (!stricmp(mode, "RETR"))
{
if (!cl->auth)
{
QueueMessage (cl, "530 Not logged in.\r\n");
continue;
}
if (cl->dataislisten) //accept a connect.
{
int _true = true;
int temp;
struct sockaddr_in adr;
int adrlen = sizeof(adr);
temp = accept(cl->datasock, (struct sockaddr *)&adr, &adrlen);
closesocket(cl->datasock);
cl->datasock = temp;
cl->dataislisten = false;
if (cl->datasock == INVALID_SOCKET)
{
QueueMessageva (cl, "425 Can't accept pasv data connection - %i.\r\n", qerrno);
continue;
}
else
ioctlsocket(cl->datasock, FIONBIO, (u_long *)&_true);
}
if (cl->datasock == INVALID_SOCKET)
{
QueueMessage (cl, "503 Bad sequence of commands.\r\n");
continue;
}
msg = COM_ParseOut(msg, resource, sizeof(resource));
if (!cl->auth & IWEBACC_READ)
{
QueueMessage (cl, "550 No read access.\r\n");
continue;
}
if (!(*resource == '/'))
{
int plen = strlen(cl->path);
if (plen && cl->path[plen-1] != '/')
{
memmove(resource+plen+1, resource, strlen(resource)+1);
memcpy(resource, cl->path, plen);
resource[plen] = '/';
}
else
{
memmove(resource+plen, resource, strlen(resource)+1);
memcpy(resource, cl->path, plen);
}
}
if (*resource == '/')
{
if (SV_AllowDownload(resource+1))
cl->file = FS_OpenVFS(resource+1, "rb", FS_GAME);
else
cl->file = IWebGenerateFile(resource+1, NULL, 0);
}
else
{
if (SV_AllowDownload(resource))
cl->file = FS_OpenVFS(resource, "rb", FS_GAME);
else
cl->file = IWebGenerateFile(resource, NULL, 0);
}
if (!cl->file)
{
QueueMessage (cl, "550 File not found.\r\n");
}
else
{ //send data
QueueMessage (cl, "125 Opening BINARY mode data connection for file.\r\n");
cl->datadir = 1;
}
}
else if (!stricmp(mode, "STOR"))
{
if (!cl->auth)
{
QueueMessage (cl, "530 Not logged in.\r\n");
continue;
}
Q_strcpyline(mode, msg+1, sizeof(mode));
if (!(cl->auth & IWEBACC_FULL) && (((cl->auth & IWEBACC_WRITE && !IWebAllowUpLoad(cl->path+1, cl->name)) || !(cl->auth & IWEBACC_WRITE))))
{
QueueMessage (cl, "550 Permission denied.\r\n");
}
else
{
if (cl->dataislisten) //accept a connect.
{
int _true = true;
int temp;
struct sockaddr_in adr;
int adrlen = sizeof(adr);
temp = accept(cl->datasock, (struct sockaddr *)&adr, &adrlen);
closesocket(cl->datasock);
cl->datasock = temp;
cl->dataislisten = false;
if (cl->datasock == INVALID_SOCKET)
{
QueueMessageva (cl, "425 Can't accept pasv data connection - %i.\r\n", qerrno);
continue;
}
else
ioctlsocket(cl->datasock, FIONBIO, (u_long *)&_true);
}
if (cl->datasock == INVALID_SOCKET)
{
QueueMessage (cl, "502 Bad sequence of commands.\r\n");
continue;
}
// msg = COM_ParseOut(msg, mode, sizeof(mode));
if (*mode == '/')
sprintf(resource, "%s%s", cl->path, mode);
else
sprintf(resource, "%s%s", cl->path, mode);
cl->file = FS_OpenVFS(resource, "rb", FS_GAMEONLY);
if (cl->file)
{
VFS_CLOSE(cl->file);
QueueMessage (cl, "550 File already exists.\r\n");
continue;
}
cl->file = FS_OpenVFS(resource, "wb", FS_GAME);
if (!cl->file)
{
QueueMessage (cl, "550 Couldn't open output.\r\n");
}
else
{ //send data
QueueMessage (cl, "125 Opening BINARY mode data connection for input.\r\n");
cl->datadir = 2;
}
}
}
else if (!stricmp(mode, "STRU"))
{
if (!cl->auth)
{
QueueMessage (cl, "530 Not logged in.\r\n");
continue;
}
msg = COM_ParseOut(msg, resource, sizeof(resource));
if (!strcmp(resource, "F"))
{
QueueMessage (cl, "200 recordless structure selected.\r\n");
}
else
{
QueueMessage (cl, "504 not implemented (it's a simple server).\r\n");
}
}
else if (!stricmp(mode, "NOOP"))
{
QueueMessage (cl, "200 Do something then!\r\n");
}
else if (!stricmp(mode, "QUIT"))
{
QueueMessage (cl, "200 About to quit.\r\n");
return true;
}
else
{
QueueMessage (cl, "502 Command not implemented.\r\n");
}
}
return false;
}
#if defined(WEBSVONLY) && defined(_WIN32)
unsigned int WINAPI BlockingClient(FTPclient_t *cl)
{
unsigned long _false = false;
if (ioctlsocket (cl->controlsock, FIONBIO, &_false) == -1)
{
IWebPrintf ("FTP_ServerRun: blocking error: %s\n", strerror(qerrno));
return 0;
}
cl->blocking = true;
while (!FTP_ServerThinkForConnection(cl))
{
Sleep(10);
}
if (cl->file)
VFS_CLOSE(cl->file);
closesocket(cl->controlsock);
if (cl->datasock)
closesocket(cl->datasock);
IWebFree(cl);
return 0;
}
#endif
iwboolean FTP_ServerRun(iwboolean ftpserverwanted, int port)
{
FTPclient_t *cl, *prevcl;
struct sockaddr_qstorage from;
int fromlen;
int clientsock;
unsigned long _true = true;
if (!ftpserverinitied)
{
if (ftpserverwanted)
{
ftpserversocket = FTP_BeginListening(0, port);
if (ftpserversocket == INVALID_SOCKET)
{
ftpserverfailed = true;
IWebPrintf("Unable to establish listening FTP socket\n");
}
ftpserverinitied = true;
}
return false;
}
else if (!ftpserverwanted)
{
FTP_ServerShutdown();
return false;
}
prevcl = NULL;
for (cl = FTPclient; cl; cl = cl->next)
{
if (FTP_ServerThinkForConnection(cl))
{
if (cl->file)
VFS_CLOSE(cl->file);
closesocket(cl->controlsock);
if (cl->datasock)
closesocket(cl->datasock);
if (prevcl)
{
prevcl->next = cl->next;
IWebFree(cl);
cl = prevcl;
if (!cl) //kills loop
break;
}
else
{
FTPclient = cl->next;
IWebFree(cl);
cl = FTPclient;
if (!cl) //kills loop
break;
}
}
prevcl = cl;
}
fromlen = sizeof(from);
if (ftpserversocket == INVALID_SOCKET)
clientsock = INVALID_SOCKET;
else
clientsock = accept(ftpserversocket, (struct sockaddr *)&from, &fromlen);
if (clientsock == INVALID_SOCKET)
{
if (qerrno == EWOULDBLOCK)
return false;
if (qerrno == ECONNABORTED || qerrno == ECONNRESET)
{
Con_TPrintf (TL_CONNECTIONLOSTORABORTED);
return false;
}
Con_TPrintf (TL_NETGETPACKETERROR, strerror(qerrno));
return false;
}
if (ioctlsocket (clientsock, FIONBIO, &_true) == -1)
{
IWebPrintf ("FTP_ServerRun: blocking error: %s\n", strerror(qerrno));
return false;
}
cl = IWebMalloc(sizeof(FTPclient_t));
if (!cl) //iwebmalloc is allowed to fail.
{
char *msg = "421 Not enough memory is allocated.\r\n"; //don't be totally anti social
send(clientsock, msg, strlen(msg), 0);
closesocket(clientsock); //try to forget this ever happend
return true;
}
{
char resource[256];
FTP_SVRemoteSocketToString(clientsock, resource, sizeof(resource));
IWebPrintf("FTP connect from %s\n", resource);
}
cl->controlsock = clientsock;
cl->datasock = INVALID_SOCKET;
cl->next = FTPclient;
cl->blocking = false;
strcpy(cl->path, "/");
QueueMessage(cl, "220-" FULLENGINENAME " FTP Server.\r\n220 Welcomes all new users.\r\n");
#if defined(WEBSVONLY) && defined(_WIN32)
if (!CreateThread(NULL, 128, BlockingClient, cl, 0, NULL))
#endif
FTPclient = cl;
return true;
}
#endif