⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 email.cpp

📁 JK Proxy Project - Version 0.1 ------------------------------ This was going to be a proxy serve
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/*
+-----------------------------------------------------------------------------+
|                                                                             |
|   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 + -