llmail.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:10k
源码类别:

游戏引擎

开发平台:

C++ Builder

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