linux c++邮件,linux下基于SMTP协议的C++邮件客户端

完整代码下载:

https://github.com/WaPonX/SMTPMail

在网络中使用SMTP登陆的时候,需要将代码转换成base64编码。

下面这个函数是从网上抄的:

#include

std::string Base64Encode(const std::string& src) {

using std::string;

int i, j, srcLen = src.length();

string dst(srcLen / 3 * 4 + 4, 0);

for(i = 0, j= 0; i <=srcLen - 3; i+=3, j+=4) {

dst[j] = (src[i] & 0xFC) >> 2;

dst[j+1] = ((src[i] & 0x03) << 4) + ((src[i+1] & 0xF0) >> 4);

dst[j+2] = ((src[i+1] & 0x0F) << 2) + ((src[i+2] & 0xC0) >> 6);

dst[j+3] = src[i+2] & 0x3F;

}

if( srcLen % 3 == 1 ) {

dst[j] = (src[i] & 0xFC) >> 2;

dst[j+1] = ((src[i] & 0x03) << 4);

dst[j+2] = 64;

dst[j+3] = 64;

j += 4;

}

else if( srcLen % 3 == 2 ) {

dst[j] = (src[i] & 0xFC) >> 2;

dst[j+1] = ((src[i] & 0x03) << 4) + ((src[i+1] & 0xF0) >> 4);

dst[j+2] = ((src[i+1] & 0x0F) << 2);

dst[j+3] = 64;

j+=4;

}

static unsigned char *base64 =

(unsigned char*)("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");

for(i = 0; i < j; ++i) { //map 6 bit value to base64 ASCII character

dst[i] = base64[(int)dst[i]];

}

return dst;

}

使用这个函数就可以完成base64转码

在linux下你可以使用如下命令:

echo string | base64将string替换成你想要转换的字符串,输出的就是对应的base64编码。

#include

#include

#include

#include

#include

namespace Mail {

bool OpenLogFile(int &log) {

if ( (log = open("./mail.log", O_WRONLY | O_APPEND)) == -1) {

printf("open log file error!\n");

printf("please, make sure have a file called mail.log in the path\n");

return false;

}

return true;

}

std::string GetTime() {

time_t tt = time(NULL);

return std::string(ctime(&tt));

}

void log(const char *str, size_t) {

static int log = -1;

if (log < 0) {

if (!OpenLogFile(log)) {

return ;

}

}

std::string res(GetTime());

res = res + str + "\n";

write (log, res.c_str(), res.length());

}

}// namespace Mail

对上面的三个函数进行说明:

1.

bool OpenLogFile(int &log);

这个函数包装了打开日志文件这个功能,使用了O_WRONLY | O_APPEND,因为只需要写,不需要读而且每次都要写到文件的最后。

2.

std::string GetTime();

这个函数为获取系统当前的时间,并按string类型返回。

3.

void log(const char *str, size_t);

这个函数就是将错误信息写入日志文件的函数,为什么第二个参数没有命名?一开始我是有命名的,后来发现这个参数没用,所以就不命名了。这样的做法可以避免编译器报错。是一个小技巧。

#include "base64encode.h"

#include "log.h"

#include "smtpmail.h"

#include

#include

#include

#include

#include

#include

namespace {

const std::string SMTPPORT("25");

}

namespace Mail {

SMTPMail::SMTPMail (const String &username,

const String &password,

const String &host) :

_islogin(false),

_reuseaddr(true),

_fd(-1),

_paddrfilter(InitAddrInfoFilter()),

_username(username),

_password(Base64Encode(password)),

_host(host){

//_paddrfilter = InitAddrInfoFilter();

}

SMTPMail::~SMTPMail() {

if (_fd > 0) {

close(_fd);

}

if (_paddrfilter != NULL) {

freeaddrinfo(_paddrfilter);

}

}

SMTPMail::AddrType *SMTPMail::InitAddrInfoFilter() {

AddrType *p = new AddrType;

if (p == NULL) {

return NULL;

}

memset(p, 0, sizeof(AddrType));

//p->ai_flags = AI_CANONNAME;

p->ai_family = AF_UNSPEC;

p->ai_socktype = SOCK_STREAM;

struct addrinfo *res = NULL;

int n = getaddrinfo(_host.c_str(), SMTPPORT.c_str(), p, &res);

if (n != 0) {

const char *str = gai_strerror(n);

log(str, strlen(str));

return NULL;

}

if (!Socket(res)) {

freeaddrinfo(res);

const char *str = strerror(errno);

log(str, strlen(str));

return NULL;

}

_islogin = true;

return p;

}

bool SMTPMail::Socket(AddrType *res) {

while (res != NULL) {

_fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

if (_fd > 0) {

if (setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, &_reuseaddr, sizeof(_reuseaddr)) == 0) {

if (connect(_fd, res->ai_addr, sizeof(struct sockaddr)) == 0){

return true;

}

}

close(_fd);

_fd = -1;

}

res = res->ai_next;

}

return false;

}

bool SMTPMail::Login() {

auto Check = [] (const String &res,

const String &mode) -> void {

if (res.substr(0, 3) != mode) {

log(res.c_str(), res.length());

}

};

Send(String("HELO "+ _username +"\r\n"));

Check(Recv(), "250");

Send(String("auth login\r\n"));

Check(Recv(), "334");

Send(Base64Encode(_username) + "\r\n");

Check(Recv(), "334");

Send(String(_password + "\r\n"));

Check(Recv(), "334");

return true;

}

void SMTPMail::SendEmail(const String &to, const String &text) {

if(false == _islogin) {

return;

}

if (!Login()) {

return ;

}

Send(String("MAIL FROM: \r\n"));

Recv();

Send(String("RCPT TO: \r\n"));

Recv();

Send(String("DATA\r\n"));

Recv();

Send(String("to:" + to + "\r\n" + text + "\r\n.\r\n"));

Recv();

Send(String("QUIT\r\n"));

Recv();

}

void SMTPMail::Send(const String &msg) {

if(send(_fd, msg.c_str(), msg.length(), MSG_CONFIRM) < 0) {

const char *str = "send error\n";

log(str, strlen(str));

}

}

SMTPMail::String SMTPMail::Recv() {

const size_t buflen = 512;

static char buf[buflen];

if (recv(_fd, buf, buflen, 0) < 0) {

const char *str = "recv error\n";

log(str, strlen(str));

return String("");

}

return String(buf);

}

}

在这段代码中我只挑一部分函数进行说明,毕竟有的函数很简单,没必要说明:

1.

SMTPMail::AddrType *SMTPMail::InitAddrInfoFilter();这个函数是为了初始化AddrInfo类型的过滤器。因为是动态分配内存,而且是初始化成员,为了能在内存分配失败的时候更加容易的添加异常处理,我讲这部分代码独立出来。

需要注意的是AddrType是我自己typedef出来的一个类型:

typedef struct addrinfo AddrType;struct addrinfo类型在删除的时候需要调用freeaddrinfo函数进行删除。

其实,这个做法还有一个更加明智的代替方法:

bool SMTPMail::Socket(AddrType *res)

使用智能指针。并且为这个智能智能指针定义自己的删除器。

具体做法请自行google

2.

bool SMTPMail::Socket(AddrType *res);

这个函数将尝试连接getaddrinfo返回的所有可用的地址,使用setsockopt函数设置端口可以复用,并连接。

如果成功,则返回true。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
因工作需要在Linux环境中用C++编写个发送邮件的程序,着实费了点周折,最终得以满意解决,现将历程与成果与大家分享! 一、刚开始网上一通逛搜,发现Linux环境下,发邮件使用较多的方法是libesmtp包,网上也有示例,按照相关章的指引,很容易就实现的邮件的发送,但问题是不知道如何实现SSL。 二、发现libesmtp文件中有个smtp_starttls_set_ctx接口,似乎是可以解决ssl问题的,逛搜libesmtp解决SSL发送邮件的解决办法,几乎无任何信息,后来下载了个libesmtp的源代码包libesmtp-1.0.6.tar.bz2,内含examples示例目录,可以直接编译成功,但似乎是只支持tls邮件发送,而不支持ssl邮件的发送,百思不得其解。 三、接着寻找别的解决办法,在CSDN搜到一个csmtp说可以解决SSL邮件发送问题的资源,但下载需要50积分,说心里话能解决问题50积分也是值得的,但没有呀,提供资源者还比较仁义,告知资来源于https://www.codeproject.com,于是乎在codeproject找到了csmtp的资源,有两个版本,v2.4版本包CSmtp_v2_4_ssl.zip,v1.8版本分为window(CSmtp_v1_8a.zip)和linux(CSmtp_v1_8b.zip)两个包。 四、为了能省点精力,就直接用版较低的linux版吧,解压后发现有makefile文件,可直接编译通过,一般的邮件能发送成功,但可惜的是v1.8版本也不支持ssl协议。 五、其实从包的名字上就能看出来v2.4版本开始支持 ssl协议,但v2.4并不分windows版本和linux版本,是否能支持linux呢,查看源代码发现有对linux的支持,只是包内没有makefile文件,似乎没有在linux目录下编译过,于是编写了个makefile文件尝试编译,竞然编通过,而且发送文件成功,经过测试可以支持ssl邮件的发送,因暂无需求tls未做测试。 六、现将程序重新打包成csmtp_v2.4_linux.tar文件,与大家分享,文中所提到的相关资源包都一并打包到资源中了。 最后感谢原创christopher w. backen提供的代码资源!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值