This commit is contained in:
Michael Zhang 2021-08-22 19:19:27 -05:00
parent 24cf869da5
commit dfdb71dea2
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
18 changed files with 545 additions and 52 deletions

114
Cargo.lock generated
View file

@ -4,9 +4,9 @@ version = 3
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.42" version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486" checksum = "28ae2b3dec75a406790005a200b1bd89785afc02517a00ca99ecfe093ee9e6cf"
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
@ -50,9 +50,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.2.1" version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bitvec" name = "bitvec"
@ -116,9 +116,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "3.0.0-beta.2" version = "3.0.0-beta.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142" checksum = "fcd70aa5597dbc42f7217a543f9ef2768b2ef823ba29036072d30e1d88e98406"
dependencies = [ dependencies = [
"atty", "atty",
"bitflags", "bitflags",
@ -129,15 +129,14 @@ dependencies = [
"strsim", "strsim",
"termcolor", "termcolor",
"textwrap", "textwrap",
"unicode-width",
"vec_map", "vec_map",
] ]
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "3.0.0-beta.2" version = "3.0.0-beta.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "370f715b81112975b1b69db93e0b56ea4cd4e5002ac43b2da8474106a54096a1" checksum = "0b5bb0d655624a0b8770d1c178fb8ffcb1f91cc722cb08f451e3dc72465421ac"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro-error", "proc-macro-error",
@ -181,6 +180,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "derivative"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "derive_builder" name = "derive_builder"
version = "0.10.2" version = "0.10.2"
@ -342,9 +352,9 @@ dependencies = [
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.3.3" version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "825343c4eef0b63f541f8903f395dc5beb362a979b5799a84062527ef1e37726" checksum = "d7f3675cfef6a30c8031cf9e6493ebdc3bb3272a3fea3923c4210d1830e6a472"
dependencies = [ dependencies = [
"bytes", "bytes",
"fnv", "fnv",
@ -407,9 +417,9 @@ dependencies = [
[[package]] [[package]]
name = "httparse" name = "httparse"
version = "1.4.1" version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3a87b616e37e93c22fb19bcd386f02f3af5ea98a25670ad0fce773de23c5e68" checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503"
[[package]] [[package]]
name = "httpdate" name = "httpdate"
@ -489,15 +499,15 @@ dependencies = [
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "0.4.7" version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.52" version = "0.3.53"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce791b7ca6638aae45be056e068fc756d871eb3b3b10b8efa62d1c9cec616752" checksum = "e4bf49d50e2961077d9c99f4b7997d770a1114f087c3c2e0069b36c13fc2979d"
dependencies = [ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
@ -523,9 +533,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.98" version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
@ -545,6 +555,19 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "mbsync"
version = "0.1.0"
dependencies = [
"anyhow",
"bitflags",
"clap",
"derivative",
"derive_builder",
"panorama-imap",
"tokio",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.3.4" version = "2.3.4"
@ -632,9 +655,9 @@ checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
[[package]] [[package]]
name = "os_str_bytes" name = "os_str_bytes"
version = "2.4.0" version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85" checksum = "6acbef58a60fe69ab50510a55bc8cdd4d6cf2283d27ad338f54cb52747a9cf2d"
[[package]] [[package]]
name = "panorama-daemon" name = "panorama-daemon"
@ -643,6 +666,7 @@ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
"clap", "clap",
"derivative",
"futures", "futures",
"hyper", "hyper",
"inotify", "inotify",
@ -863,18 +887,18 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.127" version = "1.0.128"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8" checksum = "1056a0db1978e9dbf0f6e4fca677f6f9143dc1c19de346f22cac23e422196834"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.127" version = "1.0.128"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a024926d3432516606328597e0f224a51355a493b49fdd67e9209187cbe55ecc" checksum = "13af2fbb8b60a8950d6c72a56d2095c28870367cc8e10c55e9745bac4995a2c4"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -935,9 +959,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.74" version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -961,9 +985,9 @@ dependencies = [
[[package]] [[package]]
name = "textwrap" name = "textwrap"
version = "0.12.1" version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789" checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
dependencies = [ dependencies = [
"unicode-width", "unicode-width",
] ]
@ -990,9 +1014,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.9.0" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b7b349f11a7047e6d1276853e612d152f5e8a352c61917887cc2169e2366b4c" checksum = "01cf844b23c6131f624accf65ce0e4e9956a8bb329400ea5bcc26ae3a5c20b0b"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"bytes", "bytes",
@ -1072,9 +1096,9 @@ dependencies = [
[[package]] [[package]]
name = "tracing-core" name = "tracing-core"
version = "0.1.18" version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052" checksum = "2ca517f43f0fb96e0c3072ed5c275fe5eece87e8cb52f4a77b69226d3b1c9df8"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
] ]
@ -1139,9 +1163,9 @@ checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.75" version = "0.2.76"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b608ecc8f4198fe8680e2ed18eccab5f0cd4caaf3d83516fa5fb2e927fda2586" checksum = "8ce9b1b516211d33767048e5d47fa2a381ed8b76fc48d2ce4aa39877f9f183e0"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"wasm-bindgen-macro", "wasm-bindgen-macro",
@ -1149,9 +1173,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-backend" name = "wasm-bindgen-backend"
version = "0.2.75" version = "0.2.76"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "580aa3a91a63d23aac5b6b267e2d13cb4f363e31dce6c352fca4752ae12e479f" checksum = "cfe8dc78e2326ba5f845f4b5bf548401604fa20b1dd1d365fb73b6c1d6364041"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"lazy_static", "lazy_static",
@ -1164,9 +1188,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.75" version = "0.2.76"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "171ebf0ed9e1458810dfcb31f2e766ad6b3a89dbda42d8901f2b268277e5f09c" checksum = "44468aa53335841d9d6b6c023eaab07c0cd4bddbcfdee3e2bb1e8d2cb8069fef"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@ -1174,9 +1198,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.75" version = "0.2.76"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c2657dd393f03aa2a659c25c6ae18a13a4048cebd220e147933ea837efc589f" checksum = "0195807922713af1e67dc66132c7328206ed9766af3858164fb583eedc25fbad"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1187,15 +1211,15 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.75" version = "0.2.76"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e0c4a743a309662d45f4ede961d7afa4ba4131a59a639f29b0069c3798bbcc2" checksum = "acdb075a845574a1fa5f09fd77e43f7747599301ea3417a9fbffdeedfc1f4a29"
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.52" version = "0.3.53"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01c70a82d842c9979078c772d4a1344685045f1a5628f677c2b2eab4dd7d2696" checksum = "224b2f6b67919060055ef1a67807367c2066ed520c3862cc013d26cf893a783c"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",

View file

@ -4,6 +4,7 @@ members = [
"imap", "imap",
"smtp", "smtp",
"proto-common", "proto-common",
"mbsync",
] ]
[profile.release] [profile.release]

View file

@ -3,3 +3,6 @@ fmt:
doc: doc:
cargo doc --workspace --no-deps cargo doc --workspace --no-deps
watch:
cargo watch -c

View file

@ -7,6 +7,7 @@ edition = "2018"
anyhow = "1.0.42" anyhow = "1.0.42"
async-trait = "0.1.50" async-trait = "0.1.50"
clap = "3.0.0-beta.2" clap = "3.0.0-beta.2"
derivative = "2.2.0"
futures = "0.3.16" futures = "0.3.16"
hyper = { version = "0.14.11", features = ["server", "http2", "stream"] } hyper = { version = "0.14.11", features = ["server", "http2", "stream"] }
inotify = { version = "0.9.3", features = ["stream"] } inotify = { version = "0.9.3", features = ["stream"] }

View file

@ -59,12 +59,18 @@ pub struct ImapConfig {
} }
/// Method of authentication for the IMAP server /// Method of authentication for the IMAP server
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Derivative)]
#[derivative(Debug)]
#[serde(tag = "auth")] #[serde(tag = "auth")]
pub enum ImapAuth { pub enum ImapAuth {
/// Use plain username/password authentication /// Use plain username/password authentication
#[serde(rename = "plain")] #[serde(rename = "plain")]
Plain { username: String, password: String }, Plain {
username: String,
#[derivative(Debug = "ignore")]
password: String,
},
} }
/// Describes when to perform the TLS handshake /// Describes when to perform the TLS handshake

View file

@ -6,6 +6,8 @@ extern crate anyhow;
extern crate log; extern crate log;
#[macro_use] #[macro_use]
extern crate futures; extern crate futures;
#[macro_use]
extern crate derivative;
mod config; mod config;
mod mail; mod mail;

View file

@ -84,7 +84,7 @@ impl DisplayBytes for Command {
#[cfg(feature = "rfc2177")] #[cfg(feature = "rfc2177")]
Command::Idle => write_bytes!(w, b"IDLE"), Command::Idle => write_bytes!(w, b"IDLE"),
_ => Ok(()), _ => todo!("unimplemented command"),
} }
} }
} }

