mbsync
This commit is contained in:
parent
24cf869da5
commit
dfdb71dea2
18 changed files with 545 additions and 52 deletions
114
Cargo.lock
generated
114
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -4,6 +4,7 @@ members = [
|
||||||
"imap",
|
"imap",
|
||||||
"smtp",
|
"smtp",
|
||||||
"proto-common",
|
"proto-common",
|
||||||
|
"mbsync",
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
|
3
Justfile
3
Justfile
|
@ -3,3 +3,6 @@ fmt:
|
||||||
|
|
||||||
doc:
|
doc:
|
||||||
cargo doc --workspace --no-deps
|
cargo doc --workspace --no-deps
|
||||||
|
|
||||||
|
watch:
|
||||||
|
cargo watch -c
|
|
@ -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"] }
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
13
mbsync/Cargo.toml
Normal 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
315
mbsync/src/config.rs
Normal 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
11
mbsync/src/lib.rs
Normal 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
36
mbsync/src/main.rs
Normal 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
71
mbsync/src/store.rs
Normal 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 {}
|
|
@ -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) => {
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
|
//! High-level SMTP Client
|
||||||
|
//! ---
|
||||||
|
|
|
@ -1,2 +1,5 @@
|
||||||
mod client;
|
#[macro_use]
|
||||||
|
extern crate panorama_proto_common;
|
||||||
|
|
||||||
|
pub mod client;
|
||||||
pub mod proto;
|
pub mod proto;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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"));
|
||||||
|
|
Loading…
Reference in a new issue