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
580 lines
14 KiB
C
580 lines
14 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"
|
|
|
|
//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;
|
|
}
|