zoukankan      html  css  js  c++  java
  • c++封装的发邮件类CSendMail

    项目需要做发邮件的功能,在网上找了一下代码,比较出名的SMailer编译不过(把那个Base64的encode拉到MailSender中实现就能过,但我搞不懂原来出错的原因,就不想用),另外找到了一个CSendMail的实现类,可以用,但代码的风格不好,使用起来也不方便,所以我就参考(chao)这两个类,实现了一个比较简单的发邮件类。CSendMail类支持多个收件人,支持附件(多个)。

    [原]:http://www.cnblogs.com/sixbeauty/p/3983525.html

    上代码,头文件:

    /*
    **CSendMail头文件
    **实现邮件的发送功能,支持多个用户接收,支持附件
    **program by six_beauty
    */
    
    #pragma once
    #include <string>
    #include <list>
    #include <map>
    #include "LogInfo.h"
    #include <winsock2.h>
    
    //类型定义
    const int MAX_BUFFER_SIZE = 255;                            //send和recv的缓存buffer的size
    const int SERVICE_PORT=25;                                    //端口
    
    typedef std::map<std::string, std::string> RECEIVERS;
    
    //CSendMail类
    class CSendMail
    {
    public:
        CSendMail();
        ~CSendMail();
    
    //////////////////////////////////////设置邮件信息/////////////////////////////////////////////////////////////////////////////////
        /////////////////////////connent///////////////////////////////////
        void setServerName(const std::string server_name);        //smtp服务器地址    
        void setUserName(const std::string user_name);            //邮箱用户名
        void setUserPwd(const std::string user_pwd);            //邮箱用户密码
        /////////////////////////SendMail//////////////////////////////////
        void setSenderName(const std::string sender_name);        //发送者的名字
        void setSenderAddress(const std::string sender_addr);    //发送者的邮箱(mail form:)
    
        //邮件接收者
        void setReceiver(const std::string name, const std::string address);            //先clear再add
        void addReceiver(const std::string name, const std::string address);            //增加邮件接收者,name是收件人名字,mail是地址
        void clearReceiver();                                                            //情况邮件接收者
    
        //添加附件
        void AddFilePath(std::string szFilePath);                                        //添加附件路径到附件列表中,一般的smtp服务器处理附件不超过50MB 
        void DeleteFilePath(std::string szFilePath);                                    //删除附件路径,如果有的话  
        void DeleteAllPath();                                                            //删除全部附件的路径  
    
    /////////////////////////////////////发送邮件//////////////////////////////////////////////////////////////////////////////////////
        //连接
        bool Connent();
        //邮件发送
        bool SendMail(const std::string mail_title,const std::string send_content);        //发送邮件的函数
    
    private:
        //功能函数
        inline std::string& replace_all(string& str,const string& old_value,const string& new_value);       //其实就是CString的Replace
        std::string GetFileName(std::string&szFilePath);        //从附件的路径中获取文件名称
        std::string GetFileData(std::string szFilePath);        //以字符形式读入附件内容
    
        std::string Base64Encode(std::string in_str);            //把char类型转换成Base64类型  
        //获取时间
        std::string prepareDate();
    
        //通信recv和send的封装
        int     sendRequest(const std::string content,bool bout=false);                //返回发送了多少字节
        bool rcvResponse(const std::string expected_response);    //返回接收的结果和expected_response是否相同
    
        //工作函数
        bool CReateSocket();                                    //建立socket连接  
        bool Logon();                                            //登录邮箱,主要进行发邮件前的准备工作  
    
        bool SendHead();                                        //发送邮件头  
        bool SendTextBody();                                    //发送邮件文本正文  
        bool SendFileBody();                                    //发送邮件附件  
        bool SendEnd();                                            //发送邮件结尾 
    
    
        SOCKET _socket;
        LogInfo m_logInfo;
    
        /////////////////////////邮件信息///////////////////////////////////
        /////////////////////////connent///////////////////////////////////
        std::string                    m_ServerName;        //smtp服务器地址
        std::string                    m_UserName;            //邮箱用户
        std::string                    m_UserPwd;            //邮箱用户密
        /////////////////////////SendMail//////////////////////////////////
        std::string                    m_SenderName;        //发送者的名    
        std::string                    m_SenderAddr;        //发送者的邮箱(mail form:)
        std::string                    m_MailTitle;        //邮件标题(subject)
        std::string                    m_TextBody;            //邮件正文
    
        RECEIVERS                    m_Receivers;        //邮件接收者(name,email_address)
    
        std::list<std::string>        m_FilePathList;        //附件路径_list
    
        /////////////////////////邮件信息///////////////////////////////////
    };

    源文件:

    /*
    **CSendMail源文件
    **实现邮件的发送功能,支持多个用户接收,支持附件
    **program by six_beauty
    */
    
    //#include <afx.h>
    #include "CSendMail.h"
    #include "time.h"
    #include <sstream>
    #include <fstream>
    #pragma comment(lib,"WSOCK32")  
    #pragma comment(lib, "ws2_32")
    
    
    const std::string _AppOctStrmContent_encode_chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    
    //类的实现
    
    CSendMail::CSendMail(void)  
    {  
    }  
    
    
    CSendMail::~CSendMail(void)  
    {  
        clearReceiver();
        DeleteAllPath();
    }  
    
    //连接
    bool CSendMail::Connent()
    {
        //邮件信息设置判断
        if(m_ServerName.empty() || m_UserName.empty() || m_UserPwd.empty())
        {
            m_logInfo.logInfo("Connect 失败,请先设置邮件登陆信息!");
            return false;
        }
    
        if(!CReateSocket())//建立连接  
        {  
            m_logInfo.logInfo("建立连接失败!");
            return false;  
        }
    
        if(!Logon())//建立连接  
        {  
            m_logInfo.logInfo("登陆失败!");
            return false;  
        }
        return true;
    }
    
    
    //发送邮件的函数送
    bool CSendMail::SendMail(const std::string mail_title,const std::string send_content)                                        
    {
        //参数赋值
        m_MailTitle=mail_title;
        m_TextBody=send_content;
    
        if(m_SenderName.empty() || m_SenderAddr.empty() || m_Receivers.empty())
        {
            m_logInfo.logInfo("[SendMail]邮件参数设置错误,请检查邮件发送设置信息是否完整!");
            return false;
        }
    
        if(!SendHead())//发送邮件头  
        {  
            m_logInfo.logInfo("发送邮件头失败!");
            return false;  
        }  
    
        if(!SendTextBody())//发送邮件文本部分  
        {  
            return false;  
        }  
    
        if(!SendFileBody())//发送附件  
        {  
            return false;  
        }  
    
        if(!SendEnd())//结束邮件,并关闭sock  
        {  
            return false;  
        }  
    
        return true;
    }
    
    
    ////////////////////////////////////////////设置邮件信息/////////////////////////////////////////////////////////////////////
    void CSendMail::setServerName(const std::string server_name)        //smtp服务器地址    
    {
        m_ServerName=server_name;
    }
    
    void CSendMail::setUserName(const std::string user_name)            //邮箱用户名
    {
        m_UserName=user_name;
    }
    
    void CSendMail::setUserPwd(const std::string user_pwd)                //邮箱用户密码
    {
        m_UserPwd=user_pwd;
    }
    
    void CSendMail::setSenderName(const std::string sender_name)        //发送者的名字
    {
        m_SenderName=sender_name;
    }
    
    void CSendMail::setSenderAddress(const std::string sender_addr)    //发送者的邮箱(mail form:)
    {
        m_SenderAddr=sender_addr;
    }
    
    
    void CSendMail::addReceiver(const std::string name, const std::string address)
    {
        m_Receivers.insert(RECEIVERS::value_type(name, address));
    }
    
    void CSendMail::setReceiver(const std::string name, const std::string address)
    {
        m_Receivers.clear();
        m_Receivers.insert(RECEIVERS::value_type(name, address));
    }
    
    void CSendMail::clearReceiver()
    {
        m_Receivers.clear();
    }
    
    void CSendMail::AddFilePath(std::string szFilePath)//添加附件路径  
    {  
        for(std::list<std::string>::iterator itrList=m_FilePathList.begin();itrList!=m_FilePathList.end();++itrList)
        {
            if( itrList->compare(szFilePath) == 0 )
            {
                //已经存在
                return ;
            }
        }
        //还未加入
        m_FilePathList.push_back(szFilePath);
    }  
    
    void CSendMail::DeleteFilePath(std::string szFilePath)//删除附件路径  
    {  
        for(std::list<std::string>::iterator itrList=m_FilePathList.begin();itrList!=m_FilePathList.end();)
        {
            if( itrList->compare(szFilePath) == 0 )
            {
                itrList = m_FilePathList.erase(itrList);
            }else
            {
                itrList++;
            }
        }
    }  
    
    void CSendMail::DeleteAllPath(void)  
    {  
        m_FilePathList.clear();  
    }  
    
    
    ////////////////////////////////////////////功能函数///////////////////////////////////////////////////////////////////
    
    //实现CString的Replace
    string& CSendMail::replace_all(string& str,const string& old_value,const string& new_value)
    {   
        while(true)
        {   
            string::size_type pos(0);   
            if( (pos=str.find(old_value))!=string::npos )   
                str.replace(pos,old_value.length(),new_value);   
            else
                break;   
        }   
        return str;   
    }
    
    //从附件的路径中获取文件名称
    std::string CSendMail::GetFileName(std::string &szFilePath)
    {
        replace_all(szFilePath,"/","\");
        string szFileName=szFilePath.substr(szFilePath.rfind("\")+1,szFilePath.length());
        return szFileName;
    }
    
    //以字符形式读入附件内容
    std::string CSendMail::GetFileData(std::string szFilePath)
    {
        std::string szBuffer;
        if(szFilePath.empty())  
        {
            m_logInfo.logInfo("[SendFileBody]Error:附件路径为空!");
            return szBuffer;  
        }  
    
        ifstream ifFile(szFilePath.c_str(),ios::binary|ios::in);
        if(!ifFile)  
        {  
            m_logInfo.logInfo("[SendFileBody]Error:打开附件路径错误!");
            return szBuffer;  
        }  
        ifFile.seekg(0,ios::beg);
        std::ostringstream tmp; 
        tmp << ifFile.rdbuf(); 
        szBuffer = tmp.str();    
        ifFile.close();
    
        return szBuffer;  
    }
    
    //把char类型转换成Base64类型 
    std::string CSendMail::Base64Encode(std::string in_str)        
    {
        std::string out_str;
        unsigned char c1, c2, c3;
        int i = 0;
        int len = in_str.length();
    
        while ( i<len )
        {
            // read the first byte
            c1 = in_str[i++];
            if ( i==len )       // pad with "="
            {
                out_str += _AppOctStrmContent_encode_chars[ c1>>2 ];
                out_str += _AppOctStrmContent_encode_chars[ (c1&0x3)<<4 ];
                out_str += "==";
                break;
            }
    
            // read the second byte
            c2 = in_str[i++];
            if ( i==len )       // pad with "="
            {
                out_str += _AppOctStrmContent_encode_chars[ c1>>2 ];
                out_str += _AppOctStrmContent_encode_chars[ ((c1&0x3)<<4) | ((c2&0xF0)>>4) ];
                out_str += _AppOctStrmContent_encode_chars[ (c2&0xF)<<2 ];
                out_str += "=";
                break;
            }
    
            // read the third byte
            c3 = in_str[i++];
            // convert into four bytes string
            out_str += _AppOctStrmContent_encode_chars[ c1>>2 ];
            out_str += _AppOctStrmContent_encode_chars[ ((c1&0x3)<<4) | ((c2&0xF0)>>4) ];
            out_str += _AppOctStrmContent_encode_chars[ ((c2&0xF)<<2) | ((c3&0xC0)>>6) ];
            out_str += _AppOctStrmContent_encode_chars[ c3&0x3F ];
        }
    
        return out_str;
    }
    
    int CSendMail::sendRequest(const std::string content,bool bout)
    {
        int len_s=send(_socket, content.c_str(), content.length(), 0);
        if ( len_s < 0 )
        {
            m_logInfo.logInfo("[ERROR]SEND:%s",content.c_str());
            return false;
        }
        //输出信息
        if(bout)
        {
            m_logInfo.logInfo("[INFO]SEND:%s",content.c_str());
        }
        return len_s;
    }
    
    bool CSendMail::rcvResponse(const std::string expected_response)
    {
        int recv_bytes = 0;
        char response_buffer[MAX_BUFFER_SIZE];
        if ( (recv_bytes = recv(_socket, response_buffer, MAX_BUFFER_SIZE, 0)) < 0 )
        {
            m_logInfo.logInfo("[ERROR]RECV:%s",expected_response.c_str());
            return false;
        }
        //输出信息
        std::string response(response_buffer, recv_bytes);
        m_logInfo.logInfo("[INFO]RECV(%s):%s",expected_response.c_str(),response.c_str());
        if ( response.substr(0, 3) != expected_response )
        {
            return false;
        }
        return true;
    }
    
    std::string CSendMail::prepareDate()
    {
        char date_string[MAX_BUFFER_SIZE];
    
        time_t seconds;
        time(&seconds);
        strftime(date_string, MAX_BUFFER_SIZE, 
            "%a, %d %b %y %H:%M:%S +0800", 
            localtime(&seconds));          // +0800 maybe hard code
    
        return date_string;
    }
    
    ////////////////////////////////////////////////工作函数//////////////////////////////////////////////////////////////////////
    
    bool CSendMail::CReateSocket()  
    {  
        WORD wVersionRequested;  
        WSADATA wsaData;  
        int err;  
        wVersionRequested = MAKEWORD( 2, 2 );  
        err = WSAStartup( wVersionRequested, &wsaData );  
        if ( err != 0 )   
        {  
            m_logInfo.logInfo("WSAStartup调用失败!");
            return false;  
        }  
        if ( LOBYTE( wsaData.wVersion ) != 2 ||  
            HIBYTE( wsaData.wVersion ) != 2 )  
        {  
            WSACleanup( );  
            return false;   
        }  
        _socket = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);  
        if (_socket == INVALID_SOCKET)  
        {  
            m_logInfo.logInfo("socket创建失败!");
            return false;  
        }  
    
        sockaddr_in servaddr;  
        memset(&servaddr,0,sizeof(sockaddr_in));  
        servaddr.sin_family = AF_INET;  
        servaddr.sin_port = htons(SERVICE_PORT);//发邮件一般都是25端口  
    
        struct hostent *hp=gethostbyname(m_ServerName.c_str());//使用名称  
        if (hp == NULL)
        {
            DWORD dwErrCode =    GetLastError();
            return false;
        }
        servaddr.sin_addr.s_addr=*(int*)(*hp->h_addr_list);  
    
    
        int ret = connect(_socket,(sockaddr*)&servaddr,sizeof(servaddr));//建立连接  
        if (ret == SOCKET_ERROR)  
        {  
            DWORD dwErr = GetLastError();
            return false;  
        }  
        if(!rcvResponse("220"))
            return false;
        return true;  
    }  
    
    bool CSendMail::Logon()
    {
        char local_host[MAX_BUFFER_SIZE];
        if ( gethostname(local_host, MAX_BUFFER_SIZE) != 0 )
        {
            m_logInfo.logInfo("Get local host name error!");
            return false;
        }
        
        std::string msg;
        
        msg  = "HELO ";
        msg += std::string(local_host) + "
    ";
        sendRequest(msg);
        if(!rcvResponse("250"))
        {
            return false;
        }
    
        msg = "AUTH LOGIN
    ";
        sendRequest(msg);
        if(!rcvResponse("334"))
        {
            return false;
        }
          
    
        msg = Base64Encode(m_UserName) + "
    ";
        sendRequest(msg);
        if(!rcvResponse("334"))
        {
            return false;
        }
    
        msg = Base64Encode(m_UserPwd) + "
    ";
        sendRequest(msg);
        if(!rcvResponse("235"))
        {
            return false;
        }
    
        return true;//登录成功  
    }
    
    ///////////////////////////////////SendMail////////////////////////////////////////////////////
    //发送邮件头 
    bool CSendMail::SendHead()
    {
        std::string msg;
    
        msg  = "MAIL FROM:<";
        msg += m_SenderAddr + ">
    ";
        sendRequest(msg);
        if(!rcvResponse("250"))
        {
            m_logInfo.logInfo("邮件地址错误:%s",m_SenderAddr.c_str());
            return false;
        }
    
        //遍历获得receiver
        for(RECEIVERS::iterator itrRec=m_Receivers.begin();itrRec!=m_Receivers.end();itrRec++)
        {
            msg  = "RCPT TO: <";
            msg += itrRec->second + ">
    ";
            sendRequest(msg);
            if(!rcvResponse("250"))
            {
                return false;
            }    
        }
    
        msg  = "DATA
    ";
        sendRequest(msg);
        if(!rcvResponse("354"))
        {
            return false;
        }
    
        //发送Headers
        msg  = "From:"" + m_SenderName + ""<" + m_SenderAddr + ">
    ";
    
        //遍历receiver
        msg += "To: ";    
        for(RECEIVERS::iterator itrRec=m_Receivers.begin();itrRec!=m_Receivers.end();itrRec++)
        {
            std::string szRecv;
            szRecv = """ + itrRec->first + ""<" + itrRec->second + ">, ";
            msg += szRecv;
        }
        msg += "
    ";
    
        msg += "Date: ";
        msg += prepareDate() + "
    ";
    
        msg += "Subject: ";
        msg += m_MailTitle + "
    ";
    
        msg += "X-Mailer: six_beauty 
    ";
    
        msg += "MIME-Version: 1.0
    ";
        msg += "Content-type: multipart/mixed;  boundary="INVT"
    
    ";
    
        msg += "
    ";
        sendRequest(msg);
    
        return true;  
    }
    
    bool CSendMail::SendTextBody()  
    {  
        std::string msg;
        msg  = "--INVT
    Content-Type: text/plain;
      charset="gb2312"
    
    ";
        msg += m_TextBody;
        msg += "
    
    ";
        int len_s=sendRequest(msg,true);
    
        if(len_s != msg.length())
        {
            m_logInfo.logInfo("发送邮件正文出错,应该发送长度(%d):实际发送长度(%d)",msg.length(),len_s);
            return false;
        }
    
        return true;    
    }  
    
    
    bool CSendMail::SendFileBody()  
    {  
        std::string msg;
        //遍历发送附件文件
        for(std::list<std::string>::iterator itrList=m_FilePathList.begin();itrList!=m_FilePathList.end();itrList++)
        {
            std::string filePath=*itrList;
            std::string fileName=GetFileName(filePath);
            std::string szContent=GetFileData(filePath);
            
            msg  = "--INVT
    Content-Type: application/octet-stream;
      name="";
            msg += fileName;
            msg += ""
    Content-Transfer-Encoding: base64
    Content-Disposition: attachment;
      filename="";
            msg += fileName;
            msg += ""
    
    ";
            sendRequest(msg,true);
    
            int npos=0,len=szContent.length();
            while(npos<len)
            {
                std::string szBuffer=Base64Encode(szContent.substr(npos,min(len-npos,3000)));
                szBuffer+="
    ";
                sendRequest(szBuffer);
                npos += min(len-npos,3000);
            }
        }
    
        return true;  
    }  
    
    bool CSendMail::SendEnd()  
    {  
        std::string msg;
    
        msg = "--INVT--
    .
    ";
        sendRequest(msg,true);
    
        msg = "QUIT
    ";
        sendRequest(msg,true);
    
        closesocket(_socket);  
        WSACleanup();
        
        return true;  
    }  

    然后是需要用户实现的一个输出类,请自己实现输出的方式,这里直接cout出来。

    #include<iostream>
    #include<stdarg.h>
    
    using namespace std;
    
    const int BUF_SIZE=4096;
    //实现输出类
    class LogInfo
    {
    public:
        LogInfo(){};
        ~LogInfo(){};
    
    
        void logInfo(char *szFormat,...)
        {
            char szBuf[BUF_SIZE]={};
            va_list args;                            //第一步
            va_start(args,szFormat);                 //第二步
            _vsnprintf(szBuf,BUF_SIZE,szFormat,args);    //第三步
            va_end(args);                            //第四步
    
            //在这是实现输出方式
            std::cout<<szBuf<<endl;
            return ;
        }
    };

    最后是测试代码:

    #include "CSendMail.h"
    #include "iostream"
    using namespace std;
    
    
    int main()
    {
        CSendMail sMailer;
        //    freopen("F://mailfile//out.txt","w+",stdout);
    
        sMailer.setServerName("smtp.126.com");                                                        //邮箱smtp,如"smtp.126.com"
        sMailer.setUserName("userName");                                                                //邮箱账号名,如"****@126.com"
        sMailer.setUserPwd("userPwd");                                                                //邮箱密码
        sMailer.setSenderName("陈家喵");                                                                //发件人名字
        sMailer.setSenderAddress("sanyue9394@126.com");                                                //发送邮箱地址,填你账号的地址,上面的邮箱账号名"****@126.com"
    
        sMailer.setReceiver("柯林林","kelinting@163.com");                                            //添加邮件接收者
        sMailer.addReceiver("sixbeauty","sanyue9394@126.com");
    
        sMailer.AddFilePath("F:\mailfile\out.txt");            //添加附件
    //    sMailer.AddFilePath("F:/mailfile/libcurl.exp");                                                //添加附件
    
        //发送第一份邮件
        if(sMailer.Connent())                                                                        //每次发邮件前都需要connect
        {
            if(sMailer.SendMail("CMailSender:阿垃垃圾君","你想快速发家致富吗?你想一夜成名吗?访问 http://www.sb.com"))            //第一个字符串是邮件标题,第二个是邮件内容
                cout<<"邮件发送完成!";
    
        }    
    
        //发送第二份邮件
        if(sMailer.Connent())                                                                        //每次发邮件前都需要connect
        {
            std::string title,content;
            title="测试邮件part_2";
            content="柯林是个大八嘎!";
            if(sMailer.SendMail(title,content))                                                        //第一个字符串是邮件标题,第二个是邮件内容
                cout<<"邮件发送完成!";
    
        }
    
    
        return 0;
    }

    测试时,把

    sMailer.setUserName("userName");                                                          
    sMailer.setUserPwd("userPwd");

    改了就可以发送邮件了。

    附实现好的工程下载:http://download.csdn.net/detail/biantaiwangzi/7948843

    参考:

    比较出名也写得比较好的SMailer【一组实现邮件发送功能的C++封装类】:http://morningspace.51.net/resource/SMailer.php

    实现逻辑抄自【C++发送邮件和附件】:http://blog.csdn.net/ybjx111/article/details/7031055#

  • 相关阅读:
    MySQL统计逗号分隔字段元素的个数
    MySQL学习(十)索引
    MySQL学习(十一)为什么不推荐使用uuid和雪花id
    1.计算机系统概论
    MySQL学习(十二)排序原理
    MySQL学习(九)frm与ibd了解
    MySQL学习(十一)B树与B+树了解
    MySQL 某一列的值加入到另一列
    MySQL学习(十三)InnoDB
    IOSios微信打开网页键盘弹起后页面上滑,导致弹框里的按钮响应区域错位
  • 原文地址:https://www.cnblogs.com/sixbeauty/p/3983525.html
Copyright © 2011-2022 走看看