#smtp #tokio #mail-server #system

neo-email

用 Rust 构建电子邮件服务的现代方式

2 个版本

0.1.1 2024 年 7 月 31 日
0.1.0 2024 年 7 月 28 日

#33 in 电子邮件

Download history 251/week @ 2024-07-27 9/week @ 2024-08-03

260 每月下载量

MIT 许可证

170KB
2.5K SLoC

NEO EMAIL

crates.io MIT licensed Downloads GitHub Repo stars

Neo Email 是一个专为现代电子邮件处理设计的 Rust crate,侧重于构建健壮和安全的电子邮件系统,并整合了电子邮件技术中的最新标准和实践。

安装

使用终端与 Cargo

cargo add neo-email

或添加到您的 Cargo.toml 文件中

neo-email = { version = "0.1", features = ["experimental"] }

特性

  • 易于实现且快速
  • 基于 Tokio 构建
  • 内置 SPF & DKIM 工具(实验性)

示例

// # Example
// Simple SMTP Example using custom state and some controllers
// See examples/Full.md for a more complete example using more controllers and setting up a working server

use std::net::SocketAddr;
use std::sync::Arc;

use neo_email::connection::SMTPConnection;
use neo_email::controllers::on_auth::OnAuthController;
use neo_email::controllers::on_email::OnEmailController;
use neo_email::headers::EmailHeaders;
use neo_email::mail::Mail;
use neo_email::message::Message;
use neo_email::server::SMTPServer;
use neo_email::status_code::StatusCodes;

use tokio::sync::Mutex;

#[derive(Debug, Clone, Default)]
pub struct ConnectionState {
    pub authenticated: bool,
}

#[tokio::main]
async fn main() {
    let addr = SocketAddr::from(([127, 0, 0, 1], 2526));
    // Create the server
    SMTPServer::<ConnectionState>::new()
        // Set the number of workers to 1
        .workers(1)
        // Set an controller to dispatch when an authentication is received
        .on_auth(OnAuthController::new(on_auth))
        // Set an controller to dispatch when an email is received
        .on_email(OnEmailController::new(on_email))
        // Bind the server to the address
        .bind(addr)
        .await
        .unwrap()
        // Run the server
        .run()
        .await;
}

// This function is called when an authentication is received
// Ok(Message) for successful authentication
// Err(Message) for failed authentication and the connection will be closed peacefully
pub async fn on_auth(conn: Arc<Mutex<SMTPConnection<ConnectionState>>>, _data: String) -> Result<Message, Message> {
    let conn = conn.lock().await;
    let mut state = conn.state.lock().await;

    // What is data?
    // Data is the raw data after command AUTH, example
    // Original Raw Command: AUTH PLAIN AHlvdXJfdXNlcm5hbWUAeW91cl9wYXNzd29yZA==
    // Data: PLAIN AHlvdXJfdXNlcm5hbWUAeW91cl9wYXNzd29yZA==

    // Using our custom state
    state.authenticated = true;
    // We can also decide to not authenticate the user

    Ok(Message::builder()
        .status(neo_email::status_code::StatusCodes::AuthenticationSuccessful)
        .message("Authenticated".to_string())
        .build())
}

// This function is called when an email is received
// The mail is a struct that contains the email data, in this case the raw email data in a Vec<u8>
// Headers are parsed in a hashmap and the body is a Vec<u8>
pub async fn on_email(conn: Arc<Mutex<SMTPConnection<ConnectionState>>>, mail: Mail<Vec<u8>>) -> Message {
    let conn = conn.lock().await;
    let state = conn.state.lock().await;

    // Extract headers
    let headers = mail.headers.clone(); // get the hashmap
    let _subject = headers.get(&EmailHeaders::Subject).unwrap(); // get the Option<Subject> header

    // Check if the user is authenticated from state set in on_auth
    if !state.authenticated {
        return Message::builder()
            .status(StatusCodes::AuthenticationCredetialsInvalid)
            .message("Authentication required".to_string())
            .build();
    }

    log::info!("Received email: {:?}", mail);
    
    Message::builder()
        .status(neo_email::status_code::StatusCodes::OK)
        .message("Email received".to_string())
        .build()
}

更多示例

查看 examples/ 中的示例

作者

赞助商

这里没有东西 :<

合作

欢迎合作此项目

依赖项

~12–24MB
~377K SLoC