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

0、有关TLS和SSL

SSL/TLS是用来加密邮件流的。如果不加密的话很容易被人破解。

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

另外在使用的时候,发现,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;			//用户登录邮箱的密码
	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 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().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);	// 发邮件一般都是25端口,SSL的是465端口
	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\r\n", 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\r\n");
	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_)] = '\r';
	send_buff_[strlen(send_buff_)] = '\n';
	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_pwd, strlen(mail_info_.mailbox_user_pwd));
	send_buff_[strlen(send_buff_)] = '\r';
	send_buff_[strlen(send_buff_)] = '\n';
	send(sock, send_buff_, strlen(send_buff_), 0);	// 发送用户密码
	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>\r\n", 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>\r\n", 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\r\n", strlen("DATA\r\n"));
	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>\r\n", mail_info_.mailbox_sender_show_name, mail_info_.mailbox_sender);
	sprintf_s(&send_buff_[strlen(send_buff_)], 150, "To:\"INVT.COM.CN\"<%s>\r\n", mail_info_.mailbox_receiver);
	sprintf_s(&send_buff_[strlen(send_buff_)], 150, "Subject:%s\r\nMime-Version: 1.0\r\nContent-Type: multipart/mixed; boundary=\"INVT\"\r\n\r\n", 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\r\nContent-Type: text/plain;\r\n  charset=\"gb2312\"\r\n\r\n%s\r\n\r\n", 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\r\nContent-Type: application/octet-stream;\r\n  name=\"%s\"\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment;\r\n  filename=\"%s\"\r\n\r\n", 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  ] = '\r';
				send_buff_[len1+1] = '\n';
				send_buff_[len1+2] = '\0';
			}
			else
				send_buff_[len1  ] = '\0';

			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--\r\n.\r\n");
	send(sock,send_buff_, strlen(send_buff_), 0);

	sprintf_s(send_buff_, "QUIT\r\n");
	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、测试使用以及效果

申请了一个测试邮箱(随意申请的)

账号是:[email protected]
密码是:123456789z

那么给自己发送邮箱(含有附件的测试工程如下)

// 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 = "[email protected]";
		info.mailbox_sender = "[email protected]";
		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;
}

效果如下:
c++使用stmp协议发送邮件(163的邮箱,TTL非SSL)_第1张图片



3、补充

代码是参考csdn一个工程的,原来是依赖于mfc的,给修正了一下,不过还是需要加入atl(做base64的编码)。还得多多感谢原作者!


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


不过这里主要是想要表达如何发送邮件,所以。。应该会在后面进行修复吧。


快速下载测试工程csdn(vs2012):
http://download.csdn.net/detail/zengraoli/7590761

你可能感兴趣的:(C,C++,VC++)