#include "StdAfx.h"
#include "smtp.h"
#pragma comment(lib, "ws2_32.lib") /*链接ws2_32.lib动态链接库*/
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; //因为6和8的最小公倍数为24,以24bit为一个划分块,看
//能划分出多少块
bool havePadding = origLength > numOrig24BitValues * 3; //如果havepadding == 1,即后面式子成立,则说明有余数
bool havePadding2 = origLength == numOrig24BitValues * 3 + 2;
unsigned const numResultBytes = 4 * (numOrig24BitValues + havePadding);//求出有多少个6bit,即base64码有多少位
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 = "";
/*************************************启动socket服务*********************************/
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 1); //WinSock版本2.1
err = WSAStartup(wVersionRequested, &wsaData);
this->sockClient = 0;
/************************************************************************************/
}
CSmtp::~CSmtp(void)
{
DeleteAllAttachment();
closesocket(sockClient);
WSACleanup();
/*
函数简述:
中止Windows Sockets DLL的使用.
#include <winsock.h>
int PASCAL FAR WSACleanup ( void );
注释:
应用程序或DLL在使用Windows Sockets服务之前必须要进行一次成功的WSAStartup()调用.
当它完成了Windows Sockets的使用后,应用程序或DLL必须调用WSACleanup()将其从Windows Sockets的实现中注销,
并且该实现释放为应用程序或DLL分配的任何资源.任何打开的并已建立连接的SOCK_STREAM类型套接口在调用
WSACleanup()时会重置; 而已经由closesocket()关闭却仍有要发送的悬而未决数据的套接口则不会受影响- 该
数据仍要发送.
*/
}
CSmtp::CSmtp(
int port,
std::string srvDomain,
std::string userName,
std::string password,
std::string targetEmail,
std::string emailTitle,
std::string content
)
{
this->port = port;
this->domain = srvDomain;
this->user = userName;
this->pass = password;
this->content = content;
this->targetAddr = targetEmail;
this->title = emailTitle;
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 1);
err = WSAStartup(wVersionRequested, &wsaData);
this->sockClient = 0;
}
bool CSmtp::CreateConn()
{
//为建立socket对象做准备,初始化环境
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0); //建立socket对象
/*
原型:int socket (int domain, int type, int protocol)
功能描述:初始化创建socket对象,通常是第一个调用的socket函数。
成功时,返回非负数的socket描述符;失败是返回-1。socket描述符是一个指向内部数据结构的指针,
它指向描述符表入口。调用socket()函数时,socket执行体将建立一个socket,实际上"建立一个socket"
意味着为一个socket数据结构分配存储空间。socket执行体为你管理描述符表。
参数解释:
domain -- 指明使用的协议族。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、
AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地
址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
type -- 指明socket类型,有3种:
SOCK_STREAM -- TCP类型,保证数据顺序及可靠性;
SOCK_DGRAM -- UDP类型,不保证数据接收的顺序,非可靠连接;
SOCK_RAW -- 原始类型,允许对底层协议如IP或ICMP进行直接访问,不太常用。
protocol -- 通常赋值"0",由系统自动选择。
*/
SOCKADDR_IN addrSrv;
/*
通常的做法是:填值的时候使用sockaddr_in结构,而作为函数(如bin, accept, connect等)的
参数传入的时候转换成sockaddr结构就行了,毕竟都是16个字符长。
*/
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);
/*
sin_family指代协议族,在socket编程中只能是AF_INET
sin_port存储端口号(使用网络字节顺序)
sin_addr存储IP地址,使用in_addr这个数据结构
sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
s_addr按照网络字节顺序存储IP地址
*/
int err = connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); //向服务器发送请求
/*
WINSOCK_API_LINKAGE
int
WSAAPI
connect(
SOCKET s,
const struct sockaddr FAR * name,
int namelen
);
第一个参数是客户端的套接字(表明即将发起连接请求),
第二个参数是服务端的套接字所在的“地方”(“地方”是我自定义的专有名词),
第三个参数是该“地方”的大小。
如果请求连接成功,则返回0,否则返回错误码。
*/
if (err != 0)
{
return false;
//printf("链接失败\n");
}
this->sockClient = sockClient;
if (false == Recv())
{
return false;
}
return true;
}
bool CSmtp::Send(std::string &message)
{
int err = send(sockClient, message.c_str(), message.length(), 0);
if (err == SOCKET_ERROR)
{
return false;
}
std::string message01;
return true;
}
bool CSmtp::Recv()
{
memset(buff, 0, sizeof(char) * (MAXLEN + 1)); //初始化buff数组为全0
int err = recv(sockClient, buff, MAXLEN, 0); //接收数据
if (err == SOCKET_ERROR)
{
return false;
}
buff[err] = '\0';
return true;
}
int CSmtp::Login()
{
std::string sendBuff;
sendBuff = "EHLO ";
sendBuff += user; // 这一部分需要通过telnet验证一下
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()) //发送用户名