smtp_mail

14k words

项目地址:

1
https://github.com/lishuhuakai/Mail

代码布局:

1
2
3
4
5
.
├── README.md
├── Smtp.cpp
├── Smtp.h
└── main.cpp

Smtp.h:smtp头文件

Smtp.cpp:smtp实现文件

main.cpp:主函数

smtp是一个简单的发送邮件协议,属于TCP/IP协议族,只要遵循stmp规定的格式就行,底层实现还是socket编程。

目前发送内容和txt附件没有问题

todo:发送其他格式的附件会乱码


Smtp.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#ifndef __SMTP_H__ //避免重复包含
#define __SMTP_H__

#include <iostream>
#include <list>
#include <WinSock2.h>
using namespace std;

const int MAXLEN = 1024;
const int MAX_FILE_LEN = 6000;

static const char base64Char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

struct FILEINFO /*用来记录文件的一些信息*/
{
char fileName[128]; /*文件名称*/
char filePath[256]; /*文件绝对路径*/
};

class CSmtp{
public:
CSmtp(void);
CSmtp(
int port,
string srvDomain, //smtp服务器域名
string userName, //用户名
string password, //密码
string targetEmail, //目的邮件地址
string emailTitle, //主题
string content //内容
);
public:
~CSmtp(void);
public:
int port;
public:
string domain;
string user;
string pass;
string targetAddr;
string title;
string content;
/*为了方便添加文件,删除文件神马的,使用list容器最为方便,相信大家在数据结构里面都学过*/
list <FILEINFO *> listFile;

public:
char buff[MAXLEN + 1];
int buffLen;
SOCKET sockClient; //客户端的套接字
public:
bool CreateConn(); /*创建连接*/

bool Send(string &message);
bool Recv();

void FormatEmailHead(string &email);//格式化要发送的邮件头部
int Login();
bool SendEmailHead(); //发送邮件头部信息
bool SendTextBody(); //发送文本信息
//bool SendAttachment(); //发送附件
int SendAttachment_Ex();
bool SendEnd();
public:
void AddAttachment(string &filePath); //添加附件
void DeleteAttachment(string &filePath); //删除附件
void DeleteAllAttachment(); //删除所有的附件

void SetSrvDomain(string &domain);
void SetUserName(string &user);
void SetPass(string &pass);
void SetTargetEmail(string &targetAddr);
void SetEmailTitle(string &title);
void SetContent(string &content);
void SetPort(int port);
int SendEmail_Ex();
/*关于错误码的说明:1.网络错误导致的错误2.用户名错误3.密码错误4.文件不存在0.成功*/
char* base64Encode(char const* origSigned, unsigned origLength);
};

#endif // !__SMTP_H__

Smtp.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
#include "Smtp.h"
#include <iostream>
#include <fstream>
using namespace std;

#pragma comment(lib, "ws2_32.lib") /*链接ws2_32.lib动态链接库*/

/*base64采用别人的编码,不过,这不是重点,重点是我完成了我的一个比较好的邮件发送客户端*/
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;
bool havePadding = origLength > numOrig24BitValues * 3;
bool havePadding2 = origLength == numOrig24BitValues * 3 + 2;
unsigned const numResultBytes = 4 * (numOrig24BitValues + havePadding);
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 = "";

WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 1);
err = WSAStartup(wVersionRequested, &wsaData);
this->sockClient = 0;
}

CSmtp::~CSmtp(void){
DeleteAllAttachment();
closesocket(sockClient);
WSACleanup();
}


CSmtp::CSmtp(
int port,
string srvDomain,
string userName,
string password,
string targetEmail,
string emailTitle,
string content
){
this->content = content;
this->port = port;
this->user = userName;
this->pass = password;
this->targetAddr = targetEmail;
this->title = emailTitle;
this->domain = srvDomain;

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对象
SOCKADDR_IN addrSrv;
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);
int err = connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)); //向服务器发送请求
if (err != 0)
{
return false;
//printf("链接失败\n");
}
this->sockClient = sockClient;
if (false == Recv())
{
return false;
}
return true;
}

bool CSmtp::Send(string &message){
int err = send(sockClient, message.c_str(), message.length(), 0);
if (err == SOCKET_ERROR){
return false;
}
cout << message.c_str() << endl;
return true;
}

bool CSmtp::Recv(){
memset(buff, 0, sizeof(char)* (MAXLEN + 1));
int err = recv(sockClient, buff, MAXLEN, 0); //接收数据
if (err == SOCKET_ERROR){
return false;
}
buff[err] = '\0';
cout << buff << endl;
return true;
}

