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日 |
#360 在 HTTP服务器
61 每月下载量
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