PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/raknet.mod/src/EmailSender.cpp

https://github.com/BlitzMaxModules/bah.mod
C++ | 425 lines | 332 code | 53 blank | 40 comment | 70 complexity | b6bf616d14a3ffd0ff161165a0f7c73e MD5 | raw file
  1. // Useful sites
  2. // http://www.faqs.org\rfcs\rfc2821.html
  3. // http://en.wikipedia.org/wiki/Base64
  4. // http://www2.rad.com\networks/1995/mime/examples.htm
  5. #include "EmailSender.h"
  6. #include "TCPInterface.h"
  7. #include "GetTime.h"
  8. #include "Rand.h"
  9. #include "FileList.h"
  10. #include "BitStream.h"
  11. #include <stdio.h>
  12. #if defined(_XBOX) || defined(X360)
  13. #endif
  14. #include "RakSleep.h"
  15. static const char base64Map[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  16. 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)
  17. {
  18. Packet *packet;
  19. char query[1024];
  20. TCPInterface tcpInterface;
  21. SystemAddress emailServer;
  22. if (tcpInterface.Start(0, 0)==false)
  23. return "Unknown error starting TCP";
  24. emailServer=tcpInterface.Connect(hostAddress, hostPort,true);
  25. if (emailServer==UNASSIGNED_SYSTEM_ADDRESS)
  26. return "Failed to connect to host";
  27. #ifdef OPEN_SSL_CLIENT_SUPPORT
  28. tcpInterface.StartSSLClient(emailServer);
  29. #endif
  30. RakNetTime timeoutTime = RakNet::GetTime()+3000;
  31. packet=0;
  32. while (RakNet::GetTime() < timeoutTime)
  33. {
  34. packet = tcpInterface.Receive();
  35. if (packet)
  36. {
  37. if (doPrintf)
  38. RAKNET_DEBUG_PRINTF("%s", packet->data);
  39. break;
  40. }
  41. RakSleep(250);
  42. }
  43. if (packet==0)
  44. return "Timeout while waiting for initial data from server.";
  45. tcpInterface.Send("EHLO\r\n", 6, emailServer,false);
  46. const char *response;
  47. bool authenticate=false;
  48. #ifdef _MSC_VER
  49. #pragma warning(disable:4127) // conditional expression is constant
  50. #endif
  51. while (1)
  52. {
  53. response=GetResponse(&tcpInterface, emailServer, doPrintf);
  54. if (response!=0 && strcmp(response, "AUTHENTICATE")==0)
  55. {
  56. authenticate=true;
  57. break;
  58. }
  59. // Something other than continue?
  60. if (response!=0 && strcmp(response, "CONTINUE")!=0)
  61. return response;
  62. // Success?
  63. if (response==0)
  64. break;
  65. }
  66. if (authenticate)
  67. {
  68. sprintf(query, "EHLO %s\r\n", sender);
  69. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  70. response=GetResponse(&tcpInterface, emailServer, doPrintf);
  71. if (response!=0)
  72. return response;
  73. if (password==0)
  74. return "Password needed";
  75. char *outputData = RakNet::OP_NEW_ARRAY<char >((const int) (strlen(sender)+strlen(password)+2)*3, __FILE__, __LINE__ );
  76. RakNet::BitStream bs;
  77. char zero=0;
  78. bs.Write(&zero,1);
  79. bs.Write(sender,(const unsigned int)strlen(sender));
  80. //bs.Write("jms1@jms1.net",(const unsigned int)strlen("jms1@jms1.net"));
  81. bs.Write(&zero,1);
  82. bs.Write(password,(const unsigned int)strlen(password));
  83. bs.Write(&zero,1);
  84. //bs.Write("not.my.real.password",(const unsigned int)strlen("not.my.real.password"));
  85. Base64Encoding((const char*)bs.GetData(), bs.GetNumberOfBytesUsed(), outputData, base64Map);
  86. sprintf(query, "AUTH PLAIN %s", outputData);
  87. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  88. response=GetResponse(&tcpInterface, emailServer, doPrintf);
  89. if (response!=0)
  90. return response;
  91. }
  92. if (sender)
  93. sprintf(query, "MAIL From: <%s>\r\n", sender);
  94. else
  95. sprintf(query, "MAIL From: <>\r\n");
  96. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  97. response=GetResponse(&tcpInterface, emailServer, doPrintf);
  98. if (response!=0)
  99. return response;
  100. if (recipient)
  101. sprintf(query, "RCPT TO: <%s>\r\n", recipient);
  102. else
  103. sprintf(query, "RCPT TO: <>\r\n");
  104. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  105. response=GetResponse(&tcpInterface, emailServer, doPrintf);
  106. if (response!=0)
  107. return response;
  108. tcpInterface.Send("DATA\r\n", (unsigned int)strlen("DATA\r\n"), emailServer,false);
  109. // Wait for 354...
  110. response=GetResponse(&tcpInterface, emailServer, doPrintf);
  111. if (response!=0)
  112. return response;
  113. if (subject)
  114. {
  115. sprintf(query, "Subject: %s\r\n", subject);
  116. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  117. }
  118. if (senderName)
  119. {
  120. sprintf(query, "From: %s\r\n", senderName);
  121. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  122. }
  123. if (recipientName)
  124. {
  125. sprintf(query, "To: %s\r\n", recipientName);
  126. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  127. }
  128. const int boundarySize=60;
  129. char boundary[boundarySize+1];
  130. int i,j;
  131. if (attachedFiles && attachedFiles->fileList.Size())
  132. {
  133. seedMT((unsigned int) RakNet::GetTime());
  134. // Random multipart message boundary
  135. for (i=0; i < boundarySize; i++)
  136. boundary[i]=base64Map[randomMT()%64];
  137. boundary[boundarySize]=0;
  138. }
  139. sprintf(query, "MIME-version: 1.0\r\n");
  140. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  141. if (attachedFiles && attachedFiles->fileList.Size())
  142. {
  143. sprintf(query, "Content-type: multipart/mixed; BOUNDARY=\"%s\"\r\n\r\n", boundary);
  144. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  145. sprintf(query, "This is a multi-part message in MIME format.\r\n\r\n--%s\r\n", boundary);
  146. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  147. }
  148. sprintf(query, "Content-Type: text/plain; charset=\"US-ASCII\"\r\n\r\n");
  149. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  150. // 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.
  151. char *newBody;
  152. int bodyLength;
  153. bodyLength=(int)strlen(body);
  154. newBody = (char*) rakMalloc_Ex( bodyLength*3, __FILE__, __LINE__ );
  155. if (bodyLength>0)
  156. newBody[0]=body[0];
  157. for (i=1, j=1; i < bodyLength; i++)
  158. {
  159. // Transform \n . \r \n into \n . . \r \n
  160. if (i < bodyLength-2 &&
  161. body[i-1]=='\n' &&
  162. body[i+0]=='.' &&
  163. body[i+1]=='\r' &&
  164. body[i+2]=='\n')
  165. {
  166. newBody[j++]='.';
  167. newBody[j++]='.';
  168. newBody[j++]='\r';
  169. newBody[j++]='\n';
  170. i+=2;
  171. }
  172. // Transform \n . . \r \n into \n . . . \r \n
  173. // Having to process .. is a bug in the mail server - the spec says ONLY \r\n.\r\n should be transformed
  174. else if (i <= bodyLength-3 &&
  175. body[i-1]=='\n' &&
  176. body[i+0]=='.' &&
  177. body[i+1]=='.' &&
  178. body[i+2]=='\r' &&
  179. body[i+3]=='\n')
  180. {
  181. newBody[j++]='.';
  182. newBody[j++]='.';
  183. newBody[j++]='.';
  184. newBody[j++]='\r';
  185. newBody[j++]='\n';
  186. i+=3;
  187. }
  188. // 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)
  189. else if (i < bodyLength-1 &&
  190. body[i-1]=='\n' &&
  191. body[i+0]=='.' &&
  192. body[i+1]=='\n')
  193. {
  194. newBody[j++]='.';
  195. newBody[j++]='.';
  196. newBody[j++]='\r';
  197. newBody[j++]='\n';
  198. i+=1;
  199. }
  200. // 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)
  201. // In fact having to process .. is a bug too - because the spec says ONLY \r\n.\r\n should be transformed
  202. else if (i <= bodyLength-2 &&
  203. body[i-1]=='\n' &&
  204. body[i+0]=='.' &&
  205. body[i+1]=='.' &&
  206. body[i+2]=='\n')
  207. {
  208. newBody[j++]='.';
  209. newBody[j++]='.';
  210. newBody[j++]='.';
  211. newBody[j++]='\r';
  212. newBody[j++]='\n';
  213. i+=2;
  214. }
  215. else
  216. newBody[j++]=body[i];
  217. }
  218. newBody[j++]='\r';
  219. newBody[j++]='\n';
  220. tcpInterface.Send(newBody, j, emailServer,false);
  221. rakFree_Ex(newBody, __FILE__, __LINE__ );
  222. int outputOffset;
  223. // What a pain in the rear. I have to map the binary to printable characters using 6 bits per character.
  224. if (attachedFiles && attachedFiles->fileList.Size())
  225. {
  226. for (i=0; i < (int) attachedFiles->fileList.Size(); i++)
  227. {
  228. // Write boundary
  229. sprintf(query, "\r\n--%s\r\n", boundary);
  230. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  231. 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());
  232. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  233. newBody = (char*) rakMalloc_Ex( (size_t) (attachedFiles->fileList[i].dataLengthBytes*3)/2, __FILE__, __LINE__ );
  234. outputOffset=Base64Encoding(attachedFiles->fileList[i].data, (int) attachedFiles->fileList[i].dataLengthBytes, newBody, base64Map);
  235. // Send the base64 mapped file.
  236. tcpInterface.Send(newBody, outputOffset, emailServer,false);
  237. rakFree_Ex(newBody, __FILE__, __LINE__ );
  238. }
  239. // Write last boundary
  240. sprintf(query, "\r\n--%s--\r\n", boundary);
  241. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  242. }
  243. sprintf(query, "\r\n.\r\n");
  244. tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
  245. response=GetResponse(&tcpInterface, emailServer, doPrintf);
  246. if (response!=0)
  247. return response;
  248. tcpInterface.Send("QUIT\r\n", (unsigned int)strlen("QUIT\r\n"), emailServer,false);
  249. RakSleep(30);
  250. if (doPrintf)
  251. {
  252. packet = tcpInterface.Receive();
  253. while (packet)
  254. {
  255. RAKNET_DEBUG_PRINTF("%s", packet->data);
  256. packet = tcpInterface.Receive();
  257. }
  258. }
  259. tcpInterface.Stop();
  260. return 0; // Success
  261. }
  262. const char *EmailSender::GetResponse(TCPInterface *tcpInterface, const SystemAddress &emailServer, bool doPrintf)
  263. {
  264. Packet *packet;
  265. RakNetTime timeout;
  266. timeout=RakNet::GetTime()+5000;
  267. #ifdef _MSC_VER
  268. #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
  269. #endif
  270. while (1)
  271. {
  272. if (tcpInterface->HasLostConnection()==emailServer)
  273. return "Connection to server lost.";
  274. packet = tcpInterface->Receive();
  275. if (packet)
  276. {
  277. if (doPrintf)
  278. {
  279. RAKNET_DEBUG_PRINTF("%s", packet->data);
  280. }
  281. #if defined(OPEN_SSL_CLIENT_SUPPORT)
  282. if (strstr((const char*)packet->data, "220"))
  283. {
  284. tcpInterface->StartSSLClient(packet->systemAddress);
  285. return "AUTHENTICATE"; // OK
  286. }
  287. // if (strstr((const char*)packet->data, "250-AUTH LOGIN PLAIN"))
  288. // {
  289. // tcpInterface->StartSSLClient(packet->systemAddress);
  290. // return "AUTHENTICATE"; // OK
  291. // }
  292. #endif
  293. if (strstr((const char*)packet->data, "235"))
  294. return 0; // Authentication accepted
  295. if (strstr((const char*)packet->data, "354"))
  296. return 0; // Go ahead
  297. #if defined(OPEN_SSL_CLIENT_SUPPORT)
  298. if (strstr((const char*)packet->data, "250-STARTTLS"))
  299. {
  300. tcpInterface->Send("STARTTLS\r\n", (unsigned int) strlen("STARTTLS\r\n"), packet->systemAddress, false);
  301. return "CONTINUE";
  302. }
  303. #endif
  304. if (strstr((const char*)packet->data, "250"))
  305. return 0; // OK
  306. if (strstr((const char*)packet->data, "550"))
  307. return "Failed on error code 550";
  308. if (strstr((const char*)packet->data, "553"))
  309. return "Failed on error code 553";
  310. }
  311. if (RakNet::GetTime() > timeout)
  312. return "Timed out";
  313. RakSleep(100);
  314. }
  315. }
  316. int EmailSender::Base64Encoding(const char *inputData, int dataLength, char *outputData, const char *base64Map)
  317. {
  318. int outputOffset, charCount;
  319. int write3Count;
  320. outputOffset=0;
  321. charCount=0;
  322. int j;
  323. write3Count=dataLength/3;
  324. for (j=0; j < write3Count; j++)
  325. {
  326. // 6 leftmost bits from first byte, shifted to bits 7,8 are 0
  327. outputData[outputOffset++]=base64Map[inputData[j*3+0] >> 2];
  328. if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
  329. // Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8
  330. outputData[outputOffset++]=base64Map[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63];
  331. if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
  332. // 4 low bits from the second byte and the two high bits from the third byte, masked to ignore bits 7,8
  333. outputData[outputOffset++]=base64Map[((inputData[j*3+1] << 2) | (inputData[j*3+2] >> 6)) & 63]; // Third 6 bits
  334. if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
  335. // Last 6 bits from the third byte, masked to ignore bits 7,8
  336. outputData[outputOffset++]=base64Map[inputData[j*3+2] & 63];
  337. if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
  338. }
  339. if (dataLength % 3==1)
  340. {
  341. // One input byte remaining
  342. outputData[outputOffset++]=base64Map[inputData[j*3+0] >> 2];
  343. if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
  344. // Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8
  345. outputData[outputOffset++]=base64Map[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63];
  346. if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
  347. // Pad with two equals
  348. outputData[outputOffset++]='=';
  349. outputData[outputOffset++]='=';
  350. }
  351. else if (dataLength % 3==2)
  352. {
  353. // Two input bytes remaining
  354. // 6 leftmost bits from first byte, shifted to bits 7,8 are 0
  355. outputData[outputOffset++]=base64Map[inputData[j*3+0] >> 2];
  356. if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
  357. // Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8
  358. outputData[outputOffset++]=base64Map[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63];
  359. if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
  360. // 4 low bits from the second byte, followed by 00
  361. outputData[outputOffset++]=base64Map[(inputData[j*3+1] << 2) & 63]; // Third 6 bits
  362. if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
  363. // Pad with one equal
  364. outputData[outputOffset++]='=';
  365. //outputData[outputOffset++]='=';
  366. }
  367. // Append \r\n
  368. outputData[outputOffset++]='\r';
  369. outputData[outputOffset++]='\n';
  370. outputData[outputOffset]=0;
  371. return outputOffset;
  372. }