国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

c++實現(xiàn)smtp發(fā)送郵件,支持ssl的465端口發(fā)送,支持附件、一次發(fā)送多人、抄送等

這篇具有很好參考價值的文章主要介紹了c++實現(xiàn)smtp發(fā)送郵件,支持ssl的465端口發(fā)送,支持附件、一次發(fā)送多人、抄送等。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

前言

c++實現(xiàn)smtp發(fā)送郵件,支持ssl的465端口發(fā)送,支持附件、一次發(fā)送多人、抄送等。
這里只使用了openssl庫(用來支持ssl的465端口),其他部分是原生c++,支持在win/linux運行。
網(wǎng)上很多都是原始的支持25端口,明文發(fā)送,這里介紹一下ssl思路

實現(xiàn)思路

使用sockect編程和ssl,使用SMTP協(xié)議。
使用了c++的多態(tài)語言特性,包括重載和虛函數(shù)。
文中給出mail.h 和mail.cpp 以及main.cpp部分測試代碼
Linux安裝openssl

sudo apt install libssl-dev

win下自行下載安裝

SMTP協(xié)議簡介

SMTP協(xié)議的定義:

1、SMTP 是一種TCP協(xié)議支持的提供可靠且有效電子郵件傳輸?shù)膽脤訁f(xié)議;
2、SMTP 是建立在 TCP上的一種郵件服務,主要用于傳輸系統(tǒng)之間的郵件信息并提供來信有關的通知;
3、SMTP 獨立于特定的傳輸子系統(tǒng),且只需要可靠有序的數(shù)據(jù)流信道支持;
4、SMTP 重要特性之一是其能跨越網(wǎng)絡傳輸郵件,即“ SMTP 郵件中繼”;
5、SMTP是一個相對簡單的基于文本的協(xié)議。

SMTP基本命令

SMTP基本命令包括以下幾個:HELO﹑EHLO、MAIL﹑RCPT﹑DATA、AUTH LOGIN和QUIT
1、HELO–發(fā)件方問候收件方,后面是發(fā)件人的服務器地址或標識。收件方回答OK時標識自己的身份。問候和確認過程表明兩臺機器可以進行通信,同時狀態(tài)參量被復位,緩沖區(qū)被清空。
EHLO–申明身份,表示自己身份需要驗證,注意這部分需要通過Telnet驗證一下,是user@example.com還是user,否則會出錯。
2、MAIL–這個命令用來開始傳送郵件,它的后面跟隨發(fā)件方郵件地址(返回郵件地址)。它也用來當郵件無法送達時,發(fā)送失敗通知。為保證郵件的成功發(fā)送,發(fā)件方的地址應是被對方或中間轉(zhuǎn)發(fā)方同意接受的。這個命令會清空有關的緩沖區(qū),為新的郵件做準備。
3、RCPT –這個命令告訴收件方收件人的郵箱。當有多個收件人時,需要多次使用該命令RCPT
4、TO,每次只能指明一個人。如果接收方服務器不同意轉(zhuǎn)發(fā)這個地址的郵件,它必須報550錯誤代碼通知發(fā)件方。如果服務器同意轉(zhuǎn)發(fā),它要更改郵件發(fā)送路徑,把最開始的目的地(該服務器)換成下一個服務器。
5、DATA–收件方把該命令之后的數(shù)據(jù)作為發(fā)送的數(shù)據(jù)。數(shù)據(jù)被加入數(shù)據(jù)緩沖區(qū)中,以單獨一行是”.”的行結(jié)束數(shù)據(jù)。結(jié)束行對于接收方同時意味立即開始緩沖區(qū)內(nèi)的數(shù)據(jù)傳送,傳送結(jié)束后清空緩沖區(qū)。如果傳送接受,接收方回復OK。
6、QUIT–SMTP要求接收放必須回答OK,然后中斷傳輸;在收到這個命令并回答OK前,收件方不得中斷連接,即使傳輸出現(xiàn)錯誤。發(fā)件方在發(fā)出這個命令并收到OK答復前,也不得中斷連接。
7、AUTH LOGIN–登錄郵箱,這一部分一般要用base64加密。

頭文件mail.h

#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   郵件內(nèi)容

        std::map<std::string, std::string> ccEmail;         //抄送列表
        std::vector<std::string> attachment; //附件
    };
public:

    virtual ~SmtpBase() {}
    /**
     * @brief 簡單發(fā)送文本郵件
     * @param   from 發(fā)送者的帳號
     * @param   passs 發(fā)送者密碼
     * @param   to 收件人
     * @param   subject 主題
     * @param   strMessage  郵件內(nèi)容
     */

    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 發(fā)送郵件,包括附件以及抄送人和多個收件人
     * @param   from 發(fā)送者的帳號
     * @param   passs 發(fā)送者密碼
     * @param   vecTo 收件人列表
     * @param   subject 主題
     * @param   strMessage  郵件內(nèi)容
     * @param   attachment  附件列表    附件可以是絕對路徑,默認是可執(zhí)行程序目錄下
     * @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();

    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);
};

實現(xiàn)文件mail.cpp

#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
#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);
    }
    else {
        printf("文件讀取失敗:%s\n",filename);
        return "";
    }
}
//獲取文件的后綴名 如xxx.jpg 獲取的是jpg
std::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;
    }

    //向服務器發(fā)送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;
    }

    //設置郵件發(fā)送者的郵箱地址
    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;
    }



    //準備發(fā)送郵件
    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;
    }

    //結(jié)束發(fā)送過程
    delete buffer;
    Write( "quit\r\n", 6);

    DisConnect();
    return 0;
}

std::string SmtpEmail::GetEmailBody(const EmailInfo &info)
{
    //設定郵件的發(fā)送者名稱、接收者名稱、郵件主題,郵件內(nèi)容。
    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 << "\r\n";                  //此處要加,不然郵件正文無內(nèi)容
    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 << "請檢查傳入的文件路徑是否正確,此路徑文件添加到附件失敗,不發(fā)送此文件:" << 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";        //把讀取到的文件內(nèi)容以二進制形式發(fā)送
        }
    }
    //-----------------------------------------------------------
    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;

    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)
{
    //設定郵件的發(fā)送者名稱、接收者名稱、郵件主題,郵件內(nèi)容。
    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 << "\r\n";                  //此處要加,不然郵件正文無內(nèi)容
    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 << "請檢查傳入的文件路徑是否正確,此路徑文件添加到附件失敗,不發(fā)送此文件:" << 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";        //把讀取到的文件內(nèi)容以二進制形式發(fā)送
        }
    }
    //-----------------------------------------------------------
    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)
{
    //設定郵件的發(fā)送者名稱、接收者名稱、郵件主題,郵件內(nèi)容。
    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 << "\r\n";                  //此處要加,不然郵件正文無內(nèi)容
    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 << "請檢查傳入的文件路徑是否正確,此路徑文件添加到附件失敗,不發(fā)送此文件:" << 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";        //把讀取到的文件內(nèi)容以二進制形式發(fā)送
        }
    }
    //-----------------------------------------------------------
    message << "\r\n.\r\n";
    return message.str();
}

測試代碼 main.cpp

SendEmail函數(shù)重載,支持基本的簡單發(fā)送文本郵件,和支持附件、抄送、以及發(fā)給多人的發(fā)送方式,親測能夠發(fā)送成功,默認發(fā)送的編碼是utf-8格式

#include <iostream>
#include <stdio.h>
#include "mail.h"

