龙空技术网

C++邮件发送类:基于STMP协议,全面支持SSL和附件功能

齐泽西 101

前言:

现在各位老铁们对“netsmtp抄送”大致比较看重,小伙伴们都需要了解一些“netsmtp抄送”的相关内容。那么小编在网摘上汇集了一些关于“netsmtp抄送””的相关知识,希望姐妹们能喜欢,咱们一起来了解一下吧!

一、介绍:

c++实现smtp发送邮件,支持ssl的465端口发送,支持附件、一次发送多人、抄送等。

注1:转载自csdn的“樱花回忆”,原文链接为: ``

注2:我这里新增了支持html格式,原来只有text/plain普通文本。

二、依赖openssl库:ubuntu下安装:

sudo apt install libssl-dev

其它平台大家自行搜索了解,不难,哈哈哈!

三、发送邮件demo

// SendEmail函数重载,支持基本的简单发送文本邮件,和支持附件、抄送、以及发给多人的发送方式,亲测能够发送成功,默认发送的编码是utf-8格式#include <iostream>#include <stdio.h>#include "mail.h"// g++ -o main main.cpp mail.cpp -lssl -lcrypto// ./mainint main(){    std::string from = "xxxx@163.com";    std::string passs ="xxxxxx";//这里替换成自己的授权码    std::string to = "xxxx@qq.com";    std::string subject = "C++邮件发送测试";    std::string strMessage = "<p style=\"margin-top: 100px;\">这里是c++邮件发送测试......</p><h2>Hello C++</h2>";    std::vector<std::string> vecTo; //发送列表    vecTo.push_back("xxxx@qq.com");    std::vector<std::string> ccList;    // ccList.push_back("xxx@xxx.com.cn");//抄送列表    std::vector<std::string> attachment;    attachment.push_back("mail.h");    attachment.push_back("1.txt");    SmtpBase *base;    SimpleSmtpEmail m_mail("smtp.163.com","25");    base = &m_mail;    //base->SendEmail(from, passs, to, subject, strMessage);//普通的文本发送,明文发送    SimpleSslSmtpEmail m_ssl_mail("smtp.163.com","465"); // text/plain    base=&m_ssl_mail;    //base->SendEmail(from, passs, to, subject, strMessage);    // base->SendEmail(from, passs,vecTo,subject,strMessage,attachment,ccList);//加密的发送,支持抄送、附件等    SimpleSslSmtpEmail m_ssl_mail2("smtp.163.com","465","text/html"); // html    base=&m_ssl_mail2;    //base->SendEmail(from, passs, to, subject, strMessage);    base->SendEmail(from, passs,vecTo,subject,strMessage,attachment,ccList);//加密的发送,支持抄送、附件等    return 0;}

效果图如下:

四、mail.h代码

// 依赖openssl: sudo apt install libssl-dev#pragma once#include <string>#include <vector>#include <map>#include <openssl/ossl_typ.h>#ifndef WIN32#include<netdb.h>#endifclass 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() {}    /**     * @brief 简单发送文本邮件     * @param   from 发送者的帐号     * @param   passs 发送者密码     * @param   to 收件人     * @param   subject 主题     * @param   strMessage  邮件内容     */    virtual int SendEmail(const std::string& from, const std::string& passs, const std::string& to, const std::string& subject, const std::string& strMessage) = 0;    /**     * @brief 发送邮件,包括附件以及抄送人和多个收件人     * @param   from 发送者的帐号     * @param   passs 发送者密码     * @param   vecTo 收件人列表     * @param   subject 主题     * @param   strMessage  邮件内容     * @param   attachment  附件列表    附件可以是绝对路径,默认是可执行程序目录下     * @param   ccList  抄送列表     */    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(const std::string& emailHost, const std::string& port, const std::string& content_type);    ~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;    std::string m_content_type;    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);};
五、mail.cpp代码
// 依赖openssl: sudo apt install libssl-dev#ifdef WIN32#include <WinSock2.h>#endif#include "mail.h"#include <fstream>#include <sstream>#include <iostream>#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#endiftemplate<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);    }    else {        printf("文件读取失败:%s\n",filename);        return "";    }}//获取文件的后缀名 如xxx.jpg 获取的是jpgstd::string GetFileExtension(const std::string& FileName){    if (FileName.find_last_of(".") != std::string::npos)        //find_last_of逆向查找在原字符串中最后一个与指定字符串(或字符)中的某个字符匹配的字符,返回它的位置。若查找失败,则返回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-Type: " << m_content_type << "; 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";    message << "--Separator_ztq_111--\r\n";    //---------------------文件部分处理--------------------------------------    for (auto item : info.attachment)    {        std::string filename = fileBasename(item);        std::string strContext = getFileContents(item.c_str());        if(strContext.empty())        {            std::cerr << "请检查传入的文件路径是否正确,此路径文件添加到附件失败,不发送此文件:" << std::endl;            std::cerr << item << std::endl;        }        else        {            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), m_content_type("text/plain"){}SmtpEmail::SmtpEmail(const std::string& emailHost, const std::string& port, const std::string& content_type) :m_host(emailHost), m_port(port), m_content_type(content_type){}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;    printf("host:%s port:%s \n",m_host.c_str(), m_port.c_str());    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: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-Type: " << m_content_type << "; 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";    message << "--Separator_ztq_111--\r\n";    //---------------------文件部分处理--------------------------------------    for (auto item : info.attachment)    {        std::string filename = fileBasename(item);        std::string strContext = getFileContents(item.c_str());        if(strContext.empty())        {            std::cerr << "请检查传入的文件路径是否正确,此路径文件添加到附件失败,不发送此文件:" << std::endl;            std::cerr << item << std::endl;        }        else        {            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();}/***************************************************************************************************/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){    int ret = SSL_read(m_ssl, buf, num);    return ret;}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: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-Type: " << m_content_type << "; 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";    message << "--Separator_ztq_111--\r\n";    //---------------------文件部分处理--------------------------------------    for (auto item : info.attachment)    {        std::string filename = fileBasename(item);        std::string strContext = getFileContents(item.c_str());        if(strContext.empty())        {            std::cerr << "请检查传入的文件路径是否正确,此路径文件添加到附件失败,不发送此文件:" << std::endl;            std::cerr << item << std::endl;        }        else        {            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();}
六、思考

大家在工作中都有哪些发邮件场景、以及如何合理的进行邮件发送?

不会在用户点击获取邮件验证码就之间同步发送邮件吧,哈哈哈?

预告:在接下来的章节librdkafka(c/c++ kafka客户端库)中会结合邮件发送案例与大家分析。

标签: #netsmtp抄送