874 lines
20 KiB
C
874 lines
20 KiB
C
|
#include "bothdefs.h"
|
||
|
|
||
|
#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
|
||
|
|
||
|
#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
|
||
|
|
||
|
|
||
|
|
||
|
char *COM_ParseOut (char *data, char *out, int outlen);
|
||
|
|
||
|
static iwboolean ftpserverinitied = false;
|
||
|
static int ftpserversocket;
|
||
|
|
||
|
|
||
|
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
|
||
|
IWEBFILE *file;
|
||
|
|
||
|
unsigned long blocking;
|
||
|
|
||
|
struct FTPclient_s *next;
|
||
|
} FTPclient_t;
|
||
|
|
||
|
FTPclient_t *FTPclient;
|
||
|
|
||
|
void FTP_ServerInit(void)
|
||
|
{
|
||
|
struct sockaddr_in address;
|
||
|
unsigned long _true = true;
|
||
|
int i;
|
||
|
int port = 21;
|
||
|
|
||
|
if ((ftpserversocket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
|
||
|
{
|
||
|
Sys_Error ("FTP_TCP_OpenSocket: socket:", strerror(qerrno));
|
||
|
}
|
||
|
|
||
|
if (ioctlsocket (ftpserversocket, FIONBIO, &_true) == -1)
|
||
|
{
|
||
|
Sys_Error ("FTP_TCP_OpenSocket: ioctl FIONBIO:", strerror(qerrno));
|
||
|
}
|
||
|
|
||
|
address.sin_family = AF_INET;
|
||
|
//ZOID -- check for interface binding option
|
||
|
if ((i = COM_CheckParm("-ip")) != 0 && i < com_argc) {
|
||
|
address.sin_addr.s_addr = inet_addr(com_argv[i+1]);
|
||
|
Con_TPrintf(TL_NETBINDINTERFACE,
|
||
|
inet_ntoa(address.sin_addr));
|
||
|
} else
|
||
|
address.sin_addr.s_addr = INADDR_ANY;
|
||
|
|
||
|
if (port == PORT_ANY)
|
||
|
address.sin_port = 0;
|
||
|
else
|
||
|
address.sin_port = htons((short)port);
|
||
|
|
||
|
if( bind (ftpserversocket, (void *)&address, sizeof(address)) == -1)
|
||
|
{
|
||
|
closesocket(ftpserversocket);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
listen(ftpserversocket, 3);
|
||
|
|
||
|
ftpserverinitied = true;
|
||
|
|
||
|
|
||
|
IWebPrintf("FTP server is running\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void FTP_ServerShutdown(void)
|
||
|
{
|
||
|
closesocket(ftpserversocket);
|
||
|
ftpserverinitied = false;
|
||
|
IWebPrintf("FTP server is deactivated\n");
|
||
|
}
|
||
|
|
||
|
int SendFileNameTo(char *fname, int size, void *socket)
|
||
|
{
|
||
|
// int i;
|
||
|
char buffer[256+1];
|
||
|
char *slash;
|
||
|
int isdir = fname[strlen(fname)-1] == '/';
|
||
|
|
||
|
#ifndef WEBSVONLY //copy protection of the like that QWSV normally has.
|
||
|
if (!isdir)
|
||
|
if (!SV_AllowDownload(fname)) //don't advertise if we're going to disallow it
|
||
|
return true;
|
||
|
#endif
|
||
|
|
||
|
if (isdir)
|
||
|
fname[strlen(fname)-1] = '\0';
|
||
|
|
||
|
while((slash = strchr(fname, '/')))
|
||
|
fname = slash+1;
|
||
|
|
||
|
if (isdir)
|
||
|
sprintf(buffer, "drw-r--r--\t1\troot\troot\t%8i Jan 1 12:00 %s\r\n", size, fname);
|
||
|
else
|
||
|
sprintf(buffer, "-rw-r--r--\t1\troot\troot\t%8i Jan 1 12:00 %s\r\n", size, fname);
|
||
|
|
||
|
// strcpy(buffer, fname);
|
||
|
// for (i = strlen(buffer); i < 40; i+=8)
|
||
|
// strcat(buffer, "\t");
|
||
|
send((int)socket, buffer, strlen(buffer), 0);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
int FTP_SV_makelistensocket(unsigned long blocking)
|
||
|
{
|
||
|
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:", strerror(qerrno));
|
||
|
}
|
||
|
|
||
|
if (ioctlsocket (sock, FIONBIO, &blocking) == -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, 2);
|
||
|
|
||
|
return sock;
|
||
|
}
|
||
|
iwboolean FTP_SVSocketToString (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_SVRemoteSocketToString (int socket, char *s)
|
||
|
{
|
||
|
struct sockaddr_in addr;
|
||
|
int adrlen = sizeof(addr);
|
||
|
|
||
|
addr.sin_family = AF_INET;
|
||
|
if (getpeername(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;
|
||
|
}
|
||
|
/*
|
||
|
* 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);
|
||
|
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];
|
||
|
static char resource[8192];
|
||
|
|
||
|
if (cl->datadir == 1)
|
||
|
{
|
||
|
int pos, sent;
|
||
|
int ammount, wanted = sizeof(resource);
|
||
|
|
||
|
pos = IWebFTell(cl->file);
|
||
|
ammount = IWebFRead(resource, 1, wanted, cl->file);
|
||
|
sent = send(cl->datasock, resource, ammount, 0);
|
||
|
|
||
|
if (sent == -1)
|
||
|
{
|
||
|
IWebFSeek(cl->file, pos, SEEK_SET);
|
||
|
if (qerrno != EWOULDBLOCK)
|
||
|
{
|
||
|
closesocket(cl->datasock);
|
||
|
cl->datasock = INVALID_SOCKET;
|
||
|
IWebFClose(cl->file);
|
||
|
cl->file = NULL;
|
||
|
|
||
|
QueueMessage (cl, "226 Transfer complete .\r\n");
|
||
|
cl->datadir = 0;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (sent != ammount)
|
||
|
IWebFSeek(cl->file, pos + sent, SEEK_SET);
|
||
|
|
||
|
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;
|
||
|
IWebFClose(cl->file);
|
||
|
cl->file = NULL;
|
||
|
|
||
|
QueueMessage (cl, "226 Transfer complete .\r\n");
|
||
|
cl->datadir = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (cl->datadir == 2)
|
||
|
{
|
||
|
int len;
|
||
|
while((len = recv(cl->datasock, resource, sizeof(resource), 0)) >0 )
|
||
|
{
|
||
|
IWebFWrite(resource, len, 1, cl->file);
|
||
|
}
|
||
|
if (len == -1)
|
||
|
{
|
||
|
if (qerrno != EWOULDBLOCK)
|
||
|
{
|
||
|
closesocket(cl->datasock);
|
||
|
cl->datasock = INVALID_SOCKET;
|
||
|
if (cl->file)
|
||
|
IWebFClose(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");
|
||
|
IWebFClose(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, "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_SV_makelistensocket(cl->blocking);
|
||
|
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, "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_UDP_OpenSocket: socket:", strerror(qerrno));
|
||
|
}
|
||
|
|
||
|
if (ioctlsocket (cl->datasock, FIONBIO, &cl->blocking) == -1)
|
||
|
{
|
||
|
Sys_Error ("FTTP_UDP_OpenSocket: ioctl FIONBIO:", 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 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 Your client connected too slowly - %i.\r\n", qerrno);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
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);
|
||
|
strcat(buffer, "*");
|
||
|
QueueMessage (cl, "125 Opening FAKE ASCII mode data connection for file.\r\n");
|
||
|
|
||
|
COM_EnumerateFiles(buffer, SendFileNameTo, (void *)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 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 Your client connected too slowly - %i.\r\n", qerrno);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
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 = IWebFOpenRead(resource+1);
|
||
|
else
|
||
|
cl->file = IWebGenerateFile(resource+1, NULL, 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (SV_AllowDownload(resource))
|
||
|
cl->file = IWebFOpenRead(resource);
|
||
|
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 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 Your client connected too slowly - %i.\r\n", qerrno);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
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 = IWebFOpenRead(resource);
|
||
|
if (cl->file)
|
||
|
{
|
||
|
IWebFClose(cl->file);
|
||
|
QueueMessage (cl, "550 File already exists.\r\n");
|
||
|
continue;
|
||
|
}
|
||
|
cl->file = IWebFOpenWrite(resource, false);
|
||
|
|
||
|
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 = false;
|
||
|
|
||
|
while (!FTP_ServerThinkForConnection(cl))
|
||
|
{
|
||
|
Sleep(10);
|
||
|
}
|
||
|
|
||
|
if (cl->file)
|
||
|
IWebFClose(cl->file);
|
||
|
closesocket(cl->controlsock);
|
||
|
if (cl->datasock)
|
||
|
closesocket(cl->datasock);
|
||
|
|
||
|
IWebFree(cl);
|
||
|
return 0;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
iwboolean FTP_ServerRun(iwboolean ftpserverwanted)
|
||
|
{
|
||
|
FTPclient_t *cl, *prevcl;
|
||
|
struct sockaddr_in from;
|
||
|
int fromlen;
|
||
|
int clientsock;
|
||
|
unsigned long _true = true;
|
||
|
|
||
|
if (!ftpserverinitied)
|
||
|
{
|
||
|
if (ftpserverwanted)
|
||
|
FTP_ServerInit();
|
||
|
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)
|
||
|
IWebFClose(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);
|
||
|
clientsock = accept(ftpserversocket, (struct sockaddr *)&from, &fromlen);
|
||
|
|
||
|
if (clientsock == -1)
|
||
|
{
|
||
|
if (qerrno == EWOULDBLOCK)
|
||
|
return false;
|
||
|
|
||
|
if (qerrno == ECONNABORTED || qerrno == ECONNRESET)
|
||
|
{
|
||
|
Con_TPrintf (TL_CONNECTIONLOSTORABORTED);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
Con_TPrintf (TL_NETGETPACKETERROR, strerror(qerrno));
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//is this needed?
|
||
|
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);
|
||
|
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-QuakeWorld 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
|