SLikeNet 0.1.3

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

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