• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

EmailSender.cpp

Go to the documentation of this file.
00001 #include "NativeFeatureIncludes.h"
00002 #if _RAKNET_SUPPORT_EmailSender==1
00003 
00004 // Useful sites
00005 // http://www.faqs.org\rfcs\rfc2821.html
00006 // http://en.wikipedia.org/wiki/Base64
00007 // http://www2.rad.com\networks/1995/mime/examples.htm
00008 
00009 #include "EmailSender.h"
00010 #include "TCPInterface.h"
00011 #include "GetTime.h"
00012 #include "Rand.h"
00013 #include "FileList.h"
00014 #include "BitStream.h"
00015 #include <stdio.h>
00016 
00017 #if defined(_XBOX) || defined(X360)
00018                             
00019 #endif
00020 
00021 #include "RakSleep.h"
00022 
00023 static const char base64Map[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
00024 
00025 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)
00026 {
00027         Packet *packet;
00028         char query[1024];
00029         TCPInterface tcpInterface;
00030         SystemAddress emailServer;
00031         if (tcpInterface.Start(0, 0)==false)
00032                 return "Unknown error starting TCP";
00033         emailServer=tcpInterface.Connect(hostAddress, hostPort,true);
00034         if (emailServer==UNASSIGNED_SYSTEM_ADDRESS)
00035                 return "Failed to connect to host";
00036 #ifdef OPEN_SSL_CLIENT_SUPPORT
00037         tcpInterface.StartSSLClient(emailServer);
00038 #endif
00039         RakNetTime timeoutTime = RakNet::GetTime()+3000;
00040         packet=0;
00041         while (RakNet::GetTime() < timeoutTime)
00042         {
00043                 packet = tcpInterface.Receive();
00044                 if (packet)
00045                 {
00046                         if (doPrintf)
00047                                 RAKNET_DEBUG_PRINTF("%s", packet->data);
00048                         break;
00049                 }
00050                 RakSleep(250);
00051         }
00052 
00053         if (packet==0)
00054                 return "Timeout while waiting for initial data from server.";
00055         
00056         tcpInterface.Send("EHLO\r\n", 6, emailServer,false);
00057         const char *response;
00058         bool authenticate=false;
00059 #ifdef _MSC_VER
00060 #pragma warning(disable:4127)   // conditional expression is constant
00061 #endif
00062         while (1)
00063         {
00064                 response=GetResponse(&tcpInterface, emailServer, doPrintf);
00065 
00066                 if (response!=0 && strcmp(response, "AUTHENTICATE")==0)
00067                 {
00068                         authenticate=true;
00069                         break;
00070                 }
00071 
00072                 // Something other than continue?
00073                 if (response!=0 && strcmp(response, "CONTINUE")!=0)
00074                         return response;
00075 
00076                 // Success?
00077                 if (response==0)
00078                         break;
00079         }
00080 
00081         if (authenticate)
00082         {
00083                 sprintf(query, "EHLO %s\r\n", sender);
00084                 tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
00085                 response=GetResponse(&tcpInterface, emailServer, doPrintf);
00086                 if (response!=0)
00087                         return response;
00088                 if (password==0)
00089                         return "Password needed";
00090                 char *outputData = RakNet::OP_NEW_ARRAY<char >((const int) (strlen(sender)+strlen(password)+2)*3, __FILE__, __LINE__ );
00091                 RakNet::BitStream bs;
00092                 char zero=0;
00093                 bs.Write(&zero,1);
00094                 bs.Write(sender,(const unsigned int)strlen(sender));
00095                 //bs.Write("jms1@jms1.net",(const unsigned int)strlen("jms1@jms1.net"));
00096                 bs.Write(&zero,1);
00097                 bs.Write(password,(const unsigned int)strlen(password));
00098                 bs.Write(&zero,1);
00099                 //bs.Write("not.my.real.password",(const unsigned int)strlen("not.my.real.password"));
00100                 Base64Encoding((const char*)bs.GetData(), bs.GetNumberOfBytesUsed(), outputData, base64Map);
00101                 sprintf(query, "AUTH PLAIN %s", outputData);
00102                 tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
00103                 response=GetResponse(&tcpInterface, emailServer, doPrintf);
00104                 if (response!=0)
00105                         return response;
00106         }
00107 
00108 
00109         if (sender)
00110                 sprintf(query, "MAIL From: <%s>\r\n", sender);
00111         else
00112                 sprintf(query, "MAIL From: <>\r\n");
00113         tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
00114         response=GetResponse(&tcpInterface, emailServer, doPrintf);
00115         if (response!=0)
00116                 return response;
00117 
00118         if (recipient)
00119                 sprintf(query, "RCPT TO: <%s>\r\n", recipient);
00120         else
00121                 sprintf(query, "RCPT TO: <>\r\n");
00122         tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
00123         response=GetResponse(&tcpInterface, emailServer, doPrintf);
00124         if (response!=0)
00125                 return response;
00126 
00127         tcpInterface.Send("DATA\r\n", (unsigned int)strlen("DATA\r\n"), emailServer,false);
00128 
00129         // Wait for 354...
00130 
00131         response=GetResponse(&tcpInterface, emailServer, doPrintf);
00132         if (response!=0)
00133                 return response;
00134 
00135         if (subject)
00136         {
00137                 sprintf(query, "Subject: %s\r\n", subject);
00138                 tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
00139         }
00140         if (senderName)
00141         {
00142                 sprintf(query, "From: %s\r\n", senderName);
00143                 tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
00144         }
00145         if (recipientName)
00146         {
00147                 sprintf(query, "To: %s\r\n", recipientName);
00148                 tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
00149         }
00150 
00151         const int boundarySize=60;
00152         char boundary[boundarySize+1];
00153         int i,j;
00154         if (attachedFiles && attachedFiles->fileList.Size())
00155         {
00156                 seedMT((unsigned int) RakNet::GetTime());
00157                 // Random multipart message boundary
00158                 for (i=0; i < boundarySize; i++)
00159                         boundary[i]=base64Map[randomMT()%64];
00160                 boundary[boundarySize]=0;
00161         }
00162 
00163         sprintf(query, "MIME-version: 1.0\r\n");
00164         tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
00165 
00166         if (attachedFiles && attachedFiles->fileList.Size())
00167         {
00168                 sprintf(query, "Content-type: multipart/mixed; BOUNDARY=\"%s\"\r\n\r\n", boundary);
00169                 tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
00170 
00171                 sprintf(query, "This is a multi-part message in MIME format.\r\n\r\n--%s\r\n", boundary);
00172                 tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
00173         }
00174         
00175         sprintf(query, "Content-Type: text/plain; charset=\"US-ASCII\"\r\n\r\n");
00176         tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
00177 
00178         // 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.
00179         char *newBody;
00180         int bodyLength;
00181         bodyLength=(int)strlen(body);
00182         newBody = (char*) rakMalloc_Ex( bodyLength*3, __FILE__, __LINE__ );
00183         if (bodyLength>0)
00184                 newBody[0]=body[0];
00185         for (i=1, j=1; i < bodyLength; i++)
00186         {
00187                 // Transform \n . \r \n into \n . . \r \n
00188                 if (i < bodyLength-2 &&
00189                         body[i-1]=='\n' &&
00190                         body[i+0]=='.' &&
00191                         body[i+1]=='\r' &&
00192                         body[i+2]=='\n')
00193                 {
00194                         newBody[j++]='.';
00195                         newBody[j++]='.';
00196                         newBody[j++]='\r';
00197                         newBody[j++]='\n';
00198                         i+=2;
00199                 }
00200                 // Transform \n . . \r \n into \n . . . \r \n
00201                 // Having to process .. is a bug in the mail server - the spec says ONLY \r\n.\r\n should be transformed
00202                 else if (i <= bodyLength-3 &&
00203                         body[i-1]=='\n' &&
00204                         body[i+0]=='.' &&
00205                         body[i+1]=='.' &&
00206                         body[i+2]=='\r' &&
00207                         body[i+3]=='\n')
00208                 {
00209                         newBody[j++]='.';
00210                         newBody[j++]='.';
00211                         newBody[j++]='.';
00212                         newBody[j++]='\r';
00213                         newBody[j++]='\n';
00214                         i+=3;
00215                 }
00216                 // 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)
00217                 else if (i < bodyLength-1 &&
00218                         body[i-1]=='\n' &&
00219                         body[i+0]=='.' &&
00220                         body[i+1]=='\n')
00221                 {
00222                         newBody[j++]='.';
00223                         newBody[j++]='.';
00224                         newBody[j++]='\r';
00225                         newBody[j++]='\n';
00226                         i+=1;
00227                 }
00228                 // 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)
00229                 // In fact having to process .. is a bug too - because the spec says ONLY \r\n.\r\n should be transformed
00230                 else if (i <= bodyLength-2 &&
00231                         body[i-1]=='\n' &&
00232                         body[i+0]=='.' &&
00233                         body[i+1]=='.' &&
00234                         body[i+2]=='\n')
00235                 {
00236                         newBody[j++]='.';
00237                         newBody[j++]='.';
00238                         newBody[j++]='.';
00239                         newBody[j++]='\r';
00240                         newBody[j++]='\n';
00241                         i+=2;
00242                 }
00243                 else
00244                         newBody[j++]=body[i];
00245         }
00246         
00247         newBody[j++]='\r';
00248         newBody[j++]='\n';
00249         tcpInterface.Send(newBody, j, emailServer,false);
00250 
00251         rakFree_Ex(newBody, __FILE__, __LINE__ );
00252         int outputOffset;
00253 
00254         // What a pain in the rear.  I have to map the binary to printable characters using 6 bits per character.
00255         if (attachedFiles && attachedFiles->fileList.Size())
00256         {
00257                 for (i=0; i < (int) attachedFiles->fileList.Size(); i++)
00258                 {
00259                         // Write boundary
00260                         sprintf(query, "\r\n--%s\r\n", boundary);
00261                         tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
00262 
00263                         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());
00264                         tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
00265 
00266                         newBody = (char*) rakMalloc_Ex( (size_t) (attachedFiles->fileList[i].dataLengthBytes*3)/2, __FILE__, __LINE__ );
00267 
00268                         outputOffset=Base64Encoding(attachedFiles->fileList[i].data, (int) attachedFiles->fileList[i].dataLengthBytes, newBody, base64Map);
00269 
00270                         // Send the base64 mapped file.
00271                         tcpInterface.Send(newBody, outputOffset, emailServer,false);
00272                         rakFree_Ex(newBody, __FILE__, __LINE__ );
00273 
00274                 }
00275 
00276                 // Write last boundary
00277                 sprintf(query, "\r\n--%s--\r\n", boundary);
00278                 tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
00279         }
00280 
00281 
00282         sprintf(query, "\r\n.\r\n");
00283         tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
00284         response=GetResponse(&tcpInterface, emailServer, doPrintf);
00285         if (response!=0)
00286                 return response;
00287 
00288         tcpInterface.Send("QUIT\r\n", (unsigned int)strlen("QUIT\r\n"), emailServer,false);
00289 
00290         RakSleep(30);
00291         if (doPrintf)
00292         {
00293                 packet = tcpInterface.Receive();
00294                 while (packet)
00295                 {
00296                         RAKNET_DEBUG_PRINTF("%s", packet->data);
00297                         packet = tcpInterface.Receive();
00298                 }
00299         }
00300         tcpInterface.Stop();
00301         return 0; // Success
00302 }
00303 
00304 const char *EmailSender::GetResponse(TCPInterface *tcpInterface, const SystemAddress &emailServer, bool doPrintf)
00305 {
00306         Packet *packet;
00307         RakNetTime timeout;
00308         timeout=RakNet::GetTime()+5000;
00309 #ifdef _MSC_VER
00310         #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
00311 #endif
00312         while (1)
00313         {
00314                 if (tcpInterface->HasLostConnection()==emailServer)
00315                         return "Connection to server lost.";
00316                 packet = tcpInterface->Receive();
00317                 if (packet)
00318                 {
00319                         if (doPrintf)
00320                         {
00321                                 RAKNET_DEBUG_PRINTF("%s", packet->data);
00322                         }
00323 #if defined(OPEN_SSL_CLIENT_SUPPORT)
00324                         if (strstr((const char*)packet->data, "220"))
00325                         {
00326                                 tcpInterface->StartSSLClient(packet->systemAddress);
00327                                 return "AUTHENTICATE"; // OK
00328                         }
00329 //                      if (strstr((const char*)packet->data, "250-AUTH LOGIN PLAIN"))
00330 //                      {
00331 //                              tcpInterface->StartSSLClient(packet->systemAddress);
00332 //                              return "AUTHENTICATE"; // OK
00333 //                      }
00334 #endif
00335                         if (strstr((const char*)packet->data, "235"))
00336                                 return 0; // Authentication accepted
00337                         if (strstr((const char*)packet->data, "354"))
00338                                 return 0; // Go ahead
00339 #if defined(OPEN_SSL_CLIENT_SUPPORT)
00340                         if (strstr((const char*)packet->data, "250-STARTTLS"))
00341                         {
00342                                 tcpInterface->Send("STARTTLS\r\n", (unsigned int) strlen("STARTTLS\r\n"), packet->systemAddress, false);
00343                                 return "CONTINUE";
00344                         }
00345 #endif
00346                         if (strstr((const char*)packet->data, "250"))
00347                                 return 0; // OK
00348                         if (strstr((const char*)packet->data, "550"))
00349                                 return "Failed on error code 550";
00350                         if (strstr((const char*)packet->data, "553"))
00351                                 return "Failed on error code 553";
00352                 }
00353                 if (RakNet::GetTime() > timeout)
00354                         return "Timed out";
00355                 RakSleep(100);
00356         }
00357 }
00358 
00359 int EmailSender::Base64Encoding(const char *inputData, int dataLength, char *outputData, const char *base64Map)
00360 {
00361         int outputOffset, charCount;
00362         int write3Count;
00363         outputOffset=0;
00364         charCount=0;
00365         int j;
00366 
00367         write3Count=dataLength/3;
00368         for (j=0; j < write3Count; j++)
00369         {
00370                 // 6 leftmost bits from first byte, shifted to bits 7,8 are 0
00371                 outputData[outputOffset++]=base64Map[inputData[j*3+0] >> 2];
00372                 if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
00373 
00374                 // Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8
00375                 outputData[outputOffset++]=base64Map[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63];
00376                 if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
00377 
00378                 // 4 low bits from the second byte and the two high bits from the third byte, masked to ignore bits 7,8
00379                 outputData[outputOffset++]=base64Map[((inputData[j*3+1] << 2) | (inputData[j*3+2] >> 6)) & 63]; // Third 6 bits
00380                 if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
00381 
00382                 // Last 6 bits from the third byte, masked to ignore bits 7,8
00383                 outputData[outputOffset++]=base64Map[inputData[j*3+2] & 63];
00384                 if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
00385         }
00386 
00387         if (dataLength % 3==1)
00388         {
00389                 // One input byte remaining
00390                 outputData[outputOffset++]=base64Map[inputData[j*3+0] >> 2];
00391                 if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
00392 
00393                 // Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8
00394                 outputData[outputOffset++]=base64Map[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63];
00395                 if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
00396 
00397                 // Pad with two equals
00398                 outputData[outputOffset++]='=';
00399                 outputData[outputOffset++]='=';
00400         }
00401         else if (dataLength % 3==2)
00402         {
00403                 // Two input bytes remaining
00404 
00405                 // 6 leftmost bits from first byte, shifted to bits 7,8 are 0
00406                 outputData[outputOffset++]=base64Map[inputData[j*3+0] >> 2];
00407                 if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
00408 
00409                 // Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8
00410                 outputData[outputOffset++]=base64Map[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63];
00411                 if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
00412 
00413                 // 4 low bits from the second byte, followed by 00
00414                 outputData[outputOffset++]=base64Map[(inputData[j*3+1] << 2) & 63]; // Third 6 bits
00415                 if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
00416 
00417                 // Pad with one equal
00418                 outputData[outputOffset++]='=';
00419                 //outputData[outputOffset++]='=';
00420         }
00421 
00422         // Append \r\n
00423         outputData[outputOffset++]='\r';
00424         outputData[outputOffset++]='\n';
00425         outputData[outputOffset]=0;
00426 
00427         return outputOffset;
00428 }
00429 
00430 #endif // _RAKNET_SUPPORT_*

Generated on Thu Sep 30 2010 01:27:23 for RakNet by  doxygen 1.7.1