- SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控
制信件的中转方式;
- SMTP协议属于TCP/IP协议族,它帮助每台计算机在发送或中转信件时找到下一个目的地,通过SMTP协议所指定的服务器,就可以
把E-mail寄到收信人的服务器上了,整个过程只要几分钟;而SMTP服务器则是遵循SMTP协议的发送邮件服务器,用来发送或中
转发出的电子邮件;
- MIME规范使得二进制文件能够通过SMTP来传输。目前大多数SMTP软件实现都支持8位MIME扩展,从而使二进制文件的传输变得
几乎和纯文本一样简单。
- 当SMTP客户端有邮件要传送时,与SMTP服务器建立一个双向的传输通道;SMTP客户端负责邮件信息传送到一个或多个SMTP服
务器,如果失败则给出报告;
- SMTP服务器可能最终目的地,也可能是中间的中继或者网关。SMTP命令由SMTP客户端产生,发送到SMTP服务器。SMTP响应
由SMTP服务器发送给SMTP客户端,对命令做出回应;
- 邮件传输者可以出现在起始SMTP发送方与最终的SMTP接收方之间建立的连接上,或者出现在通过中间系统的一系列跃点上。一
旦传输通道建立和初始握手完成,SMTP客户端正常初始化邮件事务。这样的事务包括一系列命令,以定义邮件的发送方和目的
地,以邮件内容本身的传递;
- 发送者发送 MAIL 命令来指定发送者的邮件,如果接受者接收这个邮件,就回复OK ,接着发送者发送 RCPT命令来指定接收者的
邮箱,如果被接收同样回复OK,如果不接受则拒绝(不会终止整个通话)。接收者邮箱确定后,发送者用DATA命令指示要发送
数据,并用一个 . 结束发送。如果数据被接收,会收到OK ,然后用QUIT结束会话;
【EHLO】:参数为SMTP客户端全称域名;与服务器确认通知其客户端使用的机器名称和地址,标识身份;
客户端发出后,若服务器支持SMTP服务,则会给出相应的回应;
【AUTH】:使用AUTH LOGIN与服务器进行登录验证;
【MAIL FROM】:发件人信息,若与认证信息不同则被定位为垃圾/恶意邮件;
【RCPT TO】:收件人地址;
【DATA】:邮件基本信息;
【FROM】:邮件基本信息;
【SUBJECT】:邮件标题;
【QUIT】:断开连接;
211 | System status, or system help reply |
214 | Help message |
220 | <domain> Service ready |
221 | <domain> Service closing transmission channel |
235 | Authentication successful |
250 | Requested mail action okay, completed |
251 | User not local; will forward to <forward-path> |
252 | Cannot VRFY user, but will accept message and attempt delivery |
334 | AUTH input |
354 | Start mail input; end with <CRLF>.<CRLF> |
421 | <domain> Service not available, closing transmission channel |
432 | A password transition is needed |
450 | Requested mail action not taken: mailbox unavailable |
451 | Requested action aborted: local error in processing |
452 | Requested action not taken: insufficient system storage" |
454 | Temporary authentication failed |
500 | Syntax error, command unrecognized |
501 | Syntax error in parameters or arguments |
502 | Command not implemented |
503 | Bad sequence of commands |
504 | Command parameter not implemented |
530 | Authentication required |
534 | Authentication mechanism is too weak |
535 | Authentication credentials invalid |
538 | Encryption required for requested authentication mechanism |
550 | Requested action not taken: mailbox unavailable |
551 | User not local; please try <forward-path> |
552 | Requested mail action aborted: exceeded storage allocation |
553 | Requested action not taken: mailbox name not allowed |
554 | Transaction failed |
#include "smtp.h"
#include <iostream>
namespace Jxiepc {
Smtp::Smtp(int port, std::string domain, std::string user, std::string pwd,
std::string t_mail, std::string title, std::string content, std::string type)
: m_port(port), m_domain(domain), m_user(user), m_password(pwd), m_tmail(t_mail),
m_title(title), m_content(content), m_type(type) {
if(init() < 0) {
perror("init error");
}
}
Smtp::~Smtp() {
close(m_sockfd);
}
int Smtp::init() {
if(make_connect() == -1) {
return -1;
}
std::string str;
Recv();
if(strstr(m_buf, "220") == nullptr) { return -1; }
std::cout << "****: " << m_buf << std::endl;
Send("HELO " + m_user + "\r\n");
Recv();
if(strstr(m_buf, "250") == nullptr) { return -1; }
Send("AUTH LOGIN\r\n");
Recv();
if(strstr(m_buf, "334") == nullptr) { return -1; }
str = m_user.substr(0, m_user.find('@', 0));
str = enBase64(str.c_str());
str += "\r\n";
Send(str);
Recv();
if(strstr(m_buf, "334") == nullptr) { return -1; }
Send(enBase64(m_password.c_str()) + "\r\n");
Recv();
if(strstr(m_buf, "235") == nullptr) { return -1; }
std::cout << "AUTH SUCCESS..." << std::endl;
Send("MAIL FROM: <" + m_user + ">\r\n");
Recv();
if(strstr(m_buf, "250") == nullptr) { return -1; }
Send("RCPT TO: <" + m_tmail+ ">\r\n");
Recv();
if(strstr(m_buf, "250") == nullptr) { return -1; }
Send("DATA\r\n");
Recv();
str = "From: " + m_user + "\r\n";
str += "To: " + m_tmail + "\r\n";
str += "Subject: " + m_title + "\r\n";
str += "Content-Type: multipart/mixed;boundary=qwertyuiop\r\n";
str += "\r\n--qwertyuiop\r\n";
if(m_type == "html"){
str += "content-type:text/html;charset=utf-8\r\n"; //html类型
}else{
str += "Content-Type: text/plain;charset=utf-8\r\n"; //文本类型
}
Send(str);
str = "\r\n" + m_content + "\r\n";
str += "\r\n--qwertyuiop--\r\n.\r\n";
Send(str);
Recv();
if(strstr(m_buf, "250") == nullptr) { return -1; }
std::cout << "send success..." << std::endl;
Send("QUIT\r\n");
return 0;
}
int Smtp::make_connect() {
m_sockfd = Socket(AF_INET, SOCK_STREAM, 0);
hostent *host_info = gethostbyname(m_domain.c_str());
if(host_info == nullptr){
perror("gethostbyname error");
return -1;
}
if(host_info->h_addrtype != AF_INET) {
perror("AF_INET error");
return -1;
}
char buf[128];
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = *((unsigned long*)host_info->h_addr_list[0]);
addr.sin_port = htons(m_port);
Connect(m_sockfd, (struct sockaddr*)&addr, sizeof(addr));
return 0;
}
void Smtp::Connect(int fd, const struct sockaddr *sa, socklen_t salen) {
if(connect(fd, sa, salen) == -1) {
perror("connect error");
exit(-1);
}
}
int Smtp::Socket(int family, int type, int protocol) {
int sockfd;
if((sockfd = socket(family, type, protocol)) == -1) {
perror("socket error");
exit(-1);
}
return sockfd;
}
ssize_t Smtp::Send(const std::string& str) {
ssize_t n;
std::cout << str;
if((n = send(m_sockfd, str.c_str(), str.length(), 0)) == -1) {
perror("write error");
exit(-1);
}
return n;
}
ssize_t Smtp::Recv() {
ssize_t n;
m_buf[0] = '\0';
if((n == recv(m_sockfd, m_buf, 0xFFF, 0)) == -1) {
perror("recv error");
exit(-1);
}
return n;
}
std::string Smtp::enBase64(const std::string& str)
{
std::string base64_table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int str_len = str.length();
std::string res="";
for (int strp=0; strp<str_len/3*3; strp+=3)
{
res+=base64_table[str[strp]>>2];
res+=base64_table[(str[strp]&0x3)<<4 | (str[strp+1])>>4];
res+=base64_table[(str[strp+1]&0xf)<<2 | (str[strp+2])>>6];
res+=base64_table[(str[strp+2])&0x3f];
}
if (str_len%3==1)
{
int pos=str_len/3 * 3;
res += base64_table[str[pos]>>2];
res += base64_table[(str[pos]&0x3)<<4];
res += "=";
res += "=";
}
else if (str_len%3==2)
{
int pos=str_len/3 * 3;
res += base64_table[str[pos]>>2];
res += base64_table[(str[pos]&0x3)<<4 | (str[pos+1])>>4];
res += base64_table[(str[pos+1]&0xf)<<2];
res += "=";
}
return res;
}
}