int CSmtp::Login(){
string sendBuff;
sendBuff = "EHLO ";
sendBuff += user;
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()) { //发送用户名,并接收服务器的返回
return 1; /*错误码1表示发送失败由于网络错误*/
}

sendBuff.empty();
ecode = base64Encode(pass.c_str(), strlen(pass.c_str()));
sendBuff = ecode;
sendBuff += "\r\n";
delete[]ecode;

if (false == Send(sendBuff) || false == Recv()) { //发送用户密码,并接收服务器的返回
return 1; /*错误码1表示发送失败由于网络错误*/
}

if (NULL != strstr(buff, "550"))
{
return 2;/*错误码2表示用户名错误*/
}

if (NULL != strstr(buff, "535")) /*535是认证失败的返回*/
{
return 3; /*错误码3表示密码错误*/
}
return 0;
}

bool CSmtp::SendEmailHead() //发送邮件头部信息
{
string sendBuff;
sendBuff = "MAIL FROM: <" + user + ">\r\n";
if (false == Send(sendBuff) || false == Recv())
{
return false; /*表示发送失败由于网络错误*/
}


sendBuff.empty();
sendBuff = "RCPT TO: <" + targetAddr + ">\r\n";
if (false == Send(sendBuff) || false == Recv())
{
return false; /*表示发送失败由于网络错误*/
}

sendBuff.empty();
sendBuff = "DATA\r\n";
if (false == Send(sendBuff) || false == Recv())
{
return false; //表示发送失败由于网络错误
}

sendBuff.empty();
FormatEmailHead(sendBuff);
if (false == Send(sendBuff))
//发送完头部之后不必调用接收函数,因为你没有\r\n.\r\n结尾,服务器认为你没有发完数据,所以不会返回什么值
{
return false; /*表示发送失败由于网络错误*/
}
return true;
}

void CSmtp::FormatEmailHead(string &email){/*格式化要发送的内容*/
email = "From: ";
email += user;
email += "\r\n";

email += "To: ";
email += targetAddr;
email += "\r\n";

email += "Subject: ";
email += title;
email += "\r\n";

email += "MIME-Version: 1.0";
email += "\r\n";

email += "Content-Type: multipart/mixed;boundary=qwertyuiop";
email += "\r\n";
email += "\r\n";
}

bool CSmtp::SendTextBody() { /*发送邮件文本*/
string sendBuff;
sendBuff = "--qwertyuiop\r\n";
sendBuff += "Content-Type: text/plain;";
sendBuff += "charset=\"gb2312\"\r\n\r\n";
sendBuff += content;
sendBuff += "\r\n\r\n";
return Send(sendBuff);
}

int CSmtp::SendAttachment_Ex() {/*发送附件*/
for (list<FILEINFO *>::iterator pIter = listFile.begin(); pIter != listFile.end(); pIter++){
cout << "Attachment is sending ~~~~~" << endl;
cout << "Please be patient!" << endl;
string sendBuff;
sendBuff = "--qwertyuiop\r\n";
sendBuff += "Content-Type: application/octet-stream;\r\n";
sendBuff += " name=\"";
sendBuff += (*pIter)->fileName;
sendBuff += "\"";
sendBuff += "\r\n";

sendBuff += "Content-Transfer-Encoding: base64\r\n";
sendBuff += "Content-Disposition: attachment;\r\n";
sendBuff += " filename=\"";
sendBuff += (*pIter)->fileName;
sendBuff += "\"";

sendBuff += "\r\n";
sendBuff += "\r\n";
Send(sendBuff);
ifstream ifs((*pIter)->filePath, ios::in | ios::binary);
if (false == ifs.is_open()){
return 4; /*错误码4表示文件打开错误*/
}
char fileBuff[MAX_FILE_LEN];
char *chSendBuff;
memset(fileBuff, 0, sizeof(fileBuff));
/*文件使用base64加密传送*/
while (ifs.read(fileBuff, MAX_FILE_LEN)){
cout << ifs.gcount() << endl;
chSendBuff = base64Encode(fileBuff, MAX_FILE_LEN);
chSendBuff[strlen(chSendBuff)] = '\r';
chSendBuff[strlen(chSendBuff)] = '\n';
send(sockClient, chSendBuff, strlen(chSendBuff), 0);
delete[]chSendBuff;
}
//while (ifs.read(fileBuff, MAX_FILE_LEN)) {
// chSendBuff = new char[MAX_FILE_LEN+3];
// strcpy(chSendBuff, fileBuff);
// chSendBuff[strlen(chSendBuff)] = '\r';
// chSendBuff[strlen(chSendBuff)] = '\n';
// send(sockClient, chSendBuff, strlen(chSendBuff), 0);
// delete[]chSendBuff;
//}


//cout << ifs.gcount() << endl;
chSendBuff = base64Encode(fileBuff, ifs.gcount());
chSendBuff[strlen(chSendBuff)] = '\r';
chSendBuff[strlen(chSendBuff)] = '\n';
int err = send(sockClient, chSendBuff, strlen(chSendBuff), 0);

if (err != strlen(chSendBuff)){
cout << "文件传送出错!" << endl;
return 1;
}
delete[]chSendBuff;
}
return 0;
}

