b3fb01bfed
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@14 fc73d0e0-1445-4013-8a0c-d673dee63da5
449 lines
9.2 KiB
C
449 lines
9.2 KiB
C
#include "bothdefs.h"
|
|
|
|
#ifdef EMAILSERVER
|
|
|
|
#include "../http/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
|
|
|
|
#define MSG_PARTIAL 0
|
|
#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>
|
|
|
|
#define closesocket close
|
|
#define ioctlsocket ioctl
|
|
#endif
|
|
|
|
|
|
#define SMTP_PORT 25
|
|
#define SMTP_TIMEOUT 30
|
|
|
|
static qboolean smtpactive;
|
|
static int smtpserversocket;
|
|
|
|
typedef struct svsmtpclient_s {
|
|
struct svsmtpclient_s *next;
|
|
|
|
int socket;
|
|
|
|
float timeout;
|
|
|
|
char fromaddr[64];
|
|
char toaddr[64];
|
|
|
|
char inmessagebuffer[1024];
|
|
int inmessagelen;
|
|
char outmessagebuffer[1024];
|
|
int outmessagelen;
|
|
|
|
qboolean gettingdata;
|
|
|
|
FILE *file;
|
|
char sendingtotemp[256];
|
|
} svsmtpclient_t;
|
|
static svsmtpclient_t *svsmtpclient;
|
|
|
|
|
|
|
|
|
|
|
|
static void SMTP_ServerInit(void)
|
|
{
|
|
struct sockaddr_in address;
|
|
unsigned long _true = true;
|
|
int port = SMTP_PORT;
|
|
|
|
if ((smtpserversocket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
|
|
{
|
|
Sys_Error ("FTP_TCP_OpenSocket: socket:", strerror(qerrno));
|
|
}
|
|
|
|
if (ioctlsocket (smtpserversocket, FIONBIO, &_true) == -1)
|
|
{
|
|
Sys_Error ("FTP_TCP_OpenSocket: ioctl FIONBIO:", strerror(qerrno));
|
|
}
|
|
|
|
address.sin_family = AF_INET;
|
|
address.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
if (port == PORT_ANY)
|
|
address.sin_port = 0;
|
|
else
|
|
address.sin_port = htons((short)port);
|
|
|
|
if( bind (smtpserversocket, (void *)&address, sizeof(address)) == -1)
|
|
{
|
|
closesocket(smtpserversocket);
|
|
return;
|
|
}
|
|
|
|
listen(smtpserversocket, 3);
|
|
|
|
smtpactive = true;
|
|
|
|
|
|
IWebPrintf("SMTP server is running\n");
|
|
return;
|
|
}
|
|
|
|
static void SMTP_ServerShutdown(void)
|
|
{
|
|
closesocket(smtpserversocket);
|
|
smtpactive = false;
|
|
}
|
|
|
|
static void SV_SMTP_QueueMessage(svsmtpclient_t *cl, char *msg)
|
|
{
|
|
int len = strlen(msg);
|
|
if (len + cl->outmessagelen > sizeof(cl->outmessagebuffer)-1)
|
|
len = sizeof(cl->outmessagebuffer)-1 - cl->outmessagelen;
|
|
Q_strncpyz(cl->outmessagebuffer+cl->outmessagelen, msg, len+1);
|
|
cl->outmessagelen += len;
|
|
}
|
|
|
|
static void SMTP_NewConnection(int socket)
|
|
{
|
|
svsmtpclient_t *newcl;
|
|
newcl = IWebMalloc(sizeof(svsmtpclient_t));
|
|
if (!newcl) //bother
|
|
{
|
|
closesocket(socket);
|
|
return;
|
|
}
|
|
memset(newcl, 0, sizeof(svsmtpclient_t));
|
|
|
|
newcl->socket = socket;
|
|
newcl->next = svsmtpclient;
|
|
svsmtpclient = newcl;
|
|
|
|
newcl->timeout = realtime + SMTP_TIMEOUT;
|
|
|
|
*newcl->outmessagebuffer = '\0';
|
|
|
|
SV_SMTP_QueueMessage(newcl, "220 81.107.21.148 Probably best to say HELO now.\r\n");
|
|
}
|
|
|
|
static qboolean SV_SMTP_RunClient(svsmtpclient_t *cl) //true means client should be dropped
|
|
{
|
|
int read;
|
|
char *nl, *token;
|
|
|
|
if (cl->outmessagelen)
|
|
{
|
|
read = send(cl->socket, cl->outmessagebuffer, cl->outmessagelen, 0);
|
|
if (read < 0)
|
|
read = 0;
|
|
memmove(cl->outmessagebuffer, cl->outmessagebuffer + read, cl->outmessagelen - read+1);
|
|
cl->outmessagelen -= read;
|
|
}
|
|
|
|
read = recv(cl->socket, cl->inmessagebuffer+cl->inmessagelen, sizeof(cl->inmessagebuffer)-1-cl->inmessagelen, 0);
|
|
|
|
if (read == -1)
|
|
{
|
|
|
|
if (qerrno != EWOULDBLOCK) //blocking is the only way to keep the connection on fail.
|
|
return true;
|
|
if (!cl->inmessagelen)
|
|
return false;
|
|
}
|
|
else if (read == 0) //don't quite know why this happens.
|
|
{
|
|
if (!cl->inmessagelen)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
cl->timeout = realtime + SMTP_TIMEOUT;
|
|
|
|
cl->inmessagelen += read;
|
|
if (cl->inmessagelen >= sizeof(cl->inmessagebuffer)-1 && !cl->gettingdata) //happens if we fill the buffer with no hope of empting it.
|
|
return true;
|
|
cl->inmessagebuffer[cl->inmessagelen] = '\0';
|
|
}
|
|
|
|
while (cl->gettingdata)
|
|
{
|
|
nl = strstr(cl->inmessagebuffer, "\r\n.");
|
|
if (!nl)
|
|
nl = cl->inmessagebuffer + cl->inmessagelen - 10;
|
|
else
|
|
nl+=2;
|
|
if (nl < cl->inmessagebuffer)
|
|
nl = cl->inmessagebuffer; //waiting for a crnl, so we can'texast this buffer in case we chop the \r\n
|
|
|
|
if (!strcmp(cl->inmessagebuffer, ".\r\n"))
|
|
{
|
|
FILE *f;
|
|
int tries;
|
|
int len;
|
|
char name[256];
|
|
char buffer[1024];
|
|
char *to;
|
|
char *at;
|
|
|
|
cl->gettingdata = false;
|
|
|
|
|
|
to = strchr(cl->toaddr, '<');
|
|
at = strchr(cl->toaddr, '@');
|
|
if (!to || !at || strstr(to, ".."))
|
|
SV_SMTP_QueueMessage(cl, "452 Couldn't open file.\r\n");
|
|
else
|
|
{
|
|
to++;
|
|
*at = '\0';
|
|
f=NULL;
|
|
for (tries = 0; tries < 10; tries++) //give it a few goes.
|
|
{
|
|
sprintf(name, "emails/%s/e%i.eml", to, rand());
|
|
COM_CreatePath(name);
|
|
f = fopen(name, "wb");
|
|
if (f)
|
|
break;
|
|
}
|
|
|
|
if (f)
|
|
{
|
|
|
|
{
|
|
netadr_t adr;
|
|
struct sockaddr_qstorage name;
|
|
int namelen = sizeof(name);
|
|
|
|
getpeername(cl->socket, (struct sockaddr*)&name, &namelen);
|
|
SockadrToNetadr(&name, &adr);
|
|
sprintf(buffer, "Received: from %s\r\n", NET_AdrToString(adr));
|
|
fwrite(buffer, strlen(buffer), 1, f);
|
|
}
|
|
|
|
fseek(cl->file, 0, SEEK_END);
|
|
len = ftell(cl->file);
|
|
fseek(cl->file, 0, SEEK_SET);
|
|
while(len)
|
|
{
|
|
tries = sizeof(buffer);
|
|
if (tries > len)
|
|
tries = len;
|
|
len -= tries;
|
|
|
|
fread(buffer, tries, 1, cl->file);
|
|
if (fwrite(buffer, tries, 1, f) != 1)
|
|
{
|
|
fclose(f);
|
|
f = NULL;
|
|
unlink(name);
|
|
len = 0;
|
|
SV_SMTP_QueueMessage(cl, "452 Insufficient system storage.\r\n");
|
|
}
|
|
}
|
|
if (f)
|
|
{
|
|
fclose(f);
|
|
SV_SMTP_QueueMessage(cl, "250 Finally. You do go on a bit.\r\n");
|
|
}
|
|
}
|
|
else
|
|
SV_SMTP_QueueMessage(cl, "452 Couldn't open file.\r\n");
|
|
}
|
|
|
|
fclose(cl->file);
|
|
}
|
|
else
|
|
{
|
|
// for (read = 0; read < nl - cl->inmessagebuffer; read++)
|
|
// putch(cl->inmessagebuffer[read]);
|
|
fwrite(cl->inmessagebuffer, nl - cl->inmessagebuffer, 1, cl->file);
|
|
}
|
|
|
|
cl->inmessagelen -= nl - cl->inmessagebuffer;
|
|
memmove(cl->inmessagebuffer, nl, strlen(nl)+1);
|
|
return false;
|
|
}
|
|
|
|
nl = strstr(cl->inmessagebuffer, "\r\n");
|
|
if (nl)
|
|
{
|
|
*nl = '\0';
|
|
nl+=2;
|
|
|
|
{
|
|
|
|
printf("%s\n", cl->inmessagebuffer);
|
|
|
|
token = COM_Parse(cl->inmessagebuffer);
|
|
|
|
if (!strcmp(com_token, "QUIT"))
|
|
{
|
|
SV_SMTP_QueueMessage(cl, "221 Well, it was good while it lasted\r\n");
|
|
return true;
|
|
}
|
|
else if (!strcmp(com_token, "HELO"))
|
|
{
|
|
SV_SMTP_QueueMessage(cl, va("250 %s Electronic you say? That's good then. :o)\r\n", "81.107.21.148"));
|
|
}
|
|
else if (!strcmp(com_token, "MAIL"))
|
|
{
|
|
token = COM_Parse(token);
|
|
if (strcmp(com_token, "FROM:"))
|
|
{
|
|
if (strncmp(com_token, "FROM:", 5))
|
|
SV_SMTP_QueueMessage(cl, "501 Syntax error. Expected MAIL FROM: fromaddr\r\n");
|
|
else
|
|
{
|
|
SV_SMTP_QueueMessage(cl, "250 Get on with it\r\n");
|
|
Q_strncpyz(cl->fromaddr, com_token+5, sizeof(cl->fromaddr));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SV_SMTP_QueueMessage(cl, "250 Get on with it\r\n");
|
|
Q_strncpyz(cl->fromaddr, token, sizeof(cl->fromaddr));
|
|
}
|
|
}
|
|
else if (!strcmp(com_token, "RCPT"))
|
|
{
|
|
token = COM_Parse(token);
|
|
if (strcmp(com_token, "TO:"))
|
|
{
|
|
if (!strncmp(com_token, "TO:<", 4))
|
|
{
|
|
token = com_token+3;
|
|
}
|
|
else
|
|
{
|
|
token = NULL;
|
|
SV_SMTP_QueueMessage(cl, "501 Syntax error. Expected RCPT TO: toaddr\r\n");
|
|
}
|
|
}
|
|
if (token)
|
|
{
|
|
SV_SMTP_QueueMessage(cl, "250 Yada yada yada\r\n");
|
|
Q_strncpyz(cl->toaddr, token, sizeof(cl->toaddr));
|
|
}
|
|
}
|
|
else if (!strcmp(com_token, "DATA"))
|
|
{
|
|
cl->file = tmpfile();
|
|
if (!cl->file)
|
|
{
|
|
SV_SMTP_QueueMessage(cl, "550 Access Denied to You.\r\n");
|
|
}
|
|
else
|
|
{
|
|
SV_SMTP_QueueMessage(cl, "354 I'm waiting\r\n");
|
|
cl->gettingdata = true;
|
|
}
|
|
}
|
|
else
|
|
SV_SMTP_QueueMessage(cl, "500 Stop speaking pig-latin\r\n");
|
|
}
|
|
cl->inmessagelen -= nl - cl->inmessagebuffer;
|
|
memmove(cl->inmessagebuffer, nl, strlen(nl)+1);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void SV_SMTP_RunClients(void)
|
|
{
|
|
svsmtpclient_t *cl, *prev;
|
|
|
|
cl = svsmtpclient;
|
|
prev = NULL;
|
|
while(cl)
|
|
{
|
|
if (cl->timeout < realtime || SV_SMTP_RunClient(cl))
|
|
{
|
|
closesocket(cl->socket);
|
|
if (prev)
|
|
prev->next = cl->next;
|
|
else
|
|
svsmtpclient = cl->next;
|
|
|
|
if (cl->file)
|
|
fclose(cl->file);
|
|
IWebFree(cl);
|
|
if (prev)
|
|
cl = prev->next;
|
|
else
|
|
cl = svsmtpclient;
|
|
continue;
|
|
}
|
|
prev = cl;
|
|
cl = cl->next;
|
|
}
|
|
}
|
|
|
|
qboolean SV_SMTP(qboolean activewanted)
|
|
{
|
|
struct sockaddr_qstorage from;
|
|
int fromlen;
|
|
int clientsock;
|
|
|
|
if (!smtpactive)
|
|
{
|
|
if (activewanted)
|
|
SMTP_ServerInit();
|
|
else
|
|
return false;
|
|
}
|
|
else if (!activewanted)
|
|
{
|
|
SMTP_ServerShutdown();
|
|
return false;
|
|
}
|
|
|
|
fromlen = sizeof(from);
|
|
clientsock = accept(smtpserversocket, (struct sockaddr *)&from, &fromlen);
|
|
|
|
if (clientsock == -1)
|
|
{
|
|
if (qerrno == ECONNABORTED || qerrno == ECONNRESET)
|
|
{
|
|
Con_TPrintf (TL_CONNECTIONLOSTORABORTED);
|
|
return false;
|
|
}
|
|
else if (qerrno != EWOULDBLOCK)
|
|
{
|
|
Con_TPrintf (TL_NETGETPACKETERROR, strerror(qerrno));
|
|
return false;
|
|
}
|
|
}
|
|
else //we got a new client. yay.
|
|
{
|
|
{
|
|
netadr_t adr;
|
|
SockadrToNetadr(&from, &adr);
|
|
Con_DPrintf("SMTP connect initiated from %s\n", NET_AdrToString(adr));
|
|
}
|
|
SMTP_NewConnection(clientsock);
|
|
}
|
|
|
|
SV_SMTP_RunClients();
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif
|