#smtp-server #client-server #smtp #mail-server #server #client #server-api

letterman

这是一个从头开始的超快、轻量级SMTP客户端和服务器库,您可以将其集成到Rust应用程序中,以原生方式管理电子邮件传输。

26个版本

0.2.7 2022年4月30日
0.2.5 2022年3月5日
0.1.0 2021年12月27日
0.0.8 2020年10月28日

#360HTTP服务器

Download history 67/week @ 2024-03-31

61 每月下载量

MITGPL-3.0 许可证

325KB
7.5K SLoC

letterman - rust

这是一个从头开始的SMTP客户端和服务器库,如果找不到记录,则使用trust-dns-resolver解析域的MX记录,如果指定域名上没有SMTP,则失败。使用native-tls来支持STARTTLS SMTP命令,几乎所有电子邮件提供商都支持。

域名设置

在您的域上设置SPF、DKIM和DMARC记录,以验证电子邮件的发送和接收。

支持的SMTP功能和扩展

  • RDNS验证
  • DKIM验证
  • SPF查找
  • 8BITMIME
  • 替代正文
  • 附件
  • Base64/Quoted-printable解码

测试SMTP服务器

您可以在dkimvalidator.com上测试DKIM验证、SPF查找和电子邮件正文验证。

请支持测试服务器的创建者

客户端API

客户端API支持流水线,可以批量发送多个SMTP命令以提高处理速度,邮件投递需要DKIM。

use std::time::Instant;
use letterman::client::{read_key,Connection,Email};

#[tokio::main]
async fn main() {

    println!(">>> sending mail async");

    //this is private dkim key for which public key is published at a dkim subdomain as a txt record.
    let key:String;
    match read_key("../secret/private.key".to_string()).await{
        Ok(v)=>{
            key = v;
        },
        Err(e)=>{
            println!("!!! {:?}",e);
            return;
        }
    }

    let mut conn:Connection;
    match Connection::new(
        String::from("localhost"),                      //receiver domain
        String::from("mailcenter.herokuapp.com"),       //server name
        key,                                            //dkim private key
        String::from("dkim"),                           //dkim txt value name
        String::from("silvergram.in"),                  //sender domain
    ){
        Ok(v)=>{conn = v;},
        Err(_)=>{
            return;
        }
    }

    //add emails to this connection
    //emails are parsed before the connection si even started
    //large number of emails in one connection will delay for parsing all emails
    for i in 0..1{
        conn.add(build_mail_new(i.to_string()));
    }

    let hold = Instant::now();

    if true{
        match conn.send().await{
            Ok(_v)=>{
                println!("send successfull  : {:?} {:?}",_v.0,_v.1);
            },
            Err(_e)=>{
                println!("send failed : {:?}",_e);
            }
        }
    }

    println!("finished in  : {:?}",hold.elapsed());

}

fn build_mail_new(tracking_id:String) -> Email{

    let mut email = Email::new();

    email.server_name(String::from("mailcenter.herokuapp.com"));
    email.name(String::from("gzbakku"));
    email.from(String::from("[email protected]"));
    email.tracking_id(tracking_id);
    email.to(String::from("gzbakku@localhost"));
    //the receivers will receive the message this feature allows cc and bcc smtp functions
    email.receiver(String::from("gzbakku@localhost"));
    email.receiver(String::from("gzbakku1@localhost"));
    email.subject(String::from("hello world"));
    email.body(String::from("first message\r\nsecond message\r\nthird message"));

    //add html body
    if 1 == 1 {
        email.html(
            String::from(
                "<html> <header><title>This is title</title></header> <body> <h1>Hello world</h1> </body> </html>"
            )
        );
    }

    //attach a file
    if 1 == 1 {
        email.attach("d://workstation/expo/rust/letterman/letterman/drink.png".to_string());
    }

    //attach a base64 encoded file
    if 1 == 1 {
        let base64_data = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAJfSURBVDiNpZLdS1MBGMafc87OPtx23M7Opse2hDb8mB+ZLRC70IhBd4GSJAUVYnXVXyBBEHST1xZRmUgXgdHFiC6KwixCyjLDmTKx4cm17Ux2trntnO3YTYrOFYTP3fvwvj8eeF5gn6L+4pPtza6bvJ3xrcWkd/9NrXXaep49vCKP3Dqbomn68Cl/w52BC8emJsYvdpTuasoB0tl88Gngc0TK5GRFUSrP9fou9fZ5tQ8eTY8COAIg+0+AKKaDYxPTbQAUAAeK+U0SKoGVlXXDH29bxM6Bc/B3K4zWWlUtZIuKnC0UCxsESSotddZBzyEXNbcQTi2GxMfxqHC1XAJSpzd1eVs76g0VZtC0dleqaE4G43CYdcLbbgAkALUUoArhpf4ui+Wj286TyXwOObUIANCTFCp1OoRiEVUIL/VvHW8DWI4fZBh2QJLEF7yd/9njO+7UECTYCiMAILGRQWFTxfinKYHlqk8zjG1EkhL3E/G1exqLveqat7njRo3LY1lbDfli+bQa2chg6ZeA+UQcAIEmloOnqgbRXL66qbVziHe6qdXwYv383Ac9yZisgzUujwUATJUspXewtH94CG5vI2aTEmaTSbibGuEfHoLBztJmhqUAwHmwzsIw7GXN+np89OXzsQaG4VoUOZdWOFtbKjBpO2ricLvLDwBwG21IBSaxHFoWgwnxC63VmyQpPqcUisFdNQLAyfrm133tnd3l/uPJzPs3r75/O7HT2/NIZkpbrCUIGEtqTMt5GAhSLd3fA5gRQmd+1LVcD0uCOyRGrVt+RpGlwMLX8+WS7Uu/AV/Q4yOF5rS7AAAAAElFTkSuQmCC".to_string();
        email.attach_base64("drink_1.png".to_string(),base64_data,"image/png".to_string());
    }

    return email;

}

服务器API

服务器API支持RDNS、SPF和DKIM验证,并带有流水线扩展。


use letterman::server::config::{ServerConfig};
use letterman::server::{init,CheckMail,ProcessMail};
use letterman::json::JsonValue;
use letterman::flume::unbounded as FlumeChannel;
use letterman::flume::Sender as FlumeSender;

#[tokio::main]
async fn main(){

    let config:ServerConfig;
    match ServerConfig::new(
        vec![587,2525],
        String::from("silversender.com"),
        format!("../secret/end.cert"),
        format!("../secret/end.rsa"),
        100_000,
        String::from("../letter_man_que/que/que.akku"),
        5_000_000,
        5,
        String::from("../letter_man_que/email_files/"),
        1,
        false,false,true
    ).await{
        Ok(v)=>{config = v;},
        Err(_)=>{
            return;
        }
    }

    println!(">>> starting server");

    let (check_mail_sender,_check_mail_receiver) = FlumeChannel();
    let (process_mail_sender,_process_mail_receiver) = FlumeChannel();

    match init(
        config,
        check_email,
        process_email,
        check_mail_sender,
        process_mail_sender
    ).await{
        Ok(_)=>{},
        Err(_e)=>{
            println!("!!! server down : {:?}",_e);
        }
    }

    println!("server closed");

}

async fn process_email(_i:ProcessMail,_sender:FlumeSender<JsonValue>) -> Result<(),()>{

    // println!("{:?}",_i.files);

    for i in _i.files.iter(){
        if false {
            match crate::io::delete_file(format!("../letter_man_que/email_files/{}",i)).await{
                Ok(_)=>{
                    // println!("file deleted");
                },
                Err(_)=>{
                    println!("file delete failed");
                }
            }
        }
    }

    Ok(())

}

async fn check_email(_i:CheckMail,_sender:FlumeSender<JsonValue>) -> Result<bool,()>{

    Ok(true)

}

依赖项

~21–36MB
~684K SLoC