int main()
{

    std::string from = "xxxx@163.com";
    std::string passs ="xxxxx";//這里替換成自己的授權碼
    std::string to = "xxx@126.com";
    std::string subject = "hello";
    std::string strMessage = "test";


    std::vector<std::string> vecTo; //發(fā)送列表
    vecTo.push_back("xxx@126.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("/home/soft/Desktop/openwrt-sdk-imx6ul/1/mybuild.sh");


    SmtpBase *base;
    SimpleSmtpEmail m_mail("smtp.163.com","25");
    base = &m_mail;
    //base->SendEmail(from, passs, to, subject, strMessage);//普通的文本發(fā)送,明文發(fā)送

    SimpleSslSmtpEmail m_ssl_mail("smtp.163.com","465");
    base=&m_ssl_mail;
    //base->SendEmail(from, passs, to, subject, strMessage);
    base->SendEmail(from, passs,vecTo,subject,strMessage,attachment,ccList);//加密的發(fā)送,支持抄送、附件等

    return 0;
}


郵箱收件箱
查看郵件頭
c++實現(xiàn)smtp發(fā)送郵件,支持ssl的465端口發(fā)送,支持附件、一次發(fā)送多人、抄送等
收到的郵件
c++實現(xiàn)smtp發(fā)送郵件,支持ssl的465端口發(fā)送,支持附件、一次發(fā)送多人、抄送等

參考鏈接:

C++基于Smtp協(xié)議發(fā)送郵件
SMTP基本原理與C++實現(xiàn)向多人發(fā)送郵件

后記

如果您覺得有幫助的話,請點個贊吧!有問題請留言評論,歡迎指出意見,謝謝。文章來源地址http://www.zghlxwxcb.cn/news/detail-474940.html

到了這里,關于c++實現(xiàn)smtp發(fā)送郵件,支持ssl的465端口發(fā)送,支持附件、一次發(fā)送多人、抄送等的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • Go:實現(xiàn)SMTP郵件發(fā)送訂閱功能(包含163郵箱、163企業(yè)郵箱、谷歌gmail郵箱)

    Go:實現(xiàn)SMTP郵件發(fā)送訂閱功能(包含163郵箱、163企業(yè)郵箱、谷歌gmail郵箱)

    需求很簡單,就是用戶輸入自己的郵箱后,使用官方郵箱給用戶發(fā)送替郵件模版 郵件模版類似如下圖發(fā)來的歡迎加入的模版 這里我們使用html格式存儲模版,上圖源碼如下 選擇好官方郵箱來開啟smtp服務,這里我測試驗證了163郵箱、163企業(yè)郵箱、谷歌gmail郵箱和騰訊企業(yè)郵箱(

    2024年02月08日
    瀏覽(218)
  • 使用SMTP協(xié)議發(fā)送郵件

    使用SMTP協(xié)議發(fā)送郵件

    劇情介紹 今天心血來潮,學了一下Python3,里面有個章節(jié)是發(fā)送郵件,用示例里面的代碼,運行后報錯,然后記錄一下問題是如何解決的,大家可以看一下,可以有效避坑。 SMTP簡介 SMTP是一種提供可靠且有效的電子郵件傳輸?shù)膮f(xié)議。SMTP是建立在FTP文件傳輸服務上的一種郵件服

    2024年02月05日
    瀏覽(29)
  • Python SMTP發(fā)送郵件和線程

    Python SMTP發(fā)送郵件和線程

    SMTP(Simple Mail Transfer Protocol)即 簡單郵件傳輸協(xié)議,它是一組用于由源地址到目的地址傳送郵件的規(guī)則,由它來控制信件的中轉(zhuǎn)方式 。 python的smtplib提供了一種很方便的途徑發(fā)送電子郵件。它對smtp協(xié)議進行了簡單的封裝。 Python創(chuàng)建 SMTP 對象語法如下: 參數(shù)說明: host: SMTP 服

    2023年04月11日
    瀏覽(20)
  • C# SMTP 郵件發(fā)送傻瓜操作

    ? /// summary ?/// 發(fā)送郵件的方法 ?/// /summary ?public OperateResultint SendMail(MailModel mails) ?{ ? ? ?var resultData = new OperateResultint(); ? ? ?if (mails.to == null || !mails.to.Any()) ? ? ?{ ? ? ? ? ?resultData.Status = OperateStatus.Failure; ? ? ? ? ?resultData.Description = \\\"收件人地址不能為空\\\"; ? ? ? ?

    2024年01月19日
    瀏覽(18)
  • 使用 office 365 SMTP 發(fā)送企業(yè)郵件

    使用 office 365 SMTP 發(fā)送企業(yè)郵件

    最近公司的企業(yè)郵箱從 gmail 遷移到了 office 365。不得不說,微軟 office 套件的功能在市場上還是無人可以取代的。公司自然需要用 office 365 向客戶發(fā)送郵件,因此需要重新配置項目的 SMTP 服務器。 一開始直接將 gamil 的 SMTP 服務器地址換成 office 365 的地址即“smtp.office365.com”,

    2024年02月07日
    瀏覽(20)
  • python 學習筆記(5)——SMTP 使用QQ郵箱發(fā)送郵件

    python 學習筆記(5)——SMTP 使用QQ郵箱發(fā)送郵件

    目錄 發(fā)送郵件 1、準備工作: 2、發(fā)送純文本信息內(nèi)容: 3、發(fā)送 HTML 格式的內(nèi)容: 4、發(fā)送帶附件的郵件: 5、群發(fā)(一個郵件,發(fā)給多個人): ??????? 以下都 以 QQ郵箱 為發(fā)送方舉例; ? 1、準備工作: ???????? 使用 QQ郵箱發(fā)送郵件,需要到 “ 設置 ”=“ 賬號 ”

    2024年02月09日
    瀏覽(29)
  • 無緩存定時發(fā)送帶附件(表格)等郵件

    1.導入發(fā)送郵件的包 2.配置yml 3.添加定時任務 4.實現(xiàn)

    2024年02月11日
    瀏覽(22)
  • SMTP郵件發(fā)送圖片-如何在github中存儲圖片并訪問

    SMTP郵件發(fā)送圖片-如何在github中存儲圖片并訪問

    之前寫了一篇文章 Go:實現(xiàn)SMTP郵件發(fā)送訂閱功能(包含163郵箱、163企業(yè)郵箱、谷歌gmail郵箱),實現(xiàn)了通過郵箱服務來發(fā)送郵件,但都是文字內(nèi)容,要是想實現(xiàn)郵件發(fā)送圖片,就需要將圖片放到公網(wǎng)可訪問的服務器,這里用了騷操作。 實現(xiàn)郵件群發(fā)功能,來發(fā)送項目的周報及

    2024年02月06日
    瀏覽(24)
  • Springboot 使用JavaMailSender發(fā)送郵件 + Excel附件

    Springboot 使用JavaMailSender發(fā)送郵件 + Excel附件

    目錄 1.生成Excel表格 1.依賴設置 2.代碼: 2.郵件發(fā)送 1.郵件發(fā)送功能實現(xiàn)-帶附件 ?2.踩過的坑 1.附件名中文亂碼問題 3.參考文章: 需求描述:項目審批完畢后,需要發(fā)送郵件通知相關人員,并且要附帶數(shù)據(jù)庫表生成的Excel表格,這就要求不光是郵件發(fā)送功能,還要臨時生成E

    2024年02月07日
    瀏覽(21)
  • 網(wǎng)工應用層:電子郵件Email、SMTP協(xié)議與使用Telnet發(fā)送電子郵件

    網(wǎng)工應用層:電子郵件Email、SMTP協(xié)議與使用Telnet發(fā)送電子郵件

    參考資料: https://www.bilibili.com/video/BV1c4411d7jb?p=72vd_source=e66dd25b0246f28e772d75f11c80f03c 湖科大的高軍老師做的計算機網(wǎng)絡課,去年選修的計網(wǎng)全靠高老師,生動,舉例豐富,配套實驗課,無廢話純干貨,單推一波! 電子郵件(E-mail)是一種用電子手段提供信息交換的通信方式,是

    2024年02月14日
    瀏覽(36)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包