mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-26 13:50:53 +00:00
6aaae617eb
pop3 is wierd, and doesn't let you poll, so instead we establish a new connection every minuite or so, in order to check for new emails. SOME POP3 SERVERS MAY DELETE YOUR EMAILS. This notification system will print the body of the message, with the header at the bottom (so it appears in the console notification area when playing). git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@1600 fc73d0e0-1445-4013-8a0c-d673dee63da5
465 lines
9.8 KiB
C
465 lines
9.8 KiB
C
/*
|
|
Copyright (C) 2005 David Walton.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
|
|
As a special exception, you may incorpotate patents and libraries regarding only hashing and security, on the conditions that it is also open source.
|
|
This means md4/5, rsa, ssl and similar.
|
|
*/
|
|
|
|
#include "../plugin.h"
|
|
|
|
//code to sit on an imap server and check for new emails every now and then.
|
|
|
|
|
|
|
|
|
|
|
|
char *STR_Parse(char *str, char *out, int outlen, char *punctuation)
|
|
{
|
|
char *s = str;
|
|
char *f;
|
|
|
|
skipwhite:
|
|
//skip over the whitespace
|
|
while (*s <= ' ' && *s)
|
|
s++;
|
|
|
|
if (*s == '/')
|
|
{
|
|
if (s[1] == '/') //c++ style comment
|
|
{
|
|
while(*s != '\n' && *s)
|
|
s++;
|
|
|
|
goto skipwhite;
|
|
}
|
|
if (s[1] == '*')
|
|
{
|
|
s+=2;
|
|
while(*s)
|
|
{
|
|
if (s[0] == '*' && s[1] == '/')
|
|
{
|
|
s+=2;
|
|
break;
|
|
}
|
|
s++;
|
|
}
|
|
goto skipwhite;
|
|
}
|
|
}
|
|
|
|
if (*s == '\"')
|
|
{
|
|
s++;
|
|
while(*s && outlen>1)
|
|
{
|
|
if (*s == '\"')
|
|
{
|
|
s++;
|
|
break;
|
|
}
|
|
*out++ = *s++;
|
|
outlen--;
|
|
}
|
|
*out++ = '\0';
|
|
return s;
|
|
}
|
|
|
|
if (strchr(punctuation, *s))
|
|
{ //starts with punctuation, so return only the first char
|
|
if (outlen < 2)
|
|
return NULL; //aaaah!
|
|
*out++ = *s;
|
|
*out++ = '\0';
|
|
s++;
|
|
|
|
return s;
|
|
}
|
|
//skip over non-white
|
|
for (f = s; outlen>1 && *(unsigned char*)f > ' '; f++, outlen--)
|
|
{
|
|
if (strchr(punctuation, *f))
|
|
{ //found punctuation, so return up to here
|
|
break;
|
|
}
|
|
*out++ = *f;
|
|
}
|
|
*out++ = '\0';
|
|
|
|
return f;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//exported.
|
|
void IMAP_CreateConnection(char *servername, char *username, char *password);
|
|
int imap_checkfrequency=60*1000;
|
|
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];
|
|
|
|
unsigned int 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;
|
|
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 = malloc(sizeof(imap_con_t));
|
|
|
|
con->socket = Net_TCPConnect(addy, IMAP_PORT);
|
|
|
|
if (!con->socket)
|
|
{
|
|
Con_Printf ("IMAP_CreateConnection: connect failed\n");
|
|
free(con);
|
|
return;
|
|
}
|
|
|
|
strlcpy(con->server, addy, sizeof(con->server));
|
|
strlcpy(con->username, username, sizeof(con->username));
|
|
strlcpy(con->password, password, sizeof(con->password));
|
|
|
|
con->next = imapsv;
|
|
imapsv = con;
|
|
|
|
Con_Printf ("Connected to %s (%s)\n", addy, username);
|
|
}
|
|
|
|
static void IMAP_EmitCommand(imap_con_t *imap, char *text)
|
|
{
|
|
int newlen;
|
|
|
|
//makes a few things easier though
|
|
|
|
newlen = imap->sendlen + 2 + strlen(text) + 2;
|
|
|
|
if (newlen >= imap->sendbuffersize || !imap->sendbuffer) //pre-length check.
|
|
{
|
|
char *newbuf;
|
|
imap->sendbuffersize = newlen*2;
|
|
newbuf = malloc(imap->sendbuffersize); //the null terminator comes from the >=
|
|
if (!newbuf)
|
|
{
|
|
Con_Printf("Memory is low\n");
|
|
imap->drop = true; //failed.
|
|
return;
|
|
}
|
|
if (imap->sendbuffer)
|
|
{
|
|
memcpy(newbuf, imap->sendbuffer, imap->sendlen);
|
|
free(imap->sendbuffer);
|
|
}
|
|
imap->sendbuffer = newbuf;
|
|
}
|
|
|
|
snprintf(imap->sendbuffer+imap->sendlen, newlen+1, "* %s\r\n", text);
|
|
imap->sendlen = newlen;
|
|
}
|
|
|
|
static char *IMAP_AddressStructure(char *msg, char *out, int outsize)
|
|
{
|
|
char name[256];
|
|
char mailbox[64];
|
|
char hostname[128];
|
|
char route[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 = STR_Parse(msg, name, sizeof(name), ""); //name
|
|
msg = STR_Parse(msg, route, sizeof(route), ""); //smtp route (ignored normally)
|
|
msg = STR_Parse(msg, mailbox, sizeof(mailbox), ""); //mailbox
|
|
msg = STR_Parse(msg, hostname, sizeof(hostname), ""); //hostname
|
|
|
|
while(indents && *msg == ')')
|
|
msg++;
|
|
|
|
if (out)
|
|
{
|
|
if (!strcmp(name, "NIL"))
|
|
snprintf(out, outsize, "%s@%s", mailbox, hostname);
|
|
else
|
|
snprintf(out, outsize, "%s <%s@%s>", name, mailbox, hostname);
|
|
}
|
|
|
|
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 = malloc(imap->readbuffersize);
|
|
if (!ending)
|
|
{
|
|
Con_Printf("Memory is low\n");
|
|
return false;
|
|
}
|
|
if (imap->readbuffer)
|
|
{
|
|
memcpy(ending, imap->readbuffer, len);
|
|
free(imap->readbuffer);
|
|
}
|
|
imap->readbuffer = ending;
|
|
}
|
|
|
|
len = Net_Recv(imap->socket, imap->readbuffer+imap->readlen, imap->readbuffersize-imap->readlen-1);
|
|
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_Milliseconds();
|
|
}
|
|
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_Milliseconds();
|
|
}
|
|
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];
|
|
char date[256];
|
|
|
|
num += 18;
|
|
|
|
num = STR_Parse(num, date, sizeof(date), "");
|
|
num = STR_Parse(num, subject, sizeof(subject), "");
|
|
// Con_Printf("Date/Time: %s\n", date);
|
|
|
|
num = IMAP_AddressStructure(num, from, sizeof(from));
|
|
|
|
|
|
if ((rand() & 3) == 3)
|
|
{
|
|
if (rand()&1)
|
|
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(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 < Sys_Milliseconds())
|
|
{ //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_Milliseconds();
|
|
}
|
|
}
|
|
|
|
if (imap->sendlen)
|
|
{
|
|
len = Net_Send(imap->socket, imap->sendbuffer, imap->sendlen);
|
|
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;
|
|
Net_Close(imap->socket);
|
|
free(imap);
|
|
if (!prev)
|
|
break;
|
|
}
|
|
|
|
prev = imap;
|
|
}
|
|
}
|