EmailSender.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. /*
  2. * Copyright (c) 2014, Oculus VR, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. *
  9. */
  10. #include "NativeFeatureIncludes.h"
  11. #if _RAKNET_SUPPORT_EmailSender==1 && _RAKNET_SUPPORT_TCPInterface==1 && _RAKNET_SUPPORT_FileOperations==1
  12. // Useful sites
  13. // http://www.faqs.org\rfcs\rfc2821.html
  14. // http://www2.rad.com\networks/1995/mime/examples.htm
  15. #include "EmailSender.h"
  16. #include "TCPInterface.h"
  17. #include "GetTime.h"
  18. #include "Rand.h"
  19. #include "FileList.h"
  20. #include "BitStream.h"
  21. #include "Base64Encoder.h"
  22. #include <stdio.h>
  23. #include "RakSleep.h"
  24. using namespace RakNet;
  25. STATIC_FACTORY_DEFINITIONS(EmailSender,EmailSender);
  26. 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)
  27. {
  28. RakNet::Packet *packet;
  29. char query[1024];
  30. TCPInterface tcpInterface;
  31. SystemAddress emailServer;
  32. if (tcpInterface.Start(0, 0)==false)
  33. return "Unknown error starting TCP";
  34. emailServer=tcpInterface.Connect(hostAddress, hostPort,true);
  35. if (emailServer==UNASSIGNED_SYSTEM_ADDRESS)
  36. return "Failed to connect to host";
  37. #if OPEN_SSL_CLIENT_SUPPORT==1
  38. tcpInterface.StartSSLClient(emailServer);
  39. #endif
  40. RakNet::TimeMS timeoutTime = RakNet::GetTimeMS()+3000;
  41. packet=0;
  42. while (RakNet::GetTimeMS() < timeoutTime)
  43. {
  44. packet = tcpInterface.Receive();
  45. if (packet)
  46. {
  47. if (doPrintf)
  48. {
  49. RAKNET_DEBUG_PRINTF("%s", packet->data);
  50. tcpInterface.DeallocatePacket(packet);
  51. }
  52. break;
  53. }
  54. RakSleep(250);
  55. }
  56. if (packet==0)
  57. return "Timeout while waiting for initial data from server.";
  58. tcpInterface.Send("EHLO\r\n", 6, emailServer,false);
  59. const char *response;
  60. bool authenticate=false;
  61. #ifdef _MSC_VER
  62. #pragma warning(disable:4127) // conditional expression is constant
  63. #endif
  64. while (1)
  65. {
  66. response=GetResponse(&tcpInterface, emailServer, doPrintf);
  67. if (response!=0 && strcmp(response, "AUTHENTICATE")==0)
  68. {
  69. authenticate=true;
  70. break;
  71. }
  72. // Something other than continue?
  73. if (response!=0 && strcmp(response, "CONTINUE")!=0)
  74. return response;
  75. // Success?
  76. if (response==0)
  77. break;
  78. }
  79. if (authenticate)
  80. {
  81. sprintf(query, "EHLO %s\r\n", sender);
  82. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  83. response=GetResponse(&tcpInterface, emailServer, doPrintf);
  84. if (response!=0)
  85. return response;
  86. if (password==0)
  87. return "Password needed";
  88. char *outputData = RakNet::OP_NEW_ARRAY<char >((const int) (strlen(sender)+strlen(password)+2)*3, _FILE_AND_LINE_ );
  89. RakNet::BitStream bs;
  90. char zero=0;
  91. bs.Write(&zero,1);
  92. bs.Write(sender,(const unsigned int)strlen(sender));
  93. //bs.Write("jms1@jms1.net",(const unsigned int)strlen("jms1@jms1.net"));
  94. bs.Write(&zero,1);
  95. bs.Write(password,(const unsigned int)strlen(password));
  96. bs.Write(&zero,1);
  97. //bs.Write("not.my.real.password",(const unsigned int)strlen("not.my.real.password"));
  98. Base64Encoding((const unsigned char*)bs.GetData(), bs.GetNumberOfBytesUsed(), outputData);
  99. sprintf(query, "AUTH PLAIN %s", outputData);
  100. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  101. response=GetResponse(&tcpInterface, emailServer, doPrintf);
  102. if (response!=0)
  103. return response;
  104. }
  105. if (sender)
  106. sprintf(query, "MAIL From: <%s>\r\n", sender);
  107. else
  108. sprintf(query, "MAIL From: <>\r\n");
  109. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  110. response=GetResponse(&tcpInterface, emailServer, doPrintf);
  111. if (response!=0)
  112. return response;
  113. if (recipient)
  114. sprintf(query, "RCPT TO: <%s>\r\n", recipient);
  115. else
  116. sprintf(query, "RCPT TO: <>\r\n");
  117. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  118. response=GetResponse(&tcpInterface, emailServer, doPrintf);
  119. if (response!=0)
  120. return response;
  121. tcpInterface.Send("DATA\r\n", (unsigned int)strlen("DATA\r\n"), emailServer,false);
  122. // Wait for 354...
  123. response=GetResponse(&tcpInterface, emailServer, doPrintf);
  124. if (response!=0)
  125. return response;
  126. if (subject)
  127. {
  128. sprintf(query, "Subject: %s\r\n", subject);
  129. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  130. }
  131. if (senderName)
  132. {
  133. sprintf(query, "From: %s\r\n", senderName);
  134. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  135. }
  136. if (recipientName)
  137. {
  138. sprintf(query, "To: %s\r\n", recipientName);
  139. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  140. }
  141. const int boundarySize=60;
  142. char boundary[boundarySize+1];
  143. int i,j;
  144. if (attachedFiles && attachedFiles->fileList.Size())
  145. {
  146. rakNetRandom.SeedMT((unsigned int) RakNet::GetTimeMS());
  147. // Random multipart message boundary
  148. for (i=0; i < boundarySize; i++)
  149. boundary[i]=Base64Map()[rakNetRandom.RandomMT()%64];
  150. boundary[boundarySize]=0;
  151. }
  152. sprintf(query, "MIME-version: 1.0\r\n");
  153. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  154. if (attachedFiles && attachedFiles->fileList.Size())
  155. {
  156. sprintf(query, "Content-type: multipart/mixed; BOUNDARY=\"%s\"\r\n\r\n", boundary);
  157. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  158. sprintf(query, "This is a multi-part message in MIME format.\r\n\r\n--%s\r\n", boundary);
  159. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  160. }
  161. sprintf(query, "Content-Type: text/plain; charset=\"US-ASCII\"\r\n\r\n");
  162. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  163. // 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.
  164. char *newBody;
  165. int bodyLength;
  166. bodyLength=(int)strlen(body);
  167. newBody = (char*) rakMalloc_Ex( bodyLength*3, _FILE_AND_LINE_ );
  168. if (bodyLength>=0)
  169. newBody[0]=body[0];
  170. for (i=1, j=1; i < bodyLength; i++)
  171. {
  172. // Transform \n . \r \n into \n . . \r \n
  173. if (i < bodyLength-2 &&
  174. body[i-1]=='\n' &&
  175. body[i+0]=='.' &&
  176. body[i+1]=='\r' &&
  177. body[i+2]=='\n')
  178. {
  179. newBody[j++]='.';
  180. newBody[j++]='.';
  181. newBody[j++]='\r';
  182. newBody[j++]='\n';
  183. i+=2;
  184. }
  185. // Transform \n . . \r \n into \n . . . \r \n
  186. // Having to process .. is a bug in the mail server - the spec says ONLY \r\n.\r\n should be transformed
  187. else if (i <= bodyLength-3 &&
  188. body[i-1]=='\n' &&
  189. body[i+0]=='.' &&
  190. body[i+1]=='.' &&
  191. body[i+2]=='\r' &&
  192. body[i+3]=='\n')
  193. {
  194. newBody[j++]='.';
  195. newBody[j++]='.';
  196. newBody[j++]='.';
  197. newBody[j++]='\r';
  198. newBody[j++]='\n';
  199. i+=3;
  200. }
  201. // 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)
  202. else if (i < bodyLength-1 &&
  203. body[i-1]=='\n' &&
  204. body[i+0]=='.' &&
  205. body[i+1]=='\n')
  206. {
  207. newBody[j++]='.';
  208. newBody[j++]='.';
  209. newBody[j++]='\r';
  210. newBody[j++]='\n';
  211. i+=1;
  212. }
  213. // 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)
  214. // In fact having to process .. is a bug too - because the spec says ONLY \r\n.\r\n should be transformed
  215. else if (i <= bodyLength-2 &&
  216. body[i-1]=='\n' &&
  217. body[i+0]=='.' &&
  218. body[i+1]=='.' &&
  219. body[i+2]=='\n')
  220. {
  221. newBody[j++]='.';
  222. newBody[j++]='.';
  223. newBody[j++]='.';
  224. newBody[j++]='\r';
  225. newBody[j++]='\n';
  226. i+=2;
  227. }
  228. else
  229. newBody[j++]=body[i];
  230. }
  231. newBody[j++]='\r';
  232. newBody[j++]='\n';
  233. tcpInterface.Send(newBody, j, emailServer,false);
  234. rakFree_Ex(newBody, _FILE_AND_LINE_ );
  235. int outputOffset;
  236. // What a pain in the rear. I have to map the binary to printable characters using 6 bits per character.
  237. if (attachedFiles && attachedFiles->fileList.Size())
  238. {
  239. for (i=0; i < (int) attachedFiles->fileList.Size(); i++)
  240. {
  241. // Write boundary
  242. sprintf(query, "\r\n--%s\r\n", boundary);
  243. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  244. 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());
  245. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  246. newBody = (char*) rakMalloc_Ex( (size_t) (attachedFiles->fileList[i].dataLengthBytes*3)/2, _FILE_AND_LINE_ );
  247. outputOffset=Base64Encoding((const unsigned char*) attachedFiles->fileList[i].data, (int) attachedFiles->fileList[i].dataLengthBytes, newBody);
  248. // Send the base64 mapped file.
  249. tcpInterface.Send(newBody, outputOffset, emailServer,false);
  250. rakFree_Ex(newBody, _FILE_AND_LINE_ );
  251. }
  252. // Write last boundary
  253. sprintf(query, "\r\n--%s--\r\n", boundary);
  254. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  255. }
  256. sprintf(query, "\r\n.\r\n");
  257. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  258. response=GetResponse(&tcpInterface, emailServer, doPrintf);
  259. if (response!=0)
  260. return response;
  261. tcpInterface.Send("QUIT\r\n", (unsigned int)strlen("QUIT\r\n"), emailServer,false);
  262. RakSleep(30);
  263. if (doPrintf)
  264. {
  265. packet = tcpInterface.Receive();
  266. while (packet)
  267. {
  268. RAKNET_DEBUG_PRINTF("%s", packet->data);
  269. tcpInterface.DeallocatePacket(packet);
  270. packet = tcpInterface.Receive();
  271. }
  272. }
  273. tcpInterface.Stop();
  274. return 0; // Success
  275. }
  276. const char *EmailSender::GetResponse(TCPInterface *tcpInterface, const SystemAddress &emailServer, bool doPrintf)
  277. {
  278. RakNet::Packet *packet;
  279. RakNet::TimeMS timeout;
  280. timeout=RakNet::GetTimeMS()+5000;
  281. #ifdef _MSC_VER
  282. #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
  283. #endif
  284. while (1)
  285. {
  286. if (tcpInterface->HasLostConnection()==emailServer)
  287. return "Connection to server lost.";
  288. packet = tcpInterface->Receive();
  289. if (packet)
  290. {
  291. if (doPrintf)
  292. {
  293. RAKNET_DEBUG_PRINTF("%s", packet->data);
  294. }
  295. #if OPEN_SSL_CLIENT_SUPPORT==1
  296. if (strstr((const char*)packet->data, "220"))
  297. {
  298. tcpInterface->StartSSLClient(packet->systemAddress);
  299. return "AUTHENTICATE"; // OK
  300. }
  301. // if (strstr((const char*)packet->data, "250-AUTH LOGIN PLAIN"))
  302. // {
  303. // tcpInterface->StartSSLClient(packet->systemAddress);
  304. // return "AUTHENTICATE"; // OK
  305. // }
  306. #endif
  307. if (strstr((const char*)packet->data, "235"))
  308. return 0; // Authentication accepted
  309. if (strstr((const char*)packet->data, "354"))
  310. return 0; // Go ahead
  311. #if OPEN_SSL_CLIENT_SUPPORT==1
  312. if (strstr((const char*)packet->data, "250-STARTTLS"))
  313. {
  314. tcpInterface->Send("STARTTLS\r\n", (unsigned int) strlen("STARTTLS\r\n"), packet->systemAddress, false);
  315. return "CONTINUE";
  316. }
  317. #endif
  318. if (strstr((const char*)packet->data, "250"))
  319. return 0; // OK
  320. if (strstr((const char*)packet->data, "550"))
  321. return "Failed on error code 550";
  322. if (strstr((const char*)packet->data, "553"))
  323. return "Failed on error code 553";
  324. }
  325. if (RakNet::GetTimeMS() > timeout)
  326. return "Timed out";
  327. RakSleep(100);
  328. }
  329. }
  330. #endif // _RAKNET_SUPPORT_*
粤ICP备19079148号