From b983d785757874e8ca533b2b8865583c11fa6c93 Mon Sep 17 00:00:00 2001 From: Franz Dietrich Date: Wed, 13 Dec 2023 13:04:15 +0100 Subject: [PATCH] gtk somewhat working --- Cargo.lock | 716 +++++++++++++++++++++++++++++++++- Cargo.toml | 21 +- gui/Sieverman.cmb | 36 ++ gui/sieverman.ui | 41 ++ src/bin/backend/mod.rs | 1 + src/bin/backend/run.rs | 38 ++ src/bin/gui/main_window.rs | 54 +++ src/bin/gui/mod.rs | 1 + src/bin/nomer1.rs | 89 +---- src/bin/protocol/mod.rs | 6 + src/bin/sieverman.rs | 125 +----- src/bin/sieverman_versuch1.rs | 183 +++++++++ src/lib.rs | 165 ++++++++ src/parser.rs | 4 + src/parser/server_config.rs | 84 ++++ src/parser/utils.rs | 19 + 16 files changed, 1377 insertions(+), 206 deletions(-) create mode 100644 gui/Sieverman.cmb create mode 100644 gui/sieverman.ui create mode 100644 src/bin/backend/mod.rs create mode 100644 src/bin/backend/run.rs create mode 100644 src/bin/gui/main_window.rs create mode 100644 src/bin/gui/mod.rs create mode 100644 src/bin/protocol/mod.rs create mode 100644 src/bin/sieverman_versuch1.rs create mode 100644 src/parser.rs create mode 100644 src/parser/server_config.rs create mode 100644 src/parser/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 8ac68c9..5717d4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index e4e42b8..902cf08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,15 +1,32 @@ [package] name = "sieverman" version = "0.1.0" +authors = ["Franz Dietrich"] 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"] } diff --git a/gui/Sieverman.cmb b/gui/Sieverman.cmb new file mode 100644 index 0000000..1305f32 --- /dev/null +++ b/gui/Sieverman.cmb @@ -0,0 +1,36 @@ + + + + + (1,None,"sieverman.ui","sieverman.ui",None,None,"Franz Dietrich",None,None,None,None) + + + (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) + + + (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) + + + (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) + + diff --git a/gui/sieverman.ui b/gui/sieverman.ui new file mode 100644 index 0000000..5f3a1d6 --- /dev/null +++ b/gui/sieverman.ui @@ -0,0 +1,41 @@ + + + + + + + + + + vertical + + + + + + 0 + True + + + Loading + + + + + True + + + + + Loading… + + + + + + + + + + + diff --git a/src/bin/backend/mod.rs b/src/bin/backend/mod.rs new file mode 100644 index 0000000..9137f27 --- /dev/null +++ b/src/bin/backend/mod.rs @@ -0,0 +1 @@ +pub mod run; diff --git a/src/bin/backend/run.rs b/src/bin/backend/run.rs new file mode 100644 index 0000000..4b32b59 --- /dev/null +++ b/src/bin/backend/run.rs @@ -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, +) -> 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(()) +} diff --git a/src/bin/gui/main_window.rs b/src/bin/gui/main_window.rs new file mode 100644 index 0000000..17d912c --- /dev/null +++ b/src/bin/gui/main_window.rs @@ -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>>>, +) -> 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: >k4::Application, + from_backend_rx: Rc>>>, +) { + 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(); +} diff --git a/src/bin/gui/mod.rs b/src/bin/gui/mod.rs new file mode 100644 index 0000000..8911da4 --- /dev/null +++ b/src/bin/gui/mod.rs @@ -0,0 +1 @@ +pub mod main_window; diff --git a/src/bin/nomer1.rs b/src/bin/nomer1.rs index 61b730e..3107304 100644 --- a/src/bin/nomer1.rs +++ b/src/bin/nomer1.rs @@ -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> { - 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> { - 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> { - 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:# {:?}", ¬ify); - 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> { env_logger::init(); @@ -105,5 +24,7 @@ fn main() -> Result<(), Box> { "MAXREDIRECTS" "1" "#; let (remaining_input, output) = parse_server_config(input)?; + + dbg!(output); Ok(()) } diff --git a/src/bin/protocol/mod.rs b/src/bin/protocol/mod.rs new file mode 100644 index 0000000..dbfe556 --- /dev/null +++ b/src/bin/protocol/mod.rs @@ -0,0 +1,6 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub enum BackToFront { + ServerConnected(String), +} diff --git a/src/bin/sieverman.rs b/src/bin/sieverman.rs index da74ffd..b254c34 100644 --- a/src/bin/sieverman.rs +++ b/src/bin/sieverman.rs @@ -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() } diff --git a/src/bin/sieverman_versuch1.rs b/src/bin/sieverman_versuch1.rs new file mode 100644 index 0000000..8b97dd6 --- /dev/null +++ b/src/bin/sieverman_versuch1.rs @@ -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)> = '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 = '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"); +} diff --git a/src/lib.rs b/src/lib.rs index e687279..c6ca726 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 { + 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>>, + writer: Arc>>, + buffer: Arc>, + //pub join_handle: Arc>, + server_settings: Vec, +} + +#[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 { + 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, anyhow::Error> { + let Self { + info, + //reader: Arc>>, + 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 { + 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, diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..fbf846b --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,4 @@ +mod server_config; +mod utils; + +pub use server_config::parse_server_config; diff --git a/src/parser/server_config.rs b/src/parser/server_config.rs new file mode 100644 index 0000000..929de54 --- /dev/null +++ b/src/parser/server_config.rs @@ -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> { + 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> { + 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> { + 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:# {:?}", ¬ify); + 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, + }, + )) +} diff --git a/src/parser/utils.rs b/src/parser/utils.rs new file mode 100644 index 0000000..db11357 --- /dev/null +++ b/src/parser/utils.rs @@ -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) +}