PageRenderTime 36ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/indra/llmessage/llmail.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 401 lines | 300 code | 38 blank | 63 comment | 36 complexity | 66e31ba739f2254912f003fac9a301ee MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llmail.cpp
  3. * @brief smtp helper functions.
  4. *
  5. * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "linden_common.h"
  27. #include "llmail.h"
  28. // APR on Windows needs full windows headers
  29. #ifdef LL_WINDOWS
  30. # undef WIN32_LEAN_AND_MEAN
  31. # include <winsock2.h>
  32. # include <windows.h>
  33. #endif
  34. #include <string>
  35. #include <sstream>
  36. #include "apr_pools.h"
  37. #include "apr_network_io.h"
  38. #include "llapr.h"
  39. #include "llbase32.h" // IM-to-email address
  40. #include "llblowfishcipher.h"
  41. #include "llerror.h"
  42. #include "llhost.h"
  43. #include "llsd.h"
  44. #include "llstring.h"
  45. #include "lluuid.h"
  46. #include "net.h"
  47. //
  48. // constants
  49. //
  50. const size_t LL_MAX_KNOWN_GOOD_MAIL_SIZE = 4096;
  51. static bool gMailEnabled = true;
  52. static apr_pool_t* gMailPool;
  53. static apr_sockaddr_t* gSockAddr;
  54. static apr_socket_t* gMailSocket;
  55. bool connect_smtp();
  56. void disconnect_smtp();
  57. //#if LL_WINDOWS
  58. //SOCKADDR_IN gMailDstAddr, gMailSrcAddr, gMailLclAddr;
  59. //#else
  60. //struct sockaddr_in gMailDstAddr, gMailSrcAddr, gMailLclAddr;
  61. //#endif
  62. // Define this for a super-spammy mail mode.
  63. //#define LL_LOG_ENTIRE_MAIL_MESSAGE_ON_SEND 1
  64. bool connect_smtp()
  65. {
  66. // Prepare an soket to talk smtp
  67. apr_status_t status;
  68. status = apr_socket_create(
  69. &gMailSocket,
  70. gSockAddr->sa.sin.sin_family,
  71. SOCK_STREAM,
  72. APR_PROTO_TCP,
  73. gMailPool);
  74. if(ll_apr_warn_status(status)) return false;
  75. status = apr_socket_connect(gMailSocket, gSockAddr);
  76. if(ll_apr_warn_status(status))
  77. {
  78. status = apr_socket_close(gMailSocket);
  79. ll_apr_warn_status(status);
  80. return false;
  81. }
  82. return true;
  83. }
  84. void disconnect_smtp()
  85. {
  86. if(gMailSocket)
  87. {
  88. apr_status_t status = apr_socket_close(gMailSocket);
  89. ll_apr_warn_status(status);
  90. gMailSocket = NULL;
  91. }
  92. }
  93. // Returns TRUE on success.
  94. // message should NOT be SMTP escaped.
  95. // static
  96. BOOL LLMail::send(
  97. const char* from_name,
  98. const char* from_address,
  99. const char* to_name,
  100. const char* to_address,
  101. const char* subject,
  102. const char* message,
  103. const LLSD& headers)
  104. {
  105. std::string header = buildSMTPTransaction(
  106. from_name,
  107. from_address,
  108. to_name,
  109. to_address,
  110. subject,
  111. headers);
  112. if(header.empty())
  113. {
  114. return FALSE;
  115. }
  116. std::string message_str;
  117. if(message)
  118. {
  119. message_str = message;
  120. }
  121. bool rv = send(header, message_str, to_address, from_address);
  122. if(rv) return TRUE;
  123. return FALSE;
  124. }
  125. // static
  126. void LLMail::init(const std::string& hostname, apr_pool_t* pool)
  127. {
  128. gMailSocket = NULL;
  129. if(hostname.empty() || !pool)
  130. {
  131. gMailPool = NULL;
  132. gSockAddr = NULL;
  133. }
  134. else
  135. {
  136. gMailPool = pool;
  137. // collect all the information into a socaddr sturcture. the
  138. // documentation is a bit unclear, but I either have to
  139. // specify APR_UNSPEC or not specify any flags. I am not sure
  140. // which option is better.
  141. apr_status_t status = apr_sockaddr_info_get(
  142. &gSockAddr,
  143. hostname.c_str(),
  144. APR_UNSPEC,
  145. 25,
  146. APR_IPV4_ADDR_OK,
  147. gMailPool);
  148. ll_apr_warn_status(status);
  149. }
  150. }
  151. // static
  152. void LLMail::enable(bool mail_enabled)
  153. {
  154. gMailEnabled = mail_enabled;
  155. }
  156. // Test a subject line for RFC2822 compliance.
  157. static bool valid_subject_chars(const char *subject)
  158. {
  159. for (; *subject != '\0'; subject++)
  160. {
  161. unsigned char c = *subject;
  162. if (c == '\xa' || c == '\xd' || c > '\x7f')
  163. {
  164. return false;
  165. }
  166. }
  167. return true;
  168. }
  169. // static
  170. std::string LLMail::buildSMTPTransaction(
  171. const char* from_name,
  172. const char* from_address,
  173. const char* to_name,
  174. const char* to_address,
  175. const char* subject,
  176. const LLSD& headers)
  177. {
  178. if(!from_address || !to_address)
  179. {
  180. llinfos << "send_mail build_smtp_transaction reject: missing to and/or"
  181. << " from address." << llendl;
  182. return std::string();
  183. }
  184. if(!valid_subject_chars(subject))
  185. {
  186. llinfos << "send_mail build_smtp_transaction reject: bad subject header: "
  187. << "to=<" << to_address
  188. << ">, from=<" << from_address << ">"
  189. << llendl;
  190. return std::string();
  191. }
  192. std::ostringstream from_fmt;
  193. if(from_name && from_name[0])
  194. {
  195. // "My Name" <myaddress@example.com>
  196. from_fmt << "\"" << from_name << "\" <" << from_address << ">";
  197. }
  198. else
  199. {
  200. // <myaddress@example.com>
  201. from_fmt << "<" << from_address << ">";
  202. }
  203. std::ostringstream to_fmt;
  204. if(to_name && to_name[0])
  205. {
  206. to_fmt << "\"" << to_name << "\" <" << to_address << ">";
  207. }
  208. else
  209. {
  210. to_fmt << "<" << to_address << ">";
  211. }
  212. std::ostringstream header;
  213. header
  214. << "HELO lindenlab.com\r\n"
  215. << "MAIL FROM:<" << from_address << ">\r\n"
  216. << "RCPT TO:<" << to_address << ">\r\n"
  217. << "DATA\r\n"
  218. << "From: " << from_fmt.str() << "\r\n"
  219. << "To: " << to_fmt.str() << "\r\n"
  220. << "Subject: " << subject << "\r\n";
  221. if(headers.isMap())
  222. {
  223. LLSD::map_const_iterator iter = headers.beginMap();
  224. LLSD::map_const_iterator end = headers.endMap();
  225. for(; iter != end; ++iter)
  226. {
  227. header << (*iter).first << ": " << ((*iter).second).asString()
  228. << "\r\n";
  229. }
  230. }
  231. header << "\r\n";
  232. return header.str();
  233. }
  234. // static
  235. bool LLMail::send(
  236. const std::string& header,
  237. const std::string& raw_message,
  238. const char* from_address,
  239. const char* to_address)
  240. {
  241. if(!from_address || !to_address)
  242. {
  243. llinfos << "send_mail reject: missing to and/or from address."
  244. << llendl;
  245. return false;
  246. }
  247. // remove any "." SMTP commands to prevent injection (DEV-35777)
  248. // we don't need to worry about "\r\n.\r\n" because of the
  249. // "\n" --> "\n\n" conversion going into rfc2822_msg below
  250. std::string message = raw_message;
  251. std::string bad_string = "\n.\n";
  252. std::string good_string = "\n..\n";
  253. while (1)
  254. {
  255. int index = message.find(bad_string);
  256. if (index == std::string::npos) break;
  257. message.replace(index, bad_string.size(), good_string);
  258. }
  259. // convert all "\n" into "\r\n"
  260. std::ostringstream rfc2822_msg;
  261. for(U32 i = 0; i < message.size(); ++i)
  262. {
  263. switch(message[i])
  264. {
  265. case '\0':
  266. break;
  267. case '\n':
  268. // *NOTE: this is kinda busted if we're fed \r\n
  269. rfc2822_msg << "\r\n";
  270. break;
  271. default:
  272. rfc2822_msg << message[i];
  273. break;
  274. }
  275. }
  276. if(!gMailEnabled)
  277. {
  278. llinfos << "send_mail reject: mail system is disabled: to=<"
  279. << to_address << ">, from=<" << from_address
  280. << ">" << llendl;
  281. // Any future interface to SMTP should return this as an
  282. // error. --mark
  283. return true;
  284. }
  285. if(!gSockAddr)
  286. {
  287. llwarns << "send_mail reject: mail system not initialized: to=<"
  288. << to_address << ">, from=<" << from_address
  289. << ">" << llendl;
  290. return false;
  291. }
  292. if(!connect_smtp())
  293. {
  294. llwarns << "send_mail reject: SMTP connect failure: to=<"
  295. << to_address << ">, from=<" << from_address
  296. << ">" << llendl;
  297. return false;
  298. }
  299. std::ostringstream smtp_fmt;
  300. smtp_fmt << header << rfc2822_msg.str() << "\r\n" << ".\r\n" << "QUIT\r\n";
  301. std::string smtp_transaction = smtp_fmt.str();
  302. size_t original_size = smtp_transaction.size();
  303. apr_size_t send_size = original_size;
  304. apr_status_t status = apr_socket_send(
  305. gMailSocket,
  306. smtp_transaction.c_str(),
  307. (apr_size_t*)&send_size);
  308. disconnect_smtp();
  309. if(ll_apr_warn_status(status))
  310. {
  311. llwarns << "send_mail socket failure: unable to write "
  312. << "to=<" << to_address
  313. << ">, from=<" << from_address << ">"
  314. << ", bytes=" << original_size
  315. << ", sent=" << send_size << llendl;
  316. return false;
  317. }
  318. if(send_size >= LL_MAX_KNOWN_GOOD_MAIL_SIZE)
  319. {
  320. llwarns << "send_mail message has been shown to fail in testing "
  321. << "when sending messages larger than " << LL_MAX_KNOWN_GOOD_MAIL_SIZE
  322. << " bytes. The next log about success is potentially a lie." << llendl;
  323. }
  324. lldebugs << "send_mail success: "
  325. << "to=<" << to_address
  326. << ">, from=<" << from_address << ">"
  327. << ", bytes=" << original_size
  328. << ", sent=" << send_size << llendl;
  329. #if LL_LOG_ENTIRE_MAIL_MESSAGE_ON_SEND
  330. llinfos << rfc2822_msg.str() << llendl;
  331. #endif
  332. return true;
  333. }
  334. // static
  335. std::string LLMail::encryptIMEmailAddress(const LLUUID& from_agent_id,
  336. const LLUUID& to_agent_id,
  337. U32 time,
  338. const U8* secret,
  339. size_t secret_size)
  340. {
  341. #if LL_WINDOWS
  342. return "blowfish-not-supported-on-windows";
  343. #else
  344. size_t data_size = 4 + UUID_BYTES + UUID_BYTES;
  345. // Convert input data into a binary blob
  346. std::vector<U8> data;
  347. data.resize(data_size);
  348. // *NOTE: This may suffer from endian issues. Could be htonmemcpy.
  349. memcpy(&data[0], &time, 4);
  350. memcpy(&data[4], &from_agent_id.mData[0], UUID_BYTES);
  351. memcpy(&data[4 + UUID_BYTES], &to_agent_id.mData[0], UUID_BYTES);
  352. // Encrypt the blob
  353. LLBlowfishCipher cipher(secret, secret_size);
  354. size_t encrypted_size = cipher.requiredEncryptionSpace(data.size());
  355. U8* encrypted = new U8[encrypted_size];
  356. cipher.encrypt(&data[0], data_size, encrypted, encrypted_size);
  357. std::string address = LLBase32::encode(encrypted, encrypted_size);
  358. // Make it more pretty for humans.
  359. LLStringUtil::toLower(address);
  360. delete [] encrypted;
  361. return address;
  362. #endif
  363. }