Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / nel / src / net / email.cpp
blob0521da12d8075036770a5acbaae995f3d8829df7
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "stdnet.h"
22 #include "nel/misc/report.h"
23 #include "nel/misc/path.h"
25 #include "nel/net/tcp_sock.h"
26 #include "nel/net/email.h"
28 using namespace std;
29 using namespace NLMISC;
32 namespace NLNET {
34 static string DefaultSMTPServer, DefaultFrom, DefaultTo;
36 /* Conversion table. for base 64 */
37 static char tbl[65] = {
38 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
39 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
40 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
41 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
42 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
43 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
44 'w', 'x', 'y', 'z', '0', '1', '2', '3',
45 '4', '5', '6', '7', '8', '9', '+', '/',
46 '=' /* termination character */
50 * Encode the string S of length LENGTH to base64 format and place it
51 * to STORE. STORE will be 0-terminated, and must point to a writable
52 * buffer of at least 1+BASE64_LENGTH(length) bytes.
53 * where BASE64_LENGTH(len) = (4 * ((LENGTH + 2) / 3))
55 static void uuencode (const char *s, const char *store, const int length)
57 int i;
58 unsigned char *p = (unsigned char *)store;
59 unsigned char *us = (unsigned char *)s;
61 /* Transform the 3x8 bits to 4x6 bits, as required by base64. */
62 for (i = 0; i < length; i += 3)
64 *p++ = tbl[us[0] >> 2];
65 *p++ = tbl[((us[0] & 3) << 4) + (us[1] >> 4)];
66 *p++ = tbl[((us[1] & 0xf) << 2) + (us[2] >> 6)];
67 *p++ = tbl[us[2] & 0x3f];
68 us += 3;
70 /* Pad the result if necessary... */
71 if (i == length + 1)
73 *(p - 1) = tbl[64];
75 else if (i == length + 2)
77 *(p - 1) = *(p - 2) = tbl[64];
79 /* ...and zero-terminate it. */
80 *p = '\0';
83 bool sendEMailCommand (CTcpSock &sock, const std::string &command, uint32 code = 250)
85 string buffer = command + "\r\n";
86 uint32 size = (uint32)buffer.size();
87 if(!command.empty())
89 if (sock.send ((uint8 *)buffer.c_str(), size) != CSock::Ok)
91 nlwarning ("EMAIL: Can't send data to the server");
92 return false;
96 string res;
97 char c;
98 for(;;)
100 size = 1;
102 if (sock.receive((uint8*)&c, size, false) == CSock::Ok)
104 res += c;
105 if (c == '\n')
107 uint32 c;
108 fromString(res, c);
109 if (c != code)
111 nlwarning ("EMAIL: EMail command '%s' returned '%s' instead of code %d on sock %s", command.substr(0, 20).c_str(), res.substr(0, res.size()-2).c_str(), code, sock.remoteAddr().asString().c_str());
112 return false;
114 return true;
117 else
119 nlwarning ("EMAIL: EMail connection closed before end of line, command '%s' returned '%s' on sock %s (code %d)", command.substr(0, 20).c_str(), res.c_str(), sock.remoteAddr().asString().c_str(), code);
120 return false;
126 bool sendEmail (const string &smtpServer, const string &from, const string &to, const string &subject, const string &body, const string &attachedFile, bool onlyCheck)
128 bool ok = false;
129 CTcpSock sock;
130 uint i;
132 string formatedBody;
133 string formatedFrom;
134 string formatedTo;
135 string formatedSMTPServer;
140 if (smtpServer.empty())
142 if(DefaultSMTPServer.empty())
144 nlwarning ("EMAIL: Can't send email because no SMTPServer was provided");
145 goto end;
147 else
149 formatedSMTPServer = DefaultSMTPServer;
152 else
154 formatedSMTPServer = smtpServer;
157 sock.connect(CInetAddress(formatedSMTPServer, 25));
159 if (!sock.connected())
161 nlwarning ("EMAIL: Can't connect to email server %s", formatedSMTPServer.c_str());
162 goto end;
165 if (to.empty())
167 if(DefaultTo.empty())
169 nlwarning ("EMAIL: Can't send email because no To was provided");
170 goto end;
172 else
174 formatedTo = DefaultTo;
177 else
179 formatedTo = to;
182 if(from.empty())
184 if (DefaultFrom.empty())
186 formatedFrom = CInetAddress::localHost().hostName();
187 formatedFrom += "@gnu.org";
189 else
191 formatedFrom = DefaultFrom;
194 else
196 formatedFrom = from;
199 // we must skip the first line
200 formatedBody = "\r\n";
202 // replace \n by \r\n
203 formatedBody += addSlashR(body);
205 // add attachment if any
206 if (!attachedFile.empty())
208 string ext = CFile::getExtension(attachedFile);
210 string mimepart;
212 // mime header and main mail text
214 mimepart += "Mime-Version: 1.0\r\n";
215 mimepart += "Content-Type: multipart/mixed;\r\n";
216 mimepart += " boundary=\"Multipart_nel\"\r\n";
217 mimepart += "\r\n";
218 mimepart += "This is a multi-part message in MIME format.\r\n";
219 mimepart += "\r\n";
220 mimepart += "--Multipart_nel\r\n";
221 mimepart += "Content-Type: text/plain; charset=us-ascii\r\n";
222 mimepart += "Content-Transfer-Encoding: 7bit\r\n";
224 formatedBody = mimepart + formatedBody;
226 // mime attachment
228 formatedBody += "--Multipart_nel\r\n";
229 formatedBody += "Content-Disposition: attachment;\r\n";
231 string lext = toLowerAscii(ext);
232 if(lext == "tga")
234 formatedBody += "Content-Type: image/x-targa;\r\n";
236 else if(lext == "bmp")
238 formatedBody += "Content-Type: image/bmp;\r\n";
240 else if(lext == "png")
242 formatedBody += "Content-Type: image/png;\r\n";
244 else if(lext == "jpg" || lext == "jpeg")
246 formatedBody += "Content-Type: image/jpeg;\r\n";
248 else if(lext == "dmp")
250 formatedBody += "Content-Type: application/octet-stream;\r\n";
252 else
254 formatedBody += "Content-Type: text/plain; charset=us-ascii\r\n";
257 formatedBody += " name=\""+CFile::getFilename(attachedFile)+"\"\r\n";
258 formatedBody += "Content-Transfer-Encoding: base64\r\n";
259 formatedBody += " filename=\""+CFile::getFilename(attachedFile)+"\"\r\n";
260 // empty line to say that it s the end of the header
261 formatedBody += "\r\n";
263 static const size_t src_buf_size = 45;// This *MUST* be a multiple of 3
264 static const size_t dst_buf_size = 4 * ((src_buf_size + 2) / 3);
265 size_t write_size = dst_buf_size;
266 char src_buf[src_buf_size + 1];
267 char dst_buf[dst_buf_size + 1];
268 size_t size;
270 FILE *src_stream = nlfopen (attachedFile, "rb");
271 if (src_stream == NULL)
273 nlwarning ("EMAIL: Can't attach file '%s' to the email because the file can't be open", attachedFile.c_str());
275 else
277 while ((size = fread(src_buf, 1, src_buf_size, src_stream)) > 0)
279 if (size != src_buf_size)
281 /* write_size is always 60 until the last line */
282 write_size=(4 * ((size + 2) / 3));
283 /* pad with 0s so we can just encode extra bits */
284 memset(&src_buf[size], 0, src_buf_size - size);
286 /* Encode the buffer we just read in */
287 uuencode(src_buf, dst_buf, (int)size);
289 formatedBody += dst_buf;
290 formatedBody += "\r\n";
292 fclose (src_stream);
294 formatedBody += "--Multipart_nel--";
297 // debug, display what we send into a file
298 // { FILE *fp = nlfopen (CFile::findNewFile(getLogDirectory() + "mail.txt"), "wb");
299 // fwrite (formatedBody.c_str(), 1, formatedBody.size(), fp);
300 // fclose (fp); }
302 if(!sendEMailCommand (sock, "", 220)) goto end;
304 if(onlyCheck)
306 if(!sendEMailCommand (sock, "HELO localhost")) goto end;
307 if(!sendEMailCommand (sock, "MAIL FROM: " + formatedFrom)) goto end;
308 if(!sendEMailCommand (sock, "RCPT TO: " + formatedTo)) goto end;
309 if(!sendEMailCommand (sock, "QUIT", 221)) goto end;
311 ok = true;
313 else
315 if(!sendEMailCommand (sock, "HELO localhost")) goto end;
316 if(!sendEMailCommand (sock, "MAIL FROM: " + formatedFrom)) goto end;
317 if(!sendEMailCommand (sock, "RCPT TO: " + formatedTo)) goto end;
318 if(!sendEMailCommand (sock, "DATA", 354)) goto end;
320 string buffer =
321 "From: " + formatedFrom + "\r\n"
322 "To: " + formatedTo + "\r\n"
323 "Subject: " + subject + "\r\n"
324 + formatedBody + "\r\n.";
326 if(!sendEMailCommand (sock, buffer)) goto end;
327 if(!sendEMailCommand (sock, "QUIT", 221)) goto end;
329 ok = true;
332 catch (const Exception &e)
334 nlwarning ("EMAIL: Can't send email: %s", e.what());
335 goto end;
338 end:
339 if (sock.connected())
340 sock.close ();
342 return ok;
345 void setDefaultEmailParams (const std::string &smtpServer, const std::string &from, const std::string &to)
347 DefaultSMTPServer = smtpServer;
348 DefaultFrom = from;
349 DefaultTo = to;
352 } // NLNET