gtk somewhat working

This commit is contained in:
Franz Dietrich 2023-12-13 13:04:15 +01:00
parent 7a578073cd
commit b983d78575
16 changed files with 1377 additions and 206 deletions

716
Cargo.lock generated
View File

@ -26,6 +26,12 @@ dependencies = [
"memchr",
]
[[package]]
name = "anyhow"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "autocfg"
version = "1.1.0"
@ -65,6 +71,31 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cairo-rs"
version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f33613627f0dea6a731b0605101fad59ba4f193a52c96c4687728d822605a8a1"
dependencies = [
"bitflags 2.4.1",
"cairo-sys-rs",
"glib",
"libc",
"once_cell",
"thiserror",
]
[[package]]
name = "cairo-sys-rs"
version = "0.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51"
dependencies = [
"glib-sys",
"libc",
"system-deps",
]
[[package]]
name = "cc"
version = "1.0.83"
@ -74,12 +105,28 @@ dependencies = [
"libc",
]
[[package]]
name = "cfg-expr"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03915af431787e6ffdcc74c645077518c6b6e01f80b761e0fbbfa288536311b3"
dependencies = [
"smallvec",
"target-lexicon",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "env_logger"
version = "0.10.1"
@ -93,6 +140,12 @@ dependencies = [
"termcolor",
]
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.8"
@ -103,6 +156,137 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "field-offset"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f"
dependencies = [
"memoffset",
"rustc_version",
]
[[package]]
name = "futures-channel"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"
dependencies = [
"futures-core",
]
[[package]]
name = "futures-core"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
[[package]]
name = "futures-executor"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa"
[[package]]
name = "futures-macro"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
]
[[package]]
name = "futures-task"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
[[package]]
name = "futures-util"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
dependencies = [
"futures-core",
"futures-macro",
"futures-task",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "gdk-pixbuf"
version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "446f32b74d22c33b7b258d4af4ffde53c2bf96ca2e29abdf1a785fe59bd6c82c"
dependencies = [
"gdk-pixbuf-sys",
"gio",
"glib",
"libc",
"once_cell",
]
[[package]]
name = "gdk-pixbuf-sys"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7"
dependencies = [
"gio-sys",
"glib-sys",
"gobject-sys",
"libc",
"system-deps",
]
[[package]]
name = "gdk4"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edb019ad581f8ecf8ea8e4baa6df7c483a95b5a59be3140be6a9c3b0c632af6"
dependencies = [
"cairo-rs",
"gdk-pixbuf",
"gdk4-sys",
"gio",
"glib",
"libc",
"pango",
]
[[package]]
name = "gdk4-sys"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbab43f332a3cf1df9974da690b5bb0e26720ed09a228178ce52175372dcfef0"
dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
"gio-sys",
"glib-sys",
"gobject-sys",
"libc",
"pango-sys",
"pkg-config",
"system-deps",
]
[[package]]
name = "getrandom"
version = "0.2.11"
@ -120,6 +304,216 @@ version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "gio"
version = "0.18.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-util",
"gio-sys",
"glib",
"libc",
"once_cell",
"pin-project-lite",
"smallvec",
"thiserror",
]
[[package]]
name = "gio-sys"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2"
dependencies = [
"glib-sys",
"gobject-sys",
"libc",
"system-deps",
"winapi",
]
[[package]]
name = "glib"
version = "0.18.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "951bbd7fdc5c044ede9f05170f05a3ae9479239c3afdfe2d22d537a3add15c4e"
dependencies = [
"bitflags 2.4.1",
"futures-channel",
"futures-core",
"futures-executor",
"futures-task",
"futures-util",
"gio-sys",
"glib-macros",
"glib-sys",
"gobject-sys",
"libc",
"memchr",
"once_cell",
"smallvec",
"thiserror",
]
[[package]]
name = "glib-macros"
version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72793962ceece3863c2965d7f10c8786323b17c7adea75a515809fa20ab799a5"
dependencies = [
"heck",
"proc-macro-crate 2.0.1",
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.39",
]
[[package]]
name = "glib-sys"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898"
dependencies = [
"libc",
"system-deps",
]
[[package]]
name = "gobject-sys"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44"
dependencies = [
"glib-sys",
"libc",
"system-deps",
]
[[package]]
name = "graphene-rs"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b2228cda1505613a7a956cca69076892cfbda84fc2b7a62b94a41a272c0c401"
dependencies = [
"glib",
"graphene-sys",
"libc",
]
[[package]]
name = "graphene-sys"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc4144cee8fc8788f2a9b73dc5f1d4e1189d1f95305c4cb7bd9c1af1cfa31f59"
dependencies = [
"glib-sys",
"libc",
"pkg-config",
"system-deps",
]
[[package]]
name = "gsk4"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d958e351d2f210309b32d081c832d7de0aca0b077aa10d88336c6379bd01f7e"
dependencies = [
"cairo-rs",
"gdk4",
"glib",
"graphene-rs",
"gsk4-sys",
"libc",
"pango",
]
[[package]]
name = "gsk4-sys"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12bd9e3effea989f020e8f1ff3fa3b8c63ba93d43b899c11a118868853a56d55"
dependencies = [
"cairo-sys-rs",
"gdk4-sys",
"glib-sys",
"gobject-sys",
"graphene-sys",
"libc",
"pango-sys",
"system-deps",
]
[[package]]
name = "gtk4"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aeb51aa3e9728575a053e1f43543cd9992ac2477e1b186ad824fd4adfb70842"
dependencies = [
"cairo-rs",
"field-offset",
"futures-channel",
"gdk-pixbuf",
"gdk4",
"gio",
"glib",
"graphene-rs",
"gsk4",
"gtk4-macros",
"gtk4-sys",
"libc",
"pango",
]
[[package]]
name = "gtk4-macros"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d57ec49cf9b657f69a05bca8027cff0a8dfd0c49e812be026fc7311f2163832f"
dependencies = [
"anyhow",
"proc-macro-crate 1.3.1",
"proc-macro-error",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "gtk4-sys"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54d8c4aa23638ce9faa2caf7e2a27d4a1295af2155c8e8d28c4d4eeca7a65eb8"
dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
"gdk4-sys",
"gio-sys",
"glib-sys",
"gobject-sys",
"graphene-sys",
"gsk4-sys",
"libc",
"pango-sys",
"system-deps",
]
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.3.3"
@ -132,6 +526,16 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "indexmap"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "is-terminal"
version = "0.4.9"
@ -171,12 +575,30 @@ version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "managesieve"
version = "0.1.1"
dependencies = [
"either",
"nom",
"thiserror",
]
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@ -194,9 +616,9 @@ dependencies = [
[[package]]
name = "mio"
version = "0.8.9"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
dependencies = [
"libc",
"wasi",
@ -232,6 +654,37 @@ dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "pango"
version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4"
dependencies = [
"gio",
"glib",
"libc",
"once_cell",
"pango-sys",
]
[[package]]
name = "pango-sys"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5"
dependencies = [
"glib-sys",
"gobject-sys",
"libc",
"system-deps",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
@ -261,6 +714,62 @@ version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
[[package]]
name = "proc-macro-crate"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [
"once_cell",
"toml_edit 0.19.15",
]
[[package]]
name = "proc-macro-crate"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a"
dependencies = [
"toml_datetime",
"toml_edit 0.20.2",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn 1.0.109",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.70"
@ -319,9 +828,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "ring"
version = "0.17.6"
version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "684d5e6e18f669ccebf64a92236bb7db9a34f07be010e3627368182027180866"
checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74"
dependencies = [
"cc",
"getrandom",
@ -338,10 +847,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustix"
version = "0.38.26"
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "0.38.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
dependencies = [
"bitflags 2.4.1",
"errno",
@ -352,9 +870,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.22.0"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bc238b76c51bbc449c55ffbc39d03772a057cc8cf783c49d4af4c2537b74a8b"
checksum = "fe6b63262c9fcac8659abfaa96cac103d28166d3ff3eaf8f412e19f3ae9e5a48"
dependencies = [
"log",
"ring",
@ -366,9 +884,9 @@ dependencies = [
[[package]]
name = "rustls-pki-types"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb0a1f9b9efec70d32e6d6aa3e58ebd88c3754ec98dfe9145c63cf54cc829b83"
checksum = "e7673e0aa20ee4937c6aacfc12bb8341cfbf054cdd21df6bec5fd0629fe9339b"
[[package]]
name = "rustls-webpki"
@ -387,14 +905,54 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "semver"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "serde"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
]
[[package]]
name = "serde_spanned"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80"
dependencies = [
"serde",
]
[[package]]
name = "sieverman"
version = "0.1.0"
dependencies = [
"anyhow",
"env_logger",
"gtk4",
"log",
"managesieve",
"nom",
"rustls-pki-types",
"serde",
"thiserror",
"tokio",
"tokio-rustls",
"webpki-roots",
@ -409,6 +967,15 @@ dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
version = "1.11.2"
@ -437,6 +1004,17 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.39"
@ -448,6 +1026,25 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "system-deps"
version = "6.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2d580ff6a20c55dfb86be5f9c238f67835d0e81cbdea8bf5680e0897320331"
dependencies = [
"cfg-expr",
"heck",
"pkg-config",
"toml",
"version-compare",
]
[[package]]
name = "target-lexicon"
version = "0.12.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a"
[[package]]
name = "termcolor"
version = "1.4.0"
@ -458,10 +1055,30 @@ dependencies = [
]
[[package]]
name = "tokio"
version = "1.34.0"
name = "thiserror"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9"
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
]
[[package]]
name = "tokio"
version = "1.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c"
dependencies = [
"backtrace",
"bytes",
@ -484,19 +1101,65 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.39",
]
[[package]]
name = "tokio-rustls"
version = "0.25.0"
source = "git+https://github.com/cpu/tokio-rustls.git?branch=cpu-0.25-prep#3be1d2edb6d2a141ec7e0c137233e4d65e1091dc"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
dependencies = [
"rustls",
"rustls-pki-types",
"tokio",
]
[[package]]
name = "toml"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit 0.20.2",
]
[[package]]
name = "toml_datetime"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.19.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap",
"toml_datetime",
"winnow",
]
[[package]]
name = "toml_edit"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
@ -509,6 +1172,18 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "version-compare"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@ -687,6 +1362,15 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "winnow"
version = "0.5.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67b5f0a4e7a27a64c651977932b9dc5667ca7fc31ac44b03ed37a0cf42fdfff"
dependencies = [
"memchr",
]
[[package]]
name = "zeroize"
version = "1.7.0"

View File

@ -1,15 +1,32 @@
[package]
name = "sieverman"
version = "0.1.0"
authors = ["Franz Dietrich<dietrich@teilgedanken.de>"]
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Connect to a Managesieve-Server"
documentation = "https://docs.rs/bitflags"
readme = "README.md"
homepage = "https://serde.rs/"
repository = "https://github.com/rust-lang/cargo/"
keywords = ["email", "sieve", "managesieve", "filter", "client"]
categories = ["command-line-utilities", "email", "api-bindings"]
default-run = "sieverman"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tokio-rustls = { git = "https://github.com/cpu/tokio-rustls.git", branch = "cpu-0.25-prep" }
pki-types = { package = "rustls-pki-types", version = "1.0", features = [
"alloc",
] }
tokio-rustls = { version = "0.25" }
env_logger = "0.10.1"
log = "0.4.20"
tokio = { version = "1.34.0", features = ["full"] }
webpki-roots = "0.26.0"
pki-types = { package = "rustls-pki-types", version = "1.0" }
nom = "7.1.3"
managesieve = { path = "../managesieve" }
anyhow = "1.0"
thiserror = "1.0"
gtk4 = { version = "0.7.3", features = ["gnome_45"] }
serde = { version = "1.0.193", features = ["derive"] }

36
gui/Sieverman.cmb Normal file
View File

@ -0,0 +1,36 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<!DOCTYPE cambalache-project SYSTEM "cambalache-project.dtd">
<cambalache-project version="0.13.1" target_tk="gtk-4.0">
<ui>
(1,None,"sieverman.ui","sieverman.ui",None,None,"Franz Dietrich",None,None,None,None)
</ui>
<object>
(1,1,"GtkApplicationWindow","sieverman",None,None,None,None,-1,None),
(1,2,"GtkBox",None,1,None,None,None,-1,None),
(1,3,"GtkBox",None,2,None,None,None,1,None),
(1,4,"GtkActionBar",None,2,None,None,None,None,None),
(1,5,"GtkStatusbar",None,2,None,None,None,2,None),
(1,6,"GtkSpinner",None,3,None,None,None,1,None),
(1,7,"GtkLabel","messages",3,None,None,None,None,None),
(1,9,"GtkFrame",None,3,None,None,None,2,None),
(1,10,"GtkLabel","serverstatus_2",9,None,None,None,None,None)
</object>
<object_property>
(1,2,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None),
(1,3,"GtkBox","baseline-child","0",None,None,None,None,None,None,None,None,None),
(1,3,"GtkBox","homogeneous","True",None,None,None,None,None,None,None,None,None),
(1,6,"GtkSpinner","spinning","True",None,None,None,None,None,None,None,None,None),
(1,7,"GtkLabel","label","Loading",None,None,None,None,None,None,None,None,None),
(1,10,"GtkLabel","label","Loading…",None,None,None,None,None,None,None,None,None)
</object_property>
<object_data>
(1,3,"GtkWidget",1,1,None,None,None,None,None,None),
(1,3,"GtkWidget",2,2,None,1,None,None,None,None),
(1,3,"GtkWidget",2,3,None,1,None,None,None,None),
(1,3,"GtkWidget",2,4,None,1,None,None,None,None),
(1,3,"GtkWidget",2,5,None,1,None,None,None,None),
(1,3,"GtkWidget",2,6,None,1,None,None,None,None),
(1,3,"GtkWidget",2,7,None,1,None,None,None,None),
(1,3,"GtkWidget",2,8,None,1,None,None,None,None)
</object_data>
</cambalache-project>

41
gui/sieverman.ui Normal file
View File

@ -0,0 +1,41 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.16.0 -->
<interface>
<!-- interface-name sieverman.ui -->
<!-- interface-authors Franz Dietrich -->
<requires lib="gtk" version="4.12"/>
<object class="GtkApplicationWindow" id="sieverman">
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkActionBar"/>
</child>
<child>
<object class="GtkBox">
<property name="baseline-child">0</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkLabel" id="messages">
<property name="label">Loading</property>
</object>
</child>
<child>
<object class="GtkSpinner">
<property name="spinning">True</property>
</object>
</child>
<child>
<object class="GtkLabel" id="serverstatus">
<property name="label">Loading…</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkStatusbar"/>
</child>
</object>
</child>
</object>
</interface>

1
src/bin/backend/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod run;

38
src/bin/backend/run.rs Normal file
View File

@ -0,0 +1,38 @@
use log::{info, trace};
use sieverman::ConnectionInfo;
use crate::protocol::BackToFront;
pub(crate) async fn run(
to_frontend_tx: tokio::sync::mpsc::Sender<BackToFront>,
) -> anyhow::Result<()> {
trace!("Starting up sieverman…");
let info = ConnectionInfo::new("teilgedanken.de", 4190);
let mut connected = info.connect().await.unwrap();
info!("connected to the server");
loop {
match connected.read_introduction().await {
Ok(sieverman::IsComplete::Yes(c)) => {
connected = c;
break;
}
Ok(sieverman::IsComplete::No(c)) => {
trace!("Incomplete data waiting for some time…");
connected = c
}
Err(e) => return Err(anyhow::anyhow!("something went wrong: {}", e)),
};
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
connected.log_buffer().await;
}
trace!("Fully read the introduction:");
connected.log_buffer().await;
connected.log_server_settings().await;
to_frontend_tx
.send(BackToFront::ServerConnected(
connected.get_greeting().unwrap(),
))
.await
.unwrap();
Ok(())
}

View File

@ -0,0 +1,54 @@
use std::{cell::RefCell, rc::Rc};
use gtk4::{
glib,
prelude::{ApplicationExt, GtkWindowExt, WidgetExt},
Application, ApplicationWindow, Builder, Label,
};
use log::trace;
use crate::protocol::BackToFront;
pub(crate) fn get_app(
from_backend_rx: Rc<RefCell<Option<tokio::sync::mpsc::Receiver<BackToFront>>>>,
) -> Application {
let app = Application::builder()
.application_id("de.teilgedanken.sieverman")
.build();
app.connect_activate(move |app| build_ui(app, from_backend_rx.clone()));
app
}
fn build_ui(
app: &gtk4::Application,
from_backend_rx: Rc<RefCell<Option<tokio::sync::mpsc::Receiver<BackToFront>>>>,
) {
let glade_src = include_str!("../../../gui/sieverman.ui");
let builder = Builder::from_string(glade_src);
let main_window: ApplicationWindow = builder.object("sieverman").expect("get main-window");
main_window.set_height_request(400);
main_window.set_application(Some(app));
main_window.present();
let future = {
let mut data_event_receiver = from_backend_rx.take().expect("data_event_reciver");
async move {
while let Some(event) = data_event_receiver.recv().await {
trace!("data event: {:?}", event);
match event {
BackToFront::ServerConnected(message) => {
trace!("Received {}", message);
let status_label: Label = builder.object("serverstatus").unwrap();
status_label.set_text(&message)
}
}
}
}
};
let c = glib::MainContext::default();
c.spawn_local(future);
main_window.present();
}

1
src/bin/gui/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod main_window;

View File

@ -1,98 +1,17 @@
use log::info;
use nom::branch::alt;
use nom::bytes::streaming::is_not;
use nom::bytes::complete::is_not;
use nom::character::is_space;
use nom::character::streaming::{
alpha1, digit1, line_ending, multispace0, multispace1, newline, none_of, not_line_ending,
};
use nom::multi::many_m_n;
use nom::sequence::{delimited, preceded, separated_pair};
use nom::{bytes::streaming::tag, IResult};
use nom::{bytes::complete::tag, IResult};
use nom::{character, number};
use sieverman::parser::parse_server_config;
use sieverman::{Methods, ServerSettings};
use std::error::Error;
pub fn do_nothing_parser(input: &str) -> IResult<&str, &str> {
Ok((input, ""))
}
fn quoted(input: &str) -> IResult<&str, &str> {
preceded(multispace0, delimited(tag("\""), is_not("\n\""), tag("\"")))(input)
}
fn key_value(input: &str) -> IResult<&str, (&str, &str)> {
// note that this is really creating a function, the parser for abc
// vvvvv
// which is then called here, returning an IResult<&str, &str>
// vvvvv
preceded(multispace0, separated_pair(quoted, multispace1, quoted))(input)
}
fn parse_version(input: &str) -> IResult<&str, Vec<i32>> {
many_m_n(
1,
5,
alt((
character::complete::i32,
preceded(character::complete::char('.'), character::complete::i32),
)),
)(input)
}
fn parse_methods(input: &str) -> IResult<&str, Vec<Methods>> {
let (rem, res) = many_m_n(1, 10, alt((alpha1, preceded(multispace1, alpha1))))(input)?;
let methods = res
.into_iter()
.map(|m| match m {
"PLAIN" => Methods::Plain,
"OAUTHBEARER" => Methods::OAuthbearer,
any => todo!("That method is not yet implemented: {}", any),
})
.collect();
Ok((rem, methods))
}
fn parse_extensions(input: &str) -> IResult<&str, Vec<String>> {
let (rem, res) = many_m_n(1, 200, alt((is_not(" "), preceded(tag(" "), is_not(" ")))))(input)?;
let extensions = res.into_iter().map(|m| m.to_string()).collect();
Ok((rem, extensions))
}
fn parse_server_config(input: &str) -> IResult<&str, ServerSettings> {
let (remain, (key, implementation)) = key_value(input)?;
info!("Implementation:# {}", implementation);
let (remain, (key, version)) = key_value(remain)?;
info!("{}", version);
let (_, version) = parse_version(version)?;
info!("Version:# {:?}", version);
let (remain, (key, sasl)) = key_value(remain)?;
info!("{}", sasl);
let (_, sasl) = parse_methods(sasl)?;
info!("SASL:# {:?}", sasl);
let (remain, (key, capabilities)) = key_value(remain)?;
info!("{}", capabilities);
let (_, capabilities) = parse_extensions(capabilities)?;
info!("Extensions:# {:?}", &capabilities);
let (remain, (key, notify)) = key_value(remain)?;
info!("{}", notify);
let (_, notify) = parse_extensions(remain)?;
info!("Notify:# {:?}", &notify);
let (remain, (key, max_redirects)) = key_value(remain)?;
info!("{}", max_redirects);
let (_, max_redirects) = character::complete::u32(max_redirects)?;
info!("Max Redirects:# {}", max_redirects);
IResult::Ok((
remain,
ServerSettings {
implementation: implementation.to_string(),
version,
sasl,
capabilities,
notify,
max_redirects,
},
))
}
fn main() -> Result<(), Box<dyn Error>> {
env_logger::init();
@ -105,5 +24,7 @@ fn main() -> Result<(), Box<dyn Error>> {
"MAXREDIRECTS" "1"
"#;
let (remaining_input, output) = parse_server_config(input)?;
dbg!(output);
Ok(())
}

6
src/bin/protocol/mod.rs Normal file
View File

@ -0,0 +1,6 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum BackToFront {
ServerConnected(String),
}

View File

@ -1,108 +1,25 @@
/// This is the simplest possible client using rustls that does something useful:
/// it accepts the default configuration, loads some root certs, and then connects
/// to google.com and issues a basic HTTP request. The response is printed to stdout.
///
/// It makes use of rustls::Stream to treat the underlying TLS connection as a basic
/// bi-directional stream -- the underlying IO is performed transparently.
///
/// Note that `unwrap()` is used to deal with networking errors; this is not something
/// that is sensible outside of example code.
use std::{os::unix::net::SocketAddr, sync::Arc};
mod backend;
mod gui;
pub mod protocol;
use std::{cell::RefCell, rc::Rc, thread};
use log::{info, trace};
use std::net::ToSocketAddrs;
use tokio::{
io::{self, split, AsyncReadExt, AsyncWriteExt, ReadHalf, WriteHalf},
net::TcpStream,
};
use tokio_rustls::{
rustls::{ClientConfig, KeyLogFile, RootCertStore},
TlsConnector,
};
use gtk4::{glib, prelude::ApplicationExtManual as _};
async fn run() -> io::Result<()> {
let mut root_store = RootCertStore::empty();
root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
let mut config = ClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth();
// Allow using SSLKEYLOGFILE.
config.key_log = Arc::new(KeyLogFile::new());
let connector = TlsConnector::from(Arc::new(config));
let server_address = ("teilgedanken.de", 4190).to_socket_addrs()?.next().unwrap();
let stream = TcpStream::connect(&server_address).await?;
let domain = pki_types::ServerName::try_from("teilgedanken.de").unwrap();
let stream = connector.connect(domain, stream).await?;
let (mut reader, mut writer) = split(stream);
let mut dst = String::new();
reader
.read_to_string(&mut dst)
.await
.expect("Failed to read");
info!("{:?}", &dst);
//writer.write(src)
/* {
let mut reader = BufReader::new(stream);
let mut read_all = String::new();
/* loop {
trace!("reading max {} bytes", buffer.len());
let bytes_read = rea.read(&mut buffer);
if let Ok(bytes_read) = bytes_read {
trace!("read {:?} bytes", bytes_read);
if bytes_read == buffer.len() {
let cur = String::from_utf8_lossy(&buffer[..bytes_read]);
read_all += &cur;
trace!("Received: \n{}", cur);
} else {
let cur = String::from_utf8_lossy(&buffer[..bytes_read]);
read_all += &cur;
trace!("Last: \n{}", cur);
break;
}
} else {
trace!("Error after reading continue");
break;
}
} */
reader.read_to_string(&mut read_all);
info!("Read all:\n{}", read_all);
}
let ciphersuite = stream.conn.negotiated_cipher_suite().unwrap();
writeln!(
&mut std::io::stderr(),
"Current ciphersuite: {:?}",
ciphersuite.suite()
)
.unwrap();
/* stream
.clone()
.write_all(concat!("UNAUTHENTICATE\r\n",).as_bytes())
.unwrap();
loop {
let bytes_read = stream.read(&mut buffer);
if let Ok(bytes_read) = bytes_read {
if bytes_read > 0 {
trace!(
"Received: {:?}",
String::from_utf8_lossy(&buffer[..bytes_read])
);
} else {
break;
}
} else {
break;
}
} */ */
Ok(())
}
fn main() {
fn main() -> glib::ExitCode {
env_logger::init();
tokio::runtime::Builder::new_current_thread()
.enable_io()
.build()
.unwrap()
.block_on(run())
.expect("Failed to run or interrupted");
gtk4::init().expect("Failed to initialize GTK");
let (to_frontent_tx, from_backend_rx) = tokio::sync::mpsc::channel(5);
let _handle = thread::spawn(move || {
tokio::runtime::Builder::new_current_thread()
.enable_io()
.enable_time()
.build()
.unwrap()
.block_on(backend::run::run(to_frontent_tx))
.expect("Failed to run or interrupted");
});
let app = gui::main_window::get_app(Rc::new(RefCell::new(Some(from_backend_rx))));
app.run()
}

View File

@ -0,0 +1,183 @@
/// This is the simplest possible client using rustls that does something useful:
/// it accepts the default configuration, loads some root certs, and then connects
/// to google.com and issues a basic HTTP request. The response is printed to stdout.
///
/// It makes use of rustls::Stream to treat the underlying TLS connection as a basic
/// bi-directional stream -- the underlying IO is performed transparently.
///
/// Note that `unwrap()` is used to deal with networking errors; this is not something
/// that is sensible outside of example code.
use std::sync::Arc;
use log::{error, info, trace};
use managesieve::{Capability, MSResult, Response};
use nom::{error::ParseError, IResult};
use sieverman::parser::parse_server_config;
use std::fmt::Debug;
use std::net::ToSocketAddrs;
use tokio::{
io::{self, split, AsyncBufReadExt, AsyncWriteExt, BufReader},
net::TcpStream,
};
use tokio_rustls::{
rustls::{ClientConfig, KeyLogFile, RootCertStore},
TlsConnector,
};
#[macro_export]
macro_rules! parse_response {
($reader:expr, $parser:expr, $dst:expr) => {{
let mut buf = String::new();
let capabilities = 'read_more: loop {
trace!("reading");
let read = $reader.read_line(&mut buf).await;
if let Ok(num) = read {
trace!("Read {} bytes", num);
trace!("Read Buffer: {:?}", &read);
trace!("Read Buffer: {:?}", &buf);
$dst += &buf;
buf.clear();
};
match $parser(&$dst) {
read @ Ok((_, _)) => {
//trace!("{:?}", &response);
break 'read_more read;
}
Err(_e) => continue,
Err(e) => {
error!("invalid response");
break Err(e);
}
Err(e) => {
error!("invalid input");
break Err(e);
}
};
};
capabilities
}};
}
async fn run() -> io::Result<()> {
let mut root_store = RootCertStore::empty();
root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
let mut config = ClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth();
// Allow using SSLKEYLOGFILE.
config.key_log = Arc::new(KeyLogFile::new());
let connector = TlsConnector::from(Arc::new(config));
let server_address = ("teilgedanken.de", 4190).to_socket_addrs()?.next().unwrap();
let stream = TcpStream::connect(&server_address).await?;
let domain = pki_types::ServerName::try_from("teilgedanken.de").unwrap();
let stream = connector.connect(domain, stream).await?;
let (reader, mut writer) = split(stream);
let mut reader = BufReader::new(reader);
let mut dst = String::new();
let mut buf = String::new();
let capabilities: Option<(&str, Vec<Capability>)> = 'read_more: loop {
trace!("reading");
let read = reader.read_line(&mut buf).await;
if let Ok(num) = read {
trace!("Read {} bytes", num);
trace!("Read Buffer: {:?}", &read);
trace!("Read Buffer: {:?}", &buf);
dst += &buf;
buf.clear();
};
match managesieve::response_capability(&dst) {
Ok((rest, capability, response)) => {
trace!("{:?}", response);
break 'read_more Some((rest, capability));
}
Err(managesieve::MSError::IncompleteResponse) => continue,
Err(managesieve::MSError::InvalidResponse) => {
error!("invalid response");
break None;
}
Err(managesieve::MSError::InvalidInput) => {
error!("invalid input");
break None;
}
};
};
if let Some((rest, capability)) = capabilities {
trace!("Parsed: \n{}", dst);
dst = rest.to_string();
info!("Rest content:\n{:?}", &dst);
info!("The server config: {:?}", capability);
let have_space_command = managesieve::Command::have_space("loser.test", 249)
.expect("failed to create have_space command");
writer
.write_all(have_space_command.to_string().as_bytes())
.await?;
let response_parser = managesieve::response_havespace;
let result = parse_response!(reader, response_parser, dst);
dbg!(result);
let auth_command = managesieve::Command::authenticate();
writer
.write_all(auth_command.to_string().as_bytes())
.await?;
let response_parser = managesieve::response_authenticate;
let result = parse_response!(reader, response_parser, dst);
dbg!(result);
}
Ok(())
}
async fn read_until_complete<'a, Reader, Target>(
mut reader: Reader,
mut parser: impl FnMut(&str) -> IResult<&'a str, (Target, Response)>,
) -> IResult<&'a str, Target>
where
Reader: AsyncBufReadExt + Unpin,
Target: Debug,
{
let mut dst = String::new();
let mut buf = String::new();
let capabilities: MSResult<Target> = 'read_more: loop {
trace!("reading");
let read = reader.read_line(&mut buf).await;
if let Ok(num) = read {
trace!("Read {} bytes", num);
trace!("Read Buffer: {:?}", &read);
trace!("Read Buffer: {:?}", &buf);
dst += &buf;
buf.clear();
};
match parser(&dst) {
read @ Ok((_, _)) => {
//trace!("{:?}", &response);
break 'read_more read;
}
Err(_e) => continue,
Err(e) => {
error!("invalid response");
break Err(e);
}
Err(e) => {
error!("invalid input");
break Err(e);
}
};
};
if let Ok((rest, (target, response))) = capabilities {
trace!("Parsed: \n{}", dst);
dst = rest.to_string();
trace!("Rest content:\n{:?}", &dst);
trace!("Response content: {:?}", &response);
info!("The server config: {:?}", target);
}
todo!()
}
fn main() {
env_logger::init();
tokio::runtime::Builder::new_current_thread()
.enable_io()
.build()
.unwrap()
.block_on(run())
.expect("Failed to run or interrupted");
}

View File

@ -1,3 +1,168 @@
use std::{any, net::SocketAddr, sync::Arc};
use log::{error, info, trace};
use managesieve::Capability;
use std::net::ToSocketAddrs;
use thiserror::Error;
use tokio::{
io::{split, AsyncBufReadExt, BufReader, ReadHalf, WriteHalf},
net::TcpStream,
sync::Mutex,
task::JoinHandle,
};
use tokio_rustls::{
client::TlsStream,
rustls::{ClientConfig, KeyLogFile, RootCertStore},
TlsConnector,
};
pub mod parser;
#[derive(Debug, Clone)]
pub struct ConnectionInfo {
domain: String,
port: u16,
}
impl ConnectionInfo {
pub fn new(domain: &str, port: u16) -> Self {
Self {
domain: domain.to_string(),
port,
}
}
pub async fn connect(self) -> Result<ConnectionConnected, anyhow::Error> {
trace!("Building Cert Store");
let mut root_store = RootCertStore::empty();
root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
let mut config = ClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth();
// Allow using SSLKEYLOGFILE.
config.key_log = Arc::new(KeyLogFile::new());
trace!("Creating connector");
let connector = TlsConnector::from(Arc::new(config));
trace!("Connecting to the server.");
let stream = TcpStream::connect((self.domain.clone(), self.port)).await?;
let domain = pki_types::ServerName::try_from(self.domain.clone())?.to_owned();
trace!("Attach the connector");
let stream = connector.connect(domain, stream).await?;
let (reader, mut writer) = split(stream);
let buffer = Arc::new(Mutex::new(String::new()));
let spawned_buffer = buffer.clone();
let join_handle = tokio::spawn(async move {
let mut buf = String::new();
let mut reader = BufReader::new(reader);
loop {
trace!("Waiting for more data to read…");
let read = reader.read_line(&mut buf).await;
if let Ok(num) = read {
trace!("Read {} bytes", num);
trace!("Read Buffer: {:?}", &buf);
{
let mut s = spawned_buffer.lock().await;
s.push_str(&buf);
}
buf.clear();
};
}
});
Ok(ConnectionConnected {
info: self,
writer: Arc::new(writer), //server_settings: todo!(),
buffer,
server_settings: Vec::new(), //join_handle: Arc::new(join_handle),
})
}
}
#[derive(Debug, Clone)]
pub struct ConnectionConnected {
info: ConnectionInfo,
//reader: Arc<ReadHalf<TlsStream<TcpStream>>>,
writer: Arc<WriteHalf<TlsStream<TcpStream>>>,
buffer: Arc<Mutex<String>>,
//pub join_handle: Arc<JoinHandle<()>>,
server_settings: Vec<Capability>,
}
#[derive(Error, Debug)]
pub enum ReadError {
#[error("The data entered was invalid")]
InvalidInput(#[source] managesieve::Error),
#[error("The data the server responded was invalid")]
InvalidResponse(#[source] managesieve::Error),
}
pub enum IsComplete<T> {
Yes(T),
No(T),
}
impl ConnectionConnected {
pub async fn log_buffer(&self) {
trace!("Buffer is: \n{}", self.buffer.lock().await)
}
pub async fn log_server_settings(&self) {
trace!("Serversettings:\n{:?}", self.server_settings)
}
pub async fn read_introduction(self) -> Result<IsComplete<Self>, anyhow::Error> {
let Self {
info,
//reader: Arc<ReadHalf<TlsStream<TcpStream>>>,
writer,
buffer,
server_settings,
} = self;
let mut bf = buffer.lock().await;
let bf_str = bf.clone();
match managesieve::response_capability(&bf_str) {
Ok((rest, capability, response)) => {
trace!("{:?}", response);
bf.clear();
bf.push_str(rest);
drop(bf);
Ok(IsComplete::Yes(Self {
info,
writer: writer.clone(),
buffer: buffer.clone(),
server_settings: capability,
}))
}
Err(managesieve::Error::IncompleteResponse) => Ok(IsComplete::No(Self {
info,
writer,
buffer: buffer.clone(),
server_settings,
})),
Err(managesieve::Error::InvalidResponse) => {
error!("invalid response");
Err(managesieve::Error::InvalidResponse)?
}
Err(managesieve::Error::InvalidInput) => {
error!("invalid input");
Err(managesieve::Error::InvalidResponse)?
}
}
}
pub fn get_greeting(&self) -> Option<String> {
for c in self.server_settings.iter() {
if let Capability::Implementation(s) = c {
return Some(s.clone());
}
}
None
}
}
#[derive(Debug, Clone)]
pub struct ConnectionAuthenticated {
info: ConnectionInfo,
server_settings: ServerSettings,
}
#[derive(Debug, Clone)]
pub struct ServerSettings {
pub implementation: String,

4
src/parser.rs Normal file
View File

@ -0,0 +1,4 @@
mod server_config;
mod utils;
pub use server_config::parse_server_config;

View File

@ -0,0 +1,84 @@
use log::{info, trace};
use nom::{
branch::alt,
bytes::complete::is_not,
bytes::complete::tag,
character::{
self,
complete::{alpha1, multispace1},
},
multi::many_m_n,
sequence::preceded,
IResult,
};
use crate::{parser::utils::key_value, Methods, ServerSettings};
fn parse_version(input: &str) -> IResult<&str, Vec<i32>> {
many_m_n(
1,
5,
alt((
character::complete::i32,
preceded(character::complete::char('.'), character::complete::i32),
)),
)(input)
}
fn parse_methods(input: &str) -> IResult<&str, Vec<Methods>> {
let (rem, res) = many_m_n(1, 10, alt((alpha1, preceded(multispace1, alpha1))))(input)?;
let methods = res
.into_iter()
.map(|m| match m {
"PLAIN" => Methods::Plain,
"OAUTHBEARER" => Methods::OAuthbearer,
any => todo!("That method is not yet implemented: {}", any),
})
.collect();
Ok((rem, methods))
}
fn parse_extensions(input: &str) -> IResult<&str, Vec<String>> {
let space_then_word = preceded(tag(" "), is_not(" "));
let word = is_not(" ");
let (rem, res) = many_m_n(1, 200, alt((word, space_then_word)))(input)?;
let extensions = res.into_iter().map(|m| m.to_string()).collect();
Ok((rem, extensions))
}
pub fn parse_server_config(input: &str) -> IResult<&str, ServerSettings> {
let (remain, (_key, implementation)) = key_value(input)?;
trace!("Implementation:# {}", implementation);
let (remain, (_key, version)) = key_value(remain)?;
trace!("{}", version);
let (_, version) = parse_version(version)?;
trace!("Version:# {:?}", version);
let (remain, (_key, sasl)) = key_value(remain)?;
trace!("{}", sasl);
let (_, sasl) = parse_methods(sasl)?;
trace!("SASL:# {:?}", sasl);
let (remain, (_key, capabilities)) = key_value(remain)?;
trace!("{}", capabilities);
let (_, capabilities) = parse_extensions(capabilities)?;
trace!("Extensions:# {:?}", &capabilities);
let (remain, (_key, notify)) = key_value(remain)?;
trace!("{}", notify);
let (_, notify) = parse_extensions(remain)?;
trace!("Notify:# {:?}", &notify);
let (remain, (_key, max_redirects)) = key_value(remain)?;
trace!("{}", max_redirects);
let (_, max_redirects) = character::complete::u32(max_redirects)?;
trace!("Max Redirects:# {}", max_redirects);
IResult::Ok((
remain,
ServerSettings {
implementation: implementation.to_string(),
version,
sasl,
capabilities,
notify,
max_redirects,
},
))
}

19
src/parser/utils.rs Normal file
View File

@ -0,0 +1,19 @@
use nom::{
bytes::complete::is_not,
bytes::complete::tag,
character::complete::{multispace0, multispace1},
sequence::{delimited, preceded, separated_pair},
IResult,
};
pub(crate) fn quoted(input: &str) -> IResult<&str, &str> {
preceded(multispace0, delimited(tag("\""), is_not("\n\""), tag("\"")))(input)
}
pub(crate) fn key_value(input: &str) -> IResult<&str, (&str, &str)> {
// note that this is really creating a function, the parser for abc
// vvvvv
// which is then called here, returning an IResult<&str, &str>
// vvvvv
preceded(multispace0, separated_pair(quoted, multispace1, quoted))(input)
}