00001
00032 #include "linden_common.h"
00033
00034 #include "llmail.h"
00035
00036
00037 #ifdef LL_WINDOWS
00038 # undef WIN32_LEAN_AND_MEAN
00039 # include <winsock2.h>
00040 # include <windows.h>
00041 #endif
00042
00043 #include <string>
00044 #include <sstream>
00045 #include <boost/regex.hpp>
00046
00047 #include "apr-1/apr_pools.h"
00048 #include "apr-1/apr_network_io.h"
00049
00050 #include "llapr.h"
00051 #include "llbase32.h"
00052 #include "llblowfishcipher.h"
00053 #include "llerror.h"
00054 #include "llhost.h"
00055 #include "llstring.h"
00056 #include "lluuid.h"
00057 #include "net.h"
00058
00059
00060
00061
00062 const size_t LL_MAX_KNOWN_GOOD_MAIL_SIZE = 4096;
00063
00064 static bool gMailEnabled = true;
00065 static apr_pool_t* gMailPool;
00066 static apr_sockaddr_t* gSockAddr;
00067 static apr_socket_t* gMailSocket;
00068
00069
00070 static const boost::regex valid_subject_chars("[\\x1-\\x9\\xb\\xc\\xe-\\x7f]*");
00071 bool connect_smtp();
00072 void disconnect_smtp();
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083 bool connect_smtp()
00084 {
00085
00086 apr_status_t status;
00087 status = apr_socket_create(
00088 &gMailSocket,
00089 gSockAddr->sa.sin.sin_family,
00090 SOCK_STREAM,
00091 APR_PROTO_TCP,
00092 gMailPool);
00093 if(ll_apr_warn_status(status)) return false;
00094 status = apr_socket_connect(gMailSocket, gSockAddr);
00095 if(ll_apr_warn_status(status))
00096 {
00097 status = apr_socket_close(gMailSocket);
00098 ll_apr_warn_status(status);
00099 return false;
00100 }
00101 return true;
00102 }
00103
00104 void disconnect_smtp()
00105 {
00106 if(gMailSocket)
00107 {
00108 apr_status_t status = apr_socket_close(gMailSocket);
00109 ll_apr_warn_status(status);
00110 gMailSocket = NULL;
00111 }
00112 }
00113
00114
00115
00116
00117 BOOL LLMail::send(const char* from_name, const char* from_address,
00118 const char* to_name, const char* to_address,
00119 const char* subject, const char* message)
00120 {
00121 std::string header = buildSMTPTransaction(
00122 from_name,
00123 from_address,
00124 to_name,
00125 to_address,
00126 subject);
00127 if(header.empty())
00128 {
00129 return FALSE;
00130 }
00131
00132 std::string message_str;
00133 if(message)
00134 {
00135 message_str = message;
00136 }
00137 bool rv = send(header, message_str, to_address, from_address);
00138 if(rv) return TRUE;
00139 return FALSE;
00140 }
00141
00142
00143 void LLMail::init(const std::string& hostname, apr_pool_t* pool)
00144 {
00145 gMailSocket = NULL;
00146 if(hostname.empty() || !pool)
00147 {
00148 gMailPool = NULL;
00149 gSockAddr = NULL;
00150 }
00151 else
00152 {
00153 gMailPool = pool;
00154
00155
00156
00157
00158
00159 apr_status_t status = apr_sockaddr_info_get(
00160 &gSockAddr,
00161 hostname.c_str(),
00162 APR_UNSPEC,
00163 25,
00164 APR_IPV4_ADDR_OK,
00165 gMailPool);
00166 ll_apr_warn_status(status);
00167 }
00168 }
00169
00170
00171 void LLMail::enable(bool mail_enabled)
00172 {
00173 gMailEnabled = mail_enabled;
00174 }
00175
00176
00177 std::string LLMail::buildSMTPTransaction(
00178 const char* from_name,
00179 const char* from_address,
00180 const char* to_name,
00181 const char* to_address,
00182 const char* subject)
00183 {
00184 if(!from_address || !to_address)
00185 {
00186 llinfos << "send_mail build_smtp_transaction reject: missing to and/or"
00187 << " from address." << llendl;
00188 return std::string();
00189 }
00190 if(! boost::regex_match(subject, valid_subject_chars))
00191 {
00192 llinfos << "send_mail build_smtp_transaction reject: bad subject header: "
00193 << "to=<" << to_address
00194 << ">, from=<" << from_address << ">"
00195 << llendl;
00196 return std::string();
00197 }
00198 std::ostringstream from_fmt;
00199 if(from_name && from_name[0])
00200 {
00201
00202 from_fmt << "\"" << from_name << "\" <" << from_address << ">";
00203 }
00204 else
00205 {
00206
00207 from_fmt << "<" << from_address << ">";
00208 }
00209 std::ostringstream to_fmt;
00210 if(to_name && to_name[0])
00211 {
00212 to_fmt << "\"" << to_name << "\" <" << to_address << ">";
00213 }
00214 else
00215 {
00216 to_fmt << "<" << to_address << ">";
00217 }
00218 std::ostringstream header;
00219 header
00220 << "HELO lindenlab.com\r\n"
00221 << "MAIL FROM:<" << from_address << ">\r\n"
00222 << "RCPT TO:<" << to_address << ">\r\n"
00223 << "DATA\r\n"
00224 << "From: " << from_fmt.str() << "\r\n"
00225 << "To: " << to_fmt.str() << "\r\n"
00226 << "Subject: " << subject << "\r\n"
00227 << "\r\n";
00228 return header.str();
00229 }
00230
00231
00232 bool LLMail::send(
00233 const std::string& header,
00234 const std::string& message,
00235 const char* from_address,
00236 const char* to_address)
00237 {
00238 if(!from_address || !to_address)
00239 {
00240 llinfos << "send_mail reject: missing to and/or from address."
00241 << llendl;
00242 return false;
00243 }
00244
00245
00246
00247 std::ostringstream rfc2822_msg;
00248 for(U32 i = 0; i < message.size(); ++i)
00249 {
00250 switch(message[i])
00251 {
00252 case '\0':
00253 break;
00254 case '\n':
00255
00256 rfc2822_msg << "\r\n";
00257 break;
00258 default:
00259 rfc2822_msg << message[i];
00260 break;
00261 }
00262 }
00263
00264 if(!gMailEnabled)
00265 {
00266 llinfos << "send_mail reject: mail system is disabled: to=<"
00267 << to_address << ">, from=<" << from_address
00268 << ">" << llendl;
00269
00270
00271 return true;
00272 }
00273 if(!gSockAddr)
00274 {
00275 llwarns << "send_mail reject: mail system not initialized: to=<"
00276 << to_address << ">, from=<" << from_address
00277 << ">" << llendl;
00278 return false;
00279 }
00280
00281 if(!connect_smtp())
00282 {
00283 llwarns << "send_mail reject: SMTP connect failure: to=<"
00284 << to_address << ">, from=<" << from_address
00285 << ">" << llendl;
00286 return false;
00287 }
00288
00289 std::ostringstream smtp_fmt;
00290 smtp_fmt << header << rfc2822_msg.str() << "\r\n" << ".\r\n" << "QUIT\r\n";
00291 std::string smtp_transaction = smtp_fmt.str();
00292 size_t original_size = smtp_transaction.size();
00293 apr_size_t send_size = original_size;
00294 apr_status_t status = apr_socket_send(
00295 gMailSocket,
00296 smtp_transaction.c_str(),
00297 (apr_size_t*)&send_size);
00298 disconnect_smtp();
00299 if(ll_apr_warn_status(status))
00300 {
00301 llwarns << "send_mail socket failure: unable to write "
00302 << "to=<" << to_address
00303 << ">, from=<" << from_address << ">"
00304 << ", bytes=" << original_size
00305 << ", sent=" << send_size << llendl;
00306 return false;
00307 }
00308 if(send_size >= LL_MAX_KNOWN_GOOD_MAIL_SIZE)
00309 {
00310 llwarns << "send_mail message has been shown to fail in testing "
00311 << "when sending messages larger than " << LL_MAX_KNOWN_GOOD_MAIL_SIZE
00312 << " bytes. The next log about success is potentially a lie." << llendl;
00313 }
00314 llinfos << "send_mail success: "
00315 << "to=<" << to_address
00316 << ">, from=<" << from_address << ">"
00317 << ", bytes=" << original_size
00318 << ", sent=" << send_size << llendl;
00319
00320 #if LL_LOG_ENTIRE_MAIL_MESSAGE_ON_SEND
00321 llinfos << rfc2822_msg.str() << llendl;
00322 #endif
00323 return true;
00324 }
00325
00326
00327
00328 std::string LLMail::encryptIMEmailAddress(const LLUUID& from_agent_id,
00329 const LLUUID& to_agent_id,
00330 U32 time,
00331 const U8* secret,
00332 size_t secret_size)
00333 {
00334 #if LL_WINDOWS
00335 return "blowfish-not-supported-on-windows";
00336 #else
00337 size_t data_size = 4 + UUID_BYTES + UUID_BYTES;
00338
00339 std::vector<U8> data;
00340 data.resize(data_size);
00341
00342 memcpy(&data[0], &time, 4);
00343 memcpy(&data[4], &from_agent_id.mData[0], UUID_BYTES);
00344 memcpy(&data[4 + UUID_BYTES], &to_agent_id.mData[0], UUID_BYTES);
00345
00346
00347 LLBlowfishCipher cipher(secret, secret_size);
00348 size_t encrypted_size = cipher.requiredEncryptionSpace(data.size());
00349 U8* encrypted = new U8[encrypted_size];
00350 cipher.encrypt(&data[0], data_size, encrypted, encrypted_size);
00351
00352 std::string address = LLBase32::encode(encrypted, encrypted_size);
00353
00354
00355 LLString::toLower(address);
00356
00357 delete [] encrypted;
00358
00359 return address;
00360 #endif
00361 }