mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-29 23:22:01 +00:00
8d5b217266
r4169 | acceptthis | 2013-01-17 08:55:12 +0000 (Thu, 17 Jan 2013) | 31 lines removed MAX_VISEDICTS limit. PEXT2_REPLACEMENTDELTAS tweaked, now has 4 million entity limit. still not enabled by default. TE_BEAM now maps to a separate TEQW_BEAM to avoid conflicts with QW. added android multitouch emulation for windows/rawinput (in_simulatemultitouch). split topcolor/bottomcolor from scoreboard, for dp's colormap|1024 feature. now using utf-8 for windows consoles. qcc warnings/errors now give clickable console links for quick+easy editing. disabled menutint when the currently active item changes contrast or gamma (for OneManClan). Added support for drawfont/drawfontscale. tweaked the qcvm a little to reduce the number of pointers. .doll file loading. still experimental and will likely crash. requires csqc active, even if its a dummy progs. this will be fixed in time. Still other things that need cleaning up. windows: gl_font "?" shows the standard windows font-selection dialog, and can be used to select windows fonts. not all work. and you probably don't want to use windings. fixed splitscreen support when playing mvds. added mini-scoreboards to splitscreen. editor/debugger now shows asm if there's no linenumber info. also, pressing f1 for help shows the shortcuts. Added support for .framegroups files for psk(psa) and iqm formats. True support for ezquake's colour codes. Mutually exclusive with background colours. path command output slightly more readable. added support for digest_hex (MD4, SHA1, CRC16). skingroups now colourmap correctly. Fix terrain colour hints, and litdata from the wrong bsp. fix ftp dual-homed issue. support epsv command, and enable ipv6 (eprt still not supported). remove d3d11 compilation from the makefile. the required headers are not provided by mingw, and are not available to the build bot, so don't bother. fix v *= v.x and similar opcodes. fteqcc: fixed support for áéíóú type chars in names. utf-8 files now properly supported (even with the utf-8 bom/identifier). utf-16 also supported. fteqcc: fixed '#if 1 == 3 && 4' parsing. fteqcc: -Werror acts on the warning, rather than as a separate error. Line numbers are thus more readable. fteqcc: copyright message now includes compile date instead. fteqccgui: the treeview control is now coloured depending on whether there were warnings/errors in the last compile. fteqccgui: the output window is now focused and scrolls down as compilation progresses. pr_dumpplatform command dumps out some pragmas to convert more serious warnings to errors. This is to avoid the infamous 'fteqcc sucks cos my code sucks' issue. rewrote prespawn/modelist/soundlist code. server tracks progress now. ------------------------------------------------------------------------ git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4167 fc73d0e0-1445-4013-8a0c-d673dee63da5
983 lines
23 KiB
C
983 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;
|
|
|
|
if ((sock = socket ((aftype!=1)?PF_INET6:PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
|
|
{
|
|
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;
|
|
}
|
|
|
|
if (aftype == 1)
|
|
{
|
|
//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);
|
|
}
|
|
else
|
|
{
|
|
//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);
|
|
}
|
|
|
|
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 SendFileNameTo(const char *rawname, int size, void *param)
|
|
{
|
|
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 == '/'))
|
|
{
|
|
memmove(resource+strlen(cl->path), resource, strlen(resource)+1);
|
|
memcpy(resource, cl->path, strlen(cl->path));
|
|
}
|
|
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
|