diff --git a/plugins/emailnot/compile.bat b/plugins/emailnot/compile.bat new file mode 100644 index 00000000..6d7e465c --- /dev/null +++ b/plugins/emailnot/compile.bat @@ -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 \ No newline at end of file diff --git a/plugins/emailnot/emailnot.dsp b/plugins/emailnot/emailnot.dsp new file mode 100644 index 00000000..48383e50 --- /dev/null +++ b/plugins/emailnot/emailnot.dsp @@ -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 diff --git a/plugins/emailnot/emailnot.q3asm b/plugins/emailnot/emailnot.q3asm new file mode 100644 index 00000000..72559e28 --- /dev/null +++ b/plugins/emailnot/emailnot.q3asm @@ -0,0 +1,7 @@ +-o "emailnot" +plugin +qvm_api +memory +imapnoti +pop3noti +md5 \ No newline at end of file diff --git a/plugins/emailnot/imapnoti.c b/plugins/emailnot/imapnoti.c new file mode 100644 index 00000000..7e800267 --- /dev/null +++ b/plugins/emailnot/imapnoti.c @@ -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; + } +} diff --git a/plugins/emailnot/install.bat b/plugins/emailnot/install.bat new file mode 100644 index 00000000..8c27b098 --- /dev/null +++ b/plugins/emailnot/install.bat @@ -0,0 +1,4 @@ +call ..\paths.bat +mkdir %PluginsDir% +copy vm\emailnot.qvm %PluginsDir% +pause \ No newline at end of file diff --git a/plugins/emailnot/md5.c b/plugins/emailnot/md5.c new file mode 100644 index 00000000..2da72a97 --- /dev/null +++ b/plugins/emailnot/md5.c @@ -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; +} diff --git a/plugins/emailnot/pop3noti.c b/plugins/emailnot/pop3noti.c new file mode 100644 index 00000000..7f715c29 --- /dev/null +++ b/plugins/emailnot/pop3noti.c @@ -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 \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 \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; +}