www.pudn.com > TestSMailer.zip > SMailer.cpp


//////////////////////////////////////////////////////////////////////////////// 
// Simple Mail Sender (Implementation) 
// 
// Copyright (c) 2003 by Morning 
// http://morningspace.51.net 
// mailto:moyingzz@etang.com 
// 
// Permission to use, copy, modify, distribute and sell this program for any  
// purpose is hereby granted without fee, provided that the above copyright  
// notice appear in all copies and that both that copyright notice and this  
// permission notice appear in supporting documentation. 
// 
// It is provided "as is" without express or implied warranty. 
//////////////////////////////////////////////////////////////////////////////// 
 
#include "stdafx.h" 
#include  
#include  
#include  
#include "SMailer.h" 
#include "../MUtils/Base64Helper.h" 
#include "../MUtils/FileHelper.h" 
// 
namespace SMailer { 
 
// constants defination 
///////////////////////////////////// 
 
enum {MAX_BUFFER_SIZE = 255}; 
 
const std::string Priority::important = "1"; 
const std::string Priority::normal    = "3"; 
const std::string Priority::trivial   = "5"; 
 
const std::string MailWrapper::_mailer_name = "SMailer"; 
const std::string MailWrapper::_boundary    = "#BOUNDARY#"; 
 
// Member functions of class MimeContent 
///////////////////////////////////// 
 
MimeContent::MimeContent(const std::string content) : _content(content) 
{ 
} 
 
std::string MimeContent::getDisposition() const 
{ 
    return ""; 
} 
 
// Member functions of class PlainTextContent 
///////////////////////////////////// 
 
TextPlainContent::TextPlainContent(const std::string content,  
                                   const std::string charset) 
    : MimeContent(content), _charset(charset) 
{ 
} 
 
std::string TextPlainContent::getType() const 
{ 
    return "text/plain; charset=" + _charset; 
} 
 
std::string TextPlainContent::getTransEncoding() const 
{ 
    return "8bit"; 
} 
 
std::string& TextPlainContent::getContent() 
{ 
    // you can add more codes here, such as wrapping lines  
    // or replacing '\n' with '\r\n', etc. 
    return _content; 
} 
 
// Member functions of class TextHtmlContent 
///////////////////////////////////// 
 
TextHtmlContent::TextHtmlContent(const std::string content,  
                                 const std::string charset) 
    : MimeContent(content), _charset(charset) 
{ 
} 
 
std::string TextHtmlContent::getType() const 
{ 
    return "text/html; charset=" + _charset; 
} 
 
std::string TextHtmlContent::getTransEncoding() const 
{ 
    return "8bit"; 
} 
 
std::string& TextHtmlContent::getContent() 
{ 
    // you can add more codes here, such as wrapping lines  
    // or replacing '\n' with '\r\n', etc. 
    return _content; 
} 
 
// Member functions of class AppOctStrmContent 
///////////////////////////////////// 
 
AppOctStrmContent::AppOctStrmContent(const std::string file_name) 
    : _file_name(file_name) 
{ 
    char drive[_MAX_DRIVE]; 
    char direc[_MAX_DIR]; 
    char fname[_MAX_FNAME]; 
    char ext[_MAX_EXT]; 
    _splitpath(file_name.c_str(), drive, direc, fname, ext); 
    _name  = fname; 
    _name += ext; 
} 
 
std::string AppOctStrmContent::getType() const 
{ 
    return "application/octet-stream; name=" + _name; 
} 
 
std::string AppOctStrmContent::getDisposition() const 
{ 
    return "attachment; filename=" + _name; 
} 
 
std::string AppOctStrmContent::getTransEncoding() const 
{ 
    return "base64"; 
} 
 
std::string& AppOctStrmContent::getContent() 
{ 
    // you can add more codes here, such as wrapping lines  
    // or replacing '\n' with '\r\n', etc. 
    MUtils::FileHelper::open(_file_name, _content); 
    _content = MUtils::Base64Helper::encode(_content); 
    return _content; 
} 
 
// Member functions of class MailInfo 
///////////////////////////////////// 
 
MailInfo::MailInfo() : _priority(Priority::normal) 
{ 
} 
 
void MailInfo::setSenderName(const std::string name) 
{ 
    _sender_name = name; 
} 
 
void MailInfo::setSenderAddress(const std::string address) 
{ 
    _sender_address = address; 
} 
 
std::string MailInfo::getSenderName() const 
{ 
    return _sender_name; 
} 
 
std::string MailInfo::getSenderAddress() const 
{ 
    return _sender_address; 
} 
 
void MailInfo::addReceiver(const std::string name, const std::string address) 
{ 
    _receivers.insert(Receivers::value_type(name, address)); 
} 
 
void MailInfo::setReceiver(const std::string name, const std::string address) 
{ 
    _receivers.clear(); 
    _receivers.insert(Receivers::value_type(name, address)); 
} 
 
const Receivers& MailInfo::getReceivers() const 
{ 
    return _receivers; 
} 
 
void MailInfo::setPriority(std::string priority) 
{ 
    _priority = priority; 
} 
 
std::string MailInfo::getPriority() const 
{ 
    return _priority; 
} 
 
void MailInfo::setSubject(const std::string subject) 
{ 
    _subject = subject; 
} 
 
std::string MailInfo::getSubject() const 
{ 
    return _subject; 
} 
 
void MailInfo::addMimeContent(MimeContent* content) 
{ 
    _contents.push_back(content); 
} 
 
void MailInfo::clearMimeContents() 
{ 
    _contents.clear(); 
} 
 
const MimeContents& MailInfo::getMimeContents() const 
{ 
    return _contents; 
} 
 
// Member functions of class MailWrapper 
///////////////////////////////////// 
 
MailWrapper::MailWrapper(MailInfo* mail_info) : _mail_info(mail_info) 
{ 
} 
 
std::string MailWrapper::getSenderAddress() 
{ 
    std::string address; 
 
    address  = "<"; 
    address += _mail_info->getSenderAddress(); 
    address += ">"; 
 
    return address; 
} 
 
std::string MailWrapper::getHeader() 
{ 
    std::string header; 
 
    header  = "From: "; 
    header += prepareFrom() + "\r\n"; 
 
    header += "To: "; 
    header += prepareTo() + "\r\n"; 
 
    header += "Date: "; 
    header += prepareDate() + "\r\n"; 
 
    header += "Subject: "; 
    header += _mail_info->getSubject() + "\r\n"; 
 
    header += "X-Mailer: "; 
    header += _mailer_name + "\r\n"; 
 
    header += "X-Priority: "; 
    header += _mail_info->getPriority() + "\r\n"; 
 
    header += "MIME-Version: 1.0\r\n"; 
    header += "Content-type: multipart/mixed; boundary=\""; 
    header += _boundary + "\"\r\n"; 
 
    header += "\r\n"; 
 
    return header; 
} 
 
void MailWrapper::traverseReceiver() 
{ 
    _rcv_itr = _mail_info->getReceivers().begin(); 
} 
 
bool MailWrapper::hasMoreReceiver() 
{ 
    return ( _rcv_itr != _mail_info->getReceivers().end() ); 
} 
 
std::string MailWrapper::nextReceiverAddress() 
{ 
    std::string address; 
 
    address  = "<"; 
    address += (_rcv_itr++)->second; 
    address += ">"; 
 
    return address; 
} 
 
void MailWrapper::traverseContent() 
{ 
    _con_itr = _mail_info->getMimeContents().begin(); 
} 
 
bool MailWrapper::hasMoreContent() 
{ 
    return ( _con_itr != _mail_info->getMimeContents().end() ); 
} 
 
std::string& MailWrapper::nextContent() 
{ 
    _content  = "--" + _boundary + "\r\n"; 
 
    _content += "Content-Type: "; 
    _content += (*_con_itr)->getType() + "\r\n"; 
 
    std::string disposition = (*_con_itr)->getDisposition(); 
    if ( !disposition.empty() ) 
    { 
        _content += "Content-Disposition: ";; 
        _content += disposition + "\r\n"; 
    } 
 
    _content += "Content-Transfer-Encoding: "; 
    _content += (*_con_itr)->getTransEncoding() + "\r\n\r\n"; 
 
    _content += (*_con_itr)->getContent() + "\r\n\r\n"; 
 
    _con_itr++; 
 
    return _content; 
} 
 
std::string MailWrapper::getEnd() 
{ 
    std::string end; 
 
    end += "\r\n--" + _boundary + "--\r\n"; 
 
    end += ".\r\n"; 
 
    return end; 
} 
 
std::string MailWrapper::prepareFrom() 
{ 
    std::string from_string; 
 
    from_string = prepareName(_mail_info->getSenderName()); 
    from_string += getSenderAddress(); 
 
    return from_string; 
} 
 
std::string MailWrapper::prepareTo() 
{ 
    std::string to_string; 
 
    traverseReceiver(); 
    while ( hasMoreReceiver() ) 
    { 
        to_string += prepareName(_rcv_itr->first); 
        to_string += nextReceiverAddress() + ", "; 
    } 
 
    return to_string.substr(0, to_string.length()-2); 
} 
 
std::string MailWrapper::prepareDate() 
{ 
    char date_string[MAX_BUFFER_SIZE]; 
 
    time_t seconds; 
    time(&seconds); 
    strftime(date_string, MAX_BUFFER_SIZE,  
             "%a, %d %b %y %H:%M:%S +0800",  
             localtime(&seconds));          // +0800 maybe hard code 
 
    return date_string; 
} 
 
std::string MailWrapper::prepareName(const std::string raw_name) 
{ 
    std::string decorated_name; 
 
    if (!raw_name.empty()) 
    { 
        decorated_name  = "\""; 
        decorated_name += raw_name; 
        decorated_name += "\" "; 
    } 
 
    return decorated_name; 
} 
 
// Member functions of class MailSender 
///////////////////////////////////// 
 
MailSender::MailSender(const std::string server_name,  
                       const std::string user_name,  
                       const std::string user_pwd) 
    : _server_name(server_name), _user_name(user_name),  
      _user_pwd(user_pwd), _mail(0) 
{ 
    conn(); 
 
    if ( !user_name.empty() ) 
        login(); 
 
    hello(); 
} 
 
MailSender::~MailSender() 
{ 
    quit(); 
} 
 
void MailSender::setMail(MailWrapper* mail) 
{ 
    _mail = mail; 
} 
 
void MailSender::sendMail() 
{ 
    if (!_mail) 
        return; 
 
    sendHeader(); 
    sendContent(); 
    sendEnd(); 
} 
 
void MailSender::conn() 
{ 
    struct hostent* host = gethostbyname(_server_name.c_str()); 
    if (host == 0) 
    { 
        throw MailException("Get server infomation error"); 
    } 
 
    _socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
 
    struct sockaddr_in server_addr; 
    server_addr.sin_family = AF_INET; 
    server_addr.sin_addr.s_addr = *(ULONG *)host->h_addr_list[0]; 
    server_addr.sin_port = htons(SERVICE_PORT); 
 
    connect(_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)); 
    rcvResponse("220"); 
} 
 
void MailSender::hello() 
{ 
    char local_host[MAX_BUFFER_SIZE]; 
    if ( gethostname(local_host, MAX_BUFFER_SIZE) != 0 ) 
    { 
        throw MailException("Get local host name error"); 
    } 
 
    std::string msg; 
 
    msg  = "HELO "; 
    msg += std::string(local_host) + "\r\n"; 
    sendRequest(send_helo_cmd, msg); 
    rcvResponse("250"); 
} 
 
void MailSender::login() 
{ 
    std::string msg; 
 
    msg = "AUTH LOGIN\r\n"; 
    sendRequest(send_auth_cmd, msg); 
    rcvResponse("334"); 
 
    msg = MUtils::Base64Helper::encode(_user_name) + "\r\n"; 
    sendRequest(send_username, msg); 
    rcvResponse("334"); 
 
    msg = MUtils::Base64Helper::encode(_user_pwd) + "\r\n"; 
    sendRequest(send_password, msg); 
    rcvResponse("235"); 
} 
 
void MailSender::sendHeader() 
{ 
    std::string msg; 
 
    msg  = "MAIL FROM: "; 
    msg += _mail->getSenderAddress() + "\r\n"; 
    sendRequest(send_mail_cmd, msg); 
    rcvResponse("250"); 
 
    _mail->traverseReceiver(); 
    while ( _mail->hasMoreReceiver() ) 
    { 
        msg  = "RCPT TO: "; 
        msg += _mail->nextReceiverAddress() + "\r\n"; 
        sendRequest(send_rcpt_cmd, msg); 
        rcvResponse("250"); 
    } 
 
    msg  = "DATA\r\n"; 
    sendRequest(send_data_cmd, msg); 
    rcvResponse("354"); 
 
    sendRequest(send_header, _mail->getHeader()); 
} 
 
void MailSender::sendContent() 
{ 
    _mail->traverseContent(); 
    while ( _mail->hasMoreContent() ) 
    { 
        sendRequest(send_content, _mail->nextContent()); 
    } 
} 
 
void MailSender::sendEnd() 
{ 
    sendRequest(send_end, _mail->getEnd()); 
    rcvResponse("250"); 
} 
 
void MailSender::quit() 
{ 
    sendRequest(send_quit_cmd, "QUIT\r\n"); 
    rcvResponse("221"); 
 
    closesocket(_socket); 
} 
 
void MailSender::rcvResponse(const std::string expected_response) 
{ 
    int recv_bytes = 0; 
    char response_buffer[MAX_BUFFER_SIZE]; 
    if ( (recv_bytes = recv(_socket, response_buffer, MAX_BUFFER_SIZE, 0)) < 0 ) 
    { 
        throw MailException(  
            ErrorMessage::getInstance().response(expected_response) 
        ); 
    } 
 
    std::string response(response_buffer, recv_bytes); 
    std::cout << "[INFO]RECV(" << expected_response << "):"  
              << response << std::endl; 
    if ( response.substr(0, 3) != expected_response ) 
    { 
        throw MailException(  
            ErrorMessage::getInstance().response(expected_response) 
        ); 
    } 
} 
 
void MailSender::sendRequest(Operaion operation,  
                             const std::string content) 
{ 
    std::cout << "[INFO]SEND:" << content << std::endl; 
 
    if ( send(_socket, content.c_str(), content.length(), 0) < 0 ) 
    { 
        throw MailException( 
            ErrorMessage::getInstance().request(operation) 
        ); 
    } 
} 
 
// Member functions of class ErrorMessage 
///////////////////////////////////// 
 
ErrorMessage& ErrorMessage::getInstance() 
{ 
    static ErrorMessage _instance; 
    return _instance; 
} 
 
ErrorMessage::ErrorMessage() 
{ 
    _request_errmsg_map[MailSender::send_helo_cmd] = "Send HELO cmd error"; 
    _request_errmsg_map[MailSender::send_auth_cmd] = "Send AUTH cmd error"; 
    _request_errmsg_map[MailSender::send_username] = "Send user name error"; 
    _request_errmsg_map[MailSender::send_password] = "Send user password error"; 
    _request_errmsg_map[MailSender::send_mail_cmd] = "Send MAIL FROM cmd error"; 
    _request_errmsg_map[MailSender::send_rcpt_cmd] = "Send RCPT TO cmd error"; 
    _request_errmsg_map[MailSender::send_data_cmd] = "Send DATA cmd error"; 
    _request_errmsg_map[MailSender::send_header  ] = "Send mail header error"; 
    _request_errmsg_map[MailSender::send_content ] = "Send mail content error"; 
    _request_errmsg_map[MailSender::send_end     ] = "Send mail end error"; 
    _request_errmsg_map[MailSender::send_quit_cmd] = "Send QUIT cmd error"; 
 
    _respons_errmsg_map["220"] = "Server connect error"; 
    _respons_errmsg_map["250"] = "General server error"; 
    _respons_errmsg_map["334"] = "Server authentication error"; 
    _respons_errmsg_map["235"] = "Password error"; 
    _respons_errmsg_map["354"] = "Server not ready for data"; 
    _respons_errmsg_map["221"] = "Server didn't terminate session"; 
} 
 
std::string& ErrorMessage::request(MailSender::Operaion request_operation) 
{ 
    return _request_errmsg_map[request_operation]; 
} 
 
std::string& ErrorMessage::response(const std::string expected_response) 
{ 
    return _respons_errmsg_map[expected_response]; 
} 
 
} // namespace SMailer