You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

479 lines
11 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
**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) + "\r\n";
sendRequest(msg);
if (!rcvResponse("250")) {
return false;
}
msg = "AUTH LOGIN\r\n";
sendRequest(msg);
if (!rcvResponse("334")) {
return false;
}
msg = Base64Encode(m_UserName) + "\r\n";
sendRequest(msg);
if (!rcvResponse("334")) {
return false;
}
msg = Base64Encode(m_UserPwd) + "\r\n";
sendRequest(msg);
if (!rcvResponse("235")) {
return false;
}
return true;//登录成功
}
///////////////////////////////////SendMail////////////////////////////////////////////////////
//发送邮件头
bool CSendMail::SendHead() {
std::string msg;
msg = "MAIL FROM:<";
msg += m_SenderAddr + ">\r\n";
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 + ">\r\n";
sendRequest(msg);
if (!rcvResponse("250")) {
return false;
}
}
msg = "DATA\r\n";
sendRequest(msg);
if (!rcvResponse("354")) {
return false;
}
//发送Headers
msg = "From:\"" + m_SenderName + "\"<" + m_SenderAddr + ">\r\n";
//遍历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 += "\r\n";
msg += "Date: ";
msg += prepareDate() + "\r\n";
msg += "Subject: ";
msg += m_MailTitle + "\r\n";
msg += "X-Mailer: six_beauty \r\n";
msg += "MIME-Version: 1.0\r\n";
msg += "Content-type: multipart/mixed; boundary=\"INVT\"\r\n\r\n";
msg += "\r\n";
sendRequest(msg);
return true;
}
bool CSendMail::SendTextBody() {
std::string msg;
msg = "--INVT\r\nContent-Type: text/plain;\r\n charset=\"gb2312\"\r\n\r\n";
msg += m_TextBody;
msg += "\r\n\r\n";
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\r\nContent-Type: application/octet-stream;\r\n name=\"";
msg += fileName;
msg += "\"\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment;\r\n filename=\"";
msg += fileName;
msg += "\"\r\n\r\n";
sendRequest(msg, true);
int npos = 0, len = szContent.length();
while (npos<len) {
std::string szBuffer = Base64Encode(szContent.substr(npos, min(len - npos, 3000)));
szBuffer += "\r\n";
sendRequest(szBuffer);
npos += min(len - npos, 3000);
}
}
return true;
}
bool CSendMail::SendEnd() {
std::string msg;
msg = "--INVT--\r\n.\r\n";
sendRequest(msg, true);
msg = "QUIT\r\n";
sendRequest(msg, true);
closesocket(_socket);
WSACleanup();
return true;
}