mirror of
				https://github.com/DarkflameUniverse/DarkflameServer.git
				synced 2025-11-04 06:32:00 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			410 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			410 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Useful sites
 | 
						|
// http://www.faqs.org\rfcs\rfc2821.html
 | 
						|
// http://en.wikipedia.org/wiki/Base64
 | 
						|
// http://www2.rad.com\networks/1995/mime/examples.htm
 | 
						|
 | 
						|
#include "EmailSender.h"
 | 
						|
#include "TCPInterface.h"
 | 
						|
#include "GetTime.h"
 | 
						|
#include "Rand.h"
 | 
						|
#include "FileList.h"
 | 
						|
#include "BitStream.h"
 | 
						|
#include <stdio.h>
 | 
						|
 | 
						|
#if defined(_XBOX360)
 | 
						|
#include "Console1Includes.h"
 | 
						|
#endif
 | 
						|
 | 
						|
#include "RakSleep.h"
 | 
						|
 | 
						|
static const char base64Map[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 | 
						|
 | 
						|
const char *EmailSender::Send(const char *hostAddress, unsigned short hostPort, const char *sender, const char *recipient, const char *senderName, const char *recipientName, const char *subject, const char *body, FileList *attachedFiles, bool doPrintf, const char *password)
 | 
						|
{
 | 
						|
	Packet *packet;
 | 
						|
	char query[1024];
 | 
						|
	TCPInterface tcpInterface;
 | 
						|
	SystemAddress emailServer;
 | 
						|
	if (tcpInterface.Start(0, 0)==false)
 | 
						|
		return "Unknown error starting TCP";
 | 
						|
	emailServer=tcpInterface.Connect(hostAddress, hostPort,true);
 | 
						|
	if (emailServer==UNASSIGNED_SYSTEM_ADDRESS)
 | 
						|
		return "Failed to connect to host";
 | 
						|
	RakNetTime timeoutTime = RakNet::GetTime()+3000;
 | 
						|
	packet=0;
 | 
						|
	while (RakNet::GetTime() < timeoutTime)
 | 
						|
	{
 | 
						|
		packet = tcpInterface.Receive();
 | 
						|
		if (packet)
 | 
						|
		{
 | 
						|
			if (doPrintf)
 | 
						|
				printf("%s", packet->data);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		RakSleep(250);
 | 
						|
	}
 | 
						|
 | 
						|
	if (packet==0)
 | 
						|
		return "Timeout while waiting for initial data from server.";
 | 
						|
	
 | 
						|
	tcpInterface.Send("EHLO\r\n", 6, emailServer);
 | 
						|
	const char *response;
 | 
						|
	bool authenticate=false;
 | 
						|
#ifdef _MSC_VER
 | 
						|
#pragma warning(disable:4127)   // conditional expression is constant
 | 
						|
#endif
 | 
						|
	while (1)
 | 
						|
	{
 | 
						|
		response=GetResponse(&tcpInterface, emailServer, doPrintf);
 | 
						|
 | 
						|
		if (response!=0 && strcmp(response, "AUTHENTICATE")==0)
 | 
						|
		{
 | 
						|
			authenticate=true;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		// Something other than continue?
 | 
						|
		if (response!=0 && strcmp(response, "CONTINUE")!=0)
 | 
						|
			return response;
 | 
						|
 | 
						|
		// Success?
 | 
						|
		if (response==0)
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	if (authenticate)
 | 
						|
	{
 | 
						|
		sprintf(query, "EHLO %s\r\n", sender);
 | 
						|
		tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
 | 
						|
		response=GetResponse(&tcpInterface, emailServer, doPrintf);
 | 
						|
		if (response!=0)
 | 
						|
			return response;
 | 
						|
 | 
						|
		if (password==0)
 | 
						|
			return "Password needed";
 | 
						|
		char *outputData = new char [(strlen(sender)+strlen(password)+2)*3];
 | 
						|
		RakNet::BitStream bs;
 | 
						|
		char zero=0;
 | 
						|
		bs.Write(&zero,1);
 | 
						|
		bs.Write(sender,(const unsigned int)strlen(sender));
 | 
						|
		//bs.Write("jms1@jms1.net",(const unsigned int)strlen("jms1@jms1.net"));
 | 
						|
		bs.Write(&zero,1);
 | 
						|
		bs.Write(password,(const unsigned int)strlen(password));
 | 
						|
		bs.Write(&zero,1);
 | 
						|
		//bs.Write("not.my.real.password",(const unsigned int)strlen("not.my.real.password"));
 | 
						|
		Base64Encoding((const char*)bs.GetData(), bs.GetNumberOfBytesUsed(), outputData, base64Map);
 | 
						|
		sprintf(query, "AUTH PLAIN %s", outputData);
 | 
						|
		tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
 | 
						|
		response=GetResponse(&tcpInterface, emailServer, doPrintf);
 | 
						|
		if (response!=0)
 | 
						|
			return response;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	if (sender)
 | 
						|
		sprintf(query, "MAIL From: <%s>\r\n", sender);
 | 
						|
	else
 | 
						|
		sprintf(query, "MAIL From: <>\r\n");
 | 
						|
	tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
 | 
						|
 | 
						|
	if (recipient)
 | 
						|
		sprintf(query, "RCPT TO: <%s>\r\n", recipient);
 | 
						|
	else
 | 
						|
		sprintf(query, "RCPT TO: <>\r\n");
 | 
						|
	tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
 | 
						|
	response=GetResponse(&tcpInterface, emailServer, doPrintf);
 | 
						|
	if (response!=0)
 | 
						|
		return response;
 | 
						|
 | 
						|
	tcpInterface.Send("DATA\r\n", (unsigned int)strlen("DATA\r\n"), emailServer);
 | 
						|
 | 
						|
	response=GetResponse(&tcpInterface, emailServer, doPrintf);
 | 
						|
	if (response!=0)
 | 
						|
		return response;
 | 
						|
 | 
						|
	if (subject)
 | 
						|
	{
 | 
						|
		sprintf(query, "Subject: %s\r\n", subject);
 | 
						|
		tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
 | 
						|
	}
 | 
						|
	if (senderName)
 | 
						|
	{
 | 
						|
		sprintf(query, "From: %s\r\n", senderName);
 | 
						|
		tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
 | 
						|
	}
 | 
						|
	if (recipientName)
 | 
						|
	{
 | 
						|
		sprintf(query, "To: %s\r\n", recipientName);
 | 
						|
		tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
 | 
						|
	}
 | 
						|
 | 
						|
	const int boundarySize=60;
 | 
						|
	char boundary[boundarySize+1];
 | 
						|
	int i,j;
 | 
						|
	if (attachedFiles && attachedFiles->fileList.Size())
 | 
						|
	{
 | 
						|
		seedMT(RakNet::GetTime());
 | 
						|
		// Random multipart message boundary
 | 
						|
		for (i=0; i < boundarySize; i++)
 | 
						|
			boundary[i]=base64Map[randomMT()%64];
 | 
						|
		boundary[boundarySize]=0;
 | 
						|
	}
 | 
						|
 | 
						|
	sprintf(query, "MIME-version: 1.0\r\n");
 | 
						|
	tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
 | 
						|
 | 
						|
	if (attachedFiles && attachedFiles->fileList.Size())
 | 
						|
	{
 | 
						|
		sprintf(query, "Content-type: multipart/mixed; BOUNDARY=\"%s\"\r\n\r\n", boundary);
 | 
						|
		tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
 | 
						|
 | 
						|
		sprintf(query, "This is a multi-part message in MIME format.\r\n\r\n--%s\r\n", boundary);
 | 
						|
		tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
 | 
						|
	}
 | 
						|
	
 | 
						|
	sprintf(query, "Content-Type: text/plain; charset=\"US-ASCII\"\r\n\r\n");
 | 
						|
	tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
 | 
						|
 | 
						|
	// Write the body of the email, doing some lame shitty shit where I have to make periods at the start of a newline have a second period.
 | 
						|
	char *newBody;
 | 
						|
	int bodyLength;
 | 
						|
	bodyLength=(int)strlen(body);
 | 
						|
	newBody = (char*) rakMalloc( bodyLength*3 );
 | 
						|
	if (bodyLength>0)
 | 
						|
		newBody[0]=body[0];
 | 
						|
	for (i=1, j=1; i < bodyLength; i++)
 | 
						|
	{
 | 
						|
		// Transform \n . \r \n into \n . . \r \n
 | 
						|
		if (i < bodyLength-2 &&
 | 
						|
			body[i-1]=='\n' &&
 | 
						|
			body[i+0]=='.' &&
 | 
						|
			body[i+1]=='\r' &&
 | 
						|
			body[i+2]=='\n')
 | 
						|
		{
 | 
						|
			newBody[j++]='.';
 | 
						|
			newBody[j++]='.';
 | 
						|
			newBody[j++]='\r';
 | 
						|
			newBody[j++]='\n';
 | 
						|
			i+=2;
 | 
						|
		}
 | 
						|
		// Transform \n . . \r \n into \n . . . \r \n
 | 
						|
		// Having to process .. is a bug in the mail server - the spec says ONLY \r\n.\r\n should be transformed
 | 
						|
		else if (i <= bodyLength-3 &&
 | 
						|
			body[i-1]=='\n' &&
 | 
						|
			body[i+0]=='.' &&
 | 
						|
			body[i+1]=='.' &&
 | 
						|
			body[i+2]=='\r' &&
 | 
						|
			body[i+3]=='\n')
 | 
						|
		{
 | 
						|
			newBody[j++]='.';
 | 
						|
			newBody[j++]='.';
 | 
						|
			newBody[j++]='.';
 | 
						|
			newBody[j++]='\r';
 | 
						|
			newBody[j++]='\n';
 | 
						|
			i+=3;
 | 
						|
		}
 | 
						|
		// Transform \n . \n into \n . . \r \n (this is a bug in the mail server - the spec says do not count \n alone but it does)
 | 
						|
		else if (i < bodyLength-1 &&
 | 
						|
			body[i-1]=='\n' &&
 | 
						|
			body[i+0]=='.' &&
 | 
						|
			body[i+1]=='\n')
 | 
						|
		{
 | 
						|
			newBody[j++]='.';
 | 
						|
			newBody[j++]='.';
 | 
						|
			newBody[j++]='\r';
 | 
						|
			newBody[j++]='\n';
 | 
						|
			i+=1;
 | 
						|
		}
 | 
						|
		// Transform \n . . \n into \n . . . \r \n (this is a bug in the mail server - the spec says do not count \n alone but it does)
 | 
						|
		// In fact having to process .. is a bug too - because the spec says ONLY \r\n.\r\n should be transformed
 | 
						|
		else if (i <= bodyLength-2 &&
 | 
						|
			body[i-1]=='\n' &&
 | 
						|
			body[i+0]=='.' &&
 | 
						|
			body[i+1]=='.' &&
 | 
						|
			body[i+2]=='\n')
 | 
						|
		{
 | 
						|
			newBody[j++]='.';
 | 
						|
			newBody[j++]='.';
 | 
						|
			newBody[j++]='.';
 | 
						|
			newBody[j++]='\r';
 | 
						|
			newBody[j++]='\n';
 | 
						|
			i+=2;
 | 
						|
		}
 | 
						|
		else
 | 
						|
			newBody[j++]=body[i];
 | 
						|
	}
 | 
						|
	
 | 
						|
	newBody[j++]='\r';
 | 
						|
	newBody[j++]='\n';
 | 
						|
	tcpInterface.Send(newBody, j, emailServer);
 | 
						|
 | 
						|
	rakFree(newBody);
 | 
						|
	int outputOffset;
 | 
						|
 | 
						|
	// What a pain in the rear.  I have to map the binary to printable characters using 6 bits per character.
 | 
						|
	if (attachedFiles && attachedFiles->fileList.Size())
 | 
						|
	{
 | 
						|
		for (i=0; i < (int) attachedFiles->fileList.Size(); i++)
 | 
						|
		{
 | 
						|
			// Write boundary
 | 
						|
			sprintf(query, "\r\n--%s\r\n", boundary);
 | 
						|
			tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
 | 
						|
 | 
						|
			sprintf(query, "Content-Type: APPLICATION/Octet-Stream; SizeOnDisk=%i; name=\"%s\"\r\nContent-Transfer-Encoding: BASE64\r\nContent-Description: %s\r\n\r\n", attachedFiles->fileList[i].dataLengthBytes, attachedFiles->fileList[i].filename, attachedFiles->fileList[i].filename);
 | 
						|
			tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
 | 
						|
 | 
						|
			newBody = (char*) rakMalloc( (size_t) (attachedFiles->fileList[i].dataLengthBytes*3)/2 );
 | 
						|
 | 
						|
			outputOffset=Base64Encoding(attachedFiles->fileList[i].data, (int) attachedFiles->fileList[i].dataLengthBytes, newBody, base64Map);
 | 
						|
 | 
						|
			// Send the base64 mapped file.
 | 
						|
			tcpInterface.Send(newBody, outputOffset, emailServer);
 | 
						|
			rakFree(newBody);
 | 
						|
 | 
						|
		}
 | 
						|
 | 
						|
		// Write last boundary
 | 
						|
		sprintf(query, "\r\n--%s--\r\n", boundary);
 | 
						|
		tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	sprintf(query, "\r\n.\r\n");
 | 
						|
	tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
 | 
						|
	response=GetResponse(&tcpInterface, emailServer, doPrintf);
 | 
						|
	if (response!=0)
 | 
						|
		return response;
 | 
						|
 | 
						|
	tcpInterface.Send("QUIT\r\n", (unsigned int)strlen("QUIT\r\n"), emailServer);
 | 
						|
 | 
						|
	RakSleep(30);
 | 
						|
	if (doPrintf)
 | 
						|
	{
 | 
						|
		packet = tcpInterface.Receive();
 | 
						|
		while (packet)
 | 
						|
		{
 | 
						|
			printf("%s", packet->data);
 | 
						|
			packet = tcpInterface.Receive();
 | 
						|
		}
 | 
						|
	}
 | 
						|
	tcpInterface.Stop();
 | 
						|
	return 0; // Success
 | 
						|
}
 | 
						|
 | 
						|
const char *EmailSender::GetResponse(TCPInterface *tcpInterface, const SystemAddress &emailServer, bool doPrintf)
 | 
						|
{
 | 
						|
	Packet *packet;
 | 
						|
	RakNetTime timeout;
 | 
						|
	timeout=RakNet::GetTime()+5000;
 | 
						|
#ifdef _MSC_VER
 | 
						|
	#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
 | 
						|
#endif
 | 
						|
	while (1)
 | 
						|
	{
 | 
						|
		if (tcpInterface->HasLostConnection()==emailServer)
 | 
						|
			return "Connection to server lost.";
 | 
						|
		packet = tcpInterface->Receive();
 | 
						|
		if (packet)
 | 
						|
		{
 | 
						|
			if (doPrintf)
 | 
						|
			{
 | 
						|
				printf("%s", packet->data);
 | 
						|
			}
 | 
						|
#if defined(OPEN_SSL_CLIENT_SUPPORT)
 | 
						|
			if (strstr((const char*)packet->data, "220"))
 | 
						|
			{
 | 
						|
				tcpInterface->StartSSLClient(packet->systemAddress);
 | 
						|
				return "AUTHENTICATE"; // OK
 | 
						|
			}
 | 
						|
#endif
 | 
						|
			if (strstr((const char*)packet->data, "235"))
 | 
						|
				return 0; // Authentication accepted
 | 
						|
			if (strstr((const char*)packet->data, "354"))
 | 
						|
				return 0; // OK
 | 
						|
#if defined(OPEN_SSL_CLIENT_SUPPORT)
 | 
						|
			if (strstr((const char*)packet->data, "250-STARTTLS"))
 | 
						|
			{
 | 
						|
				tcpInterface->Send("STARTTLS\r\n", (unsigned int) strlen("STARTTLS\r\n"), packet->systemAddress);
 | 
						|
				return "CONTINUE";
 | 
						|
			}
 | 
						|
#endif
 | 
						|
			if (strstr((const char*)packet->data, "250"))
 | 
						|
				return 0; // OK
 | 
						|
			if (strstr((const char*)packet->data, "550"))
 | 
						|
				return "Failed on error code 550";
 | 
						|
			if (strstr((const char*)packet->data, "553"))
 | 
						|
				return "Failed on error code 553";
 | 
						|
		}
 | 
						|
		if (RakNet::GetTime() > timeout)
 | 
						|
			return "Timed out";
 | 
						|
		RakSleep(100);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
int EmailSender::Base64Encoding(const char *inputData, int dataLength, char *outputData, const char *base64Map)
 | 
						|
{
 | 
						|
	int outputOffset, charCount;
 | 
						|
	int write3Count;
 | 
						|
	outputOffset=0;
 | 
						|
	charCount=0;
 | 
						|
	int j;
 | 
						|
 | 
						|
	write3Count=dataLength/3;
 | 
						|
	for (j=0; j < write3Count; j++)
 | 
						|
	{
 | 
						|
		// 6 leftmost bits from first byte, shifted to bits 7,8 are 0
 | 
						|
		outputData[outputOffset++]=base64Map[inputData[j*3+0] >> 2];
 | 
						|
		if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
 | 
						|
 | 
						|
		// Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8
 | 
						|
		outputData[outputOffset++]=base64Map[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63];
 | 
						|
		if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
 | 
						|
 | 
						|
		// 4 low bits from the second byte and the two high bits from the third byte, masked to ignore bits 7,8
 | 
						|
		outputData[outputOffset++]=base64Map[((inputData[j*3+1] << 2) | (inputData[j*3+2] >> 6)) & 63]; // Third 6 bits
 | 
						|
		if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
 | 
						|
 | 
						|
		// Last 6 bits from the third byte, masked to ignore bits 7,8
 | 
						|
		outputData[outputOffset++]=base64Map[inputData[j*3+2] & 63];
 | 
						|
		if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
 | 
						|
	}
 | 
						|
 | 
						|
	if (dataLength % 3==1)
 | 
						|
	{
 | 
						|
		// One input byte remaining
 | 
						|
		outputData[outputOffset++]=base64Map[inputData[j*3+0] >> 2];
 | 
						|
		if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
 | 
						|
	
 | 
						|
		// Pad with two equals
 | 
						|
		outputData[outputOffset++]='=';
 | 
						|
		outputData[outputOffset++]='=';
 | 
						|
	}
 | 
						|
	else if (dataLength % 3==2)
 | 
						|
	{
 | 
						|
		// Two input bytes remaining
 | 
						|
 | 
						|
		// 6 leftmost bits from first byte, shifted to bits 7,8 are 0
 | 
						|
		outputData[outputOffset++]=base64Map[inputData[j*3+0] >> 2];
 | 
						|
		if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
 | 
						|
 | 
						|
		// Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8
 | 
						|
		outputData[outputOffset++]=base64Map[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63];
 | 
						|
		if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
 | 
						|
 | 
						|
		// 4 low bits from the second byte, followed by 00
 | 
						|
		outputData[outputOffset++]=base64Map[(inputData[j*3+1] << 2) & 63]; // Third 6 bits
 | 
						|
		if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
 | 
						|
 | 
						|
		// Pad with one equal
 | 
						|
		outputData[outputOffset++]='=';
 | 
						|
		//outputData[outputOffset++]='=';
 | 
						|
	}
 | 
						|
 | 
						|
	// Append \r\n
 | 
						|
	outputData[outputOffset++]='\r';
 | 
						|
	outputData[outputOffset++]='\n';
 | 
						|
	outputData[outputOffset]=0;
 | 
						|
 | 
						|
	return outputOffset;
 | 
						|
}
 |