Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
run: |
sudo apt update
sudo apt install libssl-dev libnghttp2-dev libprotobuf-dev libprotoc-dev protobuf-compiler liblua5.4-dev
./configure --with-openssl --with-nghttp2 --with-kcp --with-mqtt --with-redis --with-rpc --with-lua --with-js
./configure --with-openssl --with-nghttp2 --with-kcp --with-mqtt --with-redis --with-mail --with-rpc --with-lua --with-js
make libhv evpp unittest
# hrpc = separate libhrpc (needs protobuf); apt installs protobuf under /usr
make libhrpc hrpc PROTOBUF_PREFIX=/usr
Expand Down
21 changes: 21 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ config_setting(
visibility = [":__subpackages__"],
)

config_setting(
name = "with_mail",
define_values = {"WITH_MAIL": "ON"},
visibility = [":__subpackages__"],
)

config_setting(
name = "with_redis",
define_values = {
Expand Down Expand Up @@ -213,6 +219,9 @@ HEADERS_DIRS = ["base", "ssl", "event"] + select({
}) + select({
"with_mqtt": ["mqtt"],
"//conditions:default": [],
}) + select({
"with_mail": ["mail"],
"//conditions:default": [],
})

COPTS = select({
Expand Down Expand Up @@ -388,6 +397,12 @@ MQTT_HEADERS = [
"mqtt/mqtt_client.h",
]

MAIL_HEADERS = [
"mail/mime.h",
"mail/smtp_client.h",
"mail/imap_client.h",
]


HEADERS = ["hv.h", ":config", "hexport.h"] + BASE_HEADERS + SSL_HEADERS + EVENT_HEADERS + UTIL_HEADERS + select({
"with_protocol": PROTOCOL_HEADERS,
Expand All @@ -413,6 +428,9 @@ HEADERS = ["hv.h", ":config", "hexport.h"] + BASE_HEADERS + SSL_HEADERS + EVENT_
}) + select({
"with_mqtt": MQTT_HEADERS,
"//conditions:default": [],
}) + select({
"with_mail": MAIL_HEADERS,
"//conditions:default": [],
})


Expand Down Expand Up @@ -453,6 +471,9 @@ SRCS = CORE_SRCS + glob(["util/*.h", "util/*.c", "util/*.cpp"], exclude = ["util
}) + select({
"with_mqtt": glob(["mqtt/*.h", "mqtt/*.c", "mqtt/*.cpp"], exclude = ["mqtt/*_test.c"]),
"//conditions:default": [],
}) + select({
"with_mail": glob(["mail/*.h", "mail/*.c", "mail/*.cpp"], exclude = ["mail/*_test.c"]),
"//conditions:default": [],
})

cc_library(
Expand Down
8 changes: 7 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ option(WITH_HTTP_SERVER "compile http/server" ON)
option(WITH_HTTP_CLIENT "compile http/client" ON)
option(WITH_MQTT "compile mqtt" OFF)
option(WITH_REDIS "compile redis" OFF)
option(WITH_MAIL "compile mail (SMTP/IMAP)" OFF)
option(WITH_RPC "compile hrpc (libhrpc, needs protobuf)" OFF)

option(ENABLE_UDS "Unix Domain Socket" OFF)
Expand Down Expand Up @@ -296,7 +297,7 @@ if(APPLE)
endif()

# see Makefile
set(ALL_SRCDIRS . base ssl event event/kcp util cpputil evpp redis protocol http http/client http/server mqtt js)
set(ALL_SRCDIRS . base ssl event event/kcp util cpputil evpp redis protocol http http/client http/server mqtt mail js)
set(CORE_SRCDIRS . base ssl event)
if(WIN32 OR MINGW)
if(WITH_WEPOLL)
Expand Down Expand Up @@ -370,6 +371,11 @@ if(WITH_MQTT)
set(LIBHV_SRCDIRS ${LIBHV_SRCDIRS} mqtt)
endif()

if(WITH_MAIL)
set(LIBHV_HEADERS ${LIBHV_HEADERS} ${MAIL_HEADERS})
set(LIBHV_SRCDIRS ${LIBHV_SRCDIRS} mail)
Comment on lines +374 to +376
endif()

list_source_directories(LIBHV_SRCS ${LIBHV_SRCDIRS})
if(NOT WITH_LUA)
list(FILTER LIBHV_SRCS EXCLUDE REGEX "(^|/)HttpLuaHandler\\.cpp$")
Expand Down
17 changes: 16 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ include config.mk
include Makefile.vars

MAKEF=$(MAKE) -f Makefile.in
ALL_SRCDIRS=. base ssl event event/kcp util cpputil evpp redis protocol http http/client http/server mqtt js
ALL_SRCDIRS=. base ssl event event/kcp util cpputil evpp redis protocol http http/client http/server mqtt mail js
CORE_SRCDIRS=. base ssl event
ifeq ($(WITH_KCP), yes)
CORE_SRCDIRS += event/kcp
Expand Down Expand Up @@ -80,6 +80,11 @@ LIBHV_HEADERS += $(MQTT_HEADERS)
LIBHV_SRCDIRS += mqtt
endif

ifeq ($(WITH_MAIL), yes)
LIBHV_HEADERS += $(MAIL_HEADERS)
LIBHV_SRCDIRS += mail
endif

default: all

all: libhv examples
Expand Down Expand Up @@ -123,6 +128,10 @@ ifeq ($(WITH_MQTT), yes)
EXAMPLES += mqtt_sub mqtt_pub mqtt_client_test
endif

ifeq ($(WITH_MAIL), yes)
EXAMPLES += sendmail_test recvmail_test
endif

ifeq ($(WITH_LUA), yes)
ifeq ($(WITH_EVPP), yes)
EXAMPLES += hvlua
Expand Down Expand Up @@ -316,6 +325,12 @@ mqtt_pub: prepare
mqtt_client_test: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS) mqtt" SRCS="examples/mqtt/mqtt_client_test.cpp"

sendmail_test: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS) util mail" SRCS="examples/mail/sendmail_test.cpp"

recvmail_test: prepare
$(MAKEF) TARGET=$@ SRCDIRS="$(CORE_SRCDIRS) util mail" SRCS="examples/mail/recvmail_test.cpp"

kcptun: kcptun_client kcptun_server

kcptun_client: prepare
Expand Down
4 changes: 4 additions & 0 deletions Makefile.vars
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ HTTP_SERVER_HEADERS = http/server/HttpServer.h\
MQTT_HEADERS = mqtt/mqtt_protocol.h\
mqtt/mqtt_client.h

MAIL_HEADERS = mail/mime.h\
mail/smtp_client.h\
mail/imap_client.h

LUA_HEADERS = lua/hvlua.h\
lua/hvlua_util.h\
lua/hvlua_json.h
Expand Down
1 change: 1 addition & 0 deletions README-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
- WebSocket服务端/客户端
- MQTT客户端
- Redis客户端
- 邮件客户端(SMTP发送 / IMAP接收)

## ⌛️ 构建

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ but simpler api and richer protocols.
- WebSocket client/server
- MQTT client
- Redis client
- Mail client (SMTP send / IMAP recv)

## ⌛️ Build

Expand Down
6 changes: 6 additions & 0 deletions cmake/vars.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ set(MQTT_HEADERS
mqtt/mqtt_client.h
)

set(MAIL_HEADERS
mail/mime.h
mail/smtp_client.h
mail/imap_client.h
)

set(LUA_HEADERS
lua/hvlua.h
lua/hvlua_util.h
Expand Down
2 changes: 2 additions & 0 deletions config.ini
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ WITH_HTTP_SERVER=yes
WITH_HTTP_CLIENT=yes
WITH_MQTT=no
WITH_REDIS=no
# mail = SMTP send + IMAP recv (requires SSL for direct TLS)
WITH_MAIL=no
# hrpc = TLV + protobuf, built as a separate libhrpc (requires protobuf)
WITH_RPC=no

Expand Down
1 change: 1 addition & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ modules:
--with-http-server compile http server module? (DEFAULT: $WITH_HTTP_SERVER)
--with-mqtt compile mqtt module? (DEFAULT: $WITH_MQTT)
--with-redis compile redis module? (DEFAULT: $WITH_REDIS)
--with-mail compile mail module (SMTP/IMAP)? (DEFAULT: $WITH_MAIL)
--with-rpc compile hrpc (libhrpc, needs protobuf)? (DEFAULT: $WITH_RPC)
--with-lua compile lua module? (DEFAULT: $WITH_LUA)
--with-js compile js module? (DEFAULT: $WITH_JS)
Expand Down
76 changes: 76 additions & 0 deletions docs/cn/ImapClient.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
IMAP 邮件接收客户端

基于事件循环的异步 IMAP 客户端,支持直连 TLS(IMAPS,如 993 端口),读取邮件(读取最小集:LOGIN / SELECT / SEARCH / FETCH / LOGOUT),并解析 MIME 报文得到发件人、主题、正文与附件。

> 注意:
> - 只支持直连 TLS(IMAPS),不支持 STARTTLS。主流邮箱(QQ/163/Gmail 等)均提供 993 直连 SSL 端口。
> - 只实现读取相关命令,不支持写操作(STORE 改标记 / COPY / 移动 / IDLE 推送等)。

编译需开启 mail 模块与 SSL:

```sh
./configure --with-mail --with-openssl && make libhv
# 或 CMake: cmake .. -DWITH_MAIL=ON -DWITH_OPENSSL=ON
```

## C++ 接口

```c++
namespace hv {

class ImapClient {
public:
ImapClient(hloop_t* loop = NULL);

// 设置服务器: host、端口(默认993)、是否直连TLS(默认true)
void setHost(const char* host, int port = 993, bool ssl = true);
// 设置认证账号
void setAuth(const char* username, const char* password);
// 设置连接超时(ms)
void setConnectTimeout(int ms);
// SSL/TLS
int setSslCtx(hssl_ctx_t ssl_ctx);
int newSslCtx(hssl_ctx_opt_t* opt);

// 每封邮件回调: mail 在回调期间有效
std::function<void(ImapClient*, mail_t*)> onMail;
// 完成/失败回调: code==0 成功; code<0 为 libhv ERR_*
std::function<void(ImapClient*, int code, const std::string& msg)> onDone;

// 拉取: LOGIN -> SELECT mailbox -> SEARCH criteria -> 逐封 FETCH -> LOGOUT
// mailbox 如 "INBOX"; criteria 为 IMAP SEARCH 条件,如 "ALL"、"UNSEEN"
int fetch(const char* mailbox = "INBOX", const char* criteria = "ALL");

void run(); // 占用当前线程运行事件循环
void stop();
};

}
```

## 示例

```c++
#include "imap_client.h"
using namespace hv;

int main() {
ImapClient cli;
cli.setHost("imap.qq.com", 993, true);
cli.setAuth("you@qq.com", "yourpassword"); // 多数邮箱这里填授权码
cli.onMail = [](ImapClient*, mail_t* mail) {
printf("From: %s\n", mail->from.addr ? mail->from.addr : "");
printf("Subject: %s\n", mail->subject ? mail->subject : "");
if (mail->text_body) printf("Body: %s\n", mail->text_body);
};
cli.onDone = [&cli](ImapClient*, int code, const std::string& msg) {
printf("done: %d %s\n", code, msg.c_str());
cli.stop();
};
cli.fetch("INBOX", "UNSEEN"); // 拉取未读邮件
cli.run();
return 0;
}
```

测试代码见 [examples/mail/recvmail_test.cpp](../../examples/mail/recvmail_test.cpp)
91 changes: 91 additions & 0 deletions docs/cn/SmtpClient.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
SMTP 邮件发送客户端

基于事件循环的异步 SMTP 客户端,支持直连 TLS(SMTPS,如 465 端口)、多收件人/抄送、附件。

> 注意:只支持直连 TLS(SMTPS),不支持 STARTTLS(587/143 明文中途升级)。主流邮箱(QQ/163/Gmail 等)均提供 465 直连 SSL 端口。

编译需开启 mail 模块与 SSL:

```sh
./configure --with-mail --with-openssl && make libhv
# 或 CMake: cmake .. -DWITH_MAIL=ON -DWITH_OPENSSL=ON
```

## C++ 接口

```c++
namespace hv {

class SmtpClient {
public:
SmtpClient(hloop_t* loop = NULL);

// 设置服务器: host、端口(默认465)、是否直连TLS(默认true)
void setHost(const char* host, int port = 465, bool ssl = true);
// 设置认证账号
void setAuth(const char* username, const char* password);
// 设置连接超时(ms)
void setConnectTimeout(int ms);
// SSL/TLS
int setSslCtx(hssl_ctx_t ssl_ctx);
int newSslCtx(hssl_ctx_opt_t* opt);

// 结果回调: code∈[200,300) 表示成功; code<0 为 libhv ERR_*
std::function<void(SmtpClient*, int code, const std::string& msg)> onResult;

// 异步发送: 连接 -> EHLO -> AUTH -> MAIL FROM -> RCPT TO -> DATA -> QUIT
int send(mail_t* mail);

void run(); // 占用当前线程运行事件循环
void stop();
};

}
```

## 邮件结构 mail_t

见 `mail/mime.h`,提供构造辅助函数(均为拷贝语义,用 `mail_clear` 释放):

```c
void mail_set_from(mail_t* mail, const char* addr, const char* name);
void mail_add_to (mail_t* mail, const char* addr, const char* name);
void mail_add_cc (mail_t* mail, const char* addr, const char* name);
void mail_set_subject(mail_t* mail, const char* subject);
void mail_set_text(mail_t* mail, const char* text); // text/plain
void mail_set_html(mail_t* mail, const char* html); // text/html
void mail_add_attachment(mail_t* mail, const char* filename,
const char* content_type, // NULL 则按扩展名推断
const void* data, size_t size);
void mail_clear(mail_t* mail);
```

## 示例

```c++
#include "smtp_client.h"
using namespace hv;

int main() {
mail_t mail;
memset(&mail, 0, sizeof(mail));
mail_set_from(&mail, "you@qq.com", NULL);
mail_add_to(&mail, "friend@example.com", NULL);
mail_set_subject(&mail, "hello from libhv");
mail_set_text(&mail, "This is a test mail.\r\n");

SmtpClient cli;
cli.setHost("smtp.qq.com", 465, true);
cli.setAuth("you@qq.com", "yourpassword"); // 多数邮箱这里填授权码
cli.onResult = [&cli](SmtpClient*, int code, const std::string& msg) {
printf("result: %d %s\n", code, msg.c_str());
cli.stop();
};
cli.send(&mail);
cli.run();
mail_clear(&mail);
return 0;
}
```

测试代码见 [examples/mail/sendmail_test.cpp](../../examples/mail/sendmail_test.cpp)
12 changes: 12 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,18 @@ if(WITH_MQTT)
list(APPEND EXAMPLES mqtt_sub mqtt_pub mqtt_client_test)
endif()

if(WITH_MAIL)
include_directories(../mail)

add_executable(sendmail_test mail/sendmail_test.cpp)
target_link_libraries(sendmail_test ${HV_LIBRARIES})

add_executable(recvmail_test mail/recvmail_test.cpp)
target_link_libraries(recvmail_test ${HV_LIBRARIES})

list(APPEND EXAMPLES sendmail_test recvmail_test)
endif()

# hrpc examples: link the hrpc library (built at top level when WITH_RPC=ON) --
# exactly how a downstream user consumes hrpc: -lhrpc -lhv -lprotobuf.
if(WITH_RPC)
Expand Down
Loading
Loading