SMTP實現發送郵箱2(封裝版)

SMTP.hios

#ifndef __SMTP_H__ //避免重複包含
#define __SMTP_H__

#include <iostream>
#include <list>
#include <WinSock2.h>
using namespace std;

const int MAXLEN = 1024;
const int MAX_FILE_LEN = 6000;

static const char base64Char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

struct FILEINFO /*記錄文件信息*/
{
    char fileName[128]; /*文件名稱*/
    char filePath[256]; /*文件絕對路徑*/
};

class CSmtp
{
public:
    CSmtp(void);
    CSmtp(
        int port,
        string srvDomain,	//smtp服務器域名
        string userName,	//用戶名
        string password,	//密碼
        string targetEmail, //目的郵件地址
        string emailTitle,  //主題
        string content       //內容
    );
public:
    ~CSmtp(void);
public:
    int port;
public:
    string domain;
    string user;
    string pass;
    string targetAddr;
    string title;
    string content;
    /*將文件存入list裏,便於刪除與增長*/
    list <FILEINFO *> listFile;

public:
    char buff[MAXLEN + 1];
    int buffLen;
    SOCKET sockClient;	//客戶端的套接字
public:
    bool CreateConn(); /*建立鏈接*/

    bool Send(string &message);
    bool Recv();

    void FormatEmailHead(string &email);//格式化要發送的郵件頭部
    int Login();
    bool SendEmailHead();		//發送郵件頭部信息
    bool SendTextBody();	    //發送文本信息
                                //bool SendAttachment();	    //發送附件
    int SendAttachment_Ex();
    bool SendEnd();
public:
    void AddAttachment(string &filePath); //添加附件
    void DeleteAttachment(string &filePath); //刪除附件
    void DeleteAllAttachment(); //刪除全部的附件

    void SetSrvDomain(string &domain);
    void SetUserName(string &user);
    void SetPass(string &pass);
    void SetTargetEmail(string &targetAddr);
    void SetEmailTitle(string &title);
    void SetContent(string &content);
    void SetPort(int port);
    int SendEmail_Ex();
    /*關於錯誤碼的說明:1.網絡錯誤致使的錯誤2.用戶名錯誤3.密碼錯誤4.文件不存在0.成功*/
    char* base64Encode(char const* origSigned, unsigned origLength);
};

#endif // !__SMTP_H__

  

SMTP.cpp數組

#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include "Smtp.h"
#include <winsock2.h>
#include <iostream>
#include <fstream>
using namespace std;

#pragma  comment(lib, "ws2_32.lib")	/*連接ws2_32.lib動態連接庫*/

/*base64編碼*/
char* CSmtp::base64Encode(char const* origSigned, unsigned origLength)
{
    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;
}
CSmtp::CSmtp(void)
{
    this->content = "";
    this->port = 25;
    this->user = "";
    this->pass = "";
    this->targetAddr = "";
    this->title = "";
    this->domain = "";

    WORD wVersionRequested;
    WSADATA wsaData;
    int err;
    wVersionRequested = MAKEWORD(2, 1);
    err = WSAStartup(wVersionRequested, &wsaData);
    this->sockClient = 0;
}

CSmtp::~CSmtp(void)
{
    DeleteAllAttachment();
    closesocket(sockClient);
    WSACleanup();
}

CSmtp::CSmtp(
    int port,
    string srvDomain,
    string userName,
    string password,
    string targetEmail,
    string emailTitle,
    string content
)
{
    this->content = content;
    this->port = port;
    this->user = userName;
    this->pass = password;
    this->targetAddr = targetEmail;
    this->title = emailTitle;
    this->domain = srvDomain;

    WORD wVersionRequested;
    WSADATA wsaData;
    int err;
    wVersionRequested = MAKEWORD(2, 1);
    err = WSAStartup(wVersionRequested, &wsaData);
    this->sockClient = 0;
}

bool CSmtp::CreateConn()
{
    SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0); //創建socket對象
    SOCKADDR_IN addrSrv;
    HOSTENT* pHostent;
    pHostent = gethostbyname(domain.c_str());  //獲得有關於域名的信息

    addrSrv.sin_addr.S_un.S_addr = *((DWORD *)pHostent->h_addr_list[0]);	//獲得smtp服務器的網絡字節序的ip地址
    addrSrv.sin_family = AF_INET;
    addrSrv.sin_port = htons(port);
    int err = connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));   //向服務器發送請求
    if (err != 0)
    {
        return false;
    }
    this->sockClient = sockClient;
    if (false == Recv())
    {
        return false;
    }
    return true;
}

bool CSmtp::Send(string &message)
{
    int err = send(sockClient, message.c_str(), message.length(), 0);
    if (err == SOCKET_ERROR)
    {
        return false;
    }
    cout << message.c_str() << endl;
    return true;
}

bool CSmtp::Recv()
{
    memset(buff, 0, sizeof(char)* (MAXLEN + 1));
    int err = recv(sockClient, buff, MAXLEN, 0); //接收數據
    if (err == SOCKET_ERROR)
    {
        return false;
    }
    buff[err] = '\0';
    cout << buff << endl;
    return true;
}

int CSmtp::Login()
{
    string sendBuff;
    sendBuff = "EHLO ";
    sendBuff += user;
    sendBuff += "\r\n";

    if (false == Send(sendBuff) || false == Recv()) //既接收也發送
    {
        return 1; /*1表示發送失敗因爲網絡錯誤*/
    }

    sendBuff.empty();
    sendBuff = "AUTH LOGIN\r\n";
    if (false == Send(sendBuff) || false == Recv()) //請求登錄
    {
        return 1; /*1表示發送失敗因爲網絡錯誤*/
    }

    sendBuff.empty();
    int pos = user.find('@', 0);
    sendBuff = user.substr(0, pos); //獲得用戶名

    char *ecode;
    /*在這裏順帶扯一句,關於string類的length函數與C語言中的strlen函數的區別,strlen計算出來的長度,只到'\0'字符爲止,而string::length()函數實際上返回的是string類中字符數組的大小,你本身能夠測試一下,這也是爲何我下面不使用string::length()的緣由*/

    ecode = base64Encode(sendBuff.c_str(), strlen(sendBuff.c_str()));
    sendBuff.empty();
    sendBuff = ecode;
    sendBuff += "\r\n";
    delete[]ecode;

    if (false == Send(sendBuff) || false == Recv()) //發送用戶名,並接收服務器的返回
    {
        return 1; /*錯誤碼1表示發送失敗因爲網絡錯誤*/
    }

    sendBuff.empty();
    ecode = base64Encode(pass.c_str(), strlen(pass.c_str()));
    sendBuff = ecode;
    sendBuff += "\r\n";
    delete[]ecode;

    if (false == Send(sendBuff) || false == Recv()) //發送用戶密碼,並接收服務器的返回
    {
        return 1; /*錯誤碼1表示發送失敗因爲網絡錯誤*/
    }

    if (NULL != strstr(buff, "550"))
    {
        return 2;/*錯誤碼2表示用戶名錯誤*/
    }

    if (NULL != strstr(buff, "535")) /*535是認證失敗的返回*/
    {
        return 3; /*錯誤碼3表示密碼錯誤*/
    }
    return 0;
}

bool CSmtp::SendEmailHead()		//發送郵件頭部信息
{
    string sendBuff;
    sendBuff = "MAIL FROM: <" + user + ">\r\n";
    if (false == Send(sendBuff) || false == Recv())
    {
        return false; /*表示發送失敗因爲網絡錯誤*/
    }

    sendBuff.empty();
    sendBuff = "RCPT TO: <" + targetAddr + ">\r\n";
    if (false == Send(sendBuff) || false == Recv())
    {
        return false; /*表示發送失敗因爲網絡錯誤*/
    }

    sendBuff.empty();
    sendBuff = "DATA\r\n";
    if (false == Send(sendBuff) || false == Recv())
    {
        return false; //表示發送失敗因爲網絡錯誤
    }

    sendBuff.empty();
    FormatEmailHead(sendBuff);
    if (false == Send(sendBuff))
        //發送完頭部以後沒必要調用接收函數,由於你沒有\r\n.\r\n結尾,服務器認爲你沒有發完數據,因此不會返回什麼值
    {
        return false; /*表示發送失敗因爲網絡錯誤*/
    }
    return true;
}

