A pop3/imap email notification system.
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
This commit is contained in:
parent
bf1d731178
commit
6aaae617eb
7 changed files with 1604 additions and 0 deletions
29
plugins/emailnot/compile.bat
Normal file
29
plugins/emailnot/compile.bat
Normal file
|
@ -0,0 +1,29 @@
|
|||
@echo off
|
||||
call ..\paths.bat
|
||||
|
||||
del vm\*.asm
|
||||
rmdir vm
|
||||
mkdir vm
|
||||
cd vm
|
||||
lcc -DQ3_VM -S -Wf-target=bytecode -Wf-g ../imapnoti.c
|
||||
if errorlevel 1 goto error
|
||||
lcc -DQ3_VM -S -Wf-target=bytecode -Wf-g ../md5.c
|
||||
if errorlevel 1 goto error
|
||||
lcc -DQ3_VM -S -Wf-target=bytecode -Wf-g ../pop3noti.c
|
||||
if errorlevel 1 goto error
|
||||
lcc -DQ3_VM -S -Wf-target=bytecode -Wf-g ../../memory.c
|
||||
if errorlevel 1 goto error
|
||||
lcc -DQ3_VM -S -Wf-target=bytecode -Wf-g ../../plugin.c
|
||||
if errorlevel 1 goto error
|
||||
lcc -DQ3_VM -S -Wf-target=bytecode -Wf-g ../../qvm_api.c
|
||||
if errorlevel 1 goto error
|
||||
q3asm -f ../emailnot
|
||||
|
||||
:error
|
||||
cd ..
|
||||
|
||||
pause
|
||||
goto endbat
|
||||
|
||||
|
||||
:endbat
|
125
plugins/emailnot/emailnot.dsp
Normal file
125
plugins/emailnot/emailnot.dsp
Normal file
|
@ -0,0 +1,125 @@
|
|||
# Microsoft Developer Studio Project File - Name="emailnot" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
|
||||
|
||||
CFG=emailnot - Win32 Debug
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "emailnot.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "emailnot.mak" CFG="emailnot - Win32 Debug"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "emailnot - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
|
||||
!MESSAGE "emailnot - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
MTL=midl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "emailnot - Win32 Release"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "Release"
|
||||
# PROP BASE Intermediate_Dir "Release"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release"
|
||||
# PROP Intermediate_Dir "Release"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EMAILNOT_EXPORTS" /YX /FD /c
|
||||
# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EMAILNOT_EXPORTS" /FR /YX /FD /c
|
||||
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||
# ADD BASE RSC /l 0x809 /d "NDEBUG"
|
||||
# ADD RSC /l 0x809 /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
|
||||
|
||||
!ELSEIF "$(CFG)" == "emailnot - Win32 Debug"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "Debug"
|
||||
# PROP BASE Intermediate_Dir "Debug"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "Debug"
|
||||
# PROP Intermediate_Dir "Debug"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EMAILNOT_EXPORTS" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EMAILNOT_EXPORTS" /FR /YX /FD /GZ /c
|
||||
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||
# ADD BASE RSC /l 0x809 /d "_DEBUG"
|
||||
# ADD RSC /l 0x809 /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "emailnot - Win32 Release"
|
||||
# Name "emailnot - Win32 Debug"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\imapnoti.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\md5.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\plugin.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\pop3noti.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\qvm_api.c
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Header Files"
|
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\plugin.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Resource Files"
|
||||
|
||||
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
7
plugins/emailnot/emailnot.q3asm
Normal file
7
plugins/emailnot/emailnot.q3asm
Normal file
|
@ -0,0 +1,7 @@
|
|||
-o "emailnot"
|
||||
plugin
|
||||
qvm_api
|
||||
memory
|
||||
imapnoti
|
||||
pop3noti
|
||||
md5
|
465
plugins/emailnot/imapnoti.c
Normal file
465
plugins/emailnot/imapnoti.c
Normal file
|
@ -0,0 +1,465 @@
|
|||
/*
|
||||
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;
|
||||
}
|
||||
}
|
4
plugins/emailnot/install.bat
Normal file
4
plugins/emailnot/install.bat
Normal file
|
@ -0,0 +1,4 @@
|
|||
call ..\paths.bat
|
||||
mkdir %PluginsDir%
|
||||
copy vm\emailnot.qvm %PluginsDir%
|
||||
pause
|
394
plugins/emailnot/md5.c
Normal file
394
plugins/emailnot/md5.c
Normal file
|
@ -0,0 +1,394 @@
|
|||
//used by pop3.
|
||||
#include "../plugin.h"
|
||||
|
||||
|
||||
/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
|
||||
*/
|
||||
|
||||
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
||||
rights reserved.
|
||||
|
||||
License to copy and use this software is granted provided that it
|
||||
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
|
||||
Algorithm" in all material mentioning or referencing this software
|
||||
or this function.
|
||||
|
||||
License is also granted to make and use derivative works provided
|
||||
that such works are identified as "derived from the RSA Data
|
||||
Security, Inc. MD5 Message-Digest Algorithm" in all material
|
||||
mentioning or referencing the derived work.
|
||||
|
||||
RSA Data Security, Inc. makes no representations concerning either
|
||||
the merchantability of this software or the suitability of this
|
||||
software for any particular purpose. It is provided "as is"
|
||||
without express or implied warranty of any kind.
|
||||
|
||||
These notices must be retained in any copies of any part of this
|
||||
documentation and/or software.
|
||||
*/
|
||||
|
||||
|
||||
/* GLOBAL.H - RSAREF types and constants
|
||||
*/
|
||||
|
||||
/* POINTER defines a generic pointer type */
|
||||
typedef unsigned char *POINTER;
|
||||
|
||||
/* UINT2 defines a two byte word */
|
||||
typedef unsigned short int UINT2;
|
||||
|
||||
/* UINT4 defines a four byte word */
|
||||
typedef unsigned long int UINT4;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* MD5.H - header file for MD5C.C
|
||||
*/
|
||||
|
||||
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
||||
rights reserved.
|
||||
|
||||
License to copy and use this software is granted provided that it
|
||||
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
|
||||
Algorithm" in all material mentioning or referencing this software
|
||||
or this function.
|
||||
|
||||
License is also granted to make and use derivative works provided
|
||||
that such works are identified as "derived from the RSA Data
|
||||
Security, Inc. MD5 Message-Digest Algorithm" in all material
|
||||
mentioning or referencing the derived work.
|
||||
|
||||
RSA Data Security, Inc. makes no representations concerning either
|
||||
the merchantability of this software or the suitability of this
|
||||
software for any particular purpose. It is provided "as is"
|
||||
without express or implied warranty of any kind.
|
||||
|
||||
These notices must be retained in any copies of any part of this
|
||||
documentation and/or software.
|
||||
*/
|
||||
|
||||
/* MD5 context. */
|
||||
typedef struct {
|
||||
UINT4 state[4]; /* state (ABCD) */
|
||||
UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
|
||||
unsigned char buffer[64]; /* input buffer */
|
||||
} MD5_CTX;
|
||||
|
||||
void MD5Init (MD5_CTX *ctx);
|
||||
void MD5Update (MD5_CTX *, unsigned char *, unsigned int);
|
||||
void MD5Final (unsigned char [16], MD5_CTX *);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Constants for MD5Transform routine.
|
||||
*/
|
||||
|
||||
#define S11 7
|
||||
#define S12 12
|
||||
#define S13 17
|
||||
#define S14 22
|
||||
#define S21 5
|
||||
#define S22 9
|
||||
#define S23 14
|
||||
#define S24 20
|
||||
#define S31 4
|
||||
#define S32 11
|
||||
#define S33 16
|
||||
#define S34 23
|
||||
#define S41 6
|
||||
#define S42 10
|
||||
#define S43 15
|
||||
#define S44 21
|
||||
|
||||
static void MD5Transform (UINT4 [4], unsigned char [64]);
|
||||
static void Encode (unsigned char *, UINT4 *, unsigned int);
|
||||
static void Decode (UINT4 *, unsigned char *, unsigned int);
|
||||
|
||||
static unsigned char PADDING[64] = {
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
/* F, G, H and I are basic MD5 functions.
|
||||
*/
|
||||
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
|
||||
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
|
||||
#define H(x, y, z) ((x) ^ (y) ^ (z))
|
||||
#define I(x, y, z) ((y) ^ ((x) | (~z)))
|
||||
|
||||
/* ROTATE_LEFT rotates x left n bits.
|
||||
*/
|
||||
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
|
||||
|
||||
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
|
||||
Rotation is separate from addition to prevent recomputation.
|
||||
*/
|
||||
#define FF(a, b, c, d, x, s, ac) { \
|
||||
(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
||||
(a) = ROTATE_LEFT ((a), (s)); \
|
||||
(a) += (b); \
|
||||
}
|
||||
#define GG(a, b, c, d, x, s, ac) { \
|
||||
(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
||||
(a) = ROTATE_LEFT ((a), (s)); \
|
||||
(a) += (b); \
|
||||
}
|
||||
#define HH(a, b, c, d, x, s, ac) { \
|
||||
(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
||||
(a) = ROTATE_LEFT ((a), (s)); \
|
||||
(a) += (b); \
|
||||
}
|
||||
#define II(a, b, c, d, x, s, ac) { \
|
||||
(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
||||
(a) = ROTATE_LEFT ((a), (s)); \
|
||||
(a) += (b); \
|
||||
}
|
||||
|
||||
/* MD5 initialization. Begins an MD5 operation, writing a new context.
|
||||
*/
|
||||
void MD5Init (MD5_CTX *context)
|
||||
{
|
||||
context->count[0] = context->count[1] = 0;
|
||||
/* Load magic initialization constants.
|
||||
*/
|
||||
context->state[0] = 0x67452301;
|
||||
context->state[1] = 0xefcdab89;
|
||||
context->state[2] = 0x98badcfe;
|
||||
context->state[3] = 0x10325476;
|
||||
}
|
||||
|
||||
/* MD5 block update operation. Continues an MD5 message-digest
|
||||
operation, processing another message block, and updating the
|
||||
context.
|
||||
*/
|
||||
void MD5Update (MD5_CTX *context, unsigned char *input, unsigned int inputLen)
|
||||
{
|
||||
unsigned int i, index, partLen;
|
||||
|
||||
/* Compute number of bytes mod 64 */
|
||||
index = (unsigned int)((context->count[0] >> 3) & 0x3F);
|
||||
|
||||
/* Update number of bits */
|
||||
if ((context->count[0] += ((UINT4)inputLen << 3))
|
||||
|
||||
< ((UINT4)inputLen << 3))
|
||||
context->count[1]++;
|
||||
context->count[1] += ((UINT4)inputLen >> 29);
|
||||
|
||||
partLen = 64 - index;
|
||||
|
||||
/* Transform as many times as possible.
|
||||
*/
|
||||
if (inputLen >= partLen) {
|
||||
memcpy
|
||||
((POINTER)&context->buffer[index], (POINTER)input, partLen);
|
||||
MD5Transform (context->state, context->buffer);
|
||||
|
||||
for (i = partLen; i + 63 < inputLen; i += 64)
|
||||
MD5Transform (context->state, &input[i]);
|
||||
|
||||
index = 0;
|
||||
}
|
||||
else
|
||||
i = 0;
|
||||
|
||||
/* Buffer remaining input */
|
||||
memcpy
|
||||
((POINTER)&context->buffer[index], (POINTER)&input[i],
|
||||
inputLen-i);
|
||||
}
|
||||
|
||||
/* MD5 finalization. Ends an MD5 message-digest operation, writing the
|
||||
the message digest and zeroizing the context.
|
||||
*/
|
||||
void MD5Final (unsigned char digest[16], MD5_CTX *context)
|
||||
{
|
||||
unsigned char bits[8];
|
||||
unsigned int index, padLen;
|
||||
|
||||
/* Save number of bits */
|
||||
Encode (bits, context->count, 8);
|
||||
|
||||
/* Pad out to 56 mod 64.
|
||||
*/
|
||||
index = (unsigned int)((context->count[0] >> 3) & 0x3f);
|
||||
padLen = (index < 56) ? (56 - index) : (120 - index);
|
||||
MD5Update (context, PADDING, padLen);
|
||||
|
||||
/* Append length (before padding) */
|
||||
MD5Update (context, bits, 8);
|
||||
|
||||
/* Store state in digest */
|
||||
Encode (digest, context->state, 16);
|
||||
|
||||
/* Zeroize sensitive information.
|
||||
*/
|
||||
memset ((POINTER)context, 0, sizeof (*context));
|
||||
}
|
||||
|
||||
/* MD5 basic transformation. Transforms state based on block.
|
||||
*/
|
||||
static void MD5Transform (UINT4 state[4], unsigned char block[64])
|
||||
{
|
||||
UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
|
||||
|
||||
Decode (x, block, 64);
|
||||
|
||||
/* Round 1 */
|
||||
FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
|
||||
FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
|
||||
FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
|
||||
FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
|
||||
FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
|
||||
FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
|
||||
FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
|
||||
FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
|
||||
FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
|
||||
FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
|
||||
FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
|
||||
FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
|
||||
FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
|
||||
FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
|
||||
FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
|
||||
FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
|
||||
|
||||
/* Round 2 */
|
||||
GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
|
||||
GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
|
||||
GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
|
||||
GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
|
||||
GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
|
||||
GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
|
||||
GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
|
||||
GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
|
||||
GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
|
||||
GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
|
||||
GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
|
||||
|
||||
GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
|
||||
GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
|
||||
GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
|
||||
GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
|
||||
GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
|
||||
|
||||
/* Round 3 */
|
||||
HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
|
||||
HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
|
||||
HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
|
||||
HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
|
||||
HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
|
||||
HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
|
||||
HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
|
||||
HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
|
||||
HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
|
||||
HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
|
||||
HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
|
||||
HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
|
||||
HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
|
||||
HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
|
||||
HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
|
||||
HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
|
||||
|
||||
/* Round 4 */
|
||||
II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
|
||||
II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
|
||||
II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
|
||||
II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
|
||||
II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
|
||||
II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
|
||||
II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
|
||||
II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
|
||||
II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
|
||||
II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
|
||||
II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
|
||||
II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
|
||||
II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
|
||||
II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
|
||||
II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
|
||||
II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
|
||||
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
|
||||
/* Zeroize sensitive information.
|
||||
|
||||
*/
|
||||
memset ((POINTER)x, 0, sizeof (x));
|
||||
}
|
||||
|
||||
/* Encodes input (UINT4) into output (unsigned char). Assumes len is
|
||||
a multiple of 4.
|
||||
*/
|
||||
static void Encode (unsigned char *output, UINT4 *input, unsigned int len)
|
||||
{
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0, j = 0; j < len; i++, j += 4) {
|
||||
output[j] = (unsigned char)(input[i] & 0xff);
|
||||
output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
|
||||
output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
|
||||
output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
/* Decodes input (unsigned char) into output (UINT4). Assumes len is
|
||||
a multiple of 4.
|
||||
*/
|
||||
static void Decode (UINT4 *output, unsigned char *input, unsigned int len)
|
||||
{
|
||||
unsigned int i, j;
|
||||
|
||||
for (i = 0, j = 0; j < len; i++, j += 4)
|
||||
output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
|
||||
(((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
char *MD5_GetPop3APOPString(char *timestamp, char *secrit)
|
||||
{
|
||||
int v, i;
|
||||
static char ret[33];
|
||||
unsigned char digest[16];
|
||||
MD5_CTX ctx;
|
||||
|
||||
MD5Init (&ctx);
|
||||
MD5Update (&ctx, (unsigned char *)timestamp, strlen(timestamp));
|
||||
MD5Update (&ctx, (unsigned char *)secrit, strlen(secrit));
|
||||
MD5Final ( (unsigned char *)digest, &ctx);
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
v = digest[i]>>4;
|
||||
if (v >= 10)
|
||||
ret[i*2+0] = (v-10) + 'a';
|
||||
else
|
||||
ret[i*2+0] = v + '0';
|
||||
v = digest[i]&0xf;
|
||||
if (v >= 10)
|
||||
ret[i*2+1] = (v-10) + 'a';
|
||||
else
|
||||
ret[i*2+1] = v + '0';
|
||||
}
|
||||
ret[i*2] = '\0';
|
||||
return ret;
|
||||
}
|
580
plugins/emailnot/pop3noti.c
Normal file
580
plugins/emailnot/pop3noti.c
Normal file
|
@ -0,0 +1,580 @@
|
|||
/*
|
||||
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"
|
||||
|
||||
//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 mailboxes are not refreshable without disconnecting.
|
||||
//so we have a special state.
|
||||
|
||||
|
||||
|
||||
|
||||
char *MD5_GetPop3APOPString(char *timestamp, char *secrit);
|
||||
|
||||
void IMAP_CreateConnection(char *servername, char *username, char *password);
|
||||
void IMAP_Think (void);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//exported.
|
||||
void POP3_CreateConnection(char *servername, char *username, char *password);
|
||||
int pop3_checkfrequency=60*1000;
|
||||
void POP3_Think (void);
|
||||
void POP3_WriteCache (void);
|
||||
//end export list.
|
||||
|
||||
typedef struct msglist_s {
|
||||
struct msglist_s *next;
|
||||
char name[4];
|
||||
} msglist_t;
|
||||
|
||||
msglist_t *msglist;
|
||||
qboolean POP3_IsMessageUnique(char *name)
|
||||
{
|
||||
msglist_t *msg;
|
||||
|
||||
for (msg = msglist; msg; msg = msg->next)
|
||||
{
|
||||
if (!strcmp(msg->name, name))
|
||||
return false;
|
||||
}
|
||||
|
||||
msg = malloc(sizeof(msglist_t) + strlen(name)+1);
|
||||
if (!msg)
|
||||
return false;
|
||||
strcpy(msg->name, name);
|
||||
msg->next = msglist;
|
||||
msglist = msg;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define POP3_PORT 110
|
||||
|
||||
|
||||
typedef struct pop3_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;
|
||||
|
||||
//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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (!con)
|
||||
{
|
||||
con = malloc(sizeof(pop3_con_t));
|
||||
if (!con)
|
||||
{
|
||||
Con_Printf ("POP3_CreateConnection: out of plugin memory\n");
|
||||
return;
|
||||
}
|
||||
memset(con, 0, sizeof(*con));
|
||||
}
|
||||
else
|
||||
con->state = POP3_WAITINGFORINITIALRESPONCE;
|
||||
|
||||
con->socket = Net_TCPConnect(addy, POP3_PORT);
|
||||
if (!con->socket)
|
||||
{
|
||||
Con_Printf ("POP3_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));
|
||||
|
||||
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 = malloc(pop3->sendbuffersize);
|
||||
if (!newbuf)
|
||||
{
|
||||
Con_Printf("Memory is low\n");
|
||||
pop3->drop = true; //failed.
|
||||
return;
|
||||
}
|
||||
if (pop3->sendbuffer)
|
||||
{
|
||||
memcpy(newbuf, pop3->sendbuffer, pop3->sendlen);
|
||||
free(pop3->sendbuffer);
|
||||
}
|
||||
pop3->sendbuffer = newbuf;
|
||||
}
|
||||
|
||||
|
||||
snprintf(pop3->sendbuffer+pop3->sendlen, newlen+1, "%s\r\n", text);
|
||||
pop3->sendlen = newlen;
|
||||
|
||||
// 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 < Sys_Milliseconds())
|
||||
{ //we need to recreate the connection now.
|
||||
pop3->lastnoop = Sys_Milliseconds();
|
||||
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 = malloc(pop3->readbuffersize);
|
||||
if (!ending)
|
||||
{
|
||||
Con_Printf("Memory is low\n");
|
||||
return false;
|
||||
}
|
||||
if (pop3->readbuffer)
|
||||
{
|
||||
memcpy(ending, pop3->readbuffer, len);
|
||||
free(pop3->readbuffer);
|
||||
}
|
||||
pop3->readbuffer = ending;
|
||||
}
|
||||
|
||||
len = Net_Recv(pop3->socket, pop3->readbuffer+pop3->readlen, pop3->readbuffersize-pop3->readlen-1);
|
||||
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_Milliseconds();
|
||||
}
|
||||
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))
|
||||
strlcpy(pop3->msgfrom, pop3->readbuffer + 6, sizeof(pop3->msgfrom));
|
||||
else if (!strnicmp(pop3->readbuffer, "Subject: ", 9))
|
||||
strlcpy(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 (BUILTINISVALID(SCR_CenterPrint))
|
||||
SCR_CenterPrint(va("NEW MAIL HAS ARRIVED\n\nTo: %s@%s\nFrom: %s\nSubject: %s", pop3->username, pop3->server, 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 (BUILTINISVALID(SCR_CenterPrint))
|
||||
SCR_CenterPrint(va("NEW MAIL HAS ARRIVED\n\nTo: %s@%s\nFrom: %s\nSubject: %s", pop3->username, pop3->server, 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;
|
||||
Net_Close(pop3->socket);
|
||||
pop3->lastnoop = Sys_Milliseconds();
|
||||
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 = Net_Send(pop3->socket, pop3->sendbuffer, pop3->sendlen);
|
||||
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)
|
||||
Net_Close(pop3->socket);
|
||||
free(pop3);
|
||||
if (!prev)
|
||||
break;
|
||||
}
|
||||
|
||||
prev = pop3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int EmailNotification_Frame(int *args)
|
||||
{
|
||||
POP3_Think();
|
||||
IMAP_Think();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void IMAP_Account(void)
|
||||
{
|
||||
char arg1[64];
|
||||
char arg2[64];
|
||||
char arg3[64];
|
||||
|
||||
Cmd_Argv(1, arg1, sizeof(arg1));
|
||||
Cmd_Argv(2, arg2, sizeof(arg2));
|
||||
Cmd_Argv(3, arg3, sizeof(arg3));
|
||||
|
||||
if (!*arg1)
|
||||
{
|
||||
Con_Printf("imapaccount <servername> <username> <password>\n");
|
||||
}
|
||||
else
|
||||
IMAP_CreateConnection(arg1, arg2, arg3);
|
||||
}
|
||||
void POP3_Account(void)
|
||||
{
|
||||
char arg1[64];
|
||||
char arg2[64];
|
||||
char arg3[64];
|
||||
|
||||
Cmd_Argv(1, arg1, sizeof(arg1));
|
||||
Cmd_Argv(2, arg2, sizeof(arg2));
|
||||
Cmd_Argv(3, arg3, sizeof(arg3));
|
||||
|
||||
if (!*arg1)
|
||||
{
|
||||
Con_Printf("pop3account <servername> <username> <password>\n");
|
||||
Con_Printf("Say you had an acount called \"foo\" at yahoo's mail servers\n");
|
||||
Con_Printf("Yahoo's pop3 servers are named \"pop.mail.yahoo.co.uk\"\n");
|
||||
Con_Printf("Then if your password was bar, this is the command you would use\n");
|
||||
Con_Printf("pop3account pop.mail.yahoo.co.uk foo bar\n");
|
||||
Con_Printf("Of course, different pop3 servers have different naming conventions\n");
|
||||
Con_Printf("So read your provider's documentation\n");
|
||||
}
|
||||
else
|
||||
POP3_CreateConnection(arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
int EmailNotification_ExecuteCommand(int *args)
|
||||
{
|
||||
char cmd[64];
|
||||
Cmd_Argv(0, cmd, sizeof(cmd));
|
||||
if (!strcmp(cmd, "imapaccount"))
|
||||
{
|
||||
IMAP_Account();
|
||||
return true;
|
||||
}
|
||||
if (!strcmp(cmd, "pop3account"))
|
||||
{
|
||||
POP3_Account();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int Plug_Init(int *args)
|
||||
{
|
||||
if (!Plug_Export("Tick", EmailNotification_Frame) || !Plug_Export("ExecuteCommand", EmailNotification_ExecuteCommand))
|
||||
{
|
||||
Con_Print("email notification plugin failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
Cmd_AddCommand("imapaccount");
|
||||
Cmd_AddCommand("pop3account");
|
||||
|
||||
Con_Print("email notification plugin loaded\n");
|
||||
|
||||
return true;
|
||||
}
|
Loading…
Reference in a new issue