llmail.cpp

Go to the documentation of this file.
00001 
00032 #include "linden_common.h"
00033 
00034 #include "llmail.h"
00035 
00036 // APR on Windows needs full windows headers
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"   // IM-to-email address
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 // constants
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 // According to RFC2822
00070 static const boost::regex valid_subject_chars("[\\x1-\\x9\\xb\\xc\\xe-\\x7f]*");
00071 bool connect_smtp();
00072 void disconnect_smtp();
00073  
00074 //#if LL_WINDOWS
00075 //SOCKADDR_IN gMailDstAddr, gMailSrcAddr, gMailLclAddr;
00076 //#else
00077 //struct sockaddr_in gMailDstAddr, gMailSrcAddr, gMailLclAddr;
00078 //#endif
00079 
00080 // Define this for a super-spammy mail mode.
00081 //#define LL_LOG_ENTIRE_MAIL_MESSAGE_ON_SEND 1
00082 
00083 bool connect_smtp()
00084 {
00085         // Prepare an soket to talk smtp
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 // Returns TRUE on success.
00115 // message should NOT be SMTP escaped.
00116 // static
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 // static
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                 // collect all the information into a socaddr sturcture. the
00156                 // documentation is a bit unclear, but I either have to
00157                 // specify APR_UNSPEC or not specify any flags. I am not sure
00158                 // which option is better.
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 // static
00171 void LLMail::enable(bool mail_enabled)
00172 {
00173         gMailEnabled = mail_enabled;
00174 }
00175 
00176 // static
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                 // "My Name" <myaddress@example.com>
00202                 from_fmt << "\"" << from_name << "\" <" << from_address << ">";
00203         }
00204         else
00205         {
00206                 // <myaddress@example.com>
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 // static
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         // *FIX: this translation doesn't deal with a single period on a
00246         // line by itself.
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                         // *NOTE: this is kinda busted if we're fed \r\n
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                 // Any future interface to SMTP should return this as an
00270                 // error.  --mark
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 // static
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         // Convert input data into a binary blob
00339         std::vector<U8> data;
00340         data.resize(data_size);
00341         // *NOTE: This may suffer from endian issues.  Could be htonmemcpy.
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         // Encrypt the blob
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         // Make it more pretty for humans.
00355         LLString::toLower(address);
00356 
00357         delete [] encrypted;
00358 
00359         return address;
00360 #endif
00361 }

Generated on Thu Jul 1 06:08:49 2010 for Second Life Viewer by  doxygen 1.4.7