Email notification and email servers.
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@14 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
c343403759
commit
b3fb01bfed
4 changed files with 2101 additions and 0 deletions
433
engine/email/imapnoti.c
Normal file
433
engine/email/imapnoti.c
Normal file
|
@ -0,0 +1,433 @@
|
|||
#include "bothdefs.h"
|
||||
|
||||
#ifdef EMAILCLIENT
|
||||
|
||||
//code to sit on an imap server and check for new emails every now and then.
|
||||
|
||||
#include "quakedef.h"
|
||||
#include "winquake.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
|
||||
|
||||
|
||||
|
||||
//exported.
|
||||
void IMAP_CreateConnection(char *servername, char *username, char *password);
|
||||
cvar_t imap_checkfrequency = {"imap_checkfrequency", "60"}; //once a min
|
||||
void IMAP_Think (void);
|
||||
//end export list.
|
||||
|
||||
|
||||
|
||||
|
||||
#define IMAP_PORT 143
|
||||
|
||||
|
||||
typedef struct imap_con_s {
|
||||
char server[128];
|
||||
char username[128];
|
||||
char password[128];
|
||||
|
||||
float lastnoop;
|
||||
|
||||
//these are used so we can fail a send.
|
||||
//or recieve only part of an input.
|
||||
//FIXME: make dynamically sizable, as it could drop if the send is too small (That's okay.
|
||||
// but if the read is bigger than one command we suddenly fail entirly.
|
||||
int sendlen;
|
||||
int sendbuffersize;
|
||||
char *sendbuffer;
|
||||
int readlen;
|
||||
int readbuffersize;
|
||||
char *readbuffer;
|
||||
|
||||
qboolean drop;
|
||||
|
||||
int socket;
|
||||
|
||||
enum {
|
||||
IMAP_WAITINGFORINITIALRESPONCE,
|
||||
IMAP_AUTHING,
|
||||
IMAP_AUTHED,
|
||||
IMAP_INBOX
|
||||
} state;
|
||||
|
||||
struct imap_con_s *next;
|
||||
} imap_con_t;
|
||||
|
||||
static imap_con_t *imapsv;
|
||||
|
||||
void IMAP_CreateConnection(char *addy, char *username, char *password)
|
||||
{
|
||||
unsigned long _true = true;
|
||||
struct sockaddr_qstorage from;
|
||||
imap_con_t *con;
|
||||
|
||||
for (con = imapsv; con; con = con->next)
|
||||
{
|
||||
if (!strcmp(con->server, addy))
|
||||
{
|
||||
Con_Printf("Already connected to that imap server\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
con = IWebMalloc(sizeof(imap_con_t));
|
||||
|
||||
|
||||
|
||||
if ((con->socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
|
||||
{
|
||||
Sys_Error ("IMAP_CreateConnection: socket: %s\n", strerror(qerrno));
|
||||
}
|
||||
|
||||
|
||||
{//quake routines using dns and stuff (Really, I wanna keep quake and imap fairly seperate)
|
||||
netadr_t qaddy;
|
||||
NET_StringToAdr (addy, &qaddy);
|
||||
if (!qaddy.port)
|
||||
qaddy.port = htons(IMAP_PORT);
|
||||
NetadrToSockadr(&qaddy, &from);
|
||||
}
|
||||
|
||||
//not yet blocking.
|
||||
if (connect(con->socket, (struct sockaddr *)&from, sizeof(from)) == -1)
|
||||
{
|
||||
IWebWarnPrintf ("IMAP_CreateConnection: connect: %i %s\n", qerrno, strerror(qerrno));
|
||||
closesocket(con->socket);
|
||||
IWebFree(con);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ioctlsocket (con->socket, FIONBIO, &_true) == -1) //now make it non blocking.
|
||||
{
|
||||
Sys_Error ("IMAP_CreateConnection: ioctl FIONBIO: %s\n", strerror(qerrno));
|
||||
}
|
||||
|
||||
Q_strncpyz(con->server, addy, sizeof(con->server));
|
||||
Q_strncpyz(con->username, username, sizeof(con->username));
|
||||
Q_strncpyz(con->password, password, sizeof(con->password));
|
||||
|
||||
con->next = imapsv;
|
||||
imapsv = con;
|
||||
}
|
||||
|
||||
static void IMAP_EmitCommand(imap_con_t *imap, char *text)
|
||||
{
|
||||
int newlen;
|
||||
char rt[64];
|
||||
sprintf(rt, "* "); //now this is lame. Quite possibly unreliable...
|
||||
//makes a few things easier though
|
||||
|
||||
newlen = imap->sendlen + strlen(text) + strlen(rt) + 2;
|
||||
|
||||
if (newlen >= imap->sendbuffersize || !imap->sendbuffer) //pre-length check.
|
||||
{
|
||||
char *newbuf;
|
||||
imap->sendbuffersize = newlen*2;
|
||||
newbuf = IWebMalloc(imap->sendbuffersize);
|
||||
if (!newbuf)
|
||||
{
|
||||
Con_Printf("Memory is low\n");
|
||||
imap->drop = true; //failed.
|
||||
return;
|
||||
}
|
||||
if (imap->sendbuffer)
|
||||
{
|
||||
memcpy(newbuf, imap->sendbuffer, imap->sendlen);
|
||||
IWebFree(imap->sendbuffer);
|
||||
}
|
||||
imap->sendbuffer = newbuf;
|
||||
}
|
||||
|
||||
imap->sendlen = newlen;
|
||||
|
||||
strncat(imap->sendbuffer, rt, imap->sendbuffersize-1);
|
||||
strncat(imap->sendbuffer, text, imap->sendbuffersize-1);
|
||||
strncat(imap->sendbuffer, "\r\n", imap->sendbuffersize-1);
|
||||
}
|
||||
|
||||
static char *IMAP_AddressStructure(char *msg, char *out, int outsize)
|
||||
{
|
||||
char name[256];
|
||||
char mailbox[64];
|
||||
char hostname[128];
|
||||
int indents=0;
|
||||
while(*msg == ' ')
|
||||
msg++;
|
||||
while(*msg == '(') //do it like this, we can get 2... I'm not sure if that's always true..
|
||||
{
|
||||
msg++;
|
||||
indents++;
|
||||
}
|
||||
|
||||
msg = COM_Parse(msg); //name
|
||||
Q_strncpyz(name, com_token, sizeof(name));
|
||||
msg = COM_Parse(msg); //smtp route (ignored normally)
|
||||
msg = COM_Parse(msg); //mailbox
|
||||
Q_strncpyz(mailbox, com_token, sizeof(mailbox));
|
||||
msg = COM_Parse(msg); //hostname
|
||||
Q_strncpyz(hostname, com_token, sizeof(hostname));
|
||||
|
||||
while(indents && *msg == ')')
|
||||
msg++;
|
||||
|
||||
if (out)
|
||||
{
|
||||
if (!strcmp(name, "NIL"))
|
||||
{
|
||||
Q_strncpyz(out, mailbox, outsize-1);
|
||||
strncat(out, "@", outsize-1);
|
||||
strncat(out, hostname, outsize-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_strncpyz(out, name, outsize-1);
|
||||
|
||||
strncat(out, " <", outsize-1);
|
||||
strncat(out, mailbox, outsize-1);
|
||||
strncat(out, "@", outsize-1);
|
||||
strncat(out, hostname, outsize-1);
|
||||
strncat(out, ">", outsize-1);
|
||||
}
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static qboolean IMAP_ThinkCon(imap_con_t *imap) //false means drop the connection.
|
||||
{
|
||||
char *ending;
|
||||
int len;
|
||||
|
||||
//get the buffer, stick it in our read holder
|
||||
if (imap->readlen+32 >= imap->readbuffersize || !imap->readbuffer)
|
||||
{
|
||||
len = imap->readbuffersize;
|
||||
if (!imap->readbuffer)
|
||||
imap->readbuffersize = 256;
|
||||
else
|
||||
imap->readbuffersize*=2;
|
||||
|
||||
ending = IWebMalloc(imap->readbuffersize);
|
||||
if (!ending)
|
||||
{
|
||||
Con_Printf("Memory is low\n");
|
||||
return false;
|
||||
}
|
||||
if (imap->readbuffer)
|
||||
{
|
||||
memcpy(ending, imap->readbuffer, len);
|
||||
IWebFree(imap->readbuffer);
|
||||
}
|
||||
imap->readbuffer = ending;
|
||||
}
|
||||
|
||||
len = recv(imap->socket, imap->readbuffer+imap->readlen, imap->readbuffersize-imap->readlen-1, 0);
|
||||
if (len>0)
|
||||
{
|
||||
imap->readlen+=len;
|
||||
imap->readbuffer[imap->readlen] = '\0';
|
||||
}
|
||||
|
||||
if (imap->readlen>0)
|
||||
{
|
||||
ending = strstr(imap->readbuffer, "\r\n");
|
||||
|
||||
if (ending) //pollable text.
|
||||
{
|
||||
*ending = '\0';
|
||||
// Con_Printf("%s\n", imap->readbuffer);
|
||||
|
||||
ending+=2;
|
||||
if (imap->state == IMAP_WAITINGFORINITIALRESPONCE)
|
||||
{
|
||||
//can be one of two things.
|
||||
if (!strncmp(imap->readbuffer, "* OK", 4))
|
||||
{
|
||||
IMAP_EmitCommand(imap, va("LOGIN %s %s", imap->username, imap->password));
|
||||
imap->state = IMAP_AUTHING;
|
||||
}
|
||||
else if (!strncmp(imap->readbuffer, "* PREAUTH", 9))
|
||||
{
|
||||
Con_Printf("Logged on to %s\n", imap->server);
|
||||
IMAP_EmitCommand(imap, "SELECT INBOX");
|
||||
imap->state = IMAP_AUTHED;
|
||||
imap->lastnoop = Sys_DoubleTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Printf("Unexpected response from IMAP server\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (imap->state == IMAP_AUTHING)
|
||||
{
|
||||
if (!strncmp(imap->readbuffer, "* OK", 4))
|
||||
{
|
||||
Con_Printf("Logged on to %s\n", imap->server);
|
||||
IMAP_EmitCommand(imap, "SELECT INBOX");
|
||||
imap->state = IMAP_AUTHED;
|
||||
imap->lastnoop = Sys_DoubleTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Printf("Unexpected response from IMAP server\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (imap->state == IMAP_AUTHED)
|
||||
{
|
||||
char *num;
|
||||
num = imap->readbuffer;
|
||||
if (!strncmp(imap->readbuffer, "* SEARCH ", 8)) //we only ever search for recent messages. So we fetch them and get sender and subject.
|
||||
{
|
||||
char *s;
|
||||
s = imap->readbuffer+8;
|
||||
num = NULL;
|
||||
while(*s)
|
||||
{
|
||||
s++;
|
||||
num = s;
|
||||
while (*s >= '0' && *s <= '9')
|
||||
s++;
|
||||
|
||||
IMAP_EmitCommand(imap, va("FETCH %i ENVELOPE", atoi(num))); //envelope so that it's all one line.
|
||||
}
|
||||
}
|
||||
else if (imap->readbuffer[0] == '*' && imap->readbuffer[1] == ' ')
|
||||
{
|
||||
num = imap->readbuffer+2;
|
||||
while(*num >= '0' && *num <= '9')
|
||||
{
|
||||
num++;
|
||||
}
|
||||
if (!strcmp(num, " RECENT"))
|
||||
{
|
||||
if (atoi(imap->readbuffer+2) > 0)
|
||||
{
|
||||
IMAP_EmitCommand(imap, "SEARCH RECENT");
|
||||
}
|
||||
}
|
||||
else if (!strncmp(num, " FETCH (ENVELOPE (", 18))
|
||||
{
|
||||
char from[256];
|
||||
char subject[256];
|
||||
|
||||
num += 18;
|
||||
|
||||
num = COM_Parse(num);
|
||||
// Con_Printf("Date/Time: %s\n", com_token);
|
||||
|
||||
num = COM_Parse(num);
|
||||
Q_strncpyz(subject, com_token, sizeof(subject));
|
||||
|
||||
num = IMAP_AddressStructure(num, from, sizeof(from));
|
||||
|
||||
|
||||
if ((rand() & 3) == 3)
|
||||
{
|
||||
if (rand())
|
||||
Con_Printf("\n^2New spam has arrived\n");
|
||||
else
|
||||
Con_Printf("\n^2You have new spam\n");
|
||||
}
|
||||
else if (rand()&1)
|
||||
Con_Printf("\n^2New mail has arrived\n");
|
||||
else
|
||||
Con_Printf("\n^2You have new mail\n");
|
||||
|
||||
Con_Printf("Subject: %s\n", subject);
|
||||
Con_Printf("From: %s\n", from);
|
||||
|
||||
SCR_CenterPrint(0, va("NEW MAIL HAS ARRIVED\n\nTo: %s@%s\nFrom: %s\nSubject: %s", imap->username, imap->server, from, subject));
|
||||
|
||||
//throw the rest away.
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Printf("Bad client state\n");
|
||||
return false;
|
||||
}
|
||||
imap->readlen -= ending - imap->readbuffer;
|
||||
memmove(imap->readbuffer, ending, strlen(ending)+1);
|
||||
}
|
||||
}
|
||||
if (imap->drop)
|
||||
return false;
|
||||
|
||||
if (imap->state == IMAP_AUTHED)
|
||||
{
|
||||
if (imap->lastnoop + imap_checkfrequency.value < Sys_DoubleTime())
|
||||
{ //we need to keep the connection reasonably active
|
||||
|
||||
IMAP_EmitCommand(imap, "SELECT INBOX"); //this causes the recent flags to be reset. This is the only way I found.
|
||||
imap->lastnoop = Sys_DoubleTime();
|
||||
}
|
||||
}
|
||||
|
||||
if (imap->sendlen)
|
||||
{
|
||||
len = send(imap->socket, imap->sendbuffer, imap->sendlen, 0);
|
||||
if (len>0)
|
||||
{
|
||||
imap->sendlen-=len;
|
||||
memmove(imap->sendbuffer, imap->sendbuffer+len, imap->sendlen+1);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void IMAP_Think (void)
|
||||
{
|
||||
imap_con_t *prev = NULL;
|
||||
imap_con_t *imap;
|
||||
|
||||
for (imap = imapsv; imap; imap = imap->next)
|
||||
{
|
||||
if (imap->drop || !IMAP_ThinkCon(imap))
|
||||
{
|
||||
if (!prev)
|
||||
imapsv = imap->next;
|
||||
else
|
||||
prev->next = imap->next;
|
||||
closesocket(imap->socket);
|
||||
BZ_Free(imap);
|
||||
if (!prev)
|
||||
break;
|
||||
}
|
||||
|
||||
prev = imap;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
549
engine/email/pop3noti.c
Normal file
549
engine/email/pop3noti.c
Normal file
|
@ -0,0 +1,549 @@
|
|||
#include "bothdefs.h"
|
||||
|
||||
#ifdef EMAILCLIENT
|
||||
|
||||
//the idea is to send a UIDL request, and compare against the previous list.
|
||||
//this list will be stored on disk on quit.
|
||||
|
||||
//be aware that we cannot stay connected. POP3 mailboxs are not refreshable without disconnecting.
|
||||
//so we have a special state.
|
||||
|
||||
|
||||
#include "quakedef.h"
|
||||
#include "winquake.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
|
||||
|
||||
|
||||
char *MD5_GetPop3APOPString(char *timestamp, char *secrit);
|
||||
|
||||
|
||||
#define HASH_FILESYSTEM
|
||||
#ifdef HASH_FILESYSTEM
|
||||
//#include "hash.h"
|
||||
|
||||
#define Hash_BytesForBuckets(b) (sizeof(bucket_t)*b)
|
||||
|
||||
#define STRCMP(s1,s2) (((*s1)!=(*s2)) || strcmp(s1+1,s2+1)) //saves about 2-6 out of 120 - expansion of idea from fastqcc
|
||||
typedef struct bucket_s {
|
||||
void *data;
|
||||
char *keystring;
|
||||
struct bucket_s *next;
|
||||
} bucket_t;
|
||||
typedef struct hashtable_s {
|
||||
int numbuckets;
|
||||
bucket_t **bucket;
|
||||
} hashtable_t;
|
||||
|
||||
void Hash_InitTable(hashtable_t *table, int numbucks, void *mem); //mem must be 0 filled. (memset(mem, 0, size))
|
||||
int Hash_Key(char *name, int modulus);
|
||||
void *Hash_Get(hashtable_t *table, char *name);
|
||||
void *Hash_GetKey(hashtable_t *table, int key);
|
||||
void *Hash_GetNext(hashtable_t *table, char *name, void *old);
|
||||
void *Hash_Add(hashtable_t *table, char *name, void *data);
|
||||
void *Hash_Add2(hashtable_t *table, char *name, void *data, bucket_t *buck);
|
||||
void *Hash_AddKey(hashtable_t *table, int key, void *data);
|
||||
void Hash_Remove(hashtable_t *table, char *name);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//exported.
|
||||
void POP3_CreateConnection(char *servername, char *username, char *password);
|
||||
cvar_t pop3_checkfrequency = {"pop3_checkfrequency", "60"}; //once a min
|
||||
void POP3_Think (void);
|
||||
void POP3_WriteCache (void);
|
||||
//end export list.
|
||||
|
||||
#define HASHELEMENTS 512
|
||||
|
||||
static hashtable_t pop3msghash;
|
||||
|
||||
qboolean POP3_IsMessageUnique(char *hash)
|
||||
{
|
||||
char *buf;
|
||||
if (!pop3msghash.numbuckets)
|
||||
Hash_InitTable(&pop3msghash, HASHELEMENTS, BZ_Malloc(Hash_BytesForBuckets(HASHELEMENTS)));
|
||||
if (Hash_Get(&pop3msghash, hash))
|
||||
return false;
|
||||
|
||||
buf = Z_Malloc(sizeof(bucket_t) + strlen(hash)+1);
|
||||
strcpy(buf+sizeof(bucket_t), hash);
|
||||
hash = buf+sizeof(bucket_t);
|
||||
Hash_Add2(&pop3msghash, hash, hash, (bucket_t *)buf);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define POP3_PORT 110
|
||||
|
||||
|
||||
typedef struct pop3_con_s {
|
||||
char server[128];
|
||||
char username[128];
|
||||
char password[128];
|
||||
|
||||
float lastnoop;
|
||||
|
||||
//these are used so we can fail a send.
|
||||
//or recieve only part of an input.
|
||||
//FIXME: make dynamically sizable, as it could drop if the send is too small (That's okay.
|
||||
// but if the read is bigger than one command we suddenly fail entirly.)
|
||||
int sendlen;
|
||||
int sendbuffersize;
|
||||
char *sendbuffer;
|
||||
int readlen;
|
||||
int readbuffersize;
|
||||
char *readbuffer;
|
||||
|
||||
qboolean drop;
|
||||
|
||||
int socket;
|
||||
|
||||
//we have a certain number of stages.
|
||||
enum {
|
||||
POP3_NOTCONNECTED,
|
||||
POP3_WAITINGFORINITIALRESPONCE, //waiting for an initial response.
|
||||
POP3_AUTHING, //wating for a response from USER
|
||||
POP3_AUTHING2, //Set PASS, waiting to see if we passed.
|
||||
POP3_LISTING, //Sent UIDL, waiting to see
|
||||
POP3_RETRIEVING, //sent TOP, waiting for message headers to print info.
|
||||
POP3_HEADER,
|
||||
POP3_BODY,
|
||||
POP3_QUITTING
|
||||
} state;
|
||||
|
||||
int retrlist[256]; //unrecognised uidls are added to this list.
|
||||
int numtoretrieve;
|
||||
|
||||
char msgsubject[256];
|
||||
char msgfrom[256];
|
||||
|
||||
struct pop3_con_s *next;
|
||||
} pop3_con_t;
|
||||
|
||||
static pop3_con_t *pop3sv;
|
||||
|
||||
void POP3_CreateConnection(char *addy, char *username, char *password)
|
||||
{
|
||||
unsigned long _true = true;
|
||||
struct sockaddr_in from;
|
||||
struct sockaddr_qstorage to;
|
||||
pop3_con_t *con;
|
||||
|
||||
for (con = pop3sv; con; con = con->next)
|
||||
{
|
||||
if (!strcmp(con->server, addy) && !strcmp(con->username, username))
|
||||
{
|
||||
if (con->state == POP3_NOTCONNECTED && !con->socket)
|
||||
break;
|
||||
Con_Printf("Already connected to that pop3 server\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{//quake routines using dns and stuff (Really, I wanna keep quake and pop3 fairly seperate)
|
||||
netadr_t qaddy;
|
||||
if (!NET_StringToAdr (addy, &qaddy))
|
||||
return; //failed to resolve dns.
|
||||
if (!qaddy.port)
|
||||
qaddy.port = htons(POP3_PORT);
|
||||
NetadrToSockadr(&qaddy, &to);
|
||||
}
|
||||
|
||||
if (!con)
|
||||
con = IWebMalloc(sizeof(pop3_con_t));
|
||||
else
|
||||
con->state = POP3_WAITINGFORINITIALRESPONCE;
|
||||
|
||||
|
||||
|
||||
if ((con->socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
|
||||
{
|
||||
Sys_Error ("POP3_CreateConnection: socket: %s\n", strerror(qerrno));
|
||||
}
|
||||
|
||||
memset(&from, 0, sizeof(from));
|
||||
from.sin_family = AF_INET;
|
||||
if (bind(con->socket, (struct sockaddr*)&from, sizeof(from)) == -1)
|
||||
{
|
||||
IWebWarnPrintf ("POP3_CreateConnection: failed to bind: %s\n", strerror(qerrno));
|
||||
}
|
||||
|
||||
//not yet blocking.
|
||||
if (connect(con->socket, (struct sockaddr*)&to, sizeof(to)) == -1)
|
||||
{
|
||||
IWebWarnPrintf ("POP3_CreateConnection: connect: %s\n", strerror(qerrno));
|
||||
closesocket(con->socket);
|
||||
IWebFree(con);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ioctlsocket (con->socket, FIONBIO, &_true) == -1) //now make it non blocking.
|
||||
{
|
||||
Sys_Error ("POP3_CreateConnection: ioctl FIONBIO: %s\n", strerror(qerrno));
|
||||
}
|
||||
|
||||
Q_strncpyz(con->server, addy, sizeof(con->server));
|
||||
Q_strncpyz(con->username, username, sizeof(con->username));
|
||||
Q_strncpyz(con->password, password, sizeof(con->password));
|
||||
|
||||
if (!con->state)
|
||||
{
|
||||
con->state = POP3_WAITINGFORINITIALRESPONCE;
|
||||
|
||||
con->next = pop3sv;
|
||||
pop3sv = con;
|
||||
|
||||
Con_Printf("Connected to %s\n", con->server);
|
||||
}
|
||||
}
|
||||
|
||||
static void POP3_EmitCommand(pop3_con_t *pop3, char *text)
|
||||
{
|
||||
int newlen;
|
||||
|
||||
newlen = pop3->sendlen + strlen(text) + 2;
|
||||
|
||||
if (newlen >= pop3->sendbuffersize || !pop3->sendbuffer) //pre-length check.
|
||||
{
|
||||
char *newbuf;
|
||||
pop3->sendbuffersize = newlen*2;
|
||||
newbuf = IWebMalloc(pop3->sendbuffersize);
|
||||
if (!newbuf)
|
||||
{
|
||||
Con_Printf("Memory is low\n");
|
||||
pop3->drop = true; //failed.
|
||||
return;
|
||||
}
|
||||
if (pop3->sendbuffer)
|
||||
{
|
||||
memcpy(newbuf, pop3->sendbuffer, pop3->sendlen);
|
||||
IWebFree(pop3->sendbuffer);
|
||||
}
|
||||
pop3->sendbuffer = newbuf;
|
||||
}
|
||||
|
||||
pop3->sendlen = newlen;
|
||||
|
||||
strncat(pop3->sendbuffer, text, pop3->sendbuffersize-1);
|
||||
strncat(pop3->sendbuffer, "\r\n", pop3->sendbuffersize-1);
|
||||
|
||||
// Con_Printf("^3%s\n", text);
|
||||
}
|
||||
|
||||
static qboolean POP3_ThinkCon(pop3_con_t *pop3) //false means drop the connection.
|
||||
{
|
||||
char *ending;
|
||||
int len;
|
||||
|
||||
if (pop3->state == POP3_NOTCONNECTED && !pop3->socket)
|
||||
{
|
||||
if (pop3->lastnoop + pop3_checkfrequency.value < Sys_DoubleTime())
|
||||
{ //we need to recreate the connection now.
|
||||
pop3->lastnoop = Sys_DoubleTime();
|
||||
POP3_CreateConnection(pop3->server, pop3->username, pop3->password);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//get the buffer, stick it in our read holder
|
||||
if (pop3->readlen+32 >= pop3->readbuffersize || !pop3->readbuffer)
|
||||
{
|
||||
len = pop3->readbuffersize;
|
||||
if (!pop3->readbuffer)
|
||||
pop3->readbuffersize = 256;
|
||||
else
|
||||
pop3->readbuffersize*=2;
|
||||
|
||||
ending = IWebMalloc(pop3->readbuffersize);
|
||||
if (!ending)
|
||||
{
|
||||
Con_Printf("Memory is low\n");
|
||||
return false;
|
||||
}
|
||||
if (pop3->readbuffer)
|
||||
{
|
||||
memcpy(ending, pop3->readbuffer, len);
|
||||
IWebFree(pop3->readbuffer);
|
||||
}
|
||||
pop3->readbuffer = ending;
|
||||
}
|
||||
|
||||
len = recv(pop3->socket, pop3->readbuffer+pop3->readlen, pop3->readbuffersize-pop3->readlen-1, 0);
|
||||
if (len>0)
|
||||
{
|
||||
pop3->readlen+=len;
|
||||
pop3->readbuffer[pop3->readlen] = '\0';
|
||||
}
|
||||
|
||||
if (pop3->readlen>0)
|
||||
{
|
||||
ending = strstr(pop3->readbuffer, "\r\n");
|
||||
|
||||
if (ending) //pollable text.
|
||||
{
|
||||
*ending = '\0';
|
||||
// Con_Printf("^2%s\n", pop3->readbuffer);
|
||||
|
||||
ending+=2;
|
||||
if (pop3->state == POP3_WAITINGFORINITIALRESPONCE)
|
||||
{
|
||||
if (!strncmp(pop3->readbuffer, "+OK", 3))
|
||||
{
|
||||
char *angle1;
|
||||
char *angle2 = NULL;
|
||||
angle1 = strchr(pop3->readbuffer, '<');
|
||||
if (angle1)
|
||||
{
|
||||
angle2 = strchr(angle1+1, '>');
|
||||
}
|
||||
if (angle2)
|
||||
{ //just in case
|
||||
angle2[1] = '\0';
|
||||
|
||||
POP3_EmitCommand(pop3, va("APOP %s %s", pop3->username, MD5_GetPop3APOPString(angle1, pop3->password)));
|
||||
pop3->state = POP3_AUTHING2;
|
||||
}
|
||||
else
|
||||
{
|
||||
POP3_EmitCommand(pop3, va("USER %s", pop3->username));
|
||||
pop3->state = POP3_AUTHING;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Printf("Unexpected response from POP3 server\n");
|
||||
return false; //some sort of error.
|
||||
}
|
||||
}
|
||||
else if (pop3->state == POP3_AUTHING)
|
||||
{
|
||||
if (!strncmp(pop3->readbuffer, "+OK", 3))
|
||||
{
|
||||
POP3_EmitCommand(pop3, va("PASS %s", pop3->password));
|
||||
pop3->state = POP3_AUTHING2;
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Printf("Unexpected response from POP3 server.\nCheck username/password\n");
|
||||
return false; //some sort of error.
|
||||
}
|
||||
}
|
||||
else if (pop3->state == POP3_AUTHING2)
|
||||
{
|
||||
if (!strncmp(pop3->readbuffer, "+OK", 3))
|
||||
{
|
||||
POP3_EmitCommand(pop3, "UIDL");
|
||||
pop3->state = POP3_LISTING;
|
||||
pop3->lastnoop = Sys_DoubleTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Printf("Unexpected response from POP3 server.\nCheck username/password\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (pop3->state == POP3_LISTING)
|
||||
{
|
||||
if (!strncmp(pop3->readbuffer, "-ERR", 4))
|
||||
{
|
||||
Con_Printf("Unexpected response from POP3 server.\nUIDL not supported?\n");
|
||||
return false;
|
||||
}
|
||||
else if (!strncmp(pop3->readbuffer, "+OK", 3))
|
||||
{
|
||||
}
|
||||
else if (!strncmp(pop3->readbuffer, ".", 1)) //we only ever search for recent messages. So we fetch them and get sender and subject.
|
||||
{
|
||||
if (!pop3->numtoretrieve)
|
||||
{
|
||||
pop3->state = POP3_QUITTING;
|
||||
POP3_EmitCommand(pop3, "QUIT");
|
||||
}
|
||||
else
|
||||
{
|
||||
pop3->state = POP3_RETRIEVING;
|
||||
POP3_EmitCommand(pop3, va("RETR %i", pop3->retrlist[--pop3->numtoretrieve]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char *s;
|
||||
s = pop3->readbuffer;
|
||||
if (*s)
|
||||
{
|
||||
s++;
|
||||
while (*s >= '0' && *s <= '9')
|
||||
s++;
|
||||
while (*s == ' ')
|
||||
s++;
|
||||
}
|
||||
|
||||
if (POP3_IsMessageUnique(s))
|
||||
if (pop3->numtoretrieve < sizeof(pop3->retrlist)/sizeof(pop3->retrlist[0]))
|
||||
pop3->retrlist[pop3->numtoretrieve++] = atoi(pop3->readbuffer);
|
||||
}
|
||||
}
|
||||
else if (pop3->state == POP3_RETRIEVING)
|
||||
{
|
||||
if (!strncmp(pop3->readbuffer, "+OK", 3))
|
||||
{
|
||||
pop3->msgsubject[0] = '\0';
|
||||
pop3->msgfrom[0] = '\0';
|
||||
|
||||
pop3->state = POP3_HEADER;
|
||||
}
|
||||
else
|
||||
{ //erm... go for the next?
|
||||
if (!pop3->numtoretrieve)
|
||||
{
|
||||
pop3->state = POP3_QUITTING;
|
||||
POP3_EmitCommand(pop3, "QUIT");
|
||||
}
|
||||
else
|
||||
POP3_EmitCommand(pop3, va("RETR %i", pop3->retrlist[--pop3->numtoretrieve]));
|
||||
}
|
||||
}
|
||||
else if (pop3->state == POP3_HEADER)
|
||||
{
|
||||
if (!strnicmp(pop3->readbuffer, "From: ", 6))
|
||||
Q_strncpyz(pop3->msgfrom, pop3->readbuffer + 6, sizeof(pop3->msgfrom));
|
||||
else if (!strnicmp(pop3->readbuffer, "Subject: ", 9))
|
||||
Q_strncpyz(pop3->msgsubject, pop3->readbuffer + 9, sizeof(pop3->msgsubject));
|
||||
else if (!strncmp(pop3->readbuffer, ".", 1))
|
||||
{
|
||||
Con_Printf("New message:\nFrom: %s\nSubject: %s\n", pop3->msgfrom, pop3->msgsubject);
|
||||
|
||||
if (!pop3->numtoretrieve)
|
||||
{
|
||||
pop3->state = POP3_QUITTING;
|
||||
POP3_EmitCommand(pop3, "QUIT");
|
||||
}
|
||||
else
|
||||
{
|
||||
pop3->state = POP3_RETRIEVING;
|
||||
POP3_EmitCommand(pop3, va("RETR %i", pop3->retrlist[--pop3->numtoretrieve]));
|
||||
}
|
||||
}
|
||||
else if (!*pop3->readbuffer)
|
||||
pop3->state = POP3_BODY;
|
||||
}
|
||||
else if (pop3->state == POP3_BODY)
|
||||
{
|
||||
if (!strncmp(pop3->readbuffer, "..", 2))
|
||||
{
|
||||
//line of text, skipping first '.'
|
||||
Con_Printf("%s\n", pop3->readbuffer+1);
|
||||
}
|
||||
else if (!strncmp(pop3->readbuffer, ".", 1))
|
||||
{
|
||||
Con_Printf("New message:\nFrom: %s\nSubject: %s\n", pop3->msgfrom, pop3->msgsubject);
|
||||
|
||||
if (!pop3->numtoretrieve)
|
||||
{
|
||||
pop3->state = POP3_QUITTING;
|
||||
POP3_EmitCommand(pop3, "QUIT");
|
||||
}
|
||||
else
|
||||
{
|
||||
pop3->state = POP3_RETRIEVING;
|
||||
POP3_EmitCommand(pop3, va("RETR %i", pop3->retrlist[--pop3->numtoretrieve]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//normal line of text
|
||||
Con_Printf("%s\n", pop3->readbuffer);
|
||||
}
|
||||
}
|
||||
else if (pop3->state == POP3_QUITTING)
|
||||
{
|
||||
pop3->state = POP3_NOTCONNECTED;
|
||||
closesocket(pop3->socket);
|
||||
pop3->lastnoop = Sys_DoubleTime();
|
||||
pop3->socket = 0;
|
||||
pop3->readlen = 0;
|
||||
pop3->sendlen = 0;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Printf("Bad client state\n");
|
||||
return false;
|
||||
}
|
||||
pop3->readlen -= ending - pop3->readbuffer;
|
||||
memmove(pop3->readbuffer, ending, strlen(ending)+1);
|
||||
}
|
||||
}
|
||||
if (pop3->drop)
|
||||
return false;
|
||||
|
||||
if (pop3->sendlen)
|
||||
{
|
||||
len = send(pop3->socket, pop3->sendbuffer, pop3->sendlen, 0);
|
||||
if (len>0)
|
||||
{
|
||||
pop3->sendlen-=len;
|
||||
memmove(pop3->sendbuffer, pop3->sendbuffer+len, pop3->sendlen+1);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void POP3_Think (void)
|
||||
{
|
||||
pop3_con_t *prev = NULL;
|
||||
pop3_con_t *pop3;
|
||||
|
||||
for (pop3 = pop3sv; pop3; pop3 = pop3->next)
|
||||
{
|
||||
if (pop3->drop || !POP3_ThinkCon(pop3))
|
||||
{
|
||||
if (!prev)
|
||||
pop3sv = pop3->next;
|
||||
else
|
||||
prev->next = pop3->next;
|
||||
if (pop3->socket)
|
||||
closesocket(pop3->socket);
|
||||
BZ_Free(pop3);
|
||||
if (!prev)
|
||||
break;
|
||||
}
|
||||
|
||||
prev = pop3;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
670
engine/email/sv_pop3.c
Normal file
670
engine/email/sv_pop3.c
Normal file
|
@ -0,0 +1,670 @@
|
|||
#include "bothdefs.h"
|
||||
|
||||
#ifdef EMAILSERVER
|
||||
|
||||
#include "quakedef.h"
|
||||
#include "winquake.h"
|
||||
|
||||
//FIXME: the DELE command's effects arn't properly checked.
|
||||
//FIXME: no UIDL command
|
||||
|
||||
//FIXME: remove sequential naming.
|
||||
|
||||
char *MD5_GetPop3APOPString(char *timestamp, char *secrit);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define HASH_FILESYSTEM
|
||||
#ifdef HASH_FILESYSTEM
|
||||
//#include "hash.h"
|
||||
|
||||
#define Hash_BytesForBuckets(b) (sizeof(bucket_t)*b)
|
||||
|
||||
#define STRCMP(s1,s2) (((*s1)!=(*s2)) || strcmp(s1+1,s2+1)) //saves about 2-6 out of 120 - expansion of idea from fastqcc
|
||||
typedef struct bucket_s {
|
||||
void *data;
|
||||
char *keystring;
|
||||
struct bucket_s *next;
|
||||
} bucket_t;
|
||||
typedef struct hashtable_s {
|
||||
int numbuckets;
|
||||
bucket_t **bucket;
|
||||
} hashtable_t;
|
||||
|
||||
void Hash_InitTable(hashtable_t *table, int numbucks, void *mem); //mem must be 0 filled. (memset(mem, 0, size))
|
||||
int Hash_Key(char *name, int modulus);
|
||||
void *Hash_Get(hashtable_t *table, char *name);
|
||||
void *Hash_GetKey(hashtable_t *table, int key);
|
||||
void *Hash_GetNext(hashtable_t *table, char *name, void *old);
|
||||
void *Hash_Add(hashtable_t *table, char *name, void *data);
|
||||
void *Hash_Add2(hashtable_t *table, char *name, void *data, bucket_t *buck);
|
||||
void *Hash_AddKey(hashtable_t *table, int key, void *data);
|
||||
void Hash_Remove(hashtable_t *table, char *name);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#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 <stdio.h>
|
||||
#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 POP3_PORT 110
|
||||
#define POP3_TIMEOUT 30
|
||||
|
||||
static qboolean pop3active;
|
||||
static int pop3serversocket;
|
||||
|
||||
typedef struct {
|
||||
char filename[MAX_QPATH];
|
||||
int size;
|
||||
qboolean deleted;
|
||||
bucket_t bucket;
|
||||
} pop3message_t;
|
||||
|
||||
typedef struct svpop3client_s {
|
||||
struct svpop3client_s *next;
|
||||
|
||||
int socket;
|
||||
|
||||
float timeout;
|
||||
|
||||
char *messagelump;
|
||||
int messagelumppos;
|
||||
int messagelumplen;
|
||||
qboolean messagelumphitbody;
|
||||
int messagelumplines; //lines to send past header
|
||||
|
||||
int nummessages;
|
||||
int totalsize;
|
||||
|
||||
char greeting[64];
|
||||
char username[64];
|
||||
qboolean loggedin;
|
||||
|
||||
char inmessagebuffer[1024];
|
||||
int inmessagelen;
|
||||
char outmessagebuffer[1024];
|
||||
int outmessagelen;
|
||||
qboolean dropwhensent;
|
||||
|
||||
#define NUMBUCKETS 64
|
||||
hashtable_t emails;
|
||||
bucket_t *bucketpointer[NUMBUCKETS];
|
||||
} svpop3client_t;
|
||||
static svpop3client_t *svpop3client;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static void POP3_ServerInit(void)
|
||||
{
|
||||
struct sockaddr_in address;
|
||||
unsigned long _true = true;
|
||||
int port = POP3_PORT;
|
||||
|
||||
if ((pop3serversocket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
|
||||
{
|
||||
Sys_Error ("FTP_TCP_OpenSocket: socket:", strerror(qerrno));
|
||||
}
|
||||
|
||||
if (ioctlsocket (pop3serversocket, 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 (pop3serversocket, (void *)&address, sizeof(address)) == -1)
|
||||
{
|
||||
closesocket(pop3serversocket);
|
||||
return;
|
||||
}
|
||||
|
||||
listen(pop3serversocket, 3);
|
||||
|
||||
pop3active = true;
|
||||
|
||||
|
||||
IWebPrintf("POP3 server is running\n");
|
||||
return;
|
||||
}
|
||||
|
||||
static void POP3_ServerShutdown(void)
|
||||
{
|
||||
closesocket(pop3serversocket);
|
||||
pop3active = false;
|
||||
}
|
||||
|
||||
static void SV_POP3_QueueMessage(svpop3client_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 POP3_NewConnection(int socket)
|
||||
{
|
||||
unsigned long _true = true;
|
||||
svpop3client_t *newcl;
|
||||
newcl = IWebMalloc(sizeof(svpop3client_t));
|
||||
if (!newcl) //bother
|
||||
{
|
||||
closesocket(socket);
|
||||
return;
|
||||
}
|
||||
if (ioctlsocket (socket, FIONBIO, &_true) == -1)
|
||||
{
|
||||
closesocket(socket);
|
||||
return;
|
||||
}
|
||||
memset(newcl, 0, sizeof(svpop3client_t));
|
||||
|
||||
newcl->socket = socket;
|
||||
newcl->next = svpop3client;
|
||||
svpop3client = newcl;
|
||||
|
||||
newcl->timeout = realtime + POP3_TIMEOUT;
|
||||
|
||||
*newcl->outmessagebuffer = '\0';
|
||||
|
||||
sprintf(newcl->greeting, "<%i.%i.%i.%i.%i.%i.%i>", rand(), rand(), rand(), rand(), rand(), rand(), rand());
|
||||
// _true = strlen(newcl->greeting);
|
||||
|
||||
printf("newclient\n");
|
||||
|
||||
SV_POP3_QueueMessage(newcl, va("+OK %s\r\n", newcl->greeting));
|
||||
}
|
||||
|
||||
static int SV_POP3_AddMessage(char *filename, int flags, void *incl)
|
||||
{
|
||||
FILE *f;
|
||||
svpop3client_t *cl = incl;
|
||||
pop3message_t *msg;
|
||||
|
||||
f = fopen(filename, "rb");
|
||||
if (!f)
|
||||
return true; //shouldn't happen
|
||||
|
||||
msg = IWebMalloc(sizeof(pop3message_t));
|
||||
if (!msg)
|
||||
{
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_strncpyz(msg->filename, filename, sizeof(msg->filename));
|
||||
msg->deleted = false;
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
msg->size = ftell(f);
|
||||
fclose(f);
|
||||
|
||||
cl->totalsize+=msg->size;
|
||||
cl->nummessages++;
|
||||
|
||||
Hash_Add2(&cl->emails, va("%i", ++cl->nummessages), msg, &msg->bucket);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void SV_POP3_CountMessages(svpop3client_t *cl)
|
||||
{
|
||||
Hash_InitTable(&cl->emails, NUMBUCKETS, cl->bucketpointer);
|
||||
cl->totalsize=0;
|
||||
Sys_EnumerateFiles(".", va("emails/%s/*.eml", cl->username), SV_POP3_AddMessage, cl);
|
||||
}
|
||||
|
||||
static pop3message_t *SV_POP3_GetMessage(svpop3client_t *cl, int num)
|
||||
{
|
||||
pop3message_t *msg;
|
||||
|
||||
msg = IWebMalloc(sizeof(pop3message_t));
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
if (msg->deleted)
|
||||
return NULL;
|
||||
|
||||
msg = Hash_Get(&cl->emails, va("%i", num));
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static void SV_POP3_CleanUp(svpop3client_t *cl, qboolean dodelete) //closes the messages list created by SV_POP3_CountMessages
|
||||
{
|
||||
pop3message_t *msg;
|
||||
int mn;
|
||||
for (mn = 1; mn <= cl->nummessages; mn++)
|
||||
{
|
||||
msg = SV_POP3_GetMessage(cl, mn);
|
||||
if (!msg)
|
||||
continue;
|
||||
|
||||
if (dodelete && msg->deleted)
|
||||
{
|
||||
remove(msg->filename);
|
||||
}
|
||||
|
||||
Hash_Remove(&cl->emails, va("%i", mn));
|
||||
BZ_Free(msg);
|
||||
}
|
||||
}
|
||||
|
||||
static qboolean SV_POP3_ReadMessage(svpop3client_t *cl, int index)
|
||||
{
|
||||
FILE *f;
|
||||
pop3message_t *msg;
|
||||
|
||||
msg = SV_POP3_GetMessage(cl, index);
|
||||
if (!msg)
|
||||
return false;
|
||||
if (msg->deleted)
|
||||
return false;
|
||||
|
||||
f = fopen(msg->filename, "rb");
|
||||
if (!f)
|
||||
return false;
|
||||
fseek(f, 0, SEEK_END);
|
||||
cl->messagelumplen = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
cl->messagelump = IWebMalloc(cl->messagelumplen+3);
|
||||
fread(cl->messagelump, 1, cl->messagelumplen, f);
|
||||
cl->messagelump[cl->messagelumplen++] = '\r';
|
||||
cl->messagelump[cl->messagelumplen++] = '\n';
|
||||
fclose(f);
|
||||
|
||||
cl->messagelumplen = strlen(cl->messagelump);
|
||||
cl->messagelumppos = 0;
|
||||
cl->messagelumphitbody = false;
|
||||
cl->messagelumplines = 0x7fffffff;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void SV_POP3_BuildListing(svpop3client_t *cl, qboolean isuidl)
|
||||
{
|
||||
pop3message_t *msg;
|
||||
int mn;
|
||||
char *listing;
|
||||
|
||||
listing = cl->messagelump = IWebMalloc(cl->nummessages*64+3);
|
||||
|
||||
for (mn = 1; mn <= cl->nummessages; mn++)
|
||||
{
|
||||
msg = SV_POP3_GetMessage(cl, mn);
|
||||
if (!msg || msg->deleted)
|
||||
continue;
|
||||
|
||||
if (isuidl)
|
||||
sprintf(listing, "%i %s,S=%i\r\n", mn, msg->filename, msg->size);
|
||||
else
|
||||
sprintf(listing, "%i %i\r\n", mn, msg->size);
|
||||
listing += strlen(listing);
|
||||
}
|
||||
|
||||
cl->messagelumplen = listing - cl->messagelump;
|
||||
}
|
||||
|
||||
static qboolean SV_POP3_RunClient(svpop3client_t *cl) //true means client should be dropped
|
||||
{
|
||||
int read;
|
||||
char *nl;
|
||||
char *token;
|
||||
int blankline;
|
||||
|
||||
if (cl->messagelump)
|
||||
{
|
||||
blankline=false;
|
||||
while (cl->outmessagelen < sizeof(cl->outmessagebuffer)-100)
|
||||
{
|
||||
if (cl->messagelumppos >= cl->messagelumplen)
|
||||
break;
|
||||
|
||||
if (cl->messagelump[cl->messagelumppos] == '.') //double up all '.'s at start of lines
|
||||
cl->outmessagebuffer[cl->outmessagelen++] = '.';
|
||||
|
||||
blankline = true;
|
||||
for(;;)
|
||||
{
|
||||
if (cl->messagelumppos >= cl->messagelumplen)
|
||||
break;
|
||||
if (cl->messagelump[cl->messagelumppos] > ' ')
|
||||
blankline = false;
|
||||
cl->outmessagebuffer[cl->outmessagelen++] = cl->messagelump[cl->messagelumppos];
|
||||
if (cl->messagelump[cl->messagelumppos++] == '\n')
|
||||
break;
|
||||
}
|
||||
if (blankline)
|
||||
cl->messagelumphitbody = true;
|
||||
if (cl->messagelumphitbody)
|
||||
{
|
||||
if (cl->messagelumplines--<=0)
|
||||
cl->messagelumppos = cl->messagelumplen; //easy way to terminate.
|
||||
}
|
||||
}
|
||||
if (cl->messagelumppos >= cl->messagelumplen)
|
||||
{ //we've sent the entire buffer now.
|
||||
cl->outmessagebuffer[cl->outmessagelen++] = '.';
|
||||
cl->outmessagebuffer[cl->outmessagelen++] = '\r';
|
||||
cl->outmessagebuffer[cl->outmessagelen++] = '\n';
|
||||
|
||||
BZ_Free(cl->messagelump);
|
||||
cl->messagelump = NULL;
|
||||
}
|
||||
cl->outmessagebuffer[cl->outmessagelen] = '\0';
|
||||
printf("%s\n", cl->outmessagebuffer);
|
||||
}
|
||||
if (cl->outmessagelen)
|
||||
{
|
||||
read = send(cl->socket, cl->outmessagebuffer, cl->outmessagelen, MSG_PARTIAL);
|
||||
if (read < 0)
|
||||
read = 0;
|
||||
memmove(cl->outmessagebuffer, cl->outmessagebuffer + read, cl->outmessagelen - read);
|
||||
cl->outmessagelen -= read;
|
||||
cl->outmessagebuffer[cl->outmessagelen] = '\0';
|
||||
|
||||
if (cl->dropwhensent && !cl->outmessagelen)
|
||||
return true;
|
||||
}
|
||||
|
||||
read = recv(cl->socket, cl->inmessagebuffer+cl->inmessagelen, sizeof(cl->inmessagebuffer)-1-cl->inmessagelen, MSG_PARTIAL);
|
||||
|
||||
if (read == -1)
|
||||
{
|
||||
if (qerrno != EWOULDBLOCK) //blocking is the only way to keep the connection on fail.
|
||||
return true;
|
||||
|
||||
if (!*cl->inmessagebuffer)
|
||||
return false;
|
||||
//nonblocking allows us to get multiple commands from one packet.
|
||||
}
|
||||
else if (read == 0) //don't quite know why this happens.
|
||||
return true; //believed to be an indication that the other end has disconnected.
|
||||
else
|
||||
{
|
||||
cl->timeout = realtime + POP3_TIMEOUT;
|
||||
|
||||
cl->inmessagelen += read;
|
||||
if (cl->inmessagelen >= sizeof(cl->inmessagebuffer)-1) //happens if we fill the buffer with no hope of empting it.
|
||||
return true;
|
||||
cl->inmessagebuffer[cl->inmessagelen] = '\0';
|
||||
}
|
||||
nl = strchr(cl->inmessagebuffer, '\n');
|
||||
if (nl)
|
||||
{
|
||||
*nl = '\0';
|
||||
//Con_Printf("%s\n", cl->inmessagebuffer);
|
||||
read = nl - cl->inmessagebuffer + 1;
|
||||
|
||||
token = COM_ParseToken(cl->inmessagebuffer);
|
||||
|
||||
//auth mechanism 1
|
||||
if (!strcmp(com_token, "USER"))
|
||||
{
|
||||
token = COM_ParseToken(token);
|
||||
if (*com_token)
|
||||
{
|
||||
Q_strncpyz(cl->username, com_token, sizeof(cl->username));
|
||||
SV_POP3_QueueMessage(cl, "+OK User name accepted, password please\r\n");
|
||||
|
||||
SV_POP3_CleanUp(cl, true);
|
||||
cl->loggedin = false;
|
||||
}
|
||||
else
|
||||
SV_POP3_QueueMessage(cl, "-ERR no username was specified\r\n");
|
||||
}
|
||||
else if (!strcmp(com_token, "PASS"))
|
||||
{
|
||||
#ifndef CLIENTONLY
|
||||
int id;
|
||||
extern cvar_t rank_filename;
|
||||
|
||||
token = COM_ParseToken(token);
|
||||
id = Rank_GetPlayerID(cl->username, atoi(com_token), false);
|
||||
if (!id && *rank_filename.string)
|
||||
{
|
||||
SV_POP3_QueueMessage(cl, "-ERR User or Password not valid\r\n");
|
||||
|
||||
SV_POP3_CleanUp(cl, true);
|
||||
cl->loggedin = false;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
SV_POP3_QueueMessage(cl, "+OK Logged in\r\n");
|
||||
cl->loggedin = true;
|
||||
SV_POP3_CountMessages(cl);
|
||||
}
|
||||
}
|
||||
|
||||
//auth2
|
||||
else if (!strcmp(com_token, "APOP"))
|
||||
{
|
||||
int id;
|
||||
int pass;
|
||||
extern cvar_t rank_filename;
|
||||
|
||||
token = COM_ParseToken(token);
|
||||
if (*com_token)
|
||||
{
|
||||
Q_strncpyz(cl->username, com_token, sizeof(cl->username));
|
||||
|
||||
#ifndef CLIENTONLY
|
||||
token = COM_ParseToken(token);
|
||||
pass = Rank_GetPass(cl->username);
|
||||
id = Rank_GetPlayerID(cl->username, pass, false);
|
||||
if ((!id && *rank_filename.string) || strcmp(MD5_GetPop3APOPString(cl->greeting, va("%i", pass)), com_token))
|
||||
{
|
||||
SV_POP3_QueueMessage(cl, "-ERR User or Password not valid\r\n");
|
||||
|
||||
SV_POP3_CleanUp(cl, true);
|
||||
cl->loggedin = false;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
SV_POP3_QueueMessage(cl, "+OK Logged in\r\n");
|
||||
cl->loggedin = true;
|
||||
SV_POP3_CountMessages(cl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//now they need to have been logged in properly.
|
||||
else if (!cl->loggedin)
|
||||
SV_POP3_QueueMessage(cl, "-ERR You didn't log in properly\r\n");
|
||||
else if (!strcmp(com_token, "STAT"))
|
||||
{
|
||||
char text[64];
|
||||
sprintf(text, "+OK %i %i\r\n", cl->nummessages, cl->totalsize);
|
||||
SV_POP3_QueueMessage(cl, text);
|
||||
}
|
||||
else if (!strcmp(com_token, "LIST"))
|
||||
{
|
||||
SV_POP3_QueueMessage(cl, "+OK EMail listing follows:\r\n");
|
||||
SV_POP3_BuildListing(cl, false);
|
||||
}
|
||||
else if (!strcmp(com_token, "DELE"))
|
||||
{
|
||||
pop3message_t *msg;
|
||||
int mnum;
|
||||
token = COM_ParseToken(token);
|
||||
mnum = atoi(com_token);
|
||||
|
||||
msg = SV_POP3_GetMessage(cl, mnum);
|
||||
if (!msg)
|
||||
SV_POP3_QueueMessage(cl, "-ERR message index out of range\r\n");
|
||||
else if (msg->deleted)
|
||||
SV_POP3_QueueMessage(cl, "-ERR message already deleted\r\n");
|
||||
else
|
||||
{
|
||||
msg->deleted = true;
|
||||
SV_POP3_QueueMessage(cl, "+OK Message marked for deleted\r\n");
|
||||
}
|
||||
}
|
||||
else if (!strcmp(com_token, "TOP"))
|
||||
{
|
||||
token = COM_ParseToken(token);
|
||||
if (SV_POP3_ReadMessage(cl, atoi(com_token)))
|
||||
{
|
||||
token = COM_ParseToken(token);
|
||||
SV_POP3_QueueMessage(cl, "+OK message contents follow:\n");
|
||||
cl->messagelumplines = atoi(com_token);
|
||||
}
|
||||
else
|
||||
SV_POP3_QueueMessage(cl, "-ERR Message index wasn't valid\n");
|
||||
}
|
||||
else if (!strcmp(com_token, "RETR"))
|
||||
{
|
||||
token = COM_ParseToken(token);
|
||||
if (SV_POP3_ReadMessage(cl, atoi(com_token)))
|
||||
SV_POP3_QueueMessage(cl, "+OK message contents follow:\n");
|
||||
else
|
||||
SV_POP3_QueueMessage(cl, "-ERR Message index wasn't valid\n");
|
||||
}
|
||||
else if (!strcmp(com_token, "UIDL"))
|
||||
{
|
||||
SV_POP3_QueueMessage(cl, "+OK I hope someone likes you\r\n");
|
||||
SV_POP3_BuildListing(cl, true);
|
||||
}
|
||||
else if (!strcmp(com_token, "QUIT"))
|
||||
{
|
||||
SV_POP3_CleanUp(cl, true);
|
||||
|
||||
SV_POP3_QueueMessage(cl, "+OK I hope someone likes you\r\n");
|
||||
cl->dropwhensent = true;
|
||||
}
|
||||
else
|
||||
SV_POP3_QueueMessage(cl, "-ERR Unrecognised command\r\n");
|
||||
//printf("%s\n", cl->outmessagebuffer);
|
||||
|
||||
memmove(cl->inmessagebuffer, cl->inmessagebuffer + read, cl->inmessagelen - read);
|
||||
cl->inmessagelen -= read;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void SV_POP3_RunClients(void)
|
||||
{
|
||||
svpop3client_t *cl, *prev;
|
||||
|
||||
cl = svpop3client;
|
||||
prev = NULL;
|
||||
while(cl)
|
||||
{
|
||||
if (cl->timeout < realtime || SV_POP3_RunClient(cl))
|
||||
{
|
||||
printf("drop client\n");
|
||||
closesocket(cl->socket);
|
||||
if (prev)
|
||||
prev->next = cl->next;
|
||||
else
|
||||
svpop3client = cl->next;
|
||||
|
||||
if (cl->messagelump)
|
||||
IWebFree(cl->messagelump);
|
||||
SV_POP3_CleanUp(cl, false);
|
||||
|
||||
IWebFree(cl);
|
||||
if (prev)
|
||||
cl = prev->next;
|
||||
else
|
||||
cl = svpop3client;
|
||||
continue;
|
||||
}
|
||||
prev = cl;
|
||||
cl = cl->next;
|
||||
}
|
||||
}
|
||||
|
||||
qboolean SV_POP3(qboolean activewanted)
|
||||
{
|
||||
struct sockaddr from;
|
||||
int fromlen;
|
||||
int clientsock;
|
||||
|
||||
if (!pop3active)
|
||||
{
|
||||
if (activewanted)
|
||||
POP3_ServerInit();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else if (!activewanted)
|
||||
{
|
||||
POP3_ServerShutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
fromlen = sizeof(from);
|
||||
clientsock = accept(pop3serversocket, (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.
|
||||
POP3_NewConnection(clientsock);
|
||||
|
||||
SV_POP3_RunClients();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
449
engine/email/sv_smtp.c
Normal file
449
engine/email/sv_smtp.c
Normal file
|
@ -0,0 +1,449 @@
|
|||
#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
|
Loading…
Reference in a new issue