Fix capability parsing

This commit is contained in:
Michael Zhang 2021-10-25 18:42:14 -05:00
parent b6301fd6ef
commit c75113d97f
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
17 changed files with 541 additions and 196 deletions

366
Cargo.lock generated
View file

@ -28,15 +28,6 @@ dependencies = [
"version_check",
]
[[package]]
name = "aho-corasick"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
dependencies = [
"memchr",
]
[[package]]
name = "anyhow"
version = "1.0.44"
@ -95,7 +86,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -112,7 +103,7 @@ checksum = "4717cfcbfaa661a0fd48f8453951837ae7e8f81e481fbb136e3202d72805a744"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"cfg-if 1.0.0",
"libc",
"miniz_oxide",
"object",
@ -198,6 +189,12 @@ version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
@ -214,7 +211,7 @@ dependencies = [
"num-integer",
"num-traits",
"time",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -284,7 +281,7 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"crossbeam-utils",
]
@ -294,7 +291,7 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"crossbeam-utils",
]
@ -304,10 +301,35 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"lazy_static",
]
[[package]]
name = "crossterm"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ebde6a9dd5e331cd6c6f48253254d117642c31653baa475e394657c59c1f7d"
dependencies = [
"bitflags",
"crossterm_winapi",
"libc",
"mio 0.7.13",
"parking_lot",
"signal-hook",
"signal-hook-mio",
"winapi 0.3.9",
]
[[package]]
name = "crossterm_winapi"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a6966607622438301997d3dac0d2f6e9a90c68bb6bc1785ea98456ab93c0507"
dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "darling"
version = "0.12.4"
@ -413,7 +435,7 @@ checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
dependencies = [
"libc",
"redox_users",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -429,16 +451,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "env_logger"
version = "0.9.0"
name = "filetime"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
"cfg-if 1.0.0",
"libc",
"redox_syscall 0.2.10",
"winapi 0.3.9",
]
[[package]]
@ -479,6 +500,41 @@ dependencies = [
"syn",
]
[[package]]
name = "fsevent"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6"
dependencies = [
"bitflags",
"fsevent-sys",
]
[[package]]
name = "fsevent-sys"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0"
dependencies = [
"libc",
]
[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
dependencies = [
"bitflags",
"fuchsia-zircon-sys",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
[[package]]
name = "funty"
version = "1.1.0"
@ -606,7 +662,7 @@ version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
@ -617,7 +673,7 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
]
@ -723,12 +779,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hyper"
version = "0.14.13"
@ -781,15 +831,13 @@ dependencies = [
[[package]]
name = "inotify"
version = "0.9.5"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5fc8f41dbaa9c8492a96c8afffda4f76896ee041d6a57606e70581b80c901f"
checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f"
dependencies = [
"bitflags",
"futures-core",
"inotify-sys",
"libc",
"tokio",
]
[[package]]
@ -807,7 +855,16 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
name = "iovec"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
dependencies = [
"libc",
]
[[package]]
@ -834,12 +891,28 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "lexical-core"
version = "0.7.6"
@ -848,7 +921,7 @@ checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
dependencies = [
"arrayvec",
"bitflags",
"cfg-if",
"cfg-if 1.0.0",
"ryu",
"static_assertions",
]
@ -885,7 +958,7 @@ version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@ -916,6 +989,25 @@ dependencies = [
"autocfg",
]
[[package]]
name = "mio"
version = "0.6.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"
dependencies = [
"cfg-if 0.1.10",
"fuchsia-zircon",
"fuchsia-zircon-sys",
"iovec",
"kernel32-sys",
"libc",
"log",
"miow 0.2.2",
"net2",
"slab",
"winapi 0.2.8",
]
[[package]]
name = "mio"
version = "0.7.13"
@ -924,9 +1016,33 @@ checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16"
dependencies = [
"libc",
"log",
"miow",
"miow 0.3.7",
"ntapi",
"winapi",
"winapi 0.3.9",
]
[[package]]
name = "mio-extras"
version = "2.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
dependencies = [
"lazycell",
"log",
"mio 0.6.23",
"slab",
]
[[package]]
name = "miow"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d"
dependencies = [
"kernel32-sys",
"net2",
"winapi 0.2.8",
"ws2_32-sys",
]
[[package]]
@ -935,7 +1051,18 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
dependencies = [
"winapi",
"winapi 0.3.9",
]
[[package]]
name = "net2"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
dependencies = [
"cfg-if 0.1.10",
"libc",
"winapi 0.3.9",
]
[[package]]
@ -962,13 +1089,31 @@ dependencies = [
"version_check",
]
[[package]]
name = "notify"
version = "4.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae03c8c853dba7bfd23e571ff0cff7bc9dceb40a4cd684cd1681824183f45257"
dependencies = [
"bitflags",
"filetime",
"fsevent",
"fsevent-sys",
"inotify",
"libc",
"mio 0.6.23",
"mio-extras",
"walkdir",
"winapi 0.3.9",
]
[[package]]
name = "ntapi"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
dependencies = [
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -1034,8 +1179,8 @@ dependencies = [
"derivative",
"futures",
"hyper",
"inotify",
"log",
"notify",
"panorama-imap",
"panorama-smtp",
"serde",
@ -1071,24 +1216,6 @@ dependencies = [
"webpki-roots",
]
[[package]]
name = "panorama-mbsync"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"bitflags",
"clap",
"derivative",
"derive_builder",
"env_logger",
"futures",
"log",
"panorama-imap",
"serde",
"tokio",
]
[[package]]
name = "panorama-proto-common"
version = "0.0.1"
@ -1111,6 +1238,13 @@ dependencies = [
"panorama-proto-common",
]
[[package]]
name = "panorama-tui"
version = "0.1.0"
dependencies = [
"crossterm",
]
[[package]]
name = "parking_lot"
version = "0.11.2"
@ -1128,12 +1262,12 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"instant",
"libc",
"redox_syscall 0.2.10",
"smallvec",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -1246,29 +1380,12 @@ dependencies = [
"rust-argon2",
]
[[package]]
name = "regex"
version = "1.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "ring"
version = "0.16.20"
@ -1281,7 +1398,7 @@ dependencies = [
"spin",
"untrusted",
"web-sys",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -1321,6 +1438,15 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
@ -1376,12 +1502,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa"
dependencies = [
"block-buffer",
"cfg-if",
"cfg-if 1.0.0",
"cpufeatures",
"digest",
"opaque-debug",
]
[[package]]
name = "signal-hook"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c98891d737e271a2954825ef19e46bd16bdb98e2746f2eec4f7a4ef7946efd1"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29fd5867f1c4f2c5be079aee7a2adf1152ebb04a4bc4d341f504b7dece607ed4"
dependencies = [
"libc",
"mio 0.7.13",
"signal-hook",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
@ -1393,9 +1540,9 @@ dependencies = [
[[package]]
name = "slab"
version = "0.4.4"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590"
checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
[[package]]
name = "smallvec"
@ -1616,7 +1763,7 @@ checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -1644,21 +1791,21 @@ dependencies = [
"bytes",
"libc",
"memchr",
"mio",
"mio 0.7.13",
"num_cpus",
"once_cell",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"tokio-macros",
"winapi",
"winapi 0.3.9",
]
[[package]]
name = "tokio-macros"
version = "1.4.1"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "154794c8f499c2619acd19e839294703e9e32e7630ef5f46ea80d4ef0fbee5eb"
checksum = "b2dd85aeaba7b68df939bd357c6afb36c87951be9e80bf9c859f2fc3e9fca0fd"
dependencies = [
"proc-macro2",
"quote",
@ -1722,7 +1869,7 @@ version = "0.1.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"pin-project-lite",
"tracing-core",
]
@ -1823,6 +1970,17 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "walkdir"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
dependencies = [
"same-file",
"winapi 0.3.9",
"winapi-util",
]
[[package]]
name = "want"
version = "0.3.0"
@ -1851,7 +2009,7 @@ version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"wasm-bindgen-macro",
]
@ -1938,6 +2096,12 @@ dependencies = [
"web-sys",
]
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
[[package]]
name = "winapi"
version = "0.3.9"
@ -1948,6 +2112,12 @@ dependencies = [
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
@ -1960,7 +2130,7 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -1969,6 +2139,16 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]]
name = "wyz"
version = "0.2.0"

View file

@ -2,10 +2,11 @@
members = [
"daemon",
"imap",
"smtp",
# "mbsync",
"proto-common",
"mbsync",
"smtp",
"tui",
]
[profile.release]
lto = true
lto = true

1
daemon/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/test.db*

View file

@ -3,6 +3,10 @@ name = "panorama-daemon"
version = "0.0.1"
edition = "2018"
[features]
default = ["config-watch"]
config-watch = ["notify"]
[dependencies]
anyhow = { version = "1.0.42", features = ["backtrace"] }
async-trait = "0.1.50"
@ -10,7 +14,6 @@ clap = "3.0.0-beta.2"
derivative = "2.2.0"
futures = "0.3.16"
hyper = { version = "0.14.11", features = ["server", "http2", "stream"] }
inotify = { version = "0.9.3", features = ["stream"] }
log = "0.4.14"
panorama-imap = { path = "../imap" }
panorama-smtp = { path = "../smtp" }
@ -21,6 +24,8 @@ tokio-rustls = "0.22.0"
toml = "0.5.8"
xdg = "2.2.0"
notify = { version = "4.0.17", optional = true }
[dependencies.sqlx]
version = "0.5.9"
features = ["runtime-tokio-rustls", "sqlite", "json", "chrono"]

View file

@ -1,5 +1,5 @@
CREATE TABLE "accounts" (
"id" PRIMARY KEY AUTOINCREMENT
"id" INTEGER PRIMARY KEY AUTOINCREMENT
);
CREATE TABLE "mailboxes" (

View file

@ -1,3 +1,4 @@
#[cfg(feature = "config-watch")]
mod watcher;
use std::collections::HashMap;
@ -7,7 +8,8 @@ use std::path::{Path, PathBuf};
use anyhow::Result;
pub use self::watcher::spawn_config_watcher_system;
#[cfg(feature = "config-watch")]
pub use self::watcher::{spawn_config_watcher_system, ConfigWatcher};
/// Configuration
#[derive(Default, Serialize, Deserialize, Clone, Debug)]

View file

@ -1,10 +1,17 @@
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::mpsc as stdmpsc;
use std::time::Duration;
use anyhow::{Context, Result};
use futures::{future::TryFutureExt, stream::StreamExt};
use inotify::{Inotify, WatchMask};
use tokio::{sync::watch, task::JoinHandle};
use futures::future::TryFutureExt;
use notify::{
watcher, DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher,
};
use tokio::{
sync::{mpsc, watch},
task::JoinHandle,
};
use xdg::BaseDirectories;
use super::Config;
@ -16,33 +23,34 @@ pub type ConfigWatcher = watch::Receiver<Config>;
/// config update events.
pub fn spawn_config_watcher_system() -> Result<(JoinHandle<()>, ConfigWatcher)>
{
let mut inotify = Inotify::init()?;
let (tx, rx) = stdmpsc::channel();
let mut dir_watcher = watcher(tx, Duration::from_secs(10))?;
let xdg = BaseDirectories::new()?;
let config_home = xdg.get_config_home().join("panorama");
if !config_home.exists() {
fs::create_dir_all(&config_home)?;
}
inotify
.add_watch(&config_home, WatchMask::CLOSE_WRITE)
dir_watcher
.watch(&config_home, RecursiveMode::Recursive)
.context("adding watch for config home")?;
debug!("watching {:?}", config_home);
let (config_tx, config_update) = watch::channel(Config::default());
let handle = tokio::spawn(
start_inotify_stream(inotify, config_home, config_tx)
start_notify_stream(dir_watcher, rx, config_home, config_tx)
.unwrap_or_else(|_err| todo!()),
);
Ok((handle, config_update))
}
async fn start_inotify_stream(
mut inotify: Inotify,
async fn start_notify_stream(
mut watcher: RecommendedWatcher,
rx: stdmpsc::Receiver<DebouncedEvent>,
config_home: impl AsRef<Path>,
config_tx: watch::Sender<Config>,
) -> Result<()> {
let mut buffer = vec![0u8; 1024];
let mut event_stream = inotify.event_stream(&mut buffer)?;
let config_home = config_home.as_ref().to_path_buf();
let config_path = config_home.join("panorama.toml");
@ -53,33 +61,42 @@ async fn start_inotify_stream(
}
debug!("listening for inotify events");
while let Some(v) = event_stream.next().await {
let event = v.context("event")?;
debug!("inotify event: {:?}", event);
if let Some(name) = event.name {
let path = PathBuf::from(name);
let path_c = config_home
.clone()
.join(path.clone())
.canonicalize()
.context("osu")?;
if !path_c.exists() {
debug!("path {:?} doesn't exist", path_c);
continue;
}
// TODO: any better way to do this?
let config_path_c =
config_path.canonicalize().context("cfg_path")?;
if config_path_c != path_c {
debug!("did not match {:?} {:?}", config_path_c, path_c);
continue;
loop {
let event = rx.recv()?;
debug!("notify event: {:?}", event);
match event {
DebouncedEvent::NoticeRemove(_)
| DebouncedEvent::NoticeWrite(_) => {
// TODO: should this be handled somehow?
// neovim sends these since it's writing to a temp buffer
}
debug!("reading config from {:?}", path_c);
let config = Config::from_file(path_c).await.context("read")?;
// debug!("sending config {:?}", config);
config_tx.send(config)?;
DebouncedEvent::Create(path) | DebouncedEvent::Write(path) => {
let path_c = config_home
.clone()
.join(path.clone())
.canonicalize()
.context("osu")?;
if !path_c.exists() {
debug!("path {:?} doesn't exist", path_c);
continue;
}
// TODO: any better way to do this?
let config_path_c =
config_path.canonicalize().context("cfg_path")?;
if config_path_c != path_c {
debug!("did not match {:?} {:?}", config_path_c, path_c);
continue;
}
debug!("reading config from {:?}", path_c);
let config = Config::from_file(path_c).await.context("read")?;
// debug!("sending config {:?}", config);
config_tx.send(config)?;
}
_ => {}
}
}
Ok(())
}

View file

@ -9,7 +9,3 @@ extern crate derivative;
pub mod config;
pub mod mail;
use sqlx::migrate::Migrator;
static MIGRATOR: Migrator = sqlx::migrate!();

View file

@ -11,12 +11,16 @@ use panorama_imap::{
response::{MailboxData, Response},
},
};
use sqlx::migrate::Migrator;
use tokio::sync::mpsc::UnboundedSender;
use crate::config::{ImapAuth, MailAccountConfig, TlsMethod};
use self::store::MailStore;
pub use self::store::MailStore;
static MIGRATOR: Migrator = sqlx::migrate!();
#[derive(Debug)]
pub enum MailEvent {}
/// The main function for the IMAP syncing thread
@ -26,7 +30,9 @@ pub async fn sync_main(
_mail2ui_tx: UnboundedSender<MailEvent>,
_mail_store: MailStore,
) -> Result<()> {
let _acct_name = acct_name.as_ref().to_owned();
let acct_name = acct_name.as_ref();
debug!("Starting main synchronization procedure for {}", acct_name);
// loop ensures that the connection is retried after it dies
loop {
let client = ConfigBuilder::default()
@ -36,16 +42,22 @@ pub async fn sync_main(
.open()
.await
.map_err(|err| anyhow!("err: {}", err))?;
debug!("Connected to {}:{}.", &acct.imap.server, acct.imap.port);
debug!("connected to {}:{}", &acct.imap.server, acct.imap.port);
debug!("TLS Upgrade option: {:?}", acct.imap.tls);
let unauth = if matches!(acct.imap.tls, TlsMethod::Starttls) {
debug!("attempting to upgrade");
let client = client.upgrade().await?;
let client = client
.upgrade()
.await
.context("could not upgrade connection")?;
debug!("upgrade successful");
client
} else {
warn!("Continuing with unencrypted connection!");
client
};
debug!("preparing to auth");
// check if the authentication method is supported
let mut authed = match &acct.imap.auth {
@ -57,12 +69,14 @@ pub async fn sync_main(
unauth.auth(login).await?
}
};
debug!("authentication successful!");
let folder_list = authed.list().await?;
// let _ = mail2ui_tx.send(MailEvent::FolderList(
// acct_name.clone(),
// folder_list.clone(),
// ));
debug!("mailbox list: {:?}", folder_list);
for folder in folder_list.iter() {
debug!("folder: {:?}", folder);
@ -102,10 +116,12 @@ pub async fn sync_main(
}
}
tokio::time::sleep(std::time::Duration::from_secs(50)).await;
// TODO: remove this later
// continue;
// let's just select INBOX for now, maybe have a config for default
// mailbox later?
debug!("selecting the INBOX mailbox");
let select = authed.select("INBOX").await?;
debug!("select result: {:?}", select);
@ -127,8 +143,10 @@ pub async fn sync_main(
// attrs); TODO: probably odn't care about this?
// let _ = mail2ui_tx.send(evt);
}
// check if IDLE is supported
// TODO: check if IDLE is supported
let supports_idle = true; // authed.has_capability("IDLE").await?;
if supports_idle {
let mut idle_stream = authed.idle().await?;
loop {
@ -136,6 +154,7 @@ pub async fn sync_main(
Some(v) => v,
None => break,
};
debug!("got an event: {:?}", evt);
match evt {
Response::MailboxData(MailboxData::Exists(uid)) => {

View file

@ -1,7 +1,7 @@
use anyhow::Result;
use sqlx::sqlite::{SqlitePool, SqlitePoolOptions};
use crate::MIGRATOR;
use super::MIGRATOR;
pub struct MailStore {
pool: SqlitePool,

View file

@ -3,16 +3,23 @@ extern crate log;
#[macro_use]
extern crate futures;
use anyhow::Result;
use anyhow::{Context, Result};
use clap::Clap;
use futures::future::{
select,
Either::{Left, Right},
FutureExt,
};
use panorama_daemon::config::{self, Config, MailAccountConfig, TlsMethod};
use panorama_daemon::{
config::{Config, MailAccountConfig, TlsMethod},
mail::{sync_main, MailStore},
};
use panorama_imap::client::ConfigBuilder;
use tokio::sync::oneshot;
use tokio::{
fs::{self, OpenOptions},
sync::{mpsc, oneshot},
};
use xdg::BaseDirectories;
type ExitListener = oneshot::Receiver<()>;
@ -34,31 +41,68 @@ async fn main() -> Result<()> {
stderrlog::new()
.module(module_path!())
.module("panorama_daemon")
.module("panorama_imap")
.verbosity(opt.verbose)
.init()
.unwrap();
let (_, mut config_watcher) = config::spawn_config_watcher_system()?;
// if we're using a config-watcher, then start the watcher system
#[cfg(feature = "config-watch")]
{
use panorama_daemon::config;
loop {
let (exit_tx, exit_rx) = oneshot::channel();
let new_config = config_watcher.borrow().clone();
tokio::spawn(run_with_config(new_config, exit_rx));
let (_, mut config_watcher) = config::spawn_config_watcher_system()?;
// wait till the config has changed, then tell the current thread to
// stop
config_watcher.changed().await?;
let _ = exit_tx.send(());
loop {
let (exit_tx, exit_rx) = oneshot::channel();
let new_config = config_watcher.borrow().clone();
tokio::spawn(run_with_config(new_config, exit_rx));
// wait till the config has changed, then tell the current thread to
// stop
config_watcher.changed().await?;
let _ = exit_tx.send(());
}
}
// TODO: handle SIGHUP on Unix? pretty common for systemd to send this when
// reloading config files
// if not, just read the config once and run the daemon
#[cfg(not(feature = "config-watch"))]
{
let xdg = BaseDirectories::new()?;
let config_home = xdg.get_config_home().join("panorama");
if !config_home.exists() {
fs::create_dir_all(&config_home).await?;
}
let config_path = config_home.join("panorama.toml");
let config_path_c = config_path.canonicalize().context("cfg_path")?;
let config = Config::from_file(config_path_c).await.context("read")?;
let (_, exit_rx) = oneshot::channel();
run_with_config(config, exit_rx).await
}
}
async fn run_with_config(config: Config, exit: ExitListener) -> Result<()> {
debug!("new config");
debug!("New config: {:?}", config);
// keep track of which threads need to be stopped when this function is
// stopped
let mut notify_mail_threads = Vec::new();
for (account_name, account) in config.mail_accounts {
let (exit_tx, exit_rx) = oneshot::channel();
tokio::spawn(run_single_mail_account(account_name, account, exit_rx));
tokio::spawn(async {
match run_single_mail_account(account_name, account, exit_rx).await
{
Ok(_) => {}
Err(err) => panic!("failed: {:?}", err),
}
});
notify_mail_threads.push(exit_tx);
}
@ -79,30 +123,59 @@ async fn run_single_mail_account(
account: MailAccountConfig,
exit: ExitListener,
) -> Result<()> {
debug!("connecting to account {}", account_name);
// set up the connection
let mut builder = ConfigBuilder::default();
let imap_cookie = builder
.hostname(account.imap.server.clone())
.port(account.imap.port)
.tls(matches!(account.imap.tls, TlsMethod::On))
.open();
pin_mut!(imap_cookie);
pin_mut!(exit);
let (imap, exit) = match select(imap_cookie, exit).await {
Left(res) => res,
Right(_) => return Ok(()),
};
debug!("connected to {}", account.imap.server);
let _imap = imap?;
debug!("run_single_mail_account({}, {:?})", account_name, account);
let mut exit = exit.fuse();
let xdg = BaseDirectories::new()?;
let data_home = xdg.get_data_home().join("panorama");
if !data_home.exists() {
fs::create_dir_all(&data_home)
.await
.context("could not create config directory")?;
}
let db_path = data_home.join("db.sqlite3");
debug!("Opening database at path: {:?}", db_path);
if !db_path.exists() {
OpenOptions::new()
.create(true)
.write(true)
.open(&db_path)
.await
.context("could not touch db path")?;
}
let db_path = db_path
.canonicalize()
.context("could not canonicalize db path")?;
let store =
MailStore::open(format!("sqlite://{}", db_path.to_string_lossy()))
.await
.context("couldn't open mail store")?;
let (tx, mut rx) = mpsc::unbounded_channel();
let sync_fut = sync_main(&account_name, account, tx, store).fuse();
pin_mut!(sync_fut);
debug!("Mail account loop for {}.", account_name);
loop {
select! {
res = sync_fut => match res {
Ok(_) => {},
Err(err) => {
error!("sync_main died with: {:?}", err);
break;
}
},
evt_opt = rx.recv().fuse() => {
let evt = match evt_opt {
Some(evt) => evt,
None => break,
};
debug!("Event: {:?}", evt);
},
// we're being told to exit the loop
_ = exit => break,
}

View file

@ -4,7 +4,7 @@ use std::sync::{
Arc,
};
use anyhow::Result;
use anyhow::{Context, Result};
use futures::{
future::{self, FutureExt, TryFutureExt},
stream::StreamExt,
@ -134,8 +134,17 @@ where
&mut self,
cap: impl AsRef<str>,
) -> Result<bool> {
let cap_bytes = cap.as_ref().as_bytes().to_vec();
let (_, cap) = parse_capability(Bytes::from(cap_bytes))?;
let mut cap_slice = cap.as_ref().as_bytes().to_vec();
// since we're doing incremental parsing, we have to finish this off
// with something that's invalid
cap_slice.push(b'\n');
let cap_bytes = Bytes::from(cap_slice);
trace!("CAP_BYTES: {:?}", cap_bytes);
let (_, cap) = parse_capability(cap_bytes)
.context("could not parse capability")?;
let contains = {
let read = self.capabilities.read().await;
@ -145,10 +154,21 @@ where
std::mem::drop(read);
let cmd = self.execute(Command::Capability).await?;
let done = cmd.done().await?;
todo!("done: {:?}", done);
let (done, res) = cmd.wait().await?;
// todo!()
let mut capabilities = HashSet::new();
// todo!("done: {:?} {:?}", done, res);
for caps in res.iter().filter_map(|res| match res {
Response::Capabilities(caps) => Some(caps),
_ => None,
}) {
capabilities.extend(caps.clone());
}
let mut write = self.capabilities.write().await;
*write = Some(capabilities);
true
}
};
@ -160,28 +180,48 @@ where
// check that this capability exists
// if it doesn't exist, then it's not an IMAP4-compliant server
if !self.has_capability("STARTTLS").await? {
if !self
.has_capability("STARTTLS")
.await
.context("could not check starttls capability")?
{
bail!("Server does not have the STARTTLS capability");
}
// issue the STARTTLS command to the server
let resp = self.execute(Command::Starttls).await?;
dbg!(resp.wait().await?);
let resp = self
.execute(Command::Starttls)
.await
.context("could not send starttls command")?;
dbg!(resp
.wait()
.await
.context("could not receive starttls response")?);
debug!("received OK from server");
// issue exit to the read loop and retrieve the read half
let _ = self.read_exit.send(());
let read_half = self.read_handle.await?;
let read_half = self
.read_handle
.await
.context("could not retrieve read half of connection")?;
// issue exit to the write loop and retrieve the write half
let _ = self.write_exit.send(());
let write_half = self.write_handle.await?;
let write_half = self
.write_handle
.await
.context("could not retrieve write half of connection")?;
// put the read half and write half back together
let stream = read_half.unsplit(write_half);
let tls_stream = wrap_tls(stream, &self.config.hostname).await?;
let tls_stream = wrap_tls(stream, &self.config.hostname)
.await
.context("could not initialize tls stream")?;
Inner::new(tls_stream, self.config).await
Inner::new(tls_stream, self.config)
.await
.context("could not construct new client")
}
pub async fn wait_for_greeting(&mut self) -> Result<()> {

View file

@ -62,6 +62,7 @@ pub enum Response {
impl DisplayBytes for Response {
fn display_bytes(&self, w: &mut dyn Write) -> io::Result<()> {
#[allow(unreachable_patterns)]
match self {
Response::Capabilities(caps) => {
write_bytes!(w, b"CAPABILITY")?;
@ -72,7 +73,7 @@ impl DisplayBytes for Response {
}
Response::Continue(cont) => write_bytes!(w, b"+ {}\r\n", cont),
Response::Condition(cond) => write_bytes!(w, b"* {}\r\n", cond),
Response::Done(done) => write_bytes!(w, b""),
Response::Done(_) => write_bytes!(w, b""),
Response::MailboxData(data) => write_bytes!(w, b"* {}\r\n", data),
Response::Fetch(n, attrs) => {
write_bytes!(w, b"{} FETCH (", n)?;
@ -127,7 +128,7 @@ pub enum MessageAttribute {
}
impl DisplayBytes for MessageAttribute {
fn display_bytes(&self, w: &mut dyn Write) -> io::Result<()> {
fn display_bytes(&self, _: &mut dyn Write) -> io::Result<()> {
match self {
_ => todo!(),
}
@ -245,7 +246,7 @@ pub enum UidSetMember {
Uid(u32),
}
#[derive(Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "fuzzing", derive(Arbitrary))]
pub enum Capability {
Imap4rev1,

View file

@ -9,7 +9,6 @@ license = "GPL-3.0-or-later"
categories = ["email"]
repository = "https://git.mzhang.io/michael/panorama"
readme = "README.md"
workspace = ".."
[dependencies]
anyhow = { version = "1.0.42", features = ["backtrace"] }

7
tui/Cargo.toml Normal file
View file

@ -0,0 +1,7 @@
[package]
name = "panorama-tui"
version = "0.1.0"
edition = "2018"
[dependencies]
crossterm = "0.20"

1
tui/src/lib.rs Normal file
View file

@ -0,0 +1 @@

3
tui/src/main.rs Normal file
View file

@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}