• c++使用stmp协议发送电子邮件(163邮箱,TTL非SSL)


    0、有关TLS和SSL

    SSL/TLS是用来加密邮件流的。

    假设不加密的话非常easy被人破解。



    只是既然是要发送广告邮件,这个就不用在意了,使用没加密的即可。

    另外在使用的时候,发现,qq的邮箱须要使用SSL才干连接上,这个再以后须要用qq发群邮件的时候再来处理。这里临时仅仅是使用163的邮箱,作为发送端

    1、基本的类

    SendMail.h:
    /***********************************************************************
    *发送邮件模块头文件
    *能够发送文本和附件(支持多个附件一起发送)
    *************************************************************************/
    
    #ifndef  __INCLUDE_SEND_MAIL_H__
    #define  __INCLUDE_SEND_MAIL_H__
    
    
    #include "vector"
    using namespace std;
    
    #include "atlenc.h"
    #include "winsock2.h"
    
    #pragma comment(lib, "wsock32.lib")
    
    
    struct sMailInfo					//邮件信息
    {
    	char *mailbox_user_name;		//用户登录邮箱的名称
    	char *mailbox_user_pwd;			//用户登录邮箱的password
    	char *mailbox_sender_show_name;	//用户发送时显示的名称
    	char *mailbox_sender;			//发送者的邮箱地址
    	char *mailbox_receiver;			//接收者的邮箱地址
    	char *mail_title;				//邮箱标题
    	char *mail_body;				//邮件文本正文
    	char mail_server_ip_addr[32];	//服务器的IP
    	char *mail_server_name;			//服务器的名称(IP与名称二选一,优先取名称)
    	sMailInfo() { memset(this, 0, sizeof(sMailInfo)); }
    };
    
    
    class CSendMail
    {
    public:
    	CSendMail(void);
    	~CSendMail(void);
    
    public:
    	bool BeginSendMail(sMailInfo &smail_info);	//发送邮件,须要在发送的时候初始化邮件信息
    	void AddFilePath(char *file_path);			//加入附件的决定路径到附件列表中
    	void DeleteFilePath(char *file_path);		//删除附件路径,假设有的话
    	void DeleteAllPath(void);					//删除所有附件的路径
    
    protected:
    	void GetFileName(char *file_name, char *file_path);	//从附件的路径中获取文件名
    	void Char2Base64(char *buff_64, char *src_buff, int length);//把char类型转换成Base64类型
    	bool CReateSocket(SOCKET &sock);			//建立socket连接
    	bool Logon(SOCKET &sock);					//登录邮箱。主要进行发邮件前的准备工作
    	int GetFileData(char *file_path);			//由文件路径获取附件内容
    
    	bool SendHead(SOCKET &sock);				//发送邮件头
    	bool SendTextBody(SOCKET &sock);			//发送邮件文本正文
    	bool SendFileBody(SOCKET &sock);			//发送邮件附件
    	bool SendEnd(SOCKET &sock);					//发送邮件结尾
    
    protected:
    	vector<char *> file_path_list_;				//记录附件路径
    	char  send_buff_[4096];						//发送缓冲区
    	char  receive_buff_[1024];
    	char *file_buffer_;							// 指向附件的内容
    	sMailInfo mail_info_;
    };
    
    
    #endif
    


    SendMail.cpp:
    /************************************************************************
    * 发送邮件模块
    *能够发送文本和附件(支持多个附件一起发送)
    *Date:2011-12-01
    ************************************************************************/
    #include "StdAfx.h"
    #include "fstream"
    using namespace std;
    
    #include "SendMail.h"
    
    
    CSendMail::CSendMail(void)
    {
    	file_buffer_ = NULL;
    	memset(send_buff_, 0, sizeof(send_buff_));
    	memset(receive_buff_, 0, sizeof(receive_buff_));
    }
    
    
    CSendMail::~CSendMail(void)
    {
    	DeleteAllPath();
    }
    
    
    void CSendMail::Char2Base64(char* buff_64, char* src_buff, int length)
    {
    	//1   1   1   1   1   1   1   1
    	// 分配给pBuff64  ↑ 分配给pBuff64+1
    	// point所在的位置
    	static char base_64_encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";//base64所映射的字符表
    	int point;		// 每个源字符拆分的位置,可取2,4,6。初始为2
    	point = 2;
    	int base_index; // base64字符的索引
    	char n = 0;		// 上一个源字符的残留值
    	for(int index = 0; index < length; index++)
    	{
    		if(point == 2)
    		{
    			base_index = ((*src_buff) >> point) & 0x3f; // 取得pSrcBuff的高point位
    		}
    		else if (point == 4)
    		{
    			base_index = ((*src_buff) >> point) & 0xf;	// 取得pSrcBuff的高point位
    		}
    		else if(point == 6)
    		{
    			base_index = ((*src_buff) >> point) & 0x3;	// 取得pSrcBuff的高point位
    		}
    		base_index += n;								// 与pSrcBuff-1的低point结合组成Base64的索引
    		*buff_64++ = base_64_encode[base_index];		// 由索引表得到pBuff64
    		n = ((*src_buff) << (6 - point));				// 计算源字符中的残留值
    		n = n & 0x3f;									//确保n的最高两位为0
    		point += 2;										//源字符的拆分位置上升2
    		if(point == 8)		//假设拆分位置为8说明pSrcBuff有6位残留。能够组成一个完整的Base64字符。所以直接再组合一次
    		{
    			base_index = (*src_buff) & 0x3f;			//提取低6位。这就是索引了
    			*buff_64++ =base_64_encode[base_index];
    			n = 0;			// 残留值为0
    			point = 2;		// 拆分位置设为2
    		}
    		src_buff++;
    
    	}
    	if(n != 0)
    	{
    		*buff_64++ = base_64_encode[n];
    	}
    	if(length % 3  == 2)	// 假设源字符串长度不是3的倍数要用'='补全
    	{
    		*buff_64 = '=';
    	}
    	else if(length % 3 == 1)
    	{
    		*buff_64++ = '=';
    		*buff_64 = '=';
    	}
    }
    
    
    void CSendMail::AddFilePath(char * file_path)	// 加入附件路径
    {
    	if(file_path == NULL)
    	{
    		return;
    	}
    	int i;
    	char *temp;
    	for (int index = 0; index < file_path_list_.size(); index++)
    	{
    		temp = file_path_list_[index];
    		if(strcmp(file_path,temp) == 0)			// 假设已经存在就不用再加入了
    		{
    			return;
    		}
    	}
    	file_path_list_.push_back(file_path);
    }
    
    
    void CSendMail::DeleteFilePath(char* file_path) // 删除附件路径
    {
    	int i;
    	char* temp;
    	for (int index = 0; index < file_path_list_.size(); index++)
    	{
    		temp = file_path_list_[index];
    		if (strcmp(file_path, temp) == 0)
    		{
    			file_path_list_.erase(file_path_list_.begin() + index);
    		}
    	}
    }
    
    
    void CSendMail::DeleteAllPath(void)
    {
    	vector<char *>().swap(file_path_list_);		// 清除容器并最小化它的容量
    }
    
    
    int CSendMail::GetFileData(char* file_path)
    {
    	file_buffer_ = NULL;
    	if(file_path == NULL)
    	{
    		return 0;
    	}
    
    	// 通过读取文件的大小,来开辟数组空间的大小
    	ifstream fin(file_path);
    	if (!fin.is_open())
    	{
    		return -1;
    	}
    	fin.seekg(0, ios::end);
    	int file_length = fin.tellg();
    	fin.seekg(0, ios::beg);
    	file_buffer_ = new char[file_length];
    	fin.read(file_buffer_, file_length);
    	fin.close();
    
    	return file_length;
    }
    
    
    void CSendMail::GetFileName(char* file_name,char* file_path)
    {
    	if(file_path == NULL || file_name == NULL)
    	{
    		return;
    	}
    	for(int index = 0; index < (int)strlen(file_path); index++)
    	{
    		if(file_path[strlen(file_path) - 1 - index] == '\')
    		{
    			memcpy(file_name, &file_path[strlen(file_path) - index], index);
    			return;
    		}
    	}
    }
    
    
    bool CSendMail::CReateSocket(SOCKET &sock)
    {
    	WORD wVersionRequested;
    	WSADATA wsaData;
    	int err;
    	wVersionRequested = MAKEWORD( 2, 2 );
    	err = WSAStartup(wVersionRequested, &wsaData);
    	if (err != 0) 
    	{
    		return false;
    	}
    	if (LOBYTE(wsaData.wVersion) != 2 ||
    		HIBYTE(wsaData.wVersion) != 2 )
    	{
    		WSACleanup();
    		return false; 
    	}
    	sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    	if (sock == INVALID_SOCKET)
    	{
    		return false;
    	}
    
    	sockaddr_in server_addr;
    	memset(&server_addr, 0, sizeof(sockaddr_in));
    	server_addr.sin_family = AF_INET;
    	server_addr.sin_port = htons(25);	// 发邮件一般都是25port,SSL的是465port
    	if(mail_info_.mail_server_name == "")
    	{
    		server_addr.sin_addr.s_addr = inet_addr(mail_info_.mail_server_ip_addr);	// 直接使用IP地址
    	}
    	else
    	{
    		struct hostent *hp = gethostbyname(mail_info_.mail_server_name);	// 使用名称
    		server_addr.sin_addr.s_addr = *(int*)(*hp->h_addr_list);
    		char* ip = inet_ntoa(server_addr.sin_addr);
    		strcpy(mail_info_.mail_server_ip_addr, ip);
    	}
    
    
    	int ret = connect(sock, (sockaddr*)&server_addr, sizeof(server_addr));	// 建立连接
    	if (ret == SOCKET_ERROR)
    	{
    		return false;
    	}
    
    	return true;
    }
    
    
    bool CSendMail::Logon(SOCKET &sock)
    {
    	recv(sock, receive_buff_, 1024, 0);
    
    	memset(send_buff_, 0, sizeof(send_buff_));
    	sprintf_s(send_buff_, "HELO %s
    ", mail_info_.mail_server_ip_addr);
    	send(sock,send_buff_, strlen(send_buff_), 0);	// 開始会话
    	memset(receive_buff_, 0, sizeof(receive_buff_));
    	recv(sock,receive_buff_, 1024, 0);
    	if(receive_buff_[0] != '2' || receive_buff_[1] != '5' || receive_buff_[2] != '0')
    	{
    		return false;
    	}
    
    	memset(send_buff_, 0, sizeof(send_buff_));
    	sprintf_s(send_buff_, "AUTH LOGIN
    ");
    	send(sock,send_buff_, strlen(send_buff_),0);	// 请求登录
    	recv(sock,receive_buff_, 1024, 0);
    	if(receive_buff_[0] != '3' || receive_buff_[1] != '3' || receive_buff_[2] != '4')
    	{
    		return false;
    	}
    
    	memset(send_buff_, 0, sizeof(send_buff_));
    	Char2Base64(send_buff_, mail_info_.mailbox_user_name, strlen(mail_info_.mailbox_user_name));
    	send_buff_[strlen(send_buff_)] = '
    ';
    	send_buff_[strlen(send_buff_)] = '
    ';
    	send(sock,send_buff_, strlen(send_buff_), 0);	// 发送username
    	recv(sock,receive_buff_, 1024, 0);
    	if(receive_buff_[0] != '3' || receive_buff_[1] != '3' || receive_buff_[2] != '4')
    	{
    		return false;
    	}
    
    	memset(send_buff_, 0, sizeof(send_buff_));
    	Char2Base64(send_buff_, mail_info_.mailbox_user_pwd, strlen(mail_info_.mailbox_user_pwd));
    	send_buff_[strlen(send_buff_)] = '
    ';
    	send_buff_[strlen(send_buff_)] = '
    ';
    	send(sock, send_buff_, strlen(send_buff_), 0);	// 发送用户password
    	recv(sock, receive_buff_, 1024, 0);
    	if(receive_buff_[0] != '2' || receive_buff_[1] != '3' || receive_buff_[2] != '5')
    	{
    		return false;
    	}
    	return true;	// 登录成功
    }
    
    
    bool CSendMail::SendHead(SOCKET &sock)
    {
    	int rt;
    	memset(send_buff_, 0, sizeof(send_buff_));
    	sprintf_s(send_buff_, "MAIL FROM:<%s>
    ", mail_info_.mailbox_sender);
    	rt = send(sock,send_buff_, strlen(send_buff_), 0);
    
    	if(rt != strlen(send_buff_))
    	{
    		return false;
    	}
    	recv(sock, receive_buff_, 1024, 0);
    
    	memset(send_buff_, 0, sizeof(send_buff_));
    	sprintf_s(send_buff_, "RCPT TO:<%s>
    ", mail_info_.mailbox_receiver);
    	rt = send(sock,send_buff_, strlen(send_buff_),0);
    	if(rt != strlen(send_buff_))
    	{
    		return false;
    	}
    	recv(sock, receive_buff_, 1024, 0);
    
    	memset(send_buff_, 0, sizeof(send_buff_));
    	memcpy(send_buff_, "DATA
    ", strlen("DATA
    "));
    	rt = send(sock, send_buff_, strlen(send_buff_),0);
    	if(rt != strlen(send_buff_))
    	{
    		return false;
    	}
    	recv(sock, receive_buff_, 1024, 0);
    
    	memset(send_buff_, 0, sizeof(send_buff_));
    	sprintf_s(send_buff_, "From:"%s"<%s>
    ", mail_info_.mailbox_sender_show_name, mail_info_.mailbox_sender);
    	sprintf_s(&send_buff_[strlen(send_buff_)], 150, "To:"INVT.COM.CN"<%s>
    ", mail_info_.mailbox_receiver);
    	sprintf_s(&send_buff_[strlen(send_buff_)], 150, "Subject:%s
    Mime-Version: 1.0
    Content-Type: multipart/mixed; boundary="INVT"
    
    ", mail_info_.mail_title);
    	rt = send(sock, send_buff_, strlen(send_buff_), 0);
    	if(rt != strlen(send_buff_))
    	{
    		return false;
    	}
    
    	return true;
    }
    
    
    bool CSendMail::SendTextBody(SOCKET &sock)
    {
    	int rt;
    	memset(send_buff_, 0, sizeof(send_buff_));
    	sprintf_s(send_buff_, "--INVT
    Content-Type: text/plain;
      charset="gb2312"
    
    %s
    
    ", mail_info_.mail_body);
    	rt = send(sock, send_buff_, strlen(send_buff_), 0);
    	if(rt != strlen(send_buff_))
    	{
    		return false;
    	}
    	else
    	{
    		return true;
    	}
    }
    
    
    bool CSendMail::SendFileBody(SOCKET &sock)
    {
    	char* file_path;
    	int rt;
    	int len;
    	char file_name[128];
    	int pt = 0;
    	int dest_length = 0;
    
    	for(int index = 0; index < file_path_list_.size(); index++)
    	{
    		pt = 0;
    		memset(file_name, 0, 128);
    		file_path = file_path_list_[index];
    		len = GetFileData(file_path);
    		GetFileName(file_name,file_path);
    
    		sprintf_s(send_buff_, "--INVT
    Content-Type: application/octet-stream;
      name="%s"
    Content-Transfer-Encoding: base64
    Content-Disposition: attachment;
      filename="%s"
    
    ", file_name, file_name);
    		send(sock,send_buff_, strlen(send_buff_), 0);
    		while (pt < len)
    		{
    			memset(send_buff_,0,sizeof(send_buff_));
    
    			// 760为1024的最大整数,(760/4)*3=570
    			dest_length = 4096;
    			Base64Encode((BYTE*)file_buffer_ + pt, min(len-pt,570), send_buff_, &dest_length);
    			int len1 = strlen(send_buff_);
    
    			pt += min(len-pt, 570);
    			if(pt == len)
    			{
    				send_buff_[len1  ] = '
    ';
    				send_buff_[len1+1] = '
    ';
    				send_buff_[len1+2] = '';
    			}
    			else
    				send_buff_[len1  ] = '';
    
    			rt = send(sock,send_buff_, strlen(send_buff_), 0);
    			if(rt != strlen(send_buff_))
    			{
    				return false;
    			}
    		}
    		if(len!=0)
    		{
    			delete[] file_buffer_;
    		}
    	}
    
    	return true;
    }
    
    
    bool CSendMail::SendEnd(SOCKET &sock)
    {
    	sprintf_s(send_buff_, "--INVT--
    .
    ");
    	send(sock,send_buff_, strlen(send_buff_), 0);
    
    	sprintf_s(send_buff_, "QUIT
    ");
    	send(sock,send_buff_, strlen(send_buff_), 0);
    	closesocket(sock);
    	WSACleanup();
    	return true;
    }
    
    
    bool CSendMail::BeginSendMail(sMailInfo &smail_info)
    {
    	memcpy(&mail_info_, &smail_info, sizeof(smail_info));
    	if(mail_info_.mail_body == NULL
    	|| mail_info_.mail_server_ip_addr == NULL
    	|| mail_info_.mail_server_name == NULL
    	|| mail_info_.mailbox_receiver == NULL
    	|| mail_info_.mailbox_sender == NULL
    	|| mail_info_.mailbox_sender_show_name == NULL
    	|| mail_info_.mail_title == NULL
    	|| mail_info_.mailbox_user_name == NULL
    	|| mail_info_.mailbox_user_pwd == NULL)
    	{
    		return false;
    	}
    
    	SOCKET sock;
    	if(!CReateSocket(sock)) // 建立连接
    	{
    		return false;
    	}
    
    	if(!Logon(sock))		// 登录邮箱
    	{
    		return false;
    	}
    
    	if(!SendHead(sock))		// 发送邮件头
    	{
    		return false;
    	}
    
    	if(!SendTextBody(sock)) // 发送邮件文本部分
    	{
    		return false;
    	}
    
    	if(!SendFileBody(sock)) // 发送附件
    	{
    		return false;
    	}
    
    	if(!SendEnd(sock))		// 结束邮件,并关闭sock
    	{
    		return false;
    	}
    
    	return true;
    }


    2、測试使用以及效果

    申请了一个測试邮箱(任意申请的)

    账号是:qq2590995733@163.com
    password是:123456789z

    那么给自己发送邮箱(含有附件的測试project例如以下)

    // ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include "iostream"
    using namespace std;
    
    #include "SendMail.h"
    
    
    void TestSendMail()
    {
    	for (int index = 0; index < 10; index++)
    	{
    		CSendMail mail;
    		sMailInfo info;
    
    	//	memcpy(info.m_pcIPAddr, "123.125.50.138", strlen("123.125.50.138"));
    		info.mail_server_name = "smtp.163.com";
    		info.mailbox_receiver = "qq2590995733@163.com";
    		info.mailbox_sender = "qq2590995733@163.com";
    		info.mailbox_user_name = "qq2590995733";
    		info.mailbox_user_pwd = "123456789z";
    
    		info.mailbox_sender_show_name = "taobao客服";
    		info.mail_title = "游戏激活key";
    		info.mail_body = "XX,你妈妈喊你回家吃饭了!!

    "; mail.AddFilePath("c:\Download History.log"); mail.AddFilePath("c:\1.jpg"); mail.AddFilePath("c:\3.log"); mail.BeginSendMail(info); } } int _tmain(int argc, _TCHAR* argv[]) { TestSendMail(); return 0; }


    效果例如以下:




    3、补充

    代码是參考csdn一个project的,原来是依赖于mfc的,给修正了一下,只是还是须要增加atl(做base64的编码)。还得多多感谢原作者!


    事实上这里还是存在有问题的,比方发送出去的附件,在下载的时候,是有那么一点点被改变了。



    只是这里主要是想要表达怎样发送邮件,所以。。应该会在后面进行修复吧。


    高速下载測试projectcsdn(vs2012):

    版权声明:本文博客原创文章。博客,未经同意,不得转载。

  • 相关阅读:
    Linux下目录的权限详解
    Linux下mknod详解
    迅雷精简版-无广告
    PotPlayer一款简洁好用的播放器
    Linux 信号signal处理机制
    Kruskal算法求最小生成树
    Prim算法求权数和,POJ(1258)
    Prim算法求最大权,POJ(2485)
    Prim算法
    DP找最优配置,(POJ1018)
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4653426.html
Copyright © 2020-2023  润新知