测试主程序
#include <iostream>
#include<boost/asio.hpp>
#include"logging.h"
#include"smtplib.h"
using namespace std;
using namespace boost::asio;
using namespace logging;
using namespace smtplib;
int main()
{
try{
io_service i;
smtp s(i);
s.set_debuglevel (true);
s.connect ("smtp.qq.com"); //服务器地址
s.helo ("******@qq.com"); //HELO指令,本帐号
s.login ("*****@qq.com","******"); //auth loginr指令,分另为用户名和密码
s.sendmail ("from***@qq.com","to****@qq.com","Subject","body_english only","copy_t0***@qq.com");
//本帐号地址---接收地址----主题-----邮件正文----抄送地址
//编码问题比较复杂.目前只能发送英文邮件内容.
}
catch (std::exception e)
{
cout << e.what ();
}
}
smtplib.h的头文件:
#ifndef SMTPLIB_H
#define SMTPLIB_H
#include<iostream>
#include<string>
#include<sstream>
#include<sstream>
#include<boost/asio.hpp>
#include<boost/format.hpp>
#include"logging.h" //昨天的日志类,
#include"internal/internal.h" //只包含一个网上下载的base64算法
namespace smtplib {
using namespace std;
using namespace boost::asio;
using namespace logging;//昨天编写的logger类,昨天的版本实现和定义写在一个头文件里,
//类内定义了静态变量,当重复包含时,会报重定义的错,已收复这个bug
class smtp
{
public:
smtp(io_service& i);
~smtp();
void set_debuglevel(bool level); //设定调试级别,抄python的smtplib
void connect (const string hostname, const string service = "smtp");
void send_cmd(const string cmd);
void helo(std::string name);
string get_reply(int& retvcode);
void login(const string username, const string password);
void noop();
void quit();
void sendmail(const string from_addr, //发送地址,本帐号
const string to_addr, // 接收地址
const string subject, //主题
const string body, //邮件正文,只能发英文
const string cc);//抄送
private:
io_service& _io_service;
ip::tcp::socket _socket;
int debuglevel;
const string body_decode(const string body); //用于生成邮件正文的编码
};
}
#endif // SMTPLIB_H
smtplib.cpp实现:
#include"smtplib.h"
namespace smtplib{
using namespace std;
using namespace boost::asio;
using namespace internal;
smtp::smtp(io_service &i):_io_service(i),_socket(i){
debuglevel = 0;
}
smtp::~smtp (){
quit (); //实例析构时发送QUIT指令
}
void smtp::set_debuglevel (bool level){
debuglevel = level; //设置调试级别
}
void smtp::connect (const string hostname,const string service){
ip::tcp::resolver resolver(_io_service);
ip::tcp::resolver::query query(hostname, service);
ip::tcp::resolver::iterator iter;
ip::tcp::resolver::iterator enditer;
boost::system::error_code err;
for(iter = resolver.resolve(query); iter != enditer; ++iter){
_socket.connect (*iter,err);
if (!err)
break; //跌代测试服务器地址.连上了就break,否则close,继续迭代
_socket.close ();
}
if(err){
throw runtime_error(err.message ()); //找不到服务器,抛出异常
}
logger().info ("connected"); //连接成功,记录下
}
void smtp::send_cmd (const string cmd){ //抄python的SMTP.docmd(cmd, args='')
boost::system::error_code err;
_socket.write_some(buffer(cmd.data (),cmd.size ()),err);
if (err)
throw runtime_error(err.message ()); //写入出错.抛出异常
int retvcode; //retvcode是答应码
string reply = get_reply (retvcode); //reply是答应的内容
if(retvcode >= 400) //答应码大于400表示出错?抛出一个SmtpError,继承run_timeerror.
throw SmtpError(reply.data ());
if (debuglevel)
logger().debug (reply); //设置了调试级别就写一个日志
}
void smtp::helo (string name){
string cmd = "HELO "+ name + "\r\n"; //HELO指令
send_cmd (cmd);
}
string smtp::get_reply(int &retvcode){ //获得应内容,设置retvcode答应码,返回答应内容
char buf[124];
boost::system::error_code err;
int n = _socket.read_some(buffer(buf,sizeof(buf)),err);
if (err)
throw runtime_error(err.message ());
buf[n-1] = '\0'; //去掉换行符
istringstream is(buf);
is >> retvcode; //提取答应码
return buf;
}
void smtp::login (const string username, const string password){
helo(username);
send_cmd ("auth login\r\n");//auto login指令
send_cmd (get_base64 (username)+ "\r\n"); //get_base64是在网上下载的算法,就不贴出来了
send_cmd (get_base64 (password) + "\r\n");
}
void smtp::noop(){
send_cmd ("noop\r\n");
}
void smtp::quit(){
send_cmd ("quit\r\n");
}
void smtp::sendmail(const string from_addr,
const string to_addr,
const string subject,
const string body,
const string cc){
send_cmd ("mail FROM:" + from_addr + "\r\n");
send_cmd ("rcpt TO:" + to_addr + "\r\n");
send_cmd ("data\r\n");
string data = str(format("From:%s\r\nTo:%s\r\nSubject:%s\r\nCc:%s\r\n%s\r\n.\r\n")
%from_addr
%to_addr
%subject
%cc
%body_decode (body));//body_decode设置邮正文的格式
send_cmd (data);
}
//body_decode设置邮正文的格式,比较复杂.直接用硬编码给出
//格式是直接抄126邮件发过来的邮件
const string smtp::body_decode(const string body){
string retval;
retval.append ("Content-Type: text/plain; charset=GBK\r\n");
retval.append ("Content-Transfer-Encoding: base64\r\n");
retval.append ("\r\n");
retval.append (get_base64 (body));
return retval;
}
}
收到邮件:
还有日志输出
0.2015-03-29T20:42:57.252910 <info> : connected
1.2015-03-29T20:42:57.270519 <debug> : 220 smtp.qq.com Esmtp QQ Mail Server
2.2015-03-29T20:42:57.270519 <debug> : 250 smtp.qq.com
3.2015-03-29T20:42:57.289488 <debug> : 250 smtp.qq.com
4.2015-03-29T20:42:57.310737 <debug> : 334 *************
5.2015-03-29T20:42:57.341512 <debug> : 334 *************
6.2015-03-29T20:42:57.396468 <debug> : 235 Authentication successful
7.2015-03-29T20:42:57.418632 <debug> : 250 Ok
8.2015-03-29T20:42:57.442891 <debug> : 250 Ok
9.2015-03-29T20:42:57.462881 <debug> : 354 End data with <CR><LF>.<CR><LF>
10.2015-03-29T20:42:57.704964 <debug> : 250 Ok: queued as
11.2015-03-29T20:42:57.809904 <debug> : 221 Bye