engine/engine/email/sv_smtp.c

450 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