void CSmtp::FormatEmailHead(string &email)
{/*格式化要發送的內容*/
    email = "From: ";
    email += user;
    email += "\r\n";

    email += "To: ";
    email += targetAddr;
    email += "\r\n";

    email += "Subject: ";
    email += title;
    email += "\r\n";

    email += "MIME-Version: 1.0";
    email += "\r\n";

    email += "Content-Type: multipart/mixed;boundary=qwertyuiop";
    email += "\r\n";
    email += "\r\n";
}

bool CSmtp::SendTextBody()  /*發送郵件文本*/
{
    string sendBuff;
    sendBuff = "--qwertyuiop\r\n";
    sendBuff += "Content-Type: text/plain;";
    sendBuff += "charset=\"gb2312\"\r\n\r\n";
    sendBuff += content;
    sendBuff += "\r\n\r\n";
    return Send(sendBuff);
}

int CSmtp::SendAttachment_Ex() /*發送附件*/
{
    for (list<FILEINFO *>::iterator pIter = listFile.begin(); pIter != listFile.end(); pIter++)
    {
        cout << "Attachment is sending ~~~~~" << endl;
        cout << "Please be patient!" << endl;
        string sendBuff;
        sendBuff = "--qwertyuiop\r\n";
        sendBuff += "Content-Type: application/octet-stream;\r\n";
        sendBuff += " name=\"";
        sendBuff += (*pIter)->fileName;
        sendBuff += "\"";
        sendBuff += "\r\n";

        sendBuff += "Content-Transfer-Encoding: base64\r\n";
        sendBuff += "Content-Disposition: attachment;\r\n";
        sendBuff += " filename=\"";
        sendBuff += (*pIter)->fileName;
        sendBuff += "\"";

        sendBuff += "\r\n";
        sendBuff += "\r\n";
        Send(sendBuff);
        ifstream ifs((*pIter)->filePath, ios::in | ios::binary);
        if (false == ifs.is_open())
        {
            return 4; /*錯誤碼4表示文件打開錯誤*/
        }
        char fileBuff[MAX_FILE_LEN];
        char *chSendBuff;
        memset(fileBuff, 0, sizeof(fileBuff));
        /*文件使用base64加密傳送*/
        while (ifs.read(fileBuff, MAX_FILE_LEN))
        {
            //cout << ifs.gcount() << endl;
            chSendBuff = base64Encode(fileBuff, MAX_FILE_LEN);
            chSendBuff[strlen(chSendBuff)] = '\r';
            chSendBuff[strlen(chSendBuff)] = '\n';
            send(sockClient, chSendBuff, strlen(chSendBuff), 0);
            delete[]chSendBuff;
        }
        //cout << ifs.gcount() << endl;
        chSendBuff = base64Encode(fileBuff, ifs.gcount());
        chSendBuff[strlen(chSendBuff)] = '\r';
        chSendBuff[strlen(chSendBuff)] = '\n';
        int err = send(sockClient, chSendBuff, strlen(chSendBuff), 0);

        if (err != strlen(chSendBuff))
        {
            cout << "文件傳送出錯!" << endl;
            return 1;
        }
        delete[]chSendBuff;
    }
    return 0;
}

bool CSmtp::SendEnd() /*發送結尾信息*/
{
    string sendBuff;
    sendBuff = "--qwertyuiop--";
    sendBuff += "\r\n.\r\n";
    if (false == Send(sendBuff) || false == Recv())
    {
        return false;
    }
    cout << buff << endl;
    sendBuff.empty();
    sendBuff = "QUIT\r\n";
    return (Send(sendBuff) && Recv());
}

