mirror of
https://github.com/soywod/himalaya.git
synced 2024-11-21 18:40:19 +00:00
add new emails listing feature #6
This commit is contained in:
parent
80fb4bd1d9
commit
2970828db9
6 changed files with 177 additions and 11 deletions
|
@ -11,8 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- Parse TOML config [#1]
|
||||
- Populate Config struct from TOML [#2]
|
||||
- Set up IMAP connection [#3]
|
||||
- List new emails [#6]
|
||||
|
||||
[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
|
||||
|
|
54
Cargo.lock
generated
54
Cargo.lock
generated
|
@ -21,6 +21,15 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
|
@ -39,6 +48,12 @@ version = "0.1.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.66"
|
||||
|
@ -57,6 +72,16 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "charset"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f426e64df1c3de26cbf44593c6ffff5dbfd43bbf9de0d075058558126b3fc73"
|
||||
dependencies = [
|
||||
"base64 0.10.1",
|
||||
"encoding_rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.19"
|
||||
|
@ -86,6 +111,15 @@ version = "0.8.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "801bbab217d7f79c0062f4f7205b5d4427c6d1a7bd7aafdd1475f7c59d62b283"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
|
@ -118,6 +152,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"imap",
|
||||
"native-tls",
|
||||
"rfc2047-decoder",
|
||||
"serde",
|
||||
"toml",
|
||||
]
|
||||
|
@ -128,7 +163,7 @@ version = "2.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b45e5e8d7783a68f2a0e2451bd19446412202fe21c24d34f4b282a510b91ede3"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.13.0",
|
||||
"bufstream",
|
||||
"chrono",
|
||||
"imap-proto",
|
||||
|
@ -298,6 +333,12 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quoted_printable"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47b080c5db639b292ac79cbd34be0cfc5d36694768d8341109634d90b86930e2"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
|
@ -372,6 +413,17 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rfc2047-decoder"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87ecf2ba387f446155e26796aabb727e9ae1427dd13ac9cc21773a3fbda19d77"
|
||||
dependencies = [
|
||||
"base64 0.13.0",
|
||||
"charset",
|
||||
"quoted_printable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
|
|
|
@ -5,10 +5,9 @@ version = "0.1.0"
|
|||
authors = ["soywod <clement.douin@posteo.net>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
imap = "2.4.0"
|
||||
native-tls = "0.2"
|
||||
rfc2047-decoder = "0.1.2"
|
||||
serde = { version = "1.0.118", features = ["derive"] }
|
||||
toml = "0.5.8"
|
||||
|
|
|
@ -15,19 +15,19 @@ pub struct ServerInfo {
|
|||
|
||||
impl ServerInfo {
|
||||
pub fn get_host(&self) -> &str {
|
||||
&self.host[..]
|
||||
&self.host
|
||||
}
|
||||
|
||||
pub fn get_addr(&self) -> (&str, u16) {
|
||||
(&self.host[..], self.port)
|
||||
(&self.host, self.port)
|
||||
}
|
||||
|
||||
pub fn get_login(&self) -> &str {
|
||||
&self.login[..]
|
||||
&self.login
|
||||
}
|
||||
|
||||
pub fn get_password(&self) -> &str {
|
||||
&self.password[..]
|
||||
&self.password
|
||||
}
|
||||
}
|
||||
|
||||
|
|
107
src/imap.rs
107
src/imap.rs
|
@ -1,8 +1,10 @@
|
|||
use imap;
|
||||
use native_tls::{TlsConnector, TlsStream};
|
||||
use rfc2047_decoder;
|
||||
use std::net::TcpStream;
|
||||
|
||||
use crate::config::{Config, ServerInfo};
|
||||
use crate::table;
|
||||
|
||||
type ImapClient = imap::Client<TlsStream<TcpStream>>;
|
||||
type ImapSession = imap::Session<TlsStream<TcpStream>>;
|
||||
|
@ -40,6 +42,107 @@ pub fn create_imap_sess(client: ImapClient, server: &ServerInfo) -> ImapSession
|
|||
pub fn login(config: &Config) -> ImapSession {
|
||||
let tls = create_tls_connector();
|
||||
let client = create_imap_client(&config.imap, &tls);
|
||||
let sess = create_imap_sess(client, &config.imap);
|
||||
sess
|
||||
let imap_sess = create_imap_sess(client, &config.imap);
|
||||
imap_sess
|
||||
}
|
||||
|
||||
fn subject_from_fetch(fetch: &imap::types::Fetch) -> String {
|
||||
let envelope = fetch.envelope().expect("envelope is missing");
|
||||
|
||||
match &envelope.subject {
|
||||
None => String::new(),
|
||||
Some(bytes) => match rfc2047_decoder::decode(bytes) {
|
||||
Err(_) => String::new(),
|
||||
Ok(subject) => subject,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn first_addr_from_fetch(fetch: &imap::types::Fetch) -> String {
|
||||
let envelope = fetch.envelope().expect("envelope is missing");
|
||||
|
||||
match &envelope.from {
|
||||
None => String::new(),
|
||||
Some(addresses) => match addresses.first() {
|
||||
None => String::new(),
|
||||
Some(address) => {
|
||||
let mbox = String::from_utf8(address.mailbox.expect("invalid addr mbox").to_vec())
|
||||
.expect("invalid addr mbox");
|
||||
let host = String::from_utf8(address.host.expect("invalid addr host").to_vec())
|
||||
.expect("invalid addr host");
|
||||
let email = format!("{}@{}", mbox, host);
|
||||
|
||||
match address.name {
|
||||
None => email,
|
||||
Some(name) => match rfc2047_decoder::decode(name) {
|
||||
Err(_) => email,
|
||||
Ok(name) => name,
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn date_from_fetch(fetch: &imap::types::Fetch) -> String {
|
||||
let envelope = fetch.envelope().expect("envelope is missing");
|
||||
|
||||
match &envelope.date {
|
||||
None => String::new(),
|
||||
Some(date) => match String::from_utf8(date.to_vec()) {
|
||||
Err(_) => String::new(),
|
||||
Ok(date) => date,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_new_emails(imap_sess: &mut ImapSession, mbox: &str) -> imap::Result<()> {
|
||||
imap_sess.select(mbox)?;
|
||||
// let mboxes = imap_sess.list(Some(""), Some("*"))?;
|
||||
// println!("Mboxes {:?}", mboxes);
|
||||
|
||||
let seqs = imap_sess
|
||||
.search("NOT SEEN")?
|
||||
.iter()
|
||||
.map(|n| n.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
|
||||
let table_head = vec![
|
||||
table::Cell::new(
|
||||
vec![table::BOLD, table::UNDERLINE, table::WHITE],
|
||||
String::from("FLAGS"),
|
||||
),
|
||||
table::Cell::new(
|
||||
vec![table::BOLD, table::UNDERLINE, table::WHITE],
|
||||
String::from("FROM"),
|
||||
),
|
||||
table::Cell::new(
|
||||
vec![table::BOLD, table::UNDERLINE, table::WHITE],
|
||||
String::from("SUBJECT"),
|
||||
),
|
||||
table::Cell::new(
|
||||
vec![table::BOLD, table::UNDERLINE, table::WHITE],
|
||||
String::from("DATE"),
|
||||
),
|
||||
];
|
||||
|
||||
let mut table_rows = imap_sess
|
||||
.fetch(seqs, "(INTERNALDATE ENVELOPE)")?
|
||||
.iter()
|
||||
.map(|fetch| {
|
||||
vec![
|
||||
table::Cell::new(vec![table::WHITE], String::from("!@")),
|
||||
table::Cell::new(vec![table::BLUE], first_addr_from_fetch(fetch)),
|
||||
table::Cell::new(vec![table::GREEN], subject_from_fetch(fetch)),
|
||||
table::Cell::new(vec![table::YELLOW], date_from_fetch(fetch)),
|
||||
]
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
table_rows.insert(0, table_head);
|
||||
|
||||
println!("{}", table::render(table_rows));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
13
src/main.rs
13
src/main.rs
|
@ -1,8 +1,17 @@
|
|||
mod config;
|
||||
mod imap;
|
||||
mod table;
|
||||
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
let mbox = env::args().nth(1).unwrap_or(String::from("inbox"));
|
||||
let args = env::args().skip(2).collect::<Vec<_>>().join(" ").to_owned();
|
||||
let config = config::read_file();
|
||||
let sess = imap::login(&config);
|
||||
println!("{:?}", sess);
|
||||
let mut imap_sess = imap::login(&config);
|
||||
|
||||
match args.as_str() {
|
||||
"read new" => imap::read_new_emails(&mut imap_sess, &mbox).unwrap(),
|
||||
_ => println!("Himalaya: command not found e"),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue