diff --git a/app/wizard/tmpl/http/master_proc.cpp b/app/wizard/tmpl/http/master_proc.cpp index d6342e85f..acfa065e2 100644 --- a/app/wizard/tmpl/http/master_proc.cpp +++ b/app/wizard/tmpl/http/master_proc.cpp @@ -2,7 +2,7 @@ #include "http_servlet.h" #include "master_service.h" -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// // 配置内容项 char *var_cfg_str; @@ -33,7 +33,7 @@ acl::master_int64_tbl var_conf_int64_tab[] = { { 0, 0 , 0 , 0, 0 } }; -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// master_service::master_service() { @@ -52,7 +52,9 @@ void master_service::on_accept(acl::socket_stream* conn) acl::memcache_session session("127.0.0.1:11211"); http_servlet servlet; - servlet.setLocalCharset("utf-8"); // charset: big5, gb2312, gb18030, gbk, utf-8 + + // charset: big5, gb2312, gb18030, gbk, utf-8 + servlet.setLocalCharset("utf-8"); while (true) { if (servlet.doRun(session, conn) == false) diff --git a/app/wizard/tmpl/http/master_threads.cpp b/app/wizard/tmpl/http/master_threads.cpp index 05a0d07b6..67f6c8f65 100644 --- a/app/wizard/tmpl/http/master_threads.cpp +++ b/app/wizard/tmpl/http/master_threads.cpp @@ -2,7 +2,7 @@ #include "http_servlet.h" #include "master_service.h" -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// // 配置内容项 char *var_cfg_redis_addrs; @@ -36,7 +36,7 @@ acl::master_int64_tbl var_conf_int64_tab[] = { { 0, 0 , 0 , 0, 0 } }; -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// master_service::master_service() { diff --git a/app/wizard/tmpl/master/master_aio.cpp b/app/wizard/tmpl/master/master_aio.cpp index 029fe9d1e..05ef43f7b 100644 --- a/app/wizard/tmpl/master/master_aio.cpp +++ b/app/wizard/tmpl/master/master_aio.cpp @@ -1,7 +1,7 @@ #include "stdafx.h" #include "master_service.h" -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// // 配置内容项 char *var_cfg_str; @@ -32,7 +32,7 @@ acl::master_int64_tbl var_conf_int64_tab[] = { { 0, 0 , 0 , 0, 0 } }; -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// // acl::aio_callback 虚类的子类定义 class io_callback : public acl::aio_callback @@ -58,7 +58,7 @@ protected: { if (strncmp(data, "quit", 4) == 0) { - // 可以显式地调用异步流的关闭过程,也可以直接返回 false, + // 可以显式调用异步流的关闭过程,也可以直接返回 false // 通知异步框架自动关闭该异步流 // client_->close(); return false; @@ -99,7 +99,7 @@ private: acl::aio_socket_stream* client_; }; -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// master_service::master_service() { diff --git a/app/wizard/tmpl/master/master_aio.h b/app/wizard/tmpl/master/master_aio.h index 7e6d6b80d..48f10b8ac 100644 --- a/app/wizard/tmpl/master/master_aio.h +++ b/app/wizard/tmpl/master/master_aio.h @@ -1,6 +1,6 @@ #pragma once -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// // 配置内容项 extern char *var_cfg_str; @@ -15,7 +15,7 @@ extern acl::master_int_tbl var_conf_int_tab[]; extern long long int var_cfg_int64; extern acl::master_int64_tbl var_conf_int64_tab[]; -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// //class acl::aio_socket_stream; diff --git a/app/wizard/tmpl/master/master_proc.cpp b/app/wizard/tmpl/master/master_proc.cpp index 04592f028..bc9fbd220 100644 --- a/app/wizard/tmpl/master/master_proc.cpp +++ b/app/wizard/tmpl/master/master_proc.cpp @@ -1,7 +1,7 @@ #include "stdafx.h" #include "master_service.h" -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// // 配置内容项 char *var_cfg_str; @@ -32,7 +32,7 @@ acl::master_int64_tbl var_conf_int64_tab[] = { { 0, 0 , 0 , 0, 0 } }; -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// master_service::master_service() { diff --git a/app/wizard/tmpl/master/master_proc.h b/app/wizard/tmpl/master/master_proc.h index c1079fc38..3898acbe5 100644 --- a/app/wizard/tmpl/master/master_proc.h +++ b/app/wizard/tmpl/master/master_proc.h @@ -1,6 +1,6 @@ #pragma once -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// // 配置内容项 extern char *var_cfg_str; @@ -15,7 +15,7 @@ extern acl::master_int_tbl var_conf_int_tab[]; extern long long int var_cfg_int64; extern acl::master_int64_tbl var_conf_int64_tab[]; -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// //class acl::socket_stream; diff --git a/app/wizard/tmpl/master/master_rpc.cpp b/app/wizard/tmpl/master/master_rpc.cpp index 32d897398..7304a3929 100644 --- a/app/wizard/tmpl/master/master_rpc.cpp +++ b/app/wizard/tmpl/master/master_rpc.cpp @@ -2,7 +2,7 @@ #include "rpc_manager.h" #include "master_service.h" -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// // 配置内容项 char *var_cfg_str; @@ -35,7 +35,7 @@ acl::master_int64_tbl var_conf_int64_tab[] = { { 0, 0 , 0 , 0, 0 } }; -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// class request_rpc : public acl::rpc_request { @@ -76,9 +76,9 @@ protected: // 开始处理该请求 handle_conn(stream); - // 将 ACL_VSTREAM 与阻塞流对象解绑定,这样才能保证当释放阻塞流对象时 - // 不会关闭与请求者的连接,因为该连接本身是属于非阻塞流对象的,需要采 - // 用异步流关闭方式进行关闭 + // 将 ACL_VSTREAM 与阻塞流对象解绑定,这样才能保证当释放阻塞 + // 流对象时不会关闭与请求者的连接,因为该连接本身是属于非阻塞 + // 流对象的,需要采用异步流关闭方式进行关闭 stream.unbind(); } @@ -127,7 +127,7 @@ private: } }; -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// // acl::aio_callback 虚类的子类定义 class io_callback : public acl::aio_callback @@ -153,7 +153,7 @@ protected: { if (strncmp(data, "quit", len) == 0) { - // 可以显式地调用异步流的关闭过程,也可以直接返回 false, + // 可以显式调用异步流的关闭过程,也可以直接返回 false // 通知异步框架自动关闭该异步流 // client_->close(); return false; @@ -209,7 +209,7 @@ private: acl::aio_socket_stream* client_; }; -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// master_service::master_service() { @@ -249,9 +249,11 @@ void master_service::proc_on_init() { // 获得异步框架的事件引擎句柄 acl::aio_handle* handle = get_handle(); + if (handle == NULL) + logger_fatal("aio handle null!"); // 初始化 rpc 服务对象 - rpc_manager::get_instance().init(handle, var_cfg_thread_pool_limit); + rpc_manager::get_instance().init(*handle, var_cfg_thread_pool_limit); } void master_service::proc_on_exit() diff --git a/app/wizard/tmpl/master/master_rpc.h b/app/wizard/tmpl/master/master_rpc.h index 7e6d6b80d..48f10b8ac 100644 --- a/app/wizard/tmpl/master/master_rpc.h +++ b/app/wizard/tmpl/master/master_rpc.h @@ -1,6 +1,6 @@ #pragma once -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// // 配置内容项 extern char *var_cfg_str; @@ -15,7 +15,7 @@ extern acl::master_int_tbl var_conf_int_tab[]; extern long long int var_cfg_int64; extern acl::master_int64_tbl var_conf_int64_tab[]; -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// //class acl::aio_socket_stream; diff --git a/app/wizard/tmpl/master/master_threads.cpp b/app/wizard/tmpl/master/master_threads.cpp index 70b45c8db..558fc44fe 100644 --- a/app/wizard/tmpl/master/master_threads.cpp +++ b/app/wizard/tmpl/master/master_threads.cpp @@ -1,7 +1,7 @@ #include "stdafx.h" #include "master_service.h" -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// // 配置内容项 char *var_cfg_str; @@ -32,7 +32,7 @@ acl::master_int64_tbl var_conf_int64_tab[] = { { 0, 0 , 0 , 0, 0 } }; -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// master_service::master_service() { diff --git a/app/wizard/tmpl/master/master_threads.h b/app/wizard/tmpl/master/master_threads.h index 669773b88..64da551b0 100644 --- a/app/wizard/tmpl/master/master_threads.h +++ b/app/wizard/tmpl/master/master_threads.h @@ -1,6 +1,6 @@ #pragma once -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// // 配置内容项 extern char *var_cfg_str; @@ -15,7 +15,7 @@ extern acl::master_int_tbl var_conf_int_tab[]; extern long long int var_cfg_int64; extern acl::master_int64_tbl var_conf_int64_tab[]; -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// //class acl::socket_stream; @@ -44,8 +44,8 @@ protected: virtual bool thread_on_accept(acl::socket_stream* stream); /** - * 当某个网络连接的 IO 读写超时时的回调函数,如果该函数返回 true 则表示继续等待下一次 - * 读写,否则则希望关闭该连接 + * 当某个网络连接的 IO 读写超时时的回调函数,如果该函数返回 true 则 + * 表示继续等待下一次读写,否则则希望关闭该连接 * @param stream {socket_stream*} * @return {bool} 如果返回 false 则表示子类要求关闭连接,而不 * 必将该连接再传递至 thread_main 过程 diff --git a/app/wizard/tmpl/master/master_trigger.cpp b/app/wizard/tmpl/master/master_trigger.cpp index 7bde3ec13..17e62bd8c 100644 --- a/app/wizard/tmpl/master/master_trigger.cpp +++ b/app/wizard/tmpl/master/master_trigger.cpp @@ -1,7 +1,7 @@ #include "stdafx.h" #include "master_service.h" -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// // 配置内容项 char *var_cfg_str; @@ -32,7 +32,7 @@ acl::master_int64_tbl var_conf_int64_tab[] = { { 0, 0 , 0 , 0, 0 } }; -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// master_service::master_service() { diff --git a/app/wizard/tmpl/master/master_trigger.h b/app/wizard/tmpl/master/master_trigger.h index 3a09e64d6..19997edd3 100644 --- a/app/wizard/tmpl/master/master_trigger.h +++ b/app/wizard/tmpl/master/master_trigger.h @@ -1,6 +1,6 @@ #pragma once -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// // 配置内容项 extern char *var_cfg_str; @@ -15,7 +15,7 @@ extern acl::master_int_tbl var_conf_int_tab[]; extern long long int var_cfg_int64; extern acl::master_int64_tbl var_conf_int64_tab[]; -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// class master_service : public acl::master_trigger { diff --git a/app/wizard/tmpl/master/master_udp.cpp b/app/wizard/tmpl/master/master_udp.cpp index 6626ab8ce..44655544e 100644 --- a/app/wizard/tmpl/master/master_udp.cpp +++ b/app/wizard/tmpl/master/master_udp.cpp @@ -1,7 +1,7 @@ #include "stdafx.h" #include "master_service.h" -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// // 配置内容项 char *var_cfg_str; @@ -32,7 +32,7 @@ acl::master_int64_tbl var_conf_int64_tab[] = { { 0, 0 , 0 , 0, 0 } }; -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// master_service::master_service() { diff --git a/app/wizard/tmpl/master/master_udp.h b/app/wizard/tmpl/master/master_udp.h index 5c1ce8216..b14aea891 100644 --- a/app/wizard/tmpl/master/master_udp.h +++ b/app/wizard/tmpl/master/master_udp.h @@ -1,6 +1,6 @@ #pragma once -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// // 配置内容项 extern char *var_cfg_str; @@ -15,7 +15,7 @@ extern acl::master_int_tbl var_conf_int_tab[]; extern long long int var_cfg_int64; extern acl::master_int64_tbl var_conf_int64_tab[]; -//////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// //class acl::socket_stream; diff --git a/app/wizard/tmpl/master/rpc_manager.cpp b/app/wizard/tmpl/master/rpc_manager.cpp index 752ed7191..babdd7238 100644 --- a/app/wizard/tmpl/master/rpc_manager.cpp +++ b/app/wizard/tmpl/master/rpc_manager.cpp @@ -11,40 +11,23 @@ rpc_manager::~rpc_manager() { delete service_; if (handle_ != NULL) - { handle_->check(); - if (internal_handle_) - delete handle_; - } logger("rpc service destroy ok!"); } -void rpc_manager::init(acl::aio_handle* handle, int max_threads /* = 10 */, - acl::aio_handle_type type /* = acl::ENGINE_SELECT */, +void rpc_manager::init(acl::aio_handle& handle, int max_threads /* = 10 */, const char* addr /* = NULL */) { - // 因为本类实例是单例,会在程序 main 之前被调用, - // 所以需要在此类中打开日志 - // 创建非阻塞框架句柄 - if (handle == NULL) - { - logger("create new handle, max_threads: %d", max_threads); - handle_ = new acl::aio_handle(type); - internal_handle_ = true; - } - else - { - logger("use input handle, max_threads: %d", max_threads); - handle_ = handle; - internal_handle_ = false; - } + handle_ = &handle; + // 创建 rpc 服务对象 service_ = new acl::rpc_service(max_threads); + // 打开消息服务 if (service_->open(handle_, addr && *addr ? addr : NULL) == false) logger_fatal("open service error: %s", acl::last_serror()); else - logger("open service ok, listening: %s!", service_->get_addr()); + logger("open service ok, listen: %s", service_->get_addr()); } void rpc_manager::fork(acl::rpc_request* req) diff --git a/app/wizard/tmpl/master/rpc_manager.h b/app/wizard/tmpl/master/rpc_manager.h index e3594c37b..6dd50dcf2 100644 --- a/app/wizard/tmpl/master/rpc_manager.h +++ b/app/wizard/tmpl/master/rpc_manager.h @@ -11,15 +11,12 @@ public: /** * 单例初始化函数 - * @param handle {acl::aio_handle*} 异步引擎句柄,当该值为空时, - * 内部会自动生成一个 + * @param handle {acl::aio_handle&} 异步引擎句柄 * @param max_threads {int} 子线程池的最大线程数量 - * @param type {acl::aio_handle_type} 当需要内部自动创建异步引擎时, - * 此值规定了内部所创建的异步引擎的类型,当 handle 为空时该参数 - * 没有意义 + * @param addr {const char*} rpc 服务监听的地址,如果为空,则内部由系 + * 统自动给定一个 127.0.0.1:PORT 地址进行监听,非空时则监听指定的地址 */ - void init(acl::aio_handle* handle, int max_threads = 10, - acl::aio_handle_type type = acl::ENGINE_SELECT, + void init(acl::aio_handle& handle, int max_threads = 10, const char* addr = NULL); /** @@ -31,7 +28,7 @@ public: private: // 异步消息句柄 acl::aio_handle* handle_; - bool internal_handle_; + // 异步 RPC 通信服务句柄 acl::rpc_service* service_; }; diff --git a/lib_acl_cpp/changes.txt b/lib_acl_cpp/changes.txt index 95ee5230a..ae993a47a 100644 --- a/lib_acl_cpp/changes.txt +++ b/lib_acl_cpp/changes.txt @@ -1,6 +1,10 @@ 修改历史列表: ------------------------------------------------------------------------ +355) 2015.9.9 +355.1) feature: smtp_client 类已经可以非常方便地发送邮件 +355.2) samples/smtp_client: 测试邮件发送过程 + 354) 2015.9.7 354.1) bugfix: geo_member::set_coordinate 中设置经纬度的参数有误 354.2) bugfix: db_row::field_int64 之前使用的将字符串转为64位整数的方法在32位 diff --git a/lib_acl_cpp/include/acl_cpp/smtp/mail_attach.hpp b/lib_acl_cpp/include/acl_cpp/smtp/mail_attach.hpp index 7e73b6ded..783887cea 100644 --- a/lib_acl_cpp/include/acl_cpp/smtp/mail_attach.hpp +++ b/lib_acl_cpp/include/acl_cpp/smtp/mail_attach.hpp @@ -7,37 +7,87 @@ namespace acl { class mime_code; class ostream; +/** + * 撰写邮件时,此类用于创建与邮件附件相关的功能 + */ class ACL_CPP_API mail_attach { public: + /** + * 构造函数 + * @param filepath {const char*} 附件文件存储路径(含文件名) + * @param content_type {const char*} 附件文件类型 + * @param charset {const char*} 若为纯文件,此参数表明纯文本的字符集 + */ mail_attach(const char* filepath, const char* content_type, const char* charset); ~mail_attach(); + /** + * 当邮件中的数据体为 multipart/relative 类型时,调用此函数设置其中的 + * html 正文中 cid 标识符 + * @param id {const char*} cid 标识符 + * @return {mail_attach&} + */ mail_attach& set_content_id(const char* id); + /** + * 获得构造函数传入的附件文件路径 + * @return {const char*} + */ const char* get_filepath() const { return filepath_.c_str(); } + /** + * 获得附件的文件名部分经 rfc2047 编码后名称 + * @return {const char*} + */ const char* get_filename() const { return filename_.c_str(); } + /** + * 获得构造函数传入的文件类型 + * @return {const char*} + */ const char* get_content_type() const { return ctype_.c_str(); } + /** + * 获得由 set_content_id 设置的该附件的 cid 标识符 + * @return {const char*} + */ const char* get_content_id() const { return cid_.c_str(); } + /** + * 将附件内容采用传入的编码器进行编码后存入内存缓冲区 + * @param coder {mime_code*} 编码器(base64/qp等) + * @param out {string&} 存储结果,采用 append 方式 + * @return {bool} 编码过程是否成功 + */ bool save_to(mime_code* coder, string& out); + + /** + * 将附件内容采用传入的编码器进行编码后存入输出流中 + * @param coder {mime_code*} 编码器(base64/qp等) + * @param out {out&} 存储结果 + * @return {bool} 编码过程是否成功 + */ bool save_to(mime_code* coder, ostream& out); + + /** + * 创建该附件在 MIME 邮件中的文件头信息 + * @param transfer_encoding {const char*} 编码方式 + * @param out {string&} 存储结果,采用 append 方式 + */ void build_header(const char* transfer_encoding, string& out); private: diff --git a/lib_acl_cpp/include/acl_cpp/smtp/mail_body.hpp b/lib_acl_cpp/include/acl_cpp/smtp/mail_body.hpp index e55de0ac7..df7e5f9f7 100644 --- a/lib_acl_cpp/include/acl_cpp/smtp/mail_body.hpp +++ b/lib_acl_cpp/include/acl_cpp/smtp/mail_body.hpp @@ -9,53 +9,171 @@ namespace acl { class mime_code; class mail_attach; +/** + * 邮件正文构建类 + */ class ACL_CPP_API mail_body { public: + /** + * 构造函数 + * @param charset {const char*} 正文的字符集 + * @param encoding {const char*} 正文的编码格式 + */ mail_body(const char* charset = "utf-8", const char* encoding = "base64"); ~mail_body(); + /** + * 获得正文折内容类型 + * @return {const string&} + */ const string& get_content_type() const { return content_type_; } + + /** + * 获得正文内容类型对象 + * @return {const http_ctype&} + */ const http_ctype& get_ctype() const { return ctype_; } + /** + * 设置邮件正文为 HTML 格式 + * @param html {const char*} HTML 数据 + * @param len {size_t} html 数据长度(虽然 html 是字符串格式,但提供 + * 数据长度有利于调用更灵活高效,内部不再重新通过 strlen 计算长度) + * @return {mail_body&} + */ mail_body& set_html(const char* html, size_t len); + + /** + * 设置邮件正文为 TEXT 格式 + * @param text {const char*} TEXT 数据 + * @param len {size_t} text 数据长度(虽然 text 是文本格式,但提供 + * 数据长度有利于调用更灵活高效,内部不再重新通过 strlen 计算长度) + * @return {mail_body&} + */ mail_body& set_text(const char* text, size_t len); + + /** + * 当邮件内容为 multipart/alternative 格式时调用此函数设置相应类型的 + * 正文内容 + * @param html {const char*} 正文中的 HTML 数据(非空) + * @param hlen {size_t} html 数据长度(>0) + * @param text {const char*} 正文中的 TEXT 数据(非空) + * @param tlen {size_t} text 数据长度(>0) + * @return {mail_body&} + */ mail_body& set_alternative(const char* html, size_t hlen, const char* text, size_t tlen); + + /** + * 当邮件正文内容为 multipart/relative 格式时调用此函数设置正文内容 + * @param html {const char*} 正文中的 HTML 数据(非空) + * @param hlen {size_t} html 数据长度(>0) + * @param text {const char*} 正文中的 TEXT 数据(非空) + * @param tlen {size_t} text 数据长度(>0) + * @param attachments {const std::vector&} 存放 + * 与 html 中的 cid 相关的图片等附件对象 + * @return {mail_body&} + */ mail_body& set_relative(const char* html, size_t hlen, const char* text, size_t tlen, const std::vector& attachments); + /** + * 获得 set_html 函数设置的 html 数据 + * @param len {size_t} 存放数据长度结果 + * @return {const char*} + */ const char* get_html(size_t& len) const { len = hlen_; return html_; } + + /** + * 获得 set_text 函数设置的 text 数据 + * @param len {size_t} 存放数据长度结果 + * @return {const char*} + */ const char* get_text(size_t& len) const { len = tlen_; return text_; } + + /** + * 获得 set_attachments 函数设置的附件集合 + * @return {const std::vector*} + */ const std::vector* get_attachments() const { return attachments_; } + /** + * 构造邮件正文并将结果追加于给定的输出流中 + * @param out {ostream&} 输出流对象 + * @return {bool} 操作是否成功 + */ bool save_to(ostream& out) const; + + /** + * 构造邮件正文并将结果追加于给定的缓冲区中 + * @param out {string&} 存储结果 + * @return {bool} 操作是否成功 + */ bool save_to(string& out) const; + + /** + * text/html 格式的邮件正文构造过程,并将结果追加于给定的缓冲区中 + * @param in {const char*} 输入的 html 格式数据 + * @param len {size_t} in 的数据长度 + * @param out {string&} 以数据追加方式存储结果 + * @return {bool} 操作是否成功 + */ bool save_html(const char* in, size_t len, string& out) const; + + /** + * text/plain 格式的邮件正文构造过程,并将结果追加于给定的缓冲区中 + * @param in {const char*} 输入的 plain 格式数据 + * @param len {size_t} in 的数据长度 + * @param out {string&} 以数据追加方式存储结果 + * @return {bool} 操作是否成功 + */ bool save_text(const char* in, size_t len, string& out) const; + + /** + * multipart/relative 格式的邮件正文构造过程,并将结果追加于给定的缓冲区中 + * @param html {const char*} 输入的 html 格式数据 + * @param hlen {size_t} html 的数据长度 + * @param text {const char*} 正文中的 TEXT 数据(非空) + * @param tlen {size_t} text 数据长度(>0) + * @param attachments {const std::vector&} 存放 + * 与 html 中的 cid 相关的图片等附件对象 + * @param out {string&} 以数据追加方式存储结果 + * @return {bool} 操作是否成功 + */ bool save_relative(const char* html, size_t hlen, const char* text, size_t tlen, const std::vector& attachments, string& out) const; + + /** + * multipart/alternative 格式的邮件正文构造过程,并将结果追加于给定的缓冲区中 + * @param html {const char*} 输入的 html 格式数据 + * @param hlen {size_t} html 的数据长度 + * @param text {const char*} 正文中的 TEXT 数据(非空) + * @param tlen {size_t} text 数据长度(>0) + * @param out {string&} 以数据追加方式存储结果 + * @return {bool} 操作是否成功 + */ bool save_alternative(const char* html, size_t hlen, const char* text, size_t tlen, string& out) const; diff --git a/lib_acl_cpp/include/acl_cpp/smtp/mail_message.hpp b/lib_acl_cpp/include/acl_cpp/smtp/mail_message.hpp index c5cac5a84..71903d7bd 100644 --- a/lib_acl_cpp/include/acl_cpp/smtp/mail_message.hpp +++ b/lib_acl_cpp/include/acl_cpp/smtp/mail_message.hpp @@ -11,67 +11,203 @@ class mail_attach; class mail_body; class ofstream; +/** + * 邮件数据构造类,此类可以生成一封完整的邮件,同时还用于构建 SMTP 发送过程 + * 的邮件信封信息 + */ class ACL_CPP_API mail_message { public: + /** + * 构造函数 + * @param charset {const char*} 字符集 + */ mail_message(const char* charset = "utf-8"); ~mail_message(); + /** + * 设置 SMTP 发送过程的身份验证信息 + * @param user {const char*} 邮箱账号 + * @param pass {const char*} 邮箱密码 + * @return {mail_message&} + */ mail_message& set_auth(const char* user, const char* pass); + + /** + * 设置邮件的发送都邮箱,此字段可用于 SMTP 发送过程的 MAIL FROM 命令, + * 同时又可作为邮件头中的 From 字段值 + * @param from {const char*} 发件人邮件地址 + * @param name {const char*} 发件人名称 + * @return {mail_message&} + */ mail_message& set_from(const char* from, const char* name = NULL); + mail_message& set_sender(const char* sender, const char* name = NULL); + + /** + * 设置邮件头中的 Reply-To 字段值 + * @param reply_to {const char*} Reply-To 邮箱字段值 + * @param name {const char*} Reply-To 对应的人员名称 + * @return {mail_message&} + */ mail_message& set_reply_to(const char* reply_to, const char* name = NULL); + + /** + * 设置邮件头中的 Return-Path 字段值 + * @param return_path {const char*} Return-Path 邮箱字段值 + * @return {mail_message&} + */ mail_message& set_return_path(const char* return_path); + + /** + * 设置邮件头中的 Delivered-To 字段值 + * @param delivered_to {const char*} Delivered-To 邮箱字段值 + * @return {mail_message&} + */ mail_message& set_delivered_to(const char* delivered_to); + + /** + * 添加收件人地址,该地址仅出现在信封中,不出现在邮件头中 + * @param recipients {const char*} 收件人集合,遵守 RFC822 格式 + * @return {mail_message&} + */ mail_message& add_recipients(const char* recipients); + + /** + * 设置邮件头中的 To 字段值,同时该收件人地址集合被用于信封中作为收件人 + * @param to {const char*} 收件人邮箱地址集合,遵守 RFC822 格式 + * @return {mail_message&} + */ mail_message& add_to(const char* to); + + /** + * 设置邮件头中的 Cc 字段值,同时该收件人地址集合被用于信封中作为收件人 + * @param cc {const char*} 收件人邮箱地址集合,遵守 RFC822 格式 + * @return {mail_message&} + */ mail_message& add_cc(const char* cc); + + /** + * 设置邮件发送的暗送地址集合,该地址集合不会出现在邮件头中 + * @param bcc {const char*} 暗送邮箱地址集合,遵守 RFC822 格式 + * @return {mail_message&} + */ mail_message& add_bcc(const char* bcc); + + /** + * 设置邮件头中的主题,该主题将采用 rfc2047 编码且采用类构造函数 + * 设置的字符集 + * @param subject {const char*} 邮件头主题字段值 + * @return {mail_message&} + */ mail_message& set_subject(const char* subject); + + /** + * 用户可以调用此函数添加邮件头中的头部扩展字段值 + * @param name {const char*} 字段名 + * @param value {const char*} 字段值 + * @return {mail_message&} + */ mail_message& add_header(const char* name, const char* value); - mail_message& set_body(const mail_body* body); + + /** + * 设置邮件的正文对象 + * @param body {const mail_body&} 邮件正文对象 + * @return {mail_message&} + */ + mail_message& set_body(const mail_body& body); + + /** + * 给一封邮件添加一个附件 + * @param filepath {const char*} 附件全路径(非空) + * @param content_type {const char*} 附件类型(非空) + * @return {mail_message&} + */ mail_message& add_attachment(const char* filepath, const char* content_type); + /** + * 构造一封完整的邮件,并将邮件内容存储于给定磁盘文件中,如果该文件 + * 存在则首先会清空,否则会创建新的文件 + * @param filepath {const char*} 目标文件 + * @return {bool} 操作是否成功 + */ bool save_to(const char* filepath); + /** + * 可以单独调用本函数用来生成邮件头数据 + * @param out {string&} 创建的邮件头数据将追加于该缓冲区中 + * @return {bool} 操作是否成功 + */ bool build_header(string& out); + /** + * 获得所创建的邮件在磁盘上的全路径,该函数必须在调用 save_to 成功后调用 + * @return {const char*} + */ const char* get_email() const { return filepath_; } + /** + * 获得用于 SMTP 身份验证时的邮箱账号 + * @return {const char*} + */ const char* get_auth_user() const { return auth_user_; } + /** + * 获得用于 SMTP 身份验证时的邮箱账号密码 + * @return {const char*} + */ const char* get_auth_pass() const { return auth_pass_; } + /** + * 获得由 set_from 设置的邮箱地址对象 + * @return {const rfc822_addr*} + */ const rfc822_addr* get_from() const { return from_; } + /** + * 获得由 set_sender 设置的邮箱地址对象 + * @return {const rfc822_addr*} + */ const rfc822_addr* get_sender() const { return sender_; } + /** + * 获得由 set_reply_to 设置的邮箱地址对象 + * @return {const rfc822_addr*} + */ const rfc822_addr* get_reply_to() const { return reply_to_; } + /** + * 获得由 set_return_path 设置的邮箱地址对象 + * @return {const rfc822_addr*} + */ const rfc822_addr* get_return_path() const { return return_path_; } + /** + * 获得由 set_delivered_to 设置的邮箱地址对象 + * @return {const rfc822_addr*} + */ const rfc822_addr* get_delivered_to() const { return delivered_to_; @@ -82,28 +218,45 @@ public: return to_list_; } + /** + * 获得由 set_cc 设置的邮箱地址对象集合 + * @return {const std::vector&} + */ const std::vector& get_cc() const { return cc_list_; } + /** + * 获得由 set_bcc 设置的邮箱地址对象集合 + * @return {const std::vector&} + */ const std::vector& get_bcc() const { return bcc_list_; } + /** + * 获得所有邮件接收者的地址集合 + * @return {const std::vector&} + */ const std::vector& get_recipients() const { return recipients_; } + /** + * 获得用户设置的邮件头扩展字段值 + * @param name {const char*} 字段名 + * @return {const char*} + */ const char* get_header_value(const char* name) const; - const char* get_filepath() const - { - return filepath_; - } - + /** + * 为 MIME 数据创建唯一的分隔符 + * @param id {const char*} 调用者填写的 ID 标识 + * @param out {string&} 存储结果 + */ static void create_boundary(const char* id, string& out); private: diff --git a/lib_acl_cpp/include/acl_cpp/smtp/smtp_client.hpp b/lib_acl_cpp/include/acl_cpp/smtp/smtp_client.hpp index 9057e8eaf..c8ba92633 100644 --- a/lib_acl_cpp/include/acl_cpp/smtp/smtp_client.hpp +++ b/lib_acl_cpp/include/acl_cpp/smtp/smtp_client.hpp @@ -11,20 +11,50 @@ class istream; class polarssl_conf; class mail_message; +/** + * SMTP 邮件发送客户端类,可以使用此类对象发送邮件,支持身份认证等功能 + */ class ACL_CPP_API smtp_client { public: - smtp_client(const char* addr, int conn_timeout, int rw_timeout); + /** + * 构造函数 + * @param addr {const char*} SMTP 邮件服务器地址,格式:IP:PORT + * 或 domain:port + * @param conn_timeout {int} 连接服务器的超时时间(秒) + * @param rw_timeout {int} 网络 IO 超时时间(秒) + */ + smtp_client(const char* addr, int conn_timeout = 60, + int rw_timeout = 60); ~smtp_client(); /** - * 调用本函数发送邮件数据至邮件服务端 + * 调用本函数发送邮件数据至邮件服务端,该函数会首先调用 send_envelop + * 发送信封,当 email 或 message.get_email() 非空时,则会调用发送邮件 + * 过程;否则(即 email 和 message.get_email() 均为 NULL)则只发送 + * 信封 * @param message {const mail_messsage&} 邮件相关信息,必须提前构建好 * @param email {const char*} 非空时,优先使用此文件做为邮件体数据发送 - * @ + * @return {bool} 发送是否成功 + * 注:如果 email 为 NULL 同时 messsage.get_email() 也为 NULL,则本 + * 函数仅发送 SMTP 信封部分,用户还需要调用: + * data_begin-->write|format|vformat|send_file-->data_end + * 过程来发送邮件数据体 */ bool send(const mail_message& message, const char* email = NULL); + /** + * 在 SMTP 会话阶段仅发送邮件信封部分数据,应用调用此函数成功后, + * 还需要调用: + * 1、data_begin:开始发送邮件体指令 + * 2、write/format/vformat/send_file:发送邮件数据 + * 3、data_end:表示发送邮件体数据完毕 + * @param message {const mail_message&} 发送邮件所构建的邮件消息对象 + * @return {bool} 是否成功 + * 注:本函数是 open/auth_login/mail_from/rcpt_to 发送信封过程的组合 + */ + bool send_envelope(const mail_message& message); + /** * 设置 SSL 数据传输模式 * @param ssl_conf {polarssl_conf*} 非空时,指定采用 SSL 传输模式 @@ -44,11 +74,14 @@ public: */ const char* get_status() const; + ///////////////////////////////////////////////////////////////////// + /** * 发送邮件体数据,可以循环调用本函数,但数据内容必须是严格的邮件格式 * @param data {const char*} 邮件内容 * @param len {size_t} data 邮件数据长度 * @return {bool} 命令操作是否成功 + * 注:在第一次调用本函数前,必须保证 SMTP 信封已经成功发送 */ bool write(const char* data, size_t len); @@ -56,6 +89,7 @@ public: * 发送邮件体数据,可以循环调用本函数,但数据内容必须是严格的邮件格式 * @param fmt {const char*} 变参格式 * @return {bool} 命令操作是否成功 + * 注:在第一次调用本函数前,必须保证 SMTP 信封已经成功发送 */ bool format(const char* fmt, ...); @@ -64,33 +98,80 @@ public: * @param fmt {const char*} 变参格式 * @param ap {va_list} * @return {bool} 命令操作是否成功 + * 注:在第一次调用本函数前,必须保证 SMTP 信封已经成功发送 */ bool vformat(const char* fmt, va_list ap); ///////////////////////////////////////////////////////////////////// - // 以下是打开连接和发送信封的分步步骤 + /** + * 连接远程 SMTP 服务器 + * @return {bool} 连接是否成功,若想使用 SSL 方式,则需要在类对象 + * 初始化后调用 set_ssl 设置 SSL 通信方式 + */ bool open(); + + /** + * 主动关闭与 SMTP 服务端的连接 + */ + void close(); + + /** + * 第一次连接成功后需要调用本函数获得 SMTP 服务端的欢迎信息 + * @return {bool} 是否成功 + */ bool get_banner(); + + /** + * 调用 get_banner 成功后调用本函数向 SMTP 服务端发送 HELO/HELO 命令 + * @return {bool} 是否成功 + */ bool greet(); + + /** + * 调用 gree 成功后调用本函数向 SMTP 服务端发送身份认证命令 + * @param user {const char*} 用户账号,非空字符串 + * @param pass {const char*} 用户账号密码,非空字符串 + * @return {bool} 是否成功 + */ bool auth_login(const char* user, const char* pass); + + /** + * 调用 auth_login 成功后(如果无身份验证,则可以在 greet 成功后) + * 调用本函数向 SMTP 服务器发送 MAIL FROM 命令 + * @param from {const char*} 发件人的邮箱地址 + * @return {bool} 是否成功 + */ bool mail_from(const char* from); + + /** + * 调用 mail_from 成功后调用本函数向 SMTP 服务端发送 RCPT TO 命令, + * 指明一个收件人,可以多次本函数将邮件发送至多个收件人 + * @param to {const char*} 收件人邮箱地址 + * @return {bool} 是否成功 + */ bool rcpt_to(const char* to); /** - * 发送一封完整的邮件,需要给出邮件存储于磁盘上的路径 + * 调用 rcpt_to 或 send_envelope 成功调用本函数向 SMTP 服务端 + * DATA 命令,表明开始发送邮件数据 + * @return {bool} 命令操作是否成功 + * 注:在调用本函数前,必须保证 SMTP 信封已经成功发送 + */ + bool data_begin(); + + /** + * 调用 data_begin 成功调用本函数向 SMTP 服务端发送一封完整的邮件, + * 需要给出邮件存储于磁盘上的路径 * @param filepath {const char*} 邮件文件路径 * @return {bool} 命令操作是否成功 + * 注:在调用本函数前,必须保证 SMTP 信封已经成功发送 */ bool send_email(const char* filepath); /** - * 开始发送邮件数据命令:DATA - * @return {bool} 命令操作是否成功 - */ - bool data_begin(); - /** - * 邮件发送完毕后,最后必须调用本函数告诉邮件服务器发送数据结束 + * 邮件发送完毕(如调用:send_email)后,最后必须调用本函数告诉 SMTP + * 邮件服务器发送数据结束 * @return {bool} 命令操作是否成功 */ bool data_end(); @@ -121,6 +202,7 @@ private: SMTP_CLIENT* client_; socket_stream stream_; bool ehlo_; + bool reuse_; bool to_recipients(const std::vector& recipients); }; diff --git a/lib_acl_cpp/include/acl_cpp/stream/istream.hpp b/lib_acl_cpp/include/acl_cpp/stream/istream.hpp index 980fab0d1..556cf6ff3 100644 --- a/lib_acl_cpp/include/acl_cpp/stream/istream.hpp +++ b/lib_acl_cpp/include/acl_cpp/stream/istream.hpp @@ -18,8 +18,6 @@ public: istream() {} virtual ~istream() {} - int read(); - /** * 从输入流中读数据 * @param buf {void*} 用户缓冲区 @@ -140,7 +138,8 @@ public: /** * 从输入流中读一个字节数据 - * @return {int} 所读字节的 ASCII 码值 + * @return {int} 所读字节的 ASCII 码值,如果返回值为 -1 则表示对方关闭或 + * 读出错 */ int getch(void); diff --git a/lib_acl_cpp/samples/mime/mail_build/main.cpp b/lib_acl_cpp/samples/mime/mail_build/main.cpp index 9473163dd..940e40248 100644 --- a/lib_acl_cpp/samples/mime/mail_build/main.cpp +++ b/lib_acl_cpp/samples/mime/mail_build/main.cpp @@ -18,7 +18,7 @@ static void build_html(void) const char* html = "中国人民银行 HTML 格式"; acl::mail_body body("gbk"); body.set_html(html, strlen(html)); - message.set_body(&body); + message.set_body(body); const char* filepath = "./html.eml"; if (message.save_to(filepath) == false) @@ -41,7 +41,7 @@ static void build_text(void) const char* text = "中国人民银行 TEXT 格式"; acl::mail_body body("gbk"); body.set_text(text, strlen(text)); - message.set_body(&body); + message.set_body(body); const char* filepath = "./text.eml"; if (message.save_to(filepath) == false) @@ -67,7 +67,7 @@ static void build_alternative(void) const char* html = "中国人民银行 HTML 格式"; acl::mail_body body("gbk"); body.set_alternative(html, strlen(html), text, strlen(text)); - message.set_body(&body); + message.set_body(body); const char* filepath = "./alternative.eml"; if (message.save_to(filepath) == false) @@ -182,7 +182,7 @@ static void build_relative(void) acl::mail_body body("gbk"); body.set_relative(html.c_str(), html.size(), text.c_str(), text.size(), attachments); - message.set_body(&body); + message.set_body(body); const char* filepath = "./relative.eml"; if (message.save_to(filepath) == false) diff --git a/lib_acl_cpp/samples/smtp_client/main.cpp b/lib_acl_cpp/samples/smtp_client/main.cpp index cf029852a..ec3a73ae1 100644 --- a/lib_acl_cpp/samples/smtp_client/main.cpp +++ b/lib_acl_cpp/samples/smtp_client/main.cpp @@ -8,67 +8,100 @@ static acl::polarssl_conf ssl_conf; static bool send_mail(const char* addr, const char* sender, const char* pass, const char* recipients, bool use_ssl) { - acl::smtp_client conn(addr, 60, 60); - - if (use_ssl) - conn.set_ssl(&ssl_conf); - - conn.set_auth(sender, pass) + acl::mail_message message("gbk"); + message.set_auth(sender, pass) .set_from(sender) .add_recipients(recipients); - if (conn.send_envelope() == false) + acl::smtp_client conn(addr, 60, 60); + + // 设置是否采用 SSL 通信方式 + if (use_ssl) + conn.set_ssl(&ssl_conf); + + // 发送信封 + if (conn.send_envelope(message) == false) { printf("send envelope error: %d, %s\r\n", - conn.get_smtp_code(), conn.get_smtp_status()); + conn.get_code(), conn.get_status()); return false; } + // 开始发送邮件体数据 if (conn.data_begin() == false) { printf("send data begin error: %d, %s\r\n", - conn.get_smtp_code(), conn.get_smtp_status()); + conn.get_code(), conn.get_status()); } - const char* data = "From: \r\n" - "To: zsxxsz@263.net\r\n" - "Subject: hello, world!\r\n" + const char* data = "From: \"郑树新1\" \r\n" + "To: \"郑树新2\" \r\n" + "Subject: 您好,hello, world!\r\n" "\r\n" "hello world!\r\n"; + // 发送邮件体数据 if (conn.write(data, strlen(data)) == false) { printf("send data error: %d, %s\r\n", - conn.get_smtp_code(), conn.get_smtp_status()); + conn.get_code(), conn.get_status()); return false; } + // 发送完毕 if (conn.data_end() == false) { printf("send data end error: %d, %s\r\n", - conn.get_smtp_code(), conn.get_smtp_status()); + conn.get_code(), conn.get_status()); return false; } printf("sendmail ok, from: %s, to: %s, code: %d, status: %s\r\n", - sender, recipients, conn.get_smtp_code(), - conn.get_smtp_status()); + sender, recipients, conn.get_code(), + conn.get_status()); + // 发送 QUIT 命令 if (conn.quit() == false) { printf("send quit error: %d, %s\r\n", - conn.get_smtp_code(), conn.get_smtp_status()); + conn.get_code(), conn.get_status()); return false; } return true; } +static bool send_mail(const char* addr, const char* sender, const char* pass, + const char* recipients, const acl::string& email_path, bool use_ssl) +{ + acl::mail_message message("utf-8"); + message.set_auth(sender, pass) + .set_from(sender) + .add_recipients(recipients); + + acl::smtp_client conn(addr, 60, 60); + + // 设置是否采用 SSL 通信方式 + if (use_ssl) + conn.set_ssl(&ssl_conf); + + if (conn.send(message, email_path) == false) + { + printf("send email %s to %s error\r\n", + email_path.c_str(), addr); + return false; + } + + printf("send email %s to %s ok\r\n", email_path.c_str(), addr); + return true; +} + static void usage(const char* procname) { printf("usage: %s -h[help]\r\n" "\t-s smtp_server_addr\r\n" "\t-e [if use ssl]\r\n" + "\t-f email_path\r\n" "\t-u sender\r\n" "\t-p sender_pass\r\n" "\t-t recipients[sample: to1@xxx.com; to2@xxx.com\r\n", @@ -79,13 +112,13 @@ int main(int argc, char* argv[]) { int ch; bool use_ssl = false; - acl::string addr("smtp.263.net:25"); + acl::string addr("smtp.263.net:25"), email_path; acl::string sender("zsxxsz@263.net"), pass, recipients; acl::acl_cpp_init(); acl::log::stdout_open(true); - while ((ch = getopt(argc, argv, "hs:u:p:t:e")) > 0) + while ((ch = getopt(argc, argv, "hs:u:p:t:ef:")) > 0) { switch (ch) { @@ -107,6 +140,9 @@ int main(int argc, char* argv[]) case 'e': use_ssl = true; break; + case 'f': + email_path = optarg; + break; default: break; } @@ -118,9 +154,12 @@ int main(int argc, char* argv[]) return 1; } - (void) send_mail(addr.c_str(), sender.c_str(), pass.c_str(), - recipients.c_str(), use_ssl); - + if (email_path.empty()) + (void) send_mail(addr.c_str(), sender.c_str(), pass.c_str(), + recipients.c_str(), use_ssl); + else + (void) send_mail(addr.c_str(), sender.c_str(), pass.c_str(), + recipients.c_str(), email_path, use_ssl); #if defined(_WIN32) || defined(_WIN64) getchar(); diff --git a/lib_acl_cpp/samples/socket/client2/tags b/lib_acl_cpp/samples/socket/client2/tags deleted file mode 100644 index 33ed9e868..000000000 --- a/lib_acl_cpp/samples/socket/client2/tags +++ /dev/null @@ -1,6 +0,0 @@ -!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ -!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ -!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ -!_TAG_PROGRAM_NAME Exuberant Ctags // -!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ -!_TAG_PROGRAM_VERSION 5.9~svn20110310 // diff --git a/lib_acl_cpp/samples/socket/server/main.cpp b/lib_acl_cpp/samples/socket/server/main.cpp index 40af2a43a..ed5d3e784 100644 --- a/lib_acl_cpp/samples/socket/server/main.cpp +++ b/lib_acl_cpp/samples/socket/server/main.cpp @@ -36,7 +36,7 @@ int main(int argc, char* argv[]) printf("after set sendbuf's size: %d, readbuf's size: %d\r\n", client->get_tcp_sendbuf(), client->get_tcp_recvbuf()); - if (client->write("hello world\r\n") == false) + if (client->write("hello world\r\n") == -1) { printf("write error\r\n"); delete client; diff --git a/lib_acl_cpp/src/memcache/memcache_pool.cpp b/lib_acl_cpp/src/memcache/memcache_pool.cpp index 437197eb7..a4f6e3d6b 100644 --- a/lib_acl_cpp/src/memcache/memcache_pool.cpp +++ b/lib_acl_cpp/src/memcache/memcache_pool.cpp @@ -28,9 +28,7 @@ memcache_pool& memcache_pool::set_timeout(int conn_timeout /* = 30 */, connect_client* memcache_pool::create_connect() { - memcache* conn = NEW memcache(addr_, conn_timeout_, - rw_timeout_); - return conn; + return NEW memcache(addr_, conn_timeout_, rw_timeout_); } } // namespace acl diff --git a/lib_acl_cpp/src/smtp/mail_attach.cpp b/lib_acl_cpp/src/smtp/mail_attach.cpp index 604e6059e..be437e85e 100644 --- a/lib_acl_cpp/src/smtp/mail_attach.cpp +++ b/lib_acl_cpp/src/smtp/mail_attach.cpp @@ -43,7 +43,7 @@ bool mail_attach::save_to(mime_code* coder, string& out) if (coder) build_header(coder->get_encoding_type(), out); else - build_header("base64", out); + build_header(NULL, out); string buf; if (ifstream::load(filepath_.c_str(), &buf) == false) @@ -72,7 +72,7 @@ bool mail_attach::save_to(mime_code* coder, ostream& out) if (coder) build_header(coder->get_encoding_type(), header); else - build_header("base64", header); + build_header(NULL, header); if (out.write(header) == -1) { @@ -140,8 +140,10 @@ void mail_attach::build_header(const char* transfer_encoding, string& out) { out.format_append("Content-Type: %s;\r\n", ctype_.c_str()); out.format_append("\tname=\"%s\"\r\n", filename_.c_str()); - out.format_append("Content-Transfer-Encoding: %s\r\n", - transfer_encoding); + + if (transfer_encoding && *transfer_encoding) + out.format_append("Content-Transfer-Encoding: %s\r\n", + transfer_encoding); if (cid_.empty()) out.format_append("Content-Disposition: attachment;\r\n" diff --git a/lib_acl_cpp/src/smtp/mail_body.cpp b/lib_acl_cpp/src/smtp/mail_body.cpp index 3f2d7e2c4..c0d7121cb 100644 --- a/lib_acl_cpp/src/smtp/mail_body.cpp +++ b/lib_acl_cpp/src/smtp/mail_body.cpp @@ -118,8 +118,8 @@ bool mail_body::save_to(string& out) const void mail_body::set_content_type(const char* content_type) { - content_type_.format(content_type); - ctype_.parse(content_type_); + content_type_ = content_type; + ctype_.parse(content_type); } bool mail_body::save_html(const char* html, size_t len, string& out) const diff --git a/lib_acl_cpp/src/smtp/mail_message.cpp b/lib_acl_cpp/src/smtp/mail_message.cpp index 12c8fbd66..c5b60696e 100644 --- a/lib_acl_cpp/src/smtp/mail_message.cpp +++ b/lib_acl_cpp/src/smtp/mail_message.cpp @@ -202,9 +202,9 @@ const char* mail_message::get_header_value(const char* name) const return NULL; } -mail_message& mail_message::set_body(const mail_body* body) +mail_message& mail_message::set_body(const mail_body& body) { - body_ = body; + body_ = &body; return *this; } diff --git a/lib_acl_cpp/src/smtp/smtp_client.cpp b/lib_acl_cpp/src/smtp/smtp_client.cpp index bda6ba909..601068d9d 100644 --- a/lib_acl_cpp/src/smtp/smtp_client.cpp +++ b/lib_acl_cpp/src/smtp/smtp_client.cpp @@ -10,7 +10,8 @@ namespace acl { -smtp_client::smtp_client(const char* addr, int conn_timeout, int rw_timeout) +smtp_client::smtp_client(const char* addr, int conn_timeout /* = 60 */, + int rw_timeout /* = 60 */) { addr_ = acl_mystrdup(addr); conn_timeout_ = conn_timeout; @@ -18,24 +19,14 @@ smtp_client::smtp_client(const char* addr, int conn_timeout, int rw_timeout) client_ = NULL; ehlo_ = true; + reuse_ = false; ssl_conf_ = NULL; } smtp_client::~smtp_client() { acl_myfree(addr_); - - if (client_) - { - // 将 SMTP_CLIENT 对象的流置空,以避免内部再次释放, - // 因为该流对象会在下面 stream_.close() 时被释放 - client_->conn = NULL; - smtp_close(client_); - } - - // 当 socket 流对象打开着,则关闭之,同时将依附于其的 SSL 对象释放 - if (stream_.opened()) - stream_.close(); + close(); } smtp_client& smtp_client::set_ssl(polarssl_conf* ssl_conf) @@ -56,6 +47,32 @@ const char* smtp_client::get_status() const bool smtp_client::send(const mail_message& message, const char* email /* = NULL */) +{ + // 发送 SMTP 邮件信封过程 + if (send_envelope(message) == false) + return false; + + // 优先使用参数给出的邮件文件,然后才是 message 中自动生成的邮件文件 + if (email == NULL) + email = message.get_email(); + + // 如果没有可发送的邮件文件,则认为调用者想通过 write 等接口直接发送数据 + if (email == NULL) + return true; + + // 发送 DATA 命令 + if (data_begin() == false) + return false; + + // 发送邮件 + if (send_email(email) == false) + return false; + + // 发送 DATA 结束符 + return data_end(); +} + +bool smtp_client::send_envelope(const mail_message& message) { if (open() == false) return false; @@ -63,31 +80,21 @@ bool smtp_client::send(const mail_message& message, return false; if (greet() == false) return false; - if (!auth_login(message.get_auth_user(), message.get_auth_pass())) + + const char* user = message.get_auth_user(); + const char* pass = message.get_auth_pass(); + if (user && pass && auth_login(user, pass) == false) return false; + const rfc822_addr* from = message.get_from(); - if (from == NULL || from->addr == NULL) + if (from == NULL) + { + logger_error("from null"); return false; + } if (mail_from(from->addr) == false) return false; - if (to_recipients(message.get_recipients()) == false) - return false; - if (email != NULL) - { - if (send_email(email) == false) - return false; - return true; - } - - email = message.get_email(); - if (email != NULL) - { - if (send_email(email) == false) - return false; - return true; - } - - return true; + return to_recipients(message.get_recipients()); } bool smtp_client::open() @@ -96,9 +103,12 @@ bool smtp_client::open() { acl_assert(client_ != NULL); acl_assert(client_->conn == stream_.get_vstream()); + reuse_ = true; return true; } + reuse_ = false; + client_ = smtp_open(addr_, conn_timeout_, rw_timeout_, 1024); if (client_ == NULL) { @@ -106,9 +116,10 @@ bool smtp_client::open() return false; } - // 打开流对象 + // 打开流对象,只所以使用 stream_ 主要为了使用 SSL 通信 stream_.open(client_->conn); + // 如果设置了 SSL 通信方式,则需要打开 SSL 通信接口 if (ssl_conf_) { polarssl_io* ssl = new polarssl_io(*ssl_conf_, false); @@ -119,11 +130,34 @@ bool smtp_client::open() return false; } } + return true; } +void smtp_client::close() +{ + if (client_) + { + // 将 SMTP_CLIENT 对象的流置空,以避免内部再次释放, + // 因为该流对象会在下面 stream_.close() 时被释放 + client_->conn = NULL; + smtp_close(client_); + client_ = NULL; + } + + // 当 socket 流对象打开着,则关闭之,同时将依附于其的 SSL 对象释放 + if (stream_.opened()) + stream_.close(); + + // 重置连接是否被重用的状态 + reuse_ = false; +} + bool smtp_client::get_banner() { + // 如果是同一个连接被使用,则不必再获得服务端的欢迎信息 + if (reuse_) + return true; return smtp_get_banner(client_) == 0 ? true : false; } @@ -173,7 +207,7 @@ bool smtp_client::to_recipients(const std::vector& recipients) std::vector::const_iterator cit; for (cit = recipients.begin(); cit != recipients.end(); ++cit) { - if ((*cit)->addr && rcpt_to((*cit)->addr) != 0) + if ((*cit)->addr && rcpt_to((*cit)->addr) == false) return false; } return true;