mirror of
https://github.com/celisej567/mcpe.git
synced 2025-12-31 17:49:17 +03:00
378 lines
11 KiB
C++
378 lines
11 KiB
C++
/*
|
|
* Copyright (c) 2014, Oculus VR, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
*/
|
|
|
|
#include "NativeFeatureIncludes.h"
|
|
#if _RAKNET_SUPPORT_EmailSender==1 && _RAKNET_SUPPORT_TCPInterface==1 && _RAKNET_SUPPORT_FileOperations==1
|
|
|
|
// Useful sites
|
|
// http://www.faqs.org\rfcs\rfc2821.html
|
|
// 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 "Base64Encoder.h"
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
|
|
|
|
#include "RakSleep.h"
|
|
|
|
using namespace RakNet;
|
|
|
|
|
|
STATIC_FACTORY_DEFINITIONS(EmailSender,EmailSender);
|
|
|
|
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)
|
|
{
|
|
RakNet::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";
|
|
#if OPEN_SSL_CLIENT_SUPPORT==1
|
|
tcpInterface.StartSSLClient(emailServer);
|
|
#endif
|
|
RakNet::TimeMS timeoutTime = RakNet::GetTimeMS()+3000;
|
|
packet=0;
|
|
while (RakNet::GetTimeMS() < timeoutTime)
|
|
{
|
|
packet = tcpInterface.Receive();
|
|
if (packet)
|
|
{
|
|
if (doPrintf)
|
|
{
|
|
RAKNET_DEBUG_PRINTF("%s", packet->data);
|
|
tcpInterface.DeallocatePacket(packet);
|
|
}
|
|
break;
|
|
}
|
|
RakSleep(250);
|
|
}
|
|
|
|
if (packet==0)
|
|
return "Timeout while waiting for initial data from server.";
|
|
|
|
tcpInterface.Send("EHLO\r\n", 6, emailServer,false);
|
|
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,false);
|
|
response=GetResponse(&tcpInterface, emailServer, doPrintf);
|
|
if (response!=0)
|
|
return response;
|
|
if (password==0)
|
|
return "Password needed";
|
|
char *outputData = RakNet::OP_NEW_ARRAY<char >((const int) (strlen(sender)+strlen(password)+2)*3, _FILE_AND_LINE_ );
|
|
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 unsigned char*)bs.GetData(), bs.GetNumberOfBytesUsed(), outputData);
|
|
sprintf(query, "AUTH PLAIN %s", outputData);
|
|
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
|
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,false);
|
|
response=GetResponse(&tcpInterface, emailServer, doPrintf);
|
|
if (response!=0)
|
|
return response;
|
|
|
|
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,false);
|
|
response=GetResponse(&tcpInterface, emailServer, doPrintf);
|
|
if (response!=0)
|
|
return response;
|
|
|
|
tcpInterface.Send("DATA\r\n", (unsigned int)strlen("DATA\r\n"), emailServer,false);
|
|
|
|
// Wait for 354...
|
|
|
|
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,false);
|
|
}
|
|
if (senderName)
|
|
{
|
|
sprintf(query, "From: %s\r\n", senderName);
|
|
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
|
}
|
|
if (recipientName)
|
|
{
|
|
sprintf(query, "To: %s\r\n", recipientName);
|
|
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
|
}
|
|
|
|
const int boundarySize=60;
|
|
char boundary[boundarySize+1];
|
|
int i,j;
|
|
if (attachedFiles && attachedFiles->fileList.Size())
|
|
{
|
|
rakNetRandom.SeedMT((unsigned int) RakNet::GetTimeMS());
|
|
// Random multipart message boundary
|
|
for (i=0; i < boundarySize; i++)
|
|
boundary[i]=Base64Map()[rakNetRandom.RandomMT()%64];
|
|
boundary[boundarySize]=0;
|
|
}
|
|
|
|
sprintf(query, "MIME-version: 1.0\r\n");
|
|
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
|
|
|
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,false);
|
|
|
|
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,false);
|
|
}
|
|
|
|
sprintf(query, "Content-Type: text/plain; charset=\"US-ASCII\"\r\n\r\n");
|
|
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
|
|
|
// 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_Ex( bodyLength*3, _FILE_AND_LINE_ );
|
|
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,false);
|
|
|
|
rakFree_Ex(newBody, _FILE_AND_LINE_ );
|
|
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,false);
|
|
|
|
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.C_String(), attachedFiles->fileList[i].filename.C_String());
|
|
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
|
|
|
newBody = (char*) rakMalloc_Ex( (size_t) (attachedFiles->fileList[i].dataLengthBytes*3)/2, _FILE_AND_LINE_ );
|
|
|
|
outputOffset=Base64Encoding((const unsigned char*) attachedFiles->fileList[i].data, (int) attachedFiles->fileList[i].dataLengthBytes, newBody);
|
|
|
|
// Send the base64 mapped file.
|
|
tcpInterface.Send(newBody, outputOffset, emailServer,false);
|
|
rakFree_Ex(newBody, _FILE_AND_LINE_ );
|
|
|
|
}
|
|
|
|
// Write last boundary
|
|
sprintf(query, "\r\n--%s--\r\n", boundary);
|
|
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
|
}
|
|
|
|
|
|
sprintf(query, "\r\n.\r\n");
|
|
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
|
response=GetResponse(&tcpInterface, emailServer, doPrintf);
|
|
if (response!=0)
|
|
return response;
|
|
|
|
tcpInterface.Send("QUIT\r\n", (unsigned int)strlen("QUIT\r\n"), emailServer,false);
|
|
|
|
RakSleep(30);
|
|
if (doPrintf)
|
|
{
|
|
packet = tcpInterface.Receive();
|
|
while (packet)
|
|
{
|
|
RAKNET_DEBUG_PRINTF("%s", packet->data);
|
|
tcpInterface.DeallocatePacket(packet);
|
|
packet = tcpInterface.Receive();
|
|
}
|
|
}
|
|
tcpInterface.Stop();
|
|
return 0; // Success
|
|
}
|
|
|
|
const char *EmailSender::GetResponse(TCPInterface *tcpInterface, const SystemAddress &emailServer, bool doPrintf)
|
|
{
|
|
RakNet::Packet *packet;
|
|
RakNet::TimeMS timeout;
|
|
timeout=RakNet::GetTimeMS()+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)
|
|
{
|
|
RAKNET_DEBUG_PRINTF("%s", packet->data);
|
|
}
|
|
#if OPEN_SSL_CLIENT_SUPPORT==1
|
|
if (strstr((const char*)packet->data, "220"))
|
|
{
|
|
tcpInterface->StartSSLClient(packet->systemAddress);
|
|
return "AUTHENTICATE"; // OK
|
|
}
|
|
// if (strstr((const char*)packet->data, "250-AUTH LOGIN PLAIN"))
|
|
// {
|
|
// 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; // Go ahead
|
|
#if OPEN_SSL_CLIENT_SUPPORT==1
|
|
if (strstr((const char*)packet->data, "250-STARTTLS"))
|
|
{
|
|
tcpInterface->Send("STARTTLS\r\n", (unsigned int) strlen("STARTTLS\r\n"), packet->systemAddress, false);
|
|
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::GetTimeMS() > timeout)
|
|
return "Timed out";
|
|
RakSleep(100);
|
|
}
|
|
}
|
|
|
|
|
|
#endif // _RAKNET_SUPPORT_*
|