bool CSmtp::SendEnd() { /*发送结尾信息*/
string sendBuff;
sendBuff = "--qwertyuiop--";
sendBuff += "\r\n.\r\n";
if (false == Send(sendBuff) || false == Recv()){
return false;
}
cout << buff << endl;
sendBuff.empty();
sendBuff = "QUIT\r\n";
return (Send(sendBuff) && Recv());
}

int CSmtp::SendEmail_Ex(){
if (false == CreateConn()){
return 1;
}
//Recv();
int err = Login(); //先登录
if (err != 0){
return err; //错误代码必须要返回
}
if (false == SendEmailHead()) { //发送EMAIL头部信息
return 1; /*错误码1是由于网络的错误*/
}
if (false == SendTextBody()){
return 1; /*错误码1是由于网络的错误*/
}
err = SendAttachment_Ex();
if (err != 0){
return err;
}
if (false == SendEnd()){
return 1; /*错误码1是由于网络的错误*/
}
return 0; /*0表示没有出错*/
}

void CSmtp::AddAttachment(string &filePath) { //添加附件
FILEINFO *pFile = new FILEINFO;
strcpy_s(pFile->filePath, filePath.c_str());
const char *p = filePath.c_str();
strcpy_s(pFile->fileName, p + filePath.find_last_of("\\") + 1);
listFile.push_back(pFile);
}

void CSmtp::DeleteAttachment(string &filePath) { //删除附件
list<FILEINFO *>::iterator pIter;
for (pIter = listFile.begin(); pIter != listFile.end(); pIter++){
if (strcmp((*pIter)->filePath, filePath.c_str()) == 0){
FILEINFO *p = *pIter;
listFile.remove(*pIter);
delete p;
break;
}
}
}

void CSmtp::DeleteAllAttachment() /*删除所有的文件*/{
for (list<FILEINFO *>::iterator pIter = listFile.begin(); pIter != listFile.end();){
FILEINFO *p = *pIter;
pIter = listFile.erase(pIter);
delete p;
}
}

void CSmtp::SetSrvDomain(string &domain){
this->domain = domain;
}

void CSmtp::SetUserName(string &user){
this->user = user;
}

void CSmtp::SetPass(string &pass){
this->pass = pass;
}

void CSmtp::SetTargetEmail(string &targetAddr){
this->targetAddr = targetAddr;
}

void CSmtp::SetEmailTitle(string &title){
this->title = title;
}

void CSmtp::SetContent(string &content){
this->content = content;
}

void CSmtp::SetPort(int port){
this->port = port;
}

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include "Smtp.h"
#include <iostream>
using namespace std;

int main(){

CSmtp smtp(
25, /*smtp端口,一般都是25*/
"**************", /*smtp服务器地址*/
"**************", /*你的邮箱地址*/
**************", /*邮箱密码*/
"**************", /*目的邮箱地址*/
"**************", /*主题*/
"**************" /*邮件正文*/
);
/**
//添加附件时注意,\一定要写成\\,因为转义字符的缘故
string filePath("D:\\****");
smtp.AddAttachment(filePath);
**/


/*还可以调用CSmtp::DeleteAttachment函数删除附件,还有一些函数,自己看头文件吧!*/
//filePath = "*****";
//smtp.AddAttachment(filePath);
int err;
if ((err = smtp.SendEmail_Ex()) != 0){
if (err == 1)
cout << "错误1: 由于网络不畅通,发送失败!" << endl;
if (err == 2)
cout << "错误2: 用户名错误,请核对!" << endl;
if (err == 3)
cout << "错误3: 用户密码错误,请核对!" << endl;
if (err == 4)
cout << "错误4: 请检查附件目录是否正确,以及文件是否存在!" << endl;
}
return 0;
}
Comments