View file

@ -19,7 +19,7 @@ use super::response::{
}; };
use super::rfc2234::{is_char, is_cr, is_ctl, is_digit, is_dquote, is_lf, is_sp, CRLF, DQUOTE, SP}; use super::rfc2234::{is_char, is_cr, is_ctl, is_digit, is_dquote, is_lf, is_sp, CRLF, DQUOTE, SP};
#[macro_export] /// Grammar rule `T / nil` produces `Option<T>`
macro_rules! opt_nil { macro_rules! opt_nil {
($t:expr) => { ($t:expr) => {
alt((map($t, Some), map(crate::proto::rfc3501::nil, |_| None))) alt((map($t, Some), map(crate::proto::rfc3501::nil, |_| None)))

13
mbsync/Cargo.toml Normal file
View file

@ -0,0 +1,13 @@
[package]
name = "mbsync"
version = "0.1.0"
edition = "2018"
[dependencies]
anyhow = "1.0.42"
bitflags = "1.2.1"
clap = "3.0.0-beta.2"
derivative = "2.2.0"
derive_builder = "0.10.2"
panorama-imap = { path = "../imap" }
tokio = { version = "1.9.0", features = ["full"] }

315
mbsync/src/config.rs Normal file
View file

@ -0,0 +1,315 @@
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader, Read};
use std::path::{Path, PathBuf};
use anyhow::Result;
pub fn read_from_file(path: impl AsRef<Path>) -> Result<Config> {
let path = path.as_ref();
let file = File::open(path)?;
read_from_reader(file)
}
pub fn read_from_reader<R: Read>(r: R) -> Result<Config> {
let r = BufReader::new(r);
let mut accounts = HashMap::new();
let mut stores = HashMap::new();
let mut channels = HashMap::new();
enum Section {
ImapAccount(ImapAccountBuilder),
ImapStore(ImapStoreBuilder),
MaildirStore(MaildirStoreBuilder),
Channel(ChannelBuilder),
}
let mut current_section = None;
let finish_section = |accounts: &mut HashMap<_, _>,
stores: &mut HashMap<_, _>,
channels: &mut HashMap<_, _>,
current_section: &mut Option<Section>|
-> Result<()> {
match current_section {
Some(Section::ImapAccount(builder)) => {
let account = builder.build()?;
accounts.insert(account.name.clone(), Account::Imap(account));
}
Some(Section::ImapStore(builder)) => {
let store = builder.build()?;
stores.insert(store.name.clone(), Store::Imap(store));
}
Some(Section::MaildirStore(builder)) => {
let store = builder.build()?;
stores.insert(store.name.clone(), Store::Maildir(store));
}
Some(Section::Channel(builder)) => {
let channel = builder.build()?;
channels.insert(channel.name.clone(), channel);
}
None => {}
};
*current_section = None;
Ok(())
};
for line in r.lines() {
let line = line?.trim().to_string();
if line.is_empty() || line.starts_with('#') {
continue;
}
let parts = line.split(" ").collect::<Vec<_>>();
match parts[0].to_lowercase().as_str() {
"imapaccount" => {
finish_section(
&mut accounts,
&mut stores,
&mut channels,
&mut current_section,
)?;
let mut builder = ImapAccountBuilder::default();
builder.name(parts[1].to_string());
current_section = Some(Section::ImapAccount(builder));
}
"host" => match current_section.as_mut() {
Some(Section::ImapAccount(ref mut builder)) => {
builder.host(parts[1].to_string());
}
_ => panic!("unexpected host keyword"),
},
"user" => match current_section.as_mut() {
Some(Section::ImapAccount(ref mut builder)) => {
builder.user(parts[1].to_string());
}
_ => panic!("unexpected user keyword"),
},
"pass" => match current_section.as_mut() {
Some(Section::ImapAccount(ref mut builder)) => {
builder.pass(parts[1].to_string());
}
_ => panic!("unexpected pass keyword"),
},
"ssltype" => match current_section.as_mut() {
Some(Section::ImapAccount(ref mut builder)) => {
builder.ssltype(match parts[1].to_lowercase().as_str() {
"none" => SslType::None,
"starttls" => SslType::Starttls,
"imaps" => SslType::Imaps,
unknown => panic!("unknown ssl type '{}'", unknown),
});
}
_ => panic!("unexpected ssltype keyword"),
},
"imapstore" => {
finish_section(
&mut accounts,
&mut stores,
&mut channels,
&mut current_section,
)?;
let mut builder = ImapStoreBuilder::default();
builder.name(parts[1].to_string());
current_section = Some(Section::ImapStore(builder));
}
"account" => match current_section.as_mut() {
Some(Section::ImapStore(ref mut builder)) => {
builder.account(parts[1].to_string());
}
_ => panic!("unexpected account keyword"),
},
"maildirstore" => {
finish_section(
&mut accounts,
&mut stores,
&mut channels,
&mut current_section,
)?;
let mut builder = MaildirStoreBuilder::default();
builder.name(parts[1].to_string());
current_section = Some(Section::MaildirStore(builder));
}
"path" => match current_section.as_mut() {
Some(Section::MaildirStore(ref mut builder)) => {
builder.path(PathBuf::from(parts[1].to_string()));
}
_ => panic!("unexpected path keyword"),
},
"inbox" => match current_section.as_mut() {
Some(Section::MaildirStore(ref mut builder)) => {
builder.inbox(PathBuf::from(parts[1].to_string()));
}
_ => panic!("unexpected inbox keyword"),
},
"subfolders" => match current_section.as_mut() {
Some(Section::MaildirStore(ref mut builder)) => {
builder.subfolders(match parts[1].to_lowercase().as_str() {
"verbatim" => MaildirSubfolderStyle::Verbatim,
"maildir++" => MaildirSubfolderStyle::Maildirpp,
"legacy" => MaildirSubfolderStyle::Legacy,
unknown => panic!("unknown subfolder style '{}'", unknown),
});
}
_ => panic!("unexpected subfolders keyword"),
},
"channel" => {
finish_section(
&mut accounts,
&mut stores,
&mut channels,
&mut current_section,
)?;
let mut builder = ChannelBuilder::default();
builder.name(parts[1].to_string());
current_section = Some(Section::Channel(builder));
}
"far" => match current_section.as_mut() {
Some(Section::Channel(ref mut builder)) => {
builder.far(parts[1].trim_matches(':').to_string());
}
_ => panic!("unexpected far keyword"),
},
"near" => match current_section.as_mut() {
Some(Section::Channel(ref mut builder)) => {
builder.near(parts[1].trim_matches(':').to_string());
}
_ => panic!("unexpected near keyword"),
},
"sync" => match current_section.as_mut() {
Some(Section::Channel(ref mut builder)) => {
builder.sync(parts[1..].iter().fold(ChannelSyncOps::empty(), |a, b| {
a | match b.to_lowercase().as_str() {
"none" => ChannelSyncOps::empty(),
"pull" => ChannelSyncOps::PULL,
"push" => ChannelSyncOps::PUSH,
"new" => ChannelSyncOps::NEW,
"renew" => ChannelSyncOps::RENEW,
"delete" => ChannelSyncOps::DELETE,
"flags" => ChannelSyncOps::FLAGS,
"all" => ChannelSyncOps::all(),
unknown => panic!("unknown sync op '{}'", unknown),
}
}));
}
_ => panic!("unexpected near keyword"),
},
unknown => panic!("unknown keyword '{}'", unknown),
}
}
finish_section(
&mut accounts,
&mut stores,
&mut channels,
&mut current_section,
)?;
let config = Config {
accounts,
stores,
channels,
};
check_config(&config)?;
Ok(config)
}
fn check_config(config: &Config) -> Result<()> {
for store in config.stores.values() {
if let Store::Imap(store) = store {
ensure!(config.accounts.contains_key(&store.account));
}
}
for channel in config.channels.values() {
ensure!(config.stores.contains_key(&channel.near));
ensure!(config.stores.contains_key(&channel.far));
}
Ok(())
}
#[derive(Debug)]
pub struct Config {
pub accounts: HashMap<String, Account>,
pub stores: HashMap<String, Store>,
pub channels: HashMap<String, Channel>,
}
#[derive(Debug)]
pub enum Account {
Imap(ImapAccount),
}
#[derive(Derivative, Builder)]
#[derivative(Debug)]
pub struct ImapAccount {
pub name: String,
pub host: String,
#[builder(default)]
pub port: Option<u16>,
#[builder(default = "20")]
pub timeout: u32,
pub user: String,
pub ssltype: SslType,
#[derivative(Debug = "ignore")]
pub pass: String,
}
#[derive(Debug, Clone)]
pub enum SslType {
None,
Starttls,
Imaps,
}
#[derive(Debug)]
pub enum Store {
Maildir(MaildirStore),
Imap(ImapStore),
}
#[derive(Debug, Builder)]
pub struct MaildirStore {
pub name: String,
pub path: PathBuf,
pub inbox: PathBuf,
pub subfolders: MaildirSubfolderStyle,
}
#[derive(Debug, Clone)]
pub enum MaildirSubfolderStyle {
Verbatim,
Maildirpp,
Legacy,
}
#[derive(Debug, Builder)]
pub struct ImapStore {
pub name: String,
pub account: String,
}
#[derive(Debug, Builder)]
pub struct Channel {
pub name: String,
pub far: String,
pub near: String,
pub sync: ChannelSyncOps,
}
bitflags! {
pub struct ChannelSyncOps: u32 {
const PULL = 1 << 1;
const PUSH = 1 << 2;
const NEW = 1 << 3;
const RENEW = 1 << 4;
const DELETE = 1 << 5;
const FLAGS = 1 << 6;
}
}

11
mbsync/src/lib.rs Normal file
View file

@ -0,0 +1,11 @@
#[macro_use]
extern crate anyhow;
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate derivative;
#[macro_use]
extern crate derive_builder;
pub mod config;
pub mod store;

36
mbsync/src/main.rs Normal file
View file

@ -0,0 +1,36 @@
use std::path::PathBuf;
use anyhow::Result;
use clap::Clap;
use mbsync::{config, store};
#[derive(Debug, Clap)]
struct Opt {
/// The path to the config file (defaults to ~/.mbsyncrc).
#[clap(name = "config", long = "config", short = 'c')]
config_path: Option<PathBuf>,
}
#[tokio::main]
async fn main() -> Result<()> {
let opt = Opt::parse();
println!("opts: {:?}", opt);
let config_path = match opt.config_path {
Some(path) => path,
None => PathBuf::from(env!("HOME")).join(".mbsyncrc"),
};
println!("config path: {:?}", config_path);
let config = config::read_from_file(&config_path)?;
println!("config: {:?}", config);
for channel in config.channels.values() {
println!("beginning to sync {}", channel.name);
let far = store::open(&config, &channel.far).await?;
let near = store::open(&config, &channel.near).await?;
}
Ok(())
}

71
mbsync/src/store.rs Normal file
View file

@ -0,0 +1,71 @@
use anyhow::Result;
use panorama_imap::client::{auth::Login, ClientAuthenticated, ConfigBuilder};
use crate::config::{Account, Config, SslType, Store};
pub async fn open(config: &Config, store_name: impl AsRef<str>) -> Result<OpenedStore> {
let store = config
.stores
.get(store_name.as_ref())
.expect("already checked by config reader");
match store {
Store::Imap(store) => {
let account = config
.accounts
.get(&store.account)
.expect("already checked by config reader");
match account {
Account::Imap(account) => {
let port = account.port.unwrap_or_else(|| match account.ssltype {
SslType::None | SslType::Starttls => 143,
SslType::Imaps => 993,
});
let tls = match account.ssltype {
SslType::None | SslType::Starttls => false,
SslType::Imaps => true,
};
let mut client = ConfigBuilder::default()
.hostname(account.host.clone())
.port(port)
.tls(tls)
.open()
.await?;
if let SslType::Starttls = account.ssltype {
client = client.upgrade().await?;
}
println!("connected");
let login = Login {
username: account.user.clone(),
password: account.pass.clone(),
};
let client = client.auth(login).await?;
println!("authenticated");
Ok(OpenedStore::Imap(ImapStore { client }))
}
}
}
Store::Maildir(store) => {
todo!()
}
}
}
pub enum OpenedStore {
Imap(ImapStore),
Maildir(MaildirStore),
}
impl OpenedStore {
}
pub struct ImapStore {
client: ClientAuthenticated,
}
pub struct MaildirStore {}

View file

@ -1,7 +1,8 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use crate::{Bytes, VResult}; use crate::{Bytes, VResult};
/// Defines a new parser rule that operates on [`Bytes`] and produces a [`VResult`]. /// Defines a new parser rule that operates on [`Bytes`] and produces a
/// [`VResult`].
#[macro_export] #[macro_export]
macro_rules! rule { macro_rules! rule {
($vis:vis $name:ident : $ret:ty => $expr:expr) => { ($vis:vis $name:ident : $ret:ty => $expr:expr) => {

View file

@ -1 +1,2 @@
//! High-level SMTP Client
//! ---

View file

@ -1,2 +1,5 @@
mod client; #[macro_use]
extern crate panorama_proto_common;
pub mod client;
pub mod proto; pub mod proto;

View file

@ -1,3 +1,5 @@
//! Helper functions for manipulating the wire protocol. //! Helper functions for manipulating the wire protocol.
#![allow(non_snake_case, dead_code)] #![allow(non_snake_case, dead_code)]
pub mod rfc5321;

View file

@ -3,3 +3,6 @@
//! //!
//! Grammar from <https://datatracker.ietf.org/doc/html/rfc5321> //! Grammar from <https://datatracker.ietf.org/doc/html/rfc5321>
use panorama_proto_common::{tagi, Bytes};
rule!(pub ehlo : Bytes => tagi(b"EHLO"));