set up smtp #4 + write email feature #8

This commit is contained in:
Clément DOUIN 2021-01-03 23:18:23 +01:00
parent 0a48df0567
commit 125c4e2b0e
No known key found for this signature in database
GPG key ID: 69C9B9CFFDEE2DEF
6 changed files with 426 additions and 6 deletions

View file

@ -16,13 +16,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Set up CLI arg parser [#15]
- List mailboxes command [#5]
- Text and HTML previews [#12] [#13]
- Set up SMTP connection [#4]
- Write new email [#8]
[unreleased]: https://github.com/soywod/himalaya
[#1]: https://github.com/soywod/himalaya/issues/1
[#2]: https://github.com/soywod/himalaya/issues/2
[#3]: https://github.com/soywod/himalaya/issues/3
[#4]: https://github.com/soywod/himalaya/issues/4
[#5]: https://github.com/soywod/himalaya/issues/5
[#8]: https://github.com/soywod/himalaya/issues/8
[#12]: https://github.com/soywod/himalaya/issues/12
[#13]: https://github.com/soywod/himalaya/issues/13
[#15]: https://github.com/soywod/himalaya/issues/15

329
Cargo.lock generated
View file

@ -68,6 +68,18 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "bitvec"
version = "0.19.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7ba35e9565969edb811639dbebfe34edc0368e472c5018474c8eb2543397f81"
dependencies = [
"funty",
"radium",
"tap",
"wyz",
]
[[package]]
name = "bufstream"
version = "0.1.4"
@ -80,6 +92,12 @@ version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
[[package]]
name = "bytes"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
[[package]]
name = "cc"
version = "1.0.66"
@ -161,6 +179,12 @@ dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
@ -176,6 +200,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "funty"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
[[package]]
name = "getrandom"
version = "0.1.15"
@ -202,6 +232,7 @@ version = "0.1.0"
dependencies = [
"clap",
"imap",
"lettre",
"mailparse",
"native-tls",
"rfc2047-decoder",
@ -209,6 +240,69 @@ dependencies = [
"toml",
]
[[package]]
name = "hostname"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867"
dependencies = [
"libc",
"match_cfg",
"winapi",
]
[[package]]
name = "http"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84129d298a6d57d246960ff8eb831ca4af3f96d29e2e28848dae275408658e26"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "httparse"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"
[[package]]
name = "httpdate"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"
[[package]]
name = "hyperx"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2adce67e2c21cd95288ae3d9f2bbb2762cf17c03744628d49679f315ed1e2e58"
dependencies = [
"base64 0.13.0",
"bytes",
"http",
"httparse",
"httpdate",
"language-tags",
"log",
"mime",
"percent-encoding",
"unicase",
]
[[package]]
name = "idna"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
dependencies = [
"matches",
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "imap"
version = "2.4.0"
@ -221,7 +315,7 @@ dependencies = [
"imap-proto",
"lazy_static",
"native-tls",
"nom",
"nom 5.1.2",
"regex",
]
@ -231,15 +325,59 @@ version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16a6def1d5ac8975d70b3fd101d57953fe3278ef2ee5d7816cba54b1d1dfc22f"
dependencies = [
"nom",
"nom 5.1.2",
]
[[package]]
name = "instant"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "itoa"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
name = "language-tags"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lettre"
version = "0.10.0-alpha.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc8c2fc7873920aca23647e5e86d44ff3f40bbc5a5efaab445c9eb0e001c9f71"
dependencies = [
"base64 0.13.0",
"hostname",
"hyperx",
"idna",
"mime",
"native-tls",
"nom 6.0.1",
"once_cell",
"quoted_printable",
"r2d2",
"rand",
"regex",
"serde",
"serde_json",
"uuid",
]
[[package]]
name = "lexical-core"
version = "0.7.4"
@ -259,6 +397,15 @@ version = "0.2.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
[[package]]
name = "lock_api"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.11"
@ -279,12 +426,30 @@ dependencies = [
"quoted_printable",
]
[[package]]
name = "match_cfg"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
[[package]]
name = "matches"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
[[package]]
name = "memchr"
version = "2.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
[[package]]
name = "mime"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "native-tls"
version = "0.2.6"
@ -314,6 +479,17 @@ dependencies = [
"version_check",
]
[[package]]
name = "nom"
version = "6.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88034cfd6b4a0d54dd14f4a507eceee36c0b70e5a02236c4e4df571102be17f0"
dependencies = [
"bitvec",
"memchr",
"version_check",
]
[[package]]
name = "num-integer"
version = "0.1.44"
@ -333,6 +509,12 @@ dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
[[package]]
name = "openssl"
version = "0.10.32"
@ -366,6 +548,37 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "parking_lot"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
dependencies = [
"instant",
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272"
dependencies = [
"cfg-if 1.0.0",
"instant",
"libc",
"redox_syscall",
"smallvec",
"winapi",
]
[[package]]
name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pkg-config"
version = "0.3.19"
@ -402,6 +615,23 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b080c5db639b292ac79cbd34be0cfc5d36694768d8341109634d90b86930e2"
[[package]]
name = "r2d2"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f"
dependencies = [
"log",
"parking_lot",
"scheduled-thread-pool",
]
[[package]]
name = "radium"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
[[package]]
name = "rand"
version = "0.7.3"
@ -503,6 +733,21 @@ dependencies = [
"winapi",
]
[[package]]
name = "scheduled-thread-pool"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7"
dependencies = [
"parking_lot",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "security-framework"
version = "2.0.0"
@ -546,6 +791,23 @@ dependencies = [
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "smallvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a55ca5f3b68e41c979bf8c46a6f1da892ca4db8f94023ce0bd32407573b1ac0"
[[package]]
name = "static_assertions"
version = "1.1.0"
@ -569,6 +831,12 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "tap"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36474e732d1affd3a6ed582781b3683df3d0563714c59c39591e8ff707cf078e"
[[package]]
name = "tempfile"
version = "3.1.0"
@ -612,6 +880,21 @@ dependencies = [
"winapi",
]
[[package]]
name = "tinyvec"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "toml"
version = "0.5.8"
@ -621,6 +904,33 @@ dependencies = [
"serde",
]
[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
dependencies = [
"matches",
]
[[package]]
name = "unicode-normalization"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-width"
version = "0.1.8"
@ -633,6 +943,15 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "uuid"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11"
dependencies = [
"rand",
]
[[package]]
name = "vcpkg"
version = "0.2.11"
@ -684,3 +1003,9 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "wyz"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"

View file

@ -8,6 +8,7 @@ edition = "2018"
[dependencies]
clap = "2.33.3"
imap = "2.4.0"
lettre = "0.10.0-alpha.4"
mailparse = "0.13.1"
native-tls = "0.2"
rfc2047-decoder = "0.1.2"

View file

@ -39,6 +39,12 @@ pub struct Config {
pub smtp: ServerInfo,
}
impl Config {
pub fn email_full(&self) -> String {
format!("{} <{}>", self.name, self.email)
}
}
pub fn from_xdg() -> Option<PathBuf> {
match env::var("XDG_CONFIG_HOME") {
Err(_) => None,

View file

@ -1,8 +1,15 @@
mod config;
mod imap;
mod smtp;
mod table;
use clap::{App, Arg, SubCommand};
use std::io::prelude::*;
use std::{env, fs, process};
fn nem_email_tpl() -> String {
["To: ", "Subject: ", ""].join("\r\n")
}
fn mailbox_arg() -> Arg<'static, 'static> {
Arg::with_name("mailbox")
@ -77,6 +84,10 @@ fn main() {
)
.get_matches();
if let Some(_) = matches.subcommand_matches("list") {
imap::list_mailboxes(&mut imap_sess).unwrap();
}
if let Some(matches) = matches.subcommand_matches("search") {
let mbox = matches.value_of("mailbox").unwrap();
@ -108,10 +119,6 @@ fn main() {
}
}
if let Some(_) = matches.subcommand_matches("list") {
imap::list_mailboxes(&mut imap_sess).unwrap();
}
if let Some(matches) = matches.subcommand_matches("read") {
let mbox = matches.value_of("mailbox").unwrap();
let mime = matches.value_of("mime-type").unwrap();
@ -120,4 +127,29 @@ fn main() {
imap::read_email(&mut imap_sess, mbox, uid, mime).unwrap();
}
}
if let Some(_) = matches.subcommand_matches("write") {
let mut draft_path = env::temp_dir();
draft_path.push("himalaya-draft.mail");
fs::File::create(&draft_path)
.expect("Could not create draft file")
.write(nem_email_tpl().as_bytes())
.expect("Could not write into draft file");
process::Command::new(env!("EDITOR"))
.arg(&draft_path)
.status()
.expect("Could not start $EDITOR");
let mut draft = String::new();
fs::File::open(&draft_path)
.expect("Could not open draft file")
.read_to_string(&mut draft)
.expect("Could not read draft file");
fs::remove_file(&draft_path).expect("Could not remove draft file");
smtp::send(&config, &draft.as_bytes());
}
}

52
src/smtp.rs Normal file
View file

@ -0,0 +1,52 @@
use lettre::{
message::{header, Message, SinglePart},
transport::smtp::{authentication::Credentials, SmtpTransport},
Transport,
};
use mailparse;
use crate::config;
pub fn send(config: &config::Config, bytes: &[u8]) {
let email_origin = mailparse::parse_mail(bytes).unwrap();
let email = email_origin
.headers
.iter()
.fold(Message::builder(), |msg, h| {
match h.get_key().to_lowercase().as_str() {
"to" => msg.to(h.get_value().parse().unwrap()),
"cc" => match h.get_value().parse() {
Err(_) => msg,
Ok(addr) => msg.cc(addr),
},
"bcc" => match h.get_value().parse() {
Err(_) => msg,
Ok(addr) => msg.bcc(addr),
},
"subject" => msg.subject(h.get_value()),
_ => msg,
}
})
.from(config.email_full().parse().unwrap())
.singlepart(
SinglePart::builder()
.header(header::ContentType(
"text/plain; charset=utf-8".parse().unwrap(),
))
.header(header::ContentTransferEncoding::Base64)
.body(email_origin.get_body_raw().unwrap()),
)
.unwrap();
let creds = Credentials::new(config.smtp.login.clone(), config.smtp.password.clone());
let mailer = SmtpTransport::relay(&config.smtp.host)
.unwrap()
.credentials(creds)
.build();
println!("Sending ...");
match mailer.send(&email) {
Ok(_) => println!("Email sent successfully!"),
Err(e) => panic!("Could not send email: {:?}", e),
}
}