int CSmtp::SendEmail_Ex()
{
    if (false == CreateConn())
    {
        return 1;
    }
    //Recv();
    int err = Login(); //先登陸
    if (err != 0)
    {
        return err; //錯誤代碼必需要返回
    }
    if (false == SendEmailHead()) //發送EMAIL頭部信息
    {
        return 1; /*錯誤碼1是因爲網絡的錯誤*/
    }
    if (false == SendTextBody())
    {
        return 1; /*錯誤碼1是因爲網絡的錯誤*/
    }
    err = SendAttachment_Ex();
    if (err != 0)
    {
        return err;
    }
    if (false == SendEnd())
    {
        return 1; /*錯誤碼1是因爲網絡的錯誤*/
    }
    return 0; /*0表示沒有出錯*/
}

void CSmtp::AddAttachment(string &filePath) //添加附件
{
    FILEINFO *pFile = new FILEINFO;
    strcpy_s(pFile->filePath, filePath.c_str());
    const char *p = filePath.c_str();
    strcpy_s(pFile->fileName, p + filePath.find_last_of("\\") + 1);
    listFile.push_back(pFile);
}

void CSmtp::DeleteAttachment(string &filePath) //刪除附件
{
    list<FILEINFO *>::iterator pIter;
    for (pIter = listFile.begin(); pIter != listFile.end(); pIter++)
    {
        if (strcmp((*pIter)->filePath, filePath.c_str()) == 0)
        {
            FILEINFO *p = *pIter;
            listFile.remove(*pIter);
            delete p;
            break;
        }
    }
}

void CSmtp::DeleteAllAttachment() /*刪除全部的文件*/
{
    for (list<FILEINFO *>::iterator pIter = listFile.begin(); pIter != listFile.end();)
    {
        FILEINFO *p = *pIter;
        pIter = listFile.erase(pIter);
        delete p;
    }
}

void CSmtp::SetSrvDomain(string &domain)
{
    this->domain = domain;
}

void CSmtp::SetUserName(string &user)
{
    this->user = user;
}

void CSmtp::SetPass(string &pass)
{
    this->pass = pass;
}
void CSmtp::SetTargetEmail(string &targetAddr)
{
    this->targetAddr = targetAddr;
}
void CSmtp::SetEmailTitle(string &title)
{
    this->title = title;
}
void CSmtp::SetContent(string &content)
{
    this->content = content;
}
void CSmtp::SetPort(int port)
{
    this->port = port;
}

  

main.cpp服務器

#include "Smtp.h"
#include <iostream>
using namespace std;

int main()
{
    CSmtp smtp(
        25,								/*smtp端口*/
        "smtp.163.com",					/*smtp服務器地址*/
        "XXX_XXX@163.com",	/*你的郵箱地址*/
        "XXXX",					/*郵箱密碼*/
        "XXXXX@qq.com",	/*目的郵箱地址*/
        "Jason!",							/*主題*/
        "Jason come from China"		/*郵件正文*/
    );

    //添加附件AddAttachment
    string filePath("D:\\1.txt");
    smtp.AddAttachment(filePath);

    //刪除附件smtp.DeleteAttachment(filePath);

    int err;
    if ((err = smtp.SendEmail_Ex()) != 0)
    {
        if (err == 1)
            cout << "錯誤1: 因爲網絡不順暢通,發送失敗!" << endl;
        if (err == 2)
            cout << "錯誤2: 用戶名錯誤,請覈對!" << endl;
        if (err == 3)
            cout << "錯誤3: 用戶密碼錯誤,請覈對!" << endl;
        if (err == 4)
            cout << "錯誤4: 請檢查附件目錄是否正確,以及文件是否存在!" << endl;
    }
    system("pause");
    return 0;
}
相關文章
相關標籤/搜索