📄 email.cpp
字号:
/*
+-----------------------------------------------------------------------------+
| |
| email.cpp |
| Last change: yyyy-mm-dd |
| |
| Author: Jan Krumsiek |
| eMail: proxy@krumsiek.com |
| Web: http://www.krumsiek.com/proxy |
| |
| Copyright 2003 - Jan Krumsiek |
| This source code can freely be modified and redistributed. No liability |
| whatsoever is taken by the author for any use of this software. |
| |
| Description: |
| This huge module completely controls the email proxy. It handles port- |
| listening, communication with server and client, archiving blocked |
| messages and logging. |
| |
+-----------------------------------------------------------------------------+
*/
#include "proxymain.h"
#include "filesystem.h"
#include "CWinsock.h"
#include "settings.h"
#include "email.h"
#include "ruleprocessor.h"
#include "settings.h"
// settings struct
// global winsock object
CWinsock* emailscks=0;
// global socket -> EMAILCONN structs map
MAILCONNMAPPTR connmap;
// handler for log file
FILE* logfile=0;
// all POP commands are 4 bytes long, so the program treats them
// as 4-byte integers for easy comparison via switch ()
const UINT POP_USER=1919251317;
const UINT POP_PASS=1936941424;
const UINT POP_STAT=1952543859;
const UINT POP_LIST=1953720684;
const UINT POP_RETR=1920230770;
const UINT POP_DELE=1701602660;
const UINT POP_QUIT=1953068401;
const UINT POP_AOK = 543911723;
const UINT POP_AERR=1920099629;
// function prototypes
void AcceptHandler(UINT socket);
void ArrivalHandler(UINT socket, CHARPTR buf, UINT length);
void CloseHandler(UINT socket);
void ConnectHandler(UINT socket);
void ErrorHandler(UINT socket, UINT errorno, CHARPTR msg);
void ForwardMessage(UINT socket, CHAR msgstart, CHAR msgend);
bool ProcessServerMsg(EMAILCONN* conn, CHARPTR msgstart, CHARPTR msgend);
bool ProcessClientMsg(EMAILCONN* conn, CHARPTR msgstart, CHARPTR msgend);
void GetMails(EMAILCONN* conn);
int GetNumOfMails(CHARPTR msgstart, CHARPTR msgend);
void GetNextMsg(EMAILCONN* conn);
CHARPTR EnsureDirectory(SERVERUSER* user);
SERVERMAILS* MailDirInfo(CHARPTR dir);
void STATResponse(EMAILCONN* conn);
void LISTResponse(EMAILCONN* conn);
void RETRResponse(EMAILCONN* conn, UINT inum);
void DELEResponse(EMAILCONN* conn, UINT inum);
bool StartMailProxy()
{
// This function runs the email proxy server
// notification msg
cout << "Starting email proxy... \0" << flush;
// create socket object
emailscks = new CWinsock;
// set handler functions
emailscks->SetEventHandlers(ConnectHandler,AcceptHandler,
CloseHandler,ArrivalHandler,ErrorHandler);
//emailscks->Connect("192.168.0.5",567);
// start listening
UINT ret = emailscks->Listen(mainset.emailsettings.port);
// if ret = 0 -> some error occured, break!
if (ret == 0) return false;
// create map
connmap = new MAILCONNMAP;
// open log file
#ifdef WIN32
// in WIN32 we open the file 'email.log' in the app's directory
CHARPTR appdir = GetAppDir();
CHARPTR logname = CombinePaths(appdir,"email.log\0");
// open
logfile = fopen(logname,"a+");
// cleanup
delete logname;
delete appdir;
#endif
// notification msg
cout << "Done\n\0" << flush;
return true;
}
void StopMailProxy()
{
// close logfile
if (logfile != 0) fclose(logfile);
// kill socket object
if (emailscks !=0) delete emailscks;
}
SERVERUSER* ExtractUsernameInfo(CHARPTR start, CHARPTR end)
{
CHARPTR seek, seek2;
SERVERUSER* srvuser;
// start from the end and check backwards for ':' or '@'
seek = end;
while ((*seek != ':') && (*seek != '@') && (seek > start))
seek--;
// if seek is now <= start then the username has definitely a wrong format
if (seek <= start) return 0;
// now create SERVERUSR struct
srvuser = new SERVERUSER;
// if ':' was found then a specific port is given, otherwise
// standart port 110 will be used
if (*seek == ':')
{
// copy port string and convert to integer
CHARPTR sport = (CHARPTR)CopyAndPtr(seek+1,end-seek);
srvuser->port = atoi(sport);
// now go on searching for host (for the '@')
seek2 = seek;
while ((*seek2 != '@') && (seek2 > start))
seek2--;
if (seek2 <= start)
{
delete srvuser;
return 0;
}
// extract host
srvuser->server = (CHARPTR)CopyAndPtr(seek2+1,seek-seek2-1);
seek = seek2;
}
else
{
srvuser->port = 110;
if (*seek == '@')
{
// now extract host
srvuser->server = (CHARPTR)CopyAndPtr(seek+1,end-seek);
}
}
// seek should point to '@' at this point, everything before
// is the real user name
srvuser->user = (CHARPTR)CopyAndPtr(start,seek-start);
return srvuser;
}
void AcceptHandler(UINT socket)
{
// create new struct and save in map
EMAILCONN* newconn = new EMAILCONN;
// initialize with zero first
memset(newconn,0,sizeof(EMAILCONN));
newconn->clientsock = socket;
newconn->state = ECS_CSENTHELLO;
// allocate memory for buffer
newconn->cbuff = new char[EMAILBUFFSIZE];
newconn->sbuff = new char[EMAILBUFFSIZE];
(*connmap)[socket] = newconn;
// send hello
emailscks->Sendsz(socket,"+OK Jan Krumsiek's POP proxy server ready...\x0D\x0A\0");
// get peer address
newconn->ipaddr = emailscks->GetPeerIP(socket);
// now write to log file (25 chars for status and 15 for ip addr)
CHARPTR tolog = new char[25 + 15 + 1];
sprintf(tolog,"Connection accepted from %s\0",newconn->ipaddr);
WriteToLog(logfile, tolog);
delete tolog;
}
void ArrivalHandler(UINT socket, CHARPTR data, UINT length)
// some data arrived
{
CHARPTR start,end;
CHARPTR buff;
UINT* buffused;
// first of all get connection struct
EMAILCONN* conn = (*connmap)[socket];
if (conn == 0) return;
// determine if it's a msg from client or server
bool isclient = (socket == conn->clientsock) ? true : false;
if (isclient == true)
{
buff = conn->cbuff;
buffused = &conn->cbuffused;
}
else
{
buff = conn->sbuff;
buffused = &conn->sbuffused;
}
// copy arrived data to buffer
memcpy(&(buff[*buffused]),data,length);
// increment buffer length counter
(*buffused) += length;
// last byte must be \0 in order for strchr to work
// (there won't be any conflicts with other \0's as
// the email protocols do not use binary data
buff[*buffused] = 0;
// now start extracting (complete) lines
start = buff;
while (start <= (buff + *buffused - 1))
{
end = strchr(start,13);
// no end-of-line found?
if (conn->state == ECS_SMSG)
conn->state = conn->state;
if (end == 0)
break;
// complete line found, now send line to correct
// message handler proc, functions return 'false' if
// the connection was closed while processing this message
// (for example because if a QUIT command by the client)
bool stillconnected;
if (isclient == true)
stillconnected = ProcessClientMsg(conn,start,end-1);
else
stillconnected = ProcessServerMsg(conn,start,end-1);
// connection closed?
if (stillconnected == false)
break;
// jump to beginning of next line
start = end + 2;
}
// if end = 0 then copy rest of buffer to the beginning
if (end == 0)
{
// calculate length of rest
UINT newused = (*buffused) - (start - buff);
memmove(buff, start, newused);
(*buffused) = newused;
}
else
// Otherwise the buffer was complete process and can be resetted
(*buffused) = 0;
}
void CloseHandler(UINT socket)
// clean up now
{
// get connection
EMAILCONN* conn = (*connmap)[socket];
if (conn == 0) return;
// set pointer to zero
(*connmap)[socket] = 0;
// write to logfile
CHARPTR tolog = new char(19 + 15 + 1);
sprintf(tolog,"Connection closed: %s\0",conn->ipaddr);
WriteToLog(logfile, tolog);
// delete ip-address
//delete conn->ipaddr;
// delete username
if (conn->username != 0)
delete conn->username;
// delete buffers for data
delete conn->cbuff;
delete conn->sbuff;
// delete msg dir
if (conn->msgdir != 0)
delete conn->msgdir;
if (conn->srvm != 0)
{
// delete paths of emails
for (UINT i=1; i<=conn->srvm->nummails;i++)
delete conn->srvm->paths[i-1];
// delete struct
//delete conn->srvm;
}
// remove both server and client mapping
connmap->erase(conn->clientsock);
// and remove
connmap->erase(conn->serversock);
// delete connection struct
delete conn;
}
void ConnectHandler(UINT socket)
{
}
void ErrorHandler(UINT socket, UINT errorno, CHARPTR msg)
{
// some socket error occured, tell in which state we are and
// take appropriate action
// get connection struct
EMAILCONN* conn = (*connmap)[socket];
if (conn == 0) return;
switch (conn->state)
{
case ECS_SCONNECT:
// an error occured while trying to connect to the server
// send error message to client and close connection
// save error number in string format
CHARPTR num = new char(9);
sprintf(num," (%d)\0",errorno);
emailscks->Sendsz(conn->clientsock ,"-ERR An error occured while connecting the the email server: \0");
emailscks->Sendsz(conn->clientsock ,msg);
emailscks->Sendsz(conn->clientsock ,num);
// close client sock
closesocket(conn->clientsock);
// as we are closing the socket, the close-handler has to be called manually
CloseHandler(conn->clientsock);
}
}
void ForwardMessage(UINT socket, CHARPTR msgstart, CHARPTR msgend)
// this is a short procedure for forwarding a server message to the client
{
CHARPTR fw = (CHARPTR)CopyAndPtr(msgstart,msgend-msgstart+3);
emailscks->Sendsz(socket, fw);
}
bool ProcessServerMsg(EMAILCONN* conn, CHARPTR msgstart, CHARPTR msgend)
// this procedure processes incoming messages from the email
// server of the specific socket
{
// extract the first 4 character which represent the POP answer
// ("+OK " includes on space) and convert to lowercase
CHARPTR cmd = (CHARPTR)CopyAndPtr(msgstart,4);
strlwr(cmd);
// convert to integer
UINT icmd = *(UINT*)cmd;
//msgstart = msgstart;
switch (conn->state)
{
case ECS_SCONNECT:
if (icmd == POP_AOK)
{
// server sent hello, service is available
// now sent user name
CHARPTR fullcmd = new char[32];
memcpy(fullcmd, "USER \0", 6);
strcat(fullcmd,conn->username);
strcat(fullcmd, "\x0D\x0A\0");
emailscks->Sendsz(conn->serversock, fullcmd);
conn->state = ECS_SSENTUSR;
}
else if (icmd = POP_AERR)
{
// server sent hello but service is obviously not avaibable
// forward error message to client
ForwardMessage(conn->clientsock,msgstart,msgend);
}
break;
case ECS_SSENTUSR:
if (icmd == POP_AOK)
{
// user name accepted by server, password needs to be sent
emailscks->Sendsz(conn->clientsock,"+OK send password\x0D\x0A");
conn->state = ECS_CWAITPASS;
}
else
{
// user name not accepted, forward error message
ForwardMessage(conn->clientsock,msgstart,msgend);
}
break;
case ECS_SSENTPASS:
if (icmd == POP_AOK)
{
// password accepted, forward message
ForwardMessage(conn->clientsock,msgstart,msgend);
conn->state = ECS_READY;
}
else
{
// password not accepted, forward error message
ForwardMessage(conn->clientsock,msgstart,msgend);
}
break;
case ECS_SSENTSTAT:
// STAT answer should arrive now
if (icmd == POP_AOK)
{
// extract number of emails
conn->srvnummails = GetNumOfMails(msgstart,msgend);
if (conn->srvnummails == 0)
{
// no messages on server, proceed with client
// simply call GetNextMsg()
GetNextMsg(conn);
}
else
{
// allocate memory for msg length
conn->srvmsglen = new UINT[conn->srvnummails];
// now send list command to find get size of each message
emailscks->Sendsz(conn->serversock, "LIST\x0D\x0A\0");
// set state
conn->state = ECS_SMSGLIST;
conn->first = true;
}
}
else
{
// [DBG] - error handler
}
break;
case ECS_SMSGLIST:
// receiving list of messages, line per line should arrive here
if (conn->first == true)
{
// it's the first line and should contain a status code
if (icmd != POP_AOK)
{
// [DBG] - error handler
}
conn->first = false;
}
else
{
// it's a normal line
// if line contains a single ".", the list is done
if (*msgstart == '.')
{
// we can start downloading all emails
GetNextMsg(conn);
}
else
{
// now extract size
CHARPTR seek=msgstart, seek2;
CHARPTR temp;
UINT isave;
// find end of first number by finding next space
while (*seek != ' ') seek++;
// extract and convert number
temp = (CHARPTR)CopyAndPtr(msgstart,seek-msgstart);
isave = atoi(temp);
delete temp;
// find beginning of next number
while (*seek == ' ') seek++;
// find end of number
seek2 = seek;
while ((*seek2 != ' ') && (*seek2 != 13)) seek2++;
// extract and convert size
temp = (CHARPTR)CopyAndPtr(seek,seek2-seek);
conn->srvmsglen[isave-1] = atoi(temp);
delete temp;
}
}
break;
case ECS_SMSG:
// receiving msg from server
// status line?
if (conn->first == true)
{
if (icmd != POP_AOK)
{
// error handler
// [DBG] - error handler
}
conn->first = false;
}
else
{
// normal line of msg
// is single . in one line?
if ((*msgstart == '.') && (*(msgstart+1) == 13))
{
// email is complete, check & save in directory now
CHARPTR spath;
// add 0-terminator first
conn->srvmail[conn->srvmaillen] = 0;
// check now
bool chkmail = (bool)CheckMsg(conn->srvmail, mainset.emailsettings.rules);
// now of all generate (hopefully) unique file name
CHARPTR sfile = GetFilename();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -