C++基于Smtp协议发送邮件

Stella981
• 阅读 841

SMTP协议的定义:

  •   SMTP 是一种TCP协议支持的提供可靠且有效电子邮件传输的应用层协议;
  •   SMTP 是建立在 TCP上的一种邮件服务,主要用于传输系统之间的邮件信息并提供来信有关的通知;
  •   SMTP 独立于特定的传输子系统,且只需要可靠有序的数据流信道支持;
  •   SMTP 重要特性之一是其能跨越网络传输邮件,即“ SMTP 邮件中继”;
  •   SMTP是一个相对简单的基于文本的协议。

SMTP邮件发送实现步骤

一、连接到邮件服务器

既然SMTP是建立在TCP协议上的,那么只需要采用传统的socket来连接到服务器即可

int SmtpEmail::Connect()
{
    m_socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if (m_socketfd == INVALID_SOCKET)
    {
        m_lastErrorMsg = "Error on creating socket fd.";
        return -1;
    }

    addrinfo inAddrInfo = { 0 };
    inAddrInfo.ai_family = AF_INET;
    inAddrInfo.ai_socktype = SOCK_STREAM;


    if (getaddrinfo(m_host.c_str(), m_port.c_str(), &inAddrInfo, &m_addrinfo) != 0) // error occurs
    {
        m_lastErrorMsg = "Error on calling getadrrinfo().";
        return -2;
    }


    if (connect(m_socketfd, m_addrinfo->ai_addr, m_addrinfo->ai_addrlen))
    {
        m_lastErrorMsg = "Error on calling connect().";
        return -3;
    }
    return 0;
}

二、向服务器发送ehlo指令

    Read(buffer, 999);
    if (strncmp(buffer, "220", 3) != 0) // not equal to 220
    {
        m_lastErrorMsg = buffer;
        return 220;
    }

    //向服务器发送ehlo
    std::string command = "ehlo EmailService\r\n";
    Write(command.c_str(), command.length());

三、进行登录验证

    command = "AUTH PLAIN ";
    std::string auth = '\0' + info.senderEmail + '\0' + info.password;
    command += base64Encode(auth.data(), auth.size());
    command += "\r\n";
    Write(command.c_str(), command.length());

四、设置邮件发送者的邮箱地址

command = "mail FROM:<" + info.senderEmail + ">\r\n";
Write( command.c_str(), command.length());

五、设置邮件接收者的邮箱地址

command = "RCPT TO:<" + info.recipientEmail + ">\r\n";
Write( command.c_str(), command.length());

六、准备发送邮件

command = "data\r\n";
Write( command.c_str(), command.length());

七、组装并发送邮件内容

std::string SimpleSmtpEmail::GetEmailBody(const EmailInfo &info)
{
    //设定邮件的发送者名称、接收者名称、邮件主题,邮件内容。    
    std::ostringstream message;
    message << "From: =?" << info.charset << "?b?" << base64Encode(info.sender.c_str(), info.sender.length()) << "?= <" << info.senderEmail << ">\r\n";

    std::vector<std::string> vecToList;
    for (auto item : info.recvList)
    {
        std::string to = "=?" + info.charset + "?b?" + base64Encode(item.second.c_str(), item.second.length()) + "?= <" + item.first + ">";
        vecToList.push_back(to);
    }

    message << "To: " << join(vecToList, ",") << "\r\n";
    message << "Subject: =?" << info.charset << "?b?" << base64Encode(info.subject.c_str(), info.subject.length()) << "?=\r\n";
    message << "MIME-Version: 1.0\r\n";

    if (info.ccEmail.size() > 0)
    {
        std::vector<std::string> vecCcList;
        for (auto item : info.ccEmail)
        {
            std::string cc = "=?" + info.charset + "?b?" + base64Encode(item.first.c_str(), item.first.length()) + "?= <" + item.second + ">";
            vecCcList.push_back(cc);
        }
        message << "Cc:" << join(vecCcList, ",") << "\r\n";
    }

    message << "Content-Type: " << "text/plain" << "; charset=\"" << info.charset << "\"\r\n";
    message << "Content-Transfer-Encoding: base64\r\n";
    message << "\r\n";
    message << base64Encode(info.message.c_str(), info.message.length());
    message << "\r\n.\r\n";
    return message.str();
}

command = std::move(GetEmailBody(info));
Write( command.c_str(), command.length());

八、结束发送过程

Write( "quit\r\n", 6);

附完整代码,支持ssl

头文件:

#pragma once
#include <string>
#include <vector>
#include <map>

#include <openssl/ossl_typ.h>

#ifndef WIN32
#include<netdb.h>
#endif

class SmtpBase
{
protected:
    struct EmailInfo
    {
        std::string smtpServer;      //the SMTP server
        std::string serverPort;      //the SMTP server port
        std::string charset;         //the email character set
        std::string sender;          //the sender's name
        std::string senderEmail;     //the sender's email
        std::string password;        //the password of sender
        std::string recipient;       //the recipient's name
        std::string recipientEmail;  //the recipient's email

        std::map<std::string, std::string> recvList; //收件人列表<email, name>

        std::string subject;         //the email message's subject
        std::string message;         //the email message body

        std::map<std::string, std::string> ccEmail;         //抄送列表
        std::vector<std::string> attachment; //附件
    };
public:
    
    virtual ~SmtpBase() {}


    virtual int SendEmail(const std::string& from, const std::string& passs, const std::string& to, const std::string& subject, const std::string& strMessage) = 0;

    virtual int SendEmail(const std::string& from, const std::string& passs, const std::vector<std::string>& vecTo,
        const std::string& subject, const std::string& strMessage, const std::vector<std::string>& attachment,const std::vector<std::string>& ccList) = 0;

    std::string GetLastError()
    {
        return m_lastErrorMsg;
    }

    virtual int Read(void* buf, int num) = 0;
    virtual int Write(const void* buf, int num) = 0;
    virtual int Connect() = 0;
    virtual int DisConnect() = 0;
    
protected:

    std::string m_lastErrorMsg;

    
};


class SmtpEmail : public SmtpBase
{

public:
    SmtpEmail(const std::string& emailHost, const std::string& port);
    ~SmtpEmail();
    
    int SendEmail(const std::string& from, const std::string& passs, const std::string& to, const std::string& subject, const std::string& strMessage);

    int SendEmail(const std::string& from, const std::string& passs, const std::vector<std::string>& vecTo,
        const std::string& subject, const std::string& strMessage, const std::vector<std::string>& attachment, const std::vector<std::string>& ccList);
protected:
    int Read(void* buf, int num);
    int Write(const void* buf, int num);
    int Connect();
    int DisConnect();

    virtual std::string GetEmailBody(const EmailInfo & info);
private:
    //int SMTPSSLComunicate(SSL *connection, const EmailInfo &info);
    int SMTPComunicate(const EmailInfo &info);

    

    
protected:
    addrinfo* m_addrinfo;
    int m_socketfd;

    std::string m_host;
    std::string m_port;

    bool m_isConnected;
};

class SimpleSmtpEmail : public SmtpEmail
{
public:
    using SmtpEmail::SmtpEmail;
    virtual std::string GetEmailBody(const EmailInfo & info);
};

class SslSmtpEmail : public SmtpEmail
{
public:
    using SmtpEmail::SmtpEmail;
    ~SslSmtpEmail();

    int Connect();
    int DisConnect();
protected:
    int Read(void* buf, int num);
    int Write(const void* buf, int num);
private:
    SSL_CTX *m_ctx;
    SSL *m_ssl;
};

class SimpleSslSmtpEmail : public SslSmtpEmail
{
public:
    using SslSmtpEmail::SslSmtpEmail;
    virtual std::string GetEmailBody(const EmailInfo & info);
};

实现文件:

#ifdef WIN32
#include <WinSock2.h>
#endif
#include "SSLEmail.h"
#include <fstream>
#include <sstream>
#include <string.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#ifdef WIN32
#include <WinSock2.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#else 
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#define INVALID_SOCKET -1
#endif

template<typename T>
std::string join(T& vecData, const std::string& delim)
{
    if (vecData.size() <= 0)
    {
        return std::string();
    }
    std::stringstream ss;
    for (auto& item : vecData)
    {
        ss << delim  << item ;
    }

    return ss.str().substr(delim.length());
}

const char MimeTypes[][2][128] =
{
    { "***",    "application/octet-stream" },
    { "csv",    "text/csv" },
    { "tsv",    "text/tab-separated-values" },
    { "tab",    "text/tab-separated-values" },
    { "html",    "text/html" },
    { "htm",    "text/html" },
    { "doc",    "application/msword" },
    { "docx",    "application/vnd.openxmlformats-officedocument.wordprocessingml.document" },
    { "ods",    "application/x-vnd.oasis.opendocument.spreadsheet" },
    { "odt",    "application/vnd.oasis.opendocument.text" },
    { "rtf",    "application/rtf" },
    { "sxw",    "application/vnd.sun.xml.writer" },
    { "txt",    "text/plain" },
    { "xls",    "application/vnd.ms-excel" },
    { "xlsx",    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" },
    { "pdf",    "application/pdf" },
    { "ppt",    "application/vnd.ms-powerpoint" },
    { "pps",    "application/vnd.ms-powerpoint" },
    { "pptx",    "application/vnd.openxmlformats-officedocument.presentationml.presentation" },
    { "wmf",    "image/x-wmf" },
    { "atom",    "application/atom+xml" },
    { "xml",    "application/xml" },
    { "json",    "application/json" },
    { "js",    "application/javascript" },
    { "ogg",    "application/ogg" },
    { "ps",    "application/postscript" },
    { "woff",    "application/x-woff" },
    { "xhtml","application/xhtml+xml" },
    { "xht",    "application/xhtml+xml" },
    { "zip",    "application/zip" },
    { "gz",    "application/x-gzip" },
    { "rar",    "application/rar" },
    { "rm",    "application/vnd.rn-realmedia" },
    { "rmvb",    "application/vnd.rn-realmedia-vbr" },
    { "swf",    "application/x-shockwave-flash" },
    { "au",        "audio/basic" },
    { "snd",    "audio/basic" },
    { "mid",    "audio/mid" },
    { "rmi",        "audio/mid" },
    { "mp3",    "audio/mpeg" },
    { "aif",    "audio/x-aiff" },
    { "aifc",    "audio/x-aiff" },
    { "aiff",    "audio/x-aiff" },
    { "m3u",    "audio/x-mpegurl" },
    { "ra",    "audio/vnd.rn-realaudio" },
    { "ram",    "audio/vnd.rn-realaudio" },
    { "wav",    "audio/x-wave" },
    { "wma",    "audio/x-ms-wma" },
    { "m4a",    "audio/x-m4a" },
    { "bmp",    "image/bmp" },
    { "gif",    "image/gif" },
    { "jpe",    "image/jpeg" },
    { "jpeg",    "image/jpeg" },
    { "jpg",    "image/jpeg" },
    { "jfif",    "image/jpeg" },
    { "png",    "image/png" },
    { "svg",    "image/svg+xml" },
    { "tif",    "image/tiff" },
    { "tiff",    "image/tiff" },
    { "ico",    "image/vnd.microsoft.icon" },
    { "css",    "text/css" },
    { "bas",    "text/plain" },
    { "c",        "text/plain" },
    { "h",        "text/plain" },
    { "rtx",    "text/richtext" },
    { "mp2",    "video/mpeg" },
    { "mpa",    "video/mpeg" },
    { "mpe",    "video/mpeg" },
    { "mpeg",    "video/mpeg" },
    { "mpg",    "video/mpeg" },
    { "mpv2",    "video/mpeg" },
    { "mov",    "video/quicktime" },
    { "qt",    "video/quicktime" },
    { "lsf",    "video/x-la-asf" },
    { "lsx",    "video/x-la-asf" },
    { "asf",    "video/x-ms-asf" },
    { "asr",    "video/x-ms-asf" },
    { "asx",    "video/x-ms-asf" },
    { "avi",    "video/x-msvideo" },
    { "3gp",    "video/3gpp" },
    { "3gpp",    "video/3gpp" },
    { "3g2",    "video/3gpp2" },
    { "movie","video/x-sgi-movie" },
    { "mp4",    "video/mp4" },
    { "wmv",    "video/x-ms-wmv" },
    { "webm","video/webm" },
    { "m4v",    "video/x-m4v" },
    { "flv",    "video/x-flv" }
};


std::string fileBasename(const std::string path) 
{
    std::string filename = path.substr(path.find_last_of("/\\") + 1);
    return filename;
}

std::string getFileContents(const char *filename)
{
    std::ifstream in(filename, std::ios::in | std::ios::binary);
    if (in)
    {
        std::string contents;
        in.seekg(0, std::ios::end);
        contents.resize(in.tellg());
        in.seekg(0, std::ios::beg);
        in.read(&contents[0], contents.size());
        in.close();
        return(contents);
    }
    throw(errno);
}

std::string GetFileExtension(const std::string& FileName)
{
    if (FileName.find_last_of(".") != std::string::npos)
        return FileName.substr(FileName.find_last_of(".") + 1);
    return "";
}

const char* GetMimeTypeFromFileName(char* szFileExt)
{
    for (unsigned int i = 0; i < sizeof(MimeTypes) / sizeof(MimeTypes[0]); i++)
    {
        if (strcmp(MimeTypes[i][0], szFileExt) == 0)
        {
            return MimeTypes[i][1];
        }
    }
    return MimeTypes[0][1];   //if does not match any,  "application/octet-stream" is returned
}

char* base64Encode(char const* origSigned, unsigned origLength)
{
    static const char base64Char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    unsigned char const* orig = (unsigned char const*)origSigned; // in case any input bytes have the MSB set
    if (orig == NULL) return NULL;

    unsigned const numOrig24BitValues = origLength / 3;
    bool havePadding = origLength > numOrig24BitValues * 3;
    bool havePadding2 = origLength == numOrig24BitValues * 3 + 2;
    unsigned const numResultBytes = 4 * (numOrig24BitValues + havePadding);
    char* result = new char[numResultBytes + 3]; // allow for trailing '/0'

                                                 // Map each full group of 3 input bytes into 4 output base-64 characters:
    unsigned i;
    for (i = 0; i < numOrig24BitValues; ++i)
    {
        result[4 * i + 0] = base64Char[(orig[3 * i] >> 2) & 0x3F];
        result[4 * i + 1] = base64Char[(((orig[3 * i] & 0x3) << 4) | (orig[3 * i + 1] >> 4)) & 0x3F];
        result[4 * i + 2] = base64Char[((orig[3 * i + 1] << 2) | (orig[3 * i + 2] >> 6)) & 0x3F];
        result[4 * i + 3] = base64Char[orig[3 * i + 2] & 0x3F];
    }

    // Now, take padding into account.  (Note: i == numOrig24BitValues)
    if (havePadding)
    {
        result[4 * i + 0] = base64Char[(orig[3 * i] >> 2) & 0x3F];
        if (havePadding2)
        {
            result[4 * i + 1] = base64Char[(((orig[3 * i] & 0x3) << 4) | (orig[3 * i + 1] >> 4)) & 0x3F];
            result[4 * i + 2] = base64Char[(orig[3 * i + 1] << 2) & 0x3F];
        }
        else
        {
            result[4 * i + 1] = base64Char[((orig[3 * i] & 0x3) << 4) & 0x3F];
            result[4 * i + 2] = '=';
        }
        result[4 * i + 3] = '=';
    }

    result[numResultBytes] = '\0';
    return result;
}

int SmtpEmail::SMTPComunicate(const EmailInfo &info)
{
    if (Connect() != 0)
    {
        return -1;
    }
    char * buffer = new char[1000];
    memset(buffer, 0, 1000);

    Read(buffer, 999);
    if (strncmp(buffer, "220", 3) != 0) // not equal to 220
    {
        m_lastErrorMsg = buffer;
        return 220;
    }

    //向服务器发送ehlo
    std::string command = "ehlo EmailService\r\n";
    Write(command.c_str(), command.length());

    memset(buffer, 0, 1000);
    Read(buffer, 999);
    if (strncmp(buffer, "250", 3) != 0) // ehlo failed
    {
        m_lastErrorMsg = buffer;
        return 250;
    }

    //进行登录验证
    command = "AUTH PLAIN ";
    std::string auth = '\0' + info.senderEmail + '\0' + info.password;
    command += base64Encode(auth.data(), auth.size());
    command += "\r\n";
    Write(command.c_str(), command.length());

    memset(buffer, 0, 1000);
    Read(buffer, 999);
    if (strncmp(buffer, "235", 3) != 0) // login failed
    {
        m_lastErrorMsg = buffer;
        return 250;
    }

    //设置邮件发送者的邮箱地址
    command = "mail FROM:<" + info.senderEmail + ">\r\n";
    Write( command.c_str(), command.length());

    memset(buffer, 0, 1000);
    Read(buffer, 999);
    if (strncmp(buffer, "250", 3) != 0) // not ok
    {
        m_lastErrorMsg = buffer;
        return 250;
    }

    //设置邮件接收者的邮箱地址
    command = "RCPT TO:<" + info.recipientEmail + ">\r\n";
    Write( command.c_str(), command.length());

    memset(buffer, 0, 1000);
    Read( buffer, 999);
    if (strncmp(buffer, "250", 3) != 0) // not ok
    {
        m_lastErrorMsg = buffer;
        return 250;
    }

    

    //准备发送邮件
    command = "data\r\n";
    Write( command.c_str(), command.length());

    memset(buffer, 0, 1000);
    Read( buffer, 999);
    if (strncmp(buffer, "354", 3) != 0) // not ready to receive message
    {
        m_lastErrorMsg = buffer;
        return 354;
    }

    command = std::move(GetEmailBody(info));
    Write( command.c_str(), command.length());

    memset(buffer, 0, 1000);
    Read(buffer, 999);
    if (strncmp(buffer, "250", 3) != 0) // not ok
    {
        m_lastErrorMsg = buffer;
        return 250;
    }

    //结束发送过程
    delete buffer;
    Write( "quit\r\n", 6);

    DisConnect();
    return 0;
}

std::string SmtpEmail::GetEmailBody(const EmailInfo &info)
{
    //设定邮件的发送者名称、接收者名称、邮件主题,邮件内容。    
    std::ostringstream message;
    message << "From: =?" << info.charset << "?b?" << base64Encode(info.sender.c_str(), info.sender.length()) << "?= <" << info.senderEmail << ">\r\n";

    std::vector<std::string> vecToList;
    for (auto item : info.recvList)
    {
        std::string to = "=?" + info.charset + "?b?" + base64Encode(item.second.c_str(), item.second.length()) + "?= <" + item.first + ">";
        vecToList.push_back(to);
    }

    message << "To: " << join(vecToList, ",") << "\r\n";
    message << "Subject: =?" << info.charset << "?b?" << base64Encode(info.subject.c_str(), info.subject.length()) << "?=\r\n";
    message << "MIME-Version: 1.0\r\n";

    if (info.ccEmail.size() > 0)
    {
        std::vector<std::string> vecCcList;
        for (auto item : info.ccEmail)
        {
            std::string cc = "=?" + info.charset + "?b?" + base64Encode(item.first.c_str(), item.first.length()) + "?= <" + item.second + ">";
            vecCcList.push_back(cc);
        }
        message << "Cc:" << join(vecCcList, ",") << "\r\n";
    }

    message << "Content-Type:multipart/mixed; boundary=\"Separator_ztq_000\"\r\n\r\n";
    message << "--Separator_ztq_000\r\n";
    message << "Content-Type: multipart/alternative; boundary=\"Separator_ztq_111\"\r\n\r\n";
    message << "--Separator_ztq_111\r\n";
    message << "Content-Type: " << "text/plain" << "; charset=\"" << info.charset << "\"\r\n";
    message << "Content-Transfer-Encoding: base64\r\n";
    message << base64Encode(info.message.c_str(), info.message.length());
    message << "\r\n\r\n";
    message << "--Separator_ztq_111--\r\n";
    //-----------------------------------------------------------

    for (auto item : info.attachment)
    {
        std::string filename = fileBasename(item);
        std::string strContext = getFileContents(item.c_str());
        std::string fileContext = base64Encode(strContext.c_str(), strContext.length());
        std::string extension = GetFileExtension(filename);
        std::string mimetype = GetMimeTypeFromFileName((char*)extension.c_str());
        message << "--Separator_ztq_000\r\n";
        message << "Content-Type: " << mimetype << "; name=\"" << filename << "\"\r\n";
        message << "Content-Transfer-Encoding: base64\r\n";
        message << "Content-Disposition: attachment; filename=\"" << filename << "\"\r\n\r\n";
        message << fileContext + "\r\n\r\n";
    }

    //-----------------------------------------------------------
    message << "\r\n.\r\n";
    return message.str();
}

SmtpEmail::SmtpEmail(const std::string& emailHost, const std::string& port) :m_host(emailHost), m_port(port)
{
    
}

SmtpEmail::~SmtpEmail()
{

}

int SmtpEmail::Read(void* buf, int num)
{
    return recv(m_socketfd, (char*)buf, num, 0);
}
int SmtpEmail::Write(const void* buf, int num)
{
    return send(m_socketfd, (char*)buf, num, 0);
}

int SmtpEmail::Connect()
{
#ifdef WIN32
    //start socket connection
    WSADATA wsadata;
    WSAStartup(MAKEWORD(2, 2), &wsadata);
#endif
    m_socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if (m_socketfd == INVALID_SOCKET)
    {
        m_lastErrorMsg = "Error on creating socket fd.";
        return -1;
    }

    addrinfo inAddrInfo = { 0 };
    inAddrInfo.ai_family = AF_INET;
    inAddrInfo.ai_socktype = SOCK_STREAM;


    if (getaddrinfo(m_host.c_str(), m_port.c_str(), &inAddrInfo, &m_addrinfo) != 0) // error occurs
    {
        m_lastErrorMsg = "Error on calling getadrrinfo().";
        return -2;
    }


    if (connect(m_socketfd, m_addrinfo->ai_addr, m_addrinfo->ai_addrlen))
    {
        m_lastErrorMsg = "Error on calling connect().";
        return -3;
    }
    return 0;
}

int SmtpEmail::DisConnect()
{
    freeaddrinfo(m_addrinfo);
#ifdef WIN32
    closesocket(m_socketfd);
#else
    close(m_socketfd);
#endif
    return 0;
}

/*********************************************************************************/


std::string SimpleSmtpEmail::GetEmailBody(const EmailInfo &info)
{
    //设定邮件的发送者名称、接收者名称、邮件主题,邮件内容。    
    std::ostringstream message;
    message << "From: =?" << info.charset << "?b?" << base64Encode(info.sender.c_str(), info.sender.length()) << "?= <" << info.senderEmail << ">\r\n";

    std::vector<std::string> vecToList;
    for (auto item : info.recvList)
    {
        std::string to = "=?" + info.charset + "?b?" + base64Encode(item.second.c_str(), item.second.length()) + "?= <" + item.first + ">";
        vecToList.push_back(to);
    }

    message << "To: " << join(vecToList, ",") << "\r\n";
    message << "Subject: =?" << info.charset << "?b?" << base64Encode(info.subject.c_str(), info.subject.length()) << "?=\r\n";
    message << "MIME-Version: 1.0\r\n";

    if (info.ccEmail.size() > 0)
    {
        std::vector<std::string> vecCcList;
        for (auto item : info.ccEmail)
        {
            std::string cc = "=?" + info.charset + "?b?" + base64Encode(item.first.c_str(), item.first.length()) + "?= <" + item.second + ">";
            vecCcList.push_back(cc);
        }
        message << "Cc:" << join(vecCcList, ",") << "\r\n";
    }

    message << "Content-Type: " << "text/plain" << "; charset=\"" << info.charset << "\"\r\n";
    message << "Content-Transfer-Encoding: base64\r\n";
    message << "\r\n";
    message << base64Encode(info.message.c_str(), info.message.length());
    message << "\r\n.\r\n";
    return message.str();
}

/***************************************************************************************************/

SslSmtpEmail::~SslSmtpEmail()
{
    
}

int SslSmtpEmail::Connect()
{    
    if (SmtpEmail::Connect() == 0)
    {
        SSL_library_init();
        OpenSSL_add_all_algorithms();
        SSL_load_error_strings();
        m_ctx = SSL_CTX_new(SSLv23_client_method());

        m_ssl = SSL_new(m_ctx);
        SSL_set_fd(m_ssl, m_socketfd);
        SSL_connect(m_ssl);
    }
    return 0;
}

int SslSmtpEmail::DisConnect()
{
    SSL_shutdown(m_ssl);
    SSL_free(m_ssl);
    SSL_CTX_free(m_ctx);

    SmtpEmail::DisConnect();
    return 0;
}



int SmtpEmail::SendEmail(const std::string& from, const std::string& passs, const std::string& to, const std::string& subject, const std::string& strMessage)
{
    EmailInfo info;
    info.charset = "UTF-8";
    info.sender = from;
    info.password = passs;
    info.senderEmail = from;
    info.recipientEmail = to;

    info.recvList[to] = "";

    info.subject = subject;
    info.message = strMessage;

    return SMTPComunicate(info);
}



int SmtpEmail::SendEmail(const std::string& from, const std::string& passs, const std::vector<std::string>& vecTo,
    const std::string& subject, const std::string& strMessage, const std::vector<std::string>& attachment, const std::vector<std::string>& ccList)
{
    std::vector<std::string> recvList;
    recvList.insert(recvList.end(), vecTo.begin(), vecTo.end());
    recvList.insert(recvList.end(), ccList.begin(), ccList.end());

    for (auto& item : recvList)
    {
        EmailInfo info;
        info.charset = "UTF-8";
        info.sender = from;
        info.password = passs;
        info.senderEmail = from;;
        info.recipientEmail = item;

        for (auto item : vecTo)
        {
            info.recvList[item] = "";
        }

        info.subject = subject;
        info.message = strMessage;

        for (auto& item : ccList)
        {
            info.ccEmail[item] = item;
        }
        
        info.attachment = attachment;
        if (SMTPComunicate(info) != 0)
        {
            return -1;
        }
    }    
    return 0;
}



int SslSmtpEmail::Read(void * buf, int num)
{
    return SSL_read(m_ssl, buf, num);
}

int SslSmtpEmail::Write(const void * buf, int num)
{
    return SSL_write(m_ssl, buf, num);
}


std::string SimpleSslSmtpEmail::GetEmailBody(const EmailInfo &info)
{
    //设定邮件的发送者名称、接收者名称、邮件主题,邮件内容。    
    std::ostringstream message;
    message << "From: =?" << info.charset << "?b?" << base64Encode(info.sender.c_str(), info.sender.length()) << "?= <" << info.senderEmail << ">\r\n";

    std::vector<std::string> vecToList;
    for (auto item : info.recvList)
    {
        std::string to = "=?" + info.charset + "?b?" + base64Encode(item.second.c_str(), item.second.length()) + "?= <" + item.first + ">";
        vecToList.push_back(to);
    }

    message << "To: " << join(vecToList, ",") << "\r\n";
    message << "Subject: =?" << info.charset << "?b?" << base64Encode(info.subject.c_str(), info.subject.length()) << "?=\r\n";
    message << "MIME-Version: 1.0\r\n";

    if (info.ccEmail.size() > 0)
    {
        std::vector<std::string> vecCcList;
        for (auto item : info.ccEmail)
        {
            std::string cc = "=?" + info.charset + "?b?" + base64Encode(item.first.c_str(), item.first.length()) + "?= <" + item.second + ">";
            vecCcList.push_back(cc);
        }
        message << "Cc:" << join(vecCcList, ",") << "\r\n";
    }

    message << "Content-Type: " << "text/plain" << "; charset=\"" << info.charset << "\"\r\n";
    message << "Content-Transfer-Encoding: base64\r\n";
    message << "\r\n";
    message << base64Encode(info.message.c_str(), info.message.length());
    message << "\r\n.\r\n";
    return message.str();
}
点赞
收藏
评论区
推荐文章
Irene181 Irene181
2年前
最全总结!聊聊 Python 发送邮件的几种方式
1\.前言邮件,作为最正式规范的沟通方式,在日常办公过程中经常被用到我们都知道Python内置了对SMTP的支持,可以发送纯文本、富文本、HTML等格式的邮件本文将聊聊利用 Python发送邮件的3种方式2\.准备以126邮箱为例,在编码之前,我们需要开启SMTP服务然后,手动新增一个授权码其中,账号、授权码和服务器地址用于连接登录
Irene181 Irene181
2年前
最全总结!聊聊 Python 发送邮件的几种方式
1\.前言邮件,作为最正式规范的沟通方式,在日常办公过程中经常被用到我们都知道Python内置了对SMTP的支持,可以发送纯文本、富文本、HTML等格式的邮件本文将聊聊利用 Python发送邮件的3种方式2\.准备以126邮箱为例,在编码之前,我们需要开启SMTP服务然后,手动新增一个授权码其中,账号、授权码和服务器地址用于连接登录
Karen110 Karen110
2年前
手把手教你使用Python轻松搞定发邮件
前言现在生活节奏加快,人们之间交流方式也有了天差地别,为了更加便捷的交流沟通,电子邮件产生了,众所周知,电子邮件其实就是客户端和服务器端发送接受数据一样,他有一个发信和一个收信的功能,电子邮件的通信协议为SMTP,POP3,IMAP,而且他们都属于tcp/ip协议,像我们经常用到的QQ邮箱,网易邮箱,这些都是同样的模式。准备编辑器:sublime
Wesley13 Wesley13
2年前
JavaMail发送和接收邮件API(详解)
一、JavaMail概述:    JavaMail是由Sun定义的一套收发电子邮件的API,不同的厂商可以提供自己的实现类。但它并没有包含在JDK中,而是作为JavaEE的一部分。    厂商所提供的JavaMail服务程序可以有选择地实现某些邮件协议,常见的邮件协议包括:SMTP:简单邮件传输
Wesley13 Wesley13
2年前
TCP、UDP和HTTP简述整理
http:是用于www浏览的一个协议。tcp:是机器之间建立连接用的到的一个协议。1、TCP/IP是个协议组,可分为三个层次:网络层、传输层和应用层。在网络层有IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议。在传输层中有TCP协议与UDP协议。在应用层有FTP、HTTP、TELNET、SMTP、DNS等协
Wesley13 Wesley13
2年前
TCP、UDP和HTTP区别详解
http:是用于www浏览的一个协议。tcp:是机器之间建立连接用的到的一个协议。1、TCP/IP是个协议组,可分为三个层次:网络层、传输层和应用层。在网络层有IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议。在传输层中有TCP协议与UDP协议。在应用层有FTP、HTTP、TELNET、SMTP、DNS等协议。
Wesley13 Wesley13
2年前
TCP与UDP与HTTP协议
http:是用于www浏览的一个协议。tcp:是机器之间建立连接用的到的一个协议。1、TCP/IP是个协议组,可分为三个层次:网络层、传输层和应用层。在网络层有IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议。在传输层中有TCP协议与UDP协议。在应用层有FTP、HTTP、TELNET、SMTP、DNS等协议。
Stella981 Stella981
2年前
Linuxprobe15
电子邮件系统简单邮件传输协议(SimpleMailTransferProtocol,SMTP):用于发送和中转发出的电子邮件,占用服务器的25/TCP端口。邮局协议版本3(PostOfficeProtocol3):用于将电子邮件存储到本地主机,占用服务器的110/TCP端口。Internet消息访问协议版本4(In
Wesley13 Wesley13
2年前
.net 获取邮箱邮件列表和内容
  需求:  最近项目里遇到了个问题,对方没有提供直接获取数据源的api接口,而是通过邮件发数据的。这就需要接收邮件后解析邮件内容获得数据。  分析:  想在代码里实时获取邮件,可以通过邮件的几个协议来实现,常用的几个协议有POP3协议,SMTP协议,IAMP协议。  1.pop3:(PostOfficeProtocal3),3是版本
浅谈TCP、UDP、ICMP三种常见协议
TCP是位于传输层的协议,它是面向链接的,提供全双工的服务,数据流可以双向传输。TCP是一种可靠的传输,它拥有三次握手机制保证数据传输的可靠性,如果对方没有接收到数据则会一直尝试重新传输。像FTP文件传输,远程登陆,HTTP,SMTP,HTTPS等等需要保证传输完整性的都是基于TCP协议。