From 783014b36967ab9f703ae66fd6e89c62f85caa6d Mon Sep 17 00:00:00 2001 From: Paul Zinselmeyer Date: Thu, 11 Apr 2024 21:13:33 +0200 Subject: [PATCH] init --- .env | 6 + .gitignore | 3 + Cargo.lock | 3308 ++++++++++++++++++++++++++ Cargo.toml | 41 + README.md | 21 + TODO.md | 12 + flake.lock | 106 + flake.nix | 75 + migrations/20231220121329_users.sql | 20 + migrations/20231220211536_shares.sql | 19 + src/dav_fs.rs | 751 ++++++ src/error.rs | 46 + src/fs.rs | 13 + src/main.rs | 418 ++++ src/ocs.rs | 159 ++ src/ocs/core.rs | 192 ++ src/ocs/files_sharing.rs | 187 ++ src/ocs/files_sharing/share.rs | 154 ++ src/ocs/files_sharing/sharees.rs | 170 ++ src/ocs/provisioning_api.rs | 126 + src/upload_fs.rs | 111 + static/ferris.png | Bin 0 -> 62131 bytes 22 files changed, 5938 insertions(+) create mode 100644 .env create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 TODO.md create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 migrations/20231220121329_users.sql create mode 100644 migrations/20231220211536_shares.sql create mode 100644 src/dav_fs.rs create mode 100644 src/error.rs create mode 100644 src/fs.rs create mode 100644 src/main.rs create mode 100644 src/ocs.rs create mode 100644 src/ocs/core.rs create mode 100644 src/ocs/files_sharing.rs create mode 100644 src/ocs/files_sharing/share.rs create mode 100644 src/ocs/files_sharing/sharees.rs create mode 100644 src/ocs/provisioning_api.rs create mode 100644 src/upload_fs.rs create mode 100644 static/ferris.png diff --git a/.env b/.env new file mode 100644 index 0000000..6edc7c4 --- /dev/null +++ b/.env @@ -0,0 +1,6 @@ +APPLICATION_BASE=https://oxidev.pfzetto.de/ +ISSUER=https://auth.zettoit.eu/realms/zettoit +CLIENT_ID=oxicloud +CLIENT_SECRET=IvBcDOfp9WBfGNmwIbiv67bxCwuQUGbl +SCOPES= +DATABASE_URL=mysql://root:start1234@127.0.0.1/oxicloud diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5a4d878 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +/data +.env diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..3573023 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,3308 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "async-trait" +version = "0.1.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf6721fb0140e4f897002dd086c06f6c27775df19cfe1fccb21181a48fd2c98" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.42", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] +name = "atomic-write-file" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edcdbedc2236483ab103a53415653d6b4442ea6141baf1ffa85df29635e88436" +dependencies = [ + "nix", + "rand", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "axum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "202651474fe73c62d9e0a56c6133f7a0ff1dc1c8cf7a5b03381af2a26553ac9d" +dependencies = [ + "async-trait", + "axum-core", + "axum-macros", + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.1.0", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77cb22c689c44d4c07b0ab44ebc25d69d8ae601a2f28fb8d672d344178fa17aa" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-macros" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a2edad600410b905404c594e2523549f1bcd4bded1e252c8f74524ccce0b867" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.42", +] + +[[package]] +name = "axum-oidc" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403062a407aced5727a5e01f332e622c3e4b0753c4b15f98190a867604db28d3" +dependencies = [ + "async-trait", + "axum", + "axum-core", + "futures-util", + "http 1.0.0", + "openidconnect", + "reqwest", + "serde", + "thiserror", + "tower-layer", + "tower-service", + "tower-sessions", +] + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +dependencies = [ + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.48.5", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "cookie" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cd91cf61412820176e137621345ee43b3f4423e589e7ae4e50d601d93e35ef8" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crossbeam-queue" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9bcf5bdbfdd6030fb4a1c497b5d5fc5921aa2f60d359a17e249c0e6df3de153" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.42", +] + +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.42", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.42", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "dyn-clone" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +dependencies = [ + "serde", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_logger" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" + +[[package]] +name = "finl_unicode" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "spin 0.9.8", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[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-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + +[[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.42", +] + +[[package]] +name = "futures-sink" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" + +[[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-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.11", + "indexmap 2.1.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d308f63daf4181410c242d34c11f928dcb3aa105852019e043c9d1f4e4368a" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 1.0.0", + "indexmap 2.1.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "handlebars" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225" +dependencies = [ + "log", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown 0.14.3", +] + +[[package]] +name = "headers" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" +dependencies = [ + "base64 0.21.5", + "bytes", + "headers-core", + "http 1.0.0", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http 1.0.0", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "htmlescape" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.11", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http 1.0.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" +dependencies = [ + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.22", + "http 0.2.11", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.0", + "http 1.0.0", + "http-body 1.0.0", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.11", + "hyper 0.14.28", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "hyper-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdea9aac0dbe5a9240d68cfd9501e2db94222c6dc06843e06640b9e07f0fdc67" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "hyper 1.1.0", + "pin-project-lite", + "socket2", + "tokio", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libsqlite3-sys" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", + "serde", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "lru" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7" +dependencies = [ + "hashbrown 0.14.3", +] + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "oauth2" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c38841cdd844847e3e7c8d29cef9dcfed8877f8f56f9071f77843ecf3baf937f" +dependencies = [ + "base64 0.13.1", + "chrono", + "getrandom", + "http 0.2.11", + "rand", + "reqwest", + "serde", + "serde_json", + "serde_path_to_error", + "sha2", + "thiserror", + "url", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openidconnect" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62d6050f6a84b81f23c569f5607ad883293e57491036e318fafe6fc4895fadb1" +dependencies = [ + "base64 0.13.1", + "chrono", + "dyn-clone", + "ed25519-dalek", + "hmac", + "http 0.2.11", + "itertools 0.10.5", + "log", + "oauth2", + "p256", + "p384", + "rand", + "rsa", + "serde", + "serde-value", + "serde_derive", + "serde_json", + "serde_path_to_error", + "serde_plain", + "serde_with", + "sha2", + "subtle", + "thiserror", + "url", +] + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "oxicloud" +version = "0.1.0" +dependencies = [ + "axum", + "axum-oidc", + "base64 0.21.5", + "bytes", + "dotenvy", + "env_logger", + "futures", + "log", + "md5", + "rand", + "serde", + "serde-xml-rs", + "serde_json", + "sha3", + "sqlx", + "thiserror", + "time", + "tokio", + "tower", + "tower-http", + "tower-sessions", + "webdav-handler", + "xml", + "xmltree", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.42", +] + +[[package]] +name = "pest_meta" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.42", +] + +[[package]] +name = "pin-project-lite" +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 = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" + +[[package]] +name = "platforms" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "reqwest" +version = "0.11.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +dependencies = [ + "base64 0.21.5", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.22", + "http 0.2.11", + "http-body 0.4.6", + "hyper 0.14.28", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-rustls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted", + "windows-sys 0.48.0", +] + +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +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", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.21.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.5", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[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-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde-xml-rs" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb3aa78ecda1ebc9ec9847d5d3aba7d618823446a049ba2491940506da6e2782" +dependencies = [ + "log", + "serde", + "thiserror", + "xml-rs", +] + +[[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.42", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_plain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" +dependencies = [ + "base64 0.21.5", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.1.0", + "serde", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.42", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlformat" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c" +dependencies = [ + "itertools 0.12.0", + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dba03c279da73694ef99763320dea58b51095dfe87d001b1d4b5fe78ba8763cf" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d84b0a3c3739e220d94b3239fd69fb1f74bc36e16643423bd99de3b43c21bfbd" +dependencies = [ + "ahash", + "atoi", + "byteorder", + "bytes", + "crc", + "crossbeam-queue", + "dotenvy", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashlink", + "hex", + "indexmap 2.1.0", + "log", + "memchr", + "once_cell", + "paste", + "percent-encoding", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlformat", + "thiserror", + "time", + "tokio", + "tokio-stream", + "tracing", + "url", +] + +[[package]] +name = "sqlx-macros" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89961c00dc4d7dffb7aee214964b065072bff69e36ddb9e2c107541f75e4f2a5" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 1.0.109", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0bd4519486723648186a08785143599760f7cc81c52334a55d6a83ea1e20841" +dependencies = [ + "atomic-write-file", + "dotenvy", + "either", + "heck", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 1.0.109", + "tempfile", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37195395df71fd068f6e2082247891bc11e3289624bbc776a0cdfa1ca7f1ea4" +dependencies = [ + "atoi", + "base64 0.21.5", + "bitflags 2.4.1", + "byteorder", + "bytes", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "time", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ac0ac3b7ccd10cc96c7ab29791a7dd236bd94021f31eec7ba3d46a74aa1c24" +dependencies = [ + "atoi", + "base64 0.21.5", + "bitflags 2.4.1", + "byteorder", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand", + "serde", + "serde_json", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "time", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "210976b7d948c7ba9fced8ca835b11cbb2d677c59c79de41ac0d397e14547490" +dependencies = [ + "atoi", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "sqlx-core", + "time", + "tracing", + "url", + "urlencoding", +] + +[[package]] +name = "stringprep" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" +dependencies = [ + "finl_unicode", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +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.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "termcolor" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.42", +] + +[[package]] +name = "time" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +dependencies = [ + "deranged", + "itoa", + "libc", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.42", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-cookies" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fd0118512cf0b3768f7fcccf0bef1ae41d68f2b45edc1e77432b36c97c56c6d" +dependencies = [ + "async-trait", + "axum-core", + "cookie", + "futures-util", + "http 1.0.0", + "parking_lot", + "pin-project-lite", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e12e6351354851911bdf8c2b8f2ab15050c567d70a8b9a37ae7b8301a4080d" +dependencies = [ + "bitflags 2.4.1", + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tower-sessions" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23a9827d593712d471a19d3cb1334e57a97e5b34a9f02b4eef2114b7405dcd" +dependencies = [ + "tower-sessions-core", + "tower-sessions-memory-store", +] + +[[package]] +name = "tower-sessions-core" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d733b3ae4a6a4cb80cd585e87ffe1a1a7011174581785039c83c4cd63ee61c" +dependencies = [ + "async-trait", + "axum-core", + "futures", + "http 1.0.0", + "parking_lot", + "serde", + "serde_json", + "thiserror", + "time", + "tower-cookies", + "tower-layer", + "tower-service", + "tracing", + "uuid", +] + +[[package]] +name = "tower-sessions-memory-store" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78d855ef8feaef2382c6df2cf4c4b6651dce890019ceaf0ec70c8af2516a1ce9" +dependencies = [ + "async-trait", + "parking_lot", + "time", + "tower-sessions-core", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.42", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "uuid" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +dependencies = [ + "getrandom", + "serde", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.42", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.42", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "web-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webdav-handler" +version = "0.2.0" +source = "git+https://github.com/pfzetto/webdav-handler-rs#ffc9caaadfaab1eb4d1f2b40880b18085b0d129f" +dependencies = [ + "bytes", + "futures", + "handlebars", + "headers", + "htmlescape", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "lazy_static", + "libc", + "log", + "lru", + "mime_guess", + "parking_lot", + "percent-encoding", + "pin-project", + "pin-utils", + "regex", + "time", + "tokio", + "url", + "uuid", + "xml-rs", + "xmltree", +] + +[[package]] +name = "webpki-roots" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" + +[[package]] +name = "whoami" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "xml" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cb5b0add8d5aaa69357818de5f3b60104a62ef345dca81d0947e2148347a7f7" +dependencies = [ + "xml-rs", +] + +[[package]] +name = "xml-rs" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" + +[[package]] +name = "xmltree" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb" +dependencies = [ + "xml-rs", +] + +[[package]] +name = "zerocopy" +version = "0.7.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.42", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9e434a2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "oxicloud" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +dotenvy = "0.15.7" +log = "0.4.20" +env_logger = "0.10.1" +thiserror = "1.0.51" + +tokio = { version = "1.35.1", features = ["full"] } +axum = { version = "0.7.2", features = [ "macros" ] } +axum-oidc = "0.2.1" + +serde = { version = "1.0.193", features = [ "derive" ] } +serde-xml-rs = "0.6.0" +serde_json = "1.0.108" + +webdav-handler = { git="https://github.com/pfzetto/webdav-handler-rs" } + +tower = "0.4.13" +tower-http = { version = "0.5.0", features = [ "trace" ] } +tower-sessions = "0.7.0" + +sqlx = { version="0.7.3", features=["runtime-tokio", "mysql", "time"] } + +sha3 = "0.10.8" +md5 = "0.7.0" +rand = "0.8.5" +base64 = "0.21.5" + +bytes = "1.5.0" +futures = "0.3.29" + +xmltree = "0.10.3" +xml = "0.8.10" + +time = "0.3.31" diff --git a/README.md b/README.md new file mode 100644 index 0000000..c8a99af --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +Oxicloud is a file server that aims to be a minimal implementation of a Nextcloud combatible client API. + +# Disclaimer +Please report any bugs you find using Oxicloud to this project. +Do not report issues at the Nexclound clients. + +# Features +## General +- [x] File browsing +- [x] Folder creation +- [x] Small file upload +- [ ] Large file upload +- [x] File deletion + +## Sharing +- [ ] User shares +- [ ] Link sharing + +## Preview +- [ ] Images +- [ ] Documents diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..85f1cd3 --- /dev/null +++ b/TODO.md @@ -0,0 +1,12 @@ +- XML permissions field dynamic + + +# XML `oc:permissions` +|Permission|Meaning| +|---|---| +|`CK`|Can Write| +|`S`| Shared with me| +|`R`|Can Reshare| +|`M`|Groupfolder| + + diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..3895a71 --- /dev/null +++ b/flake.lock @@ -0,0 +1,106 @@ +{ + "nodes": { + "crane": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1703089493, + "narHash": "sha256-WUjYqUP/Lhhop9+aiHVFREgElunx1AHEWxqMT8ePfzo=", + "owner": "ipetkov", + "repo": "crane", + "rev": "2a5136f14a9ac93d9d370d64a36026c5de3ae8a4", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1703013332, + "narHash": "sha256-+tFNwMvlXLbJZXiMHqYq77z/RfmpfpiI3yjL6o/Zo9M=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "54aac082a4d9bb5bbc5c4e899603abfb76a3f6d6", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "crane": "crane", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1703124916, + "narHash": "sha256-LNAqNYcJf0iCm6jbzhzsQOC4F8SLyma5sckySn2Iffg=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "81cb529bd066cd3668f9aa88d2afa8fbbbcd1208", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..19e0c3f --- /dev/null +++ b/flake.nix @@ -0,0 +1,75 @@ +{ + description = "oxicloud service"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs = { + nixpkgs.follows = "nixpkgs"; + flake-utils.follows = "flake-utils"; + }; + }; + crane = { + url = "github:ipetkov/crane"; + inputs = { + nixpkgs.follows = "nixpkgs"; + }; + }; + }; + + outputs = { self, nixpkgs, flake-utils, rust-overlay, crane}: let + forAllSystems = function: + nixpkgs.lib.genAttrs [ + "x86_64-linux" + "aarch64-linux" + ] (system: function system nixpkgs.legacyPackages.${system}); + in rec { + packages = forAllSystems(system: syspkgs: let + pkgs = import nixpkgs { + inherit system; + overlays = [ (import rust-overlay) ]; + }; + rustToolchain = pkgs.rust-bin.nightly.latest.default; + + craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchain; + src = pkgs.lib.cleanSourceWith { + src = craneLib.path ./.; + filter = path: type: + (pkgs.lib.hasSuffix "\.stpl" path) || + (pkgs.lib.hasInfix "static" path) || + (craneLib.filterCargoSources path type) + ; + }; + + nativeBuildInputs = with pkgs; [ rustToolchain pkg-config ]; + buildInputs = with pkgs; [ ]; + + commonArgs = { + inherit src buildInputs nativeBuildInputs; + }; + cargoArtifacts = craneLib.buildDepsOnly commonArgs; + + bin = craneLib.buildPackage (commonArgs // { + inherit cargoArtifacts; + pname = "oxicloud"; + installPhaseCommand = '' + mkdir -p $out/bin + cp target/release/oxicloud $out/bin/oxicloud + cp -r static $out/static + ''; + }); + in { + inherit bin; + default = bin; + }); + devShells = forAllSystems(system: pkgs: { + default = pkgs.mkShell { + packages = with pkgs; [ sqlx-cli cargo-watch mysql-client ]; + inputsFrom = [ packages.${system}.bin ]; + }; + }); + hydraJobs."bin" = forAllSystems(system: pkgs: packages.${system}.bin); + }; +} diff --git a/migrations/20231220121329_users.sql b/migrations/20231220121329_users.sql new file mode 100644 index 0000000..53beeaa --- /dev/null +++ b/migrations/20231220121329_users.sql @@ -0,0 +1,20 @@ +-- Add migration script here + +CREATE TABLE users ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT, + oidc_id VARCHAR(48) NOT NULL, + name VARCHAR(128) NOT NULL, + + PRIMARY KEY (id), + UNIQUE (oidc_id), + FULLTEXT (name) +); + +CREATE TABLE user_tokens ( + id int UNSIGNED NOT NULL AUTO_INCREMENT, + user_id INT UNSIGNED NOT NULL, + name VARCHAR(255) NOT NULL, + hash BINARY(32) NOT NULL, + PRIMARY KEY (id), + CONSTRAINT fk_users_app_passords FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +); diff --git a/migrations/20231220211536_shares.sql b/migrations/20231220211536_shares.sql new file mode 100644 index 0000000..3029c63 --- /dev/null +++ b/migrations/20231220211536_shares.sql @@ -0,0 +1,19 @@ +-- Add migration script here + +CREATE TABLE user_shares ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT, + created_by INT UNSIGNED NOT NULL, + src_user INT UNSIGNED NOT NULL, + src_path TEXT NOT NULL, + dst_user INT UNSIGNED NOT NULL, + dst_path TEXT NOT NULL, + expires_at DATETIME NULL, + note TEXT NOT NULL, + permissions TINYINT UNSIGNED NOT NULL, + + PRIMARY KEY (id), + CONSTRAINT fk_user_shares_src FOREIGN KEY (src_user) REFERENCES users(id) ON DELETE CASCADE, + CONSTRAINT fk_user_shares_dst FOREIGN KEY (dst_user) REFERENCES users(id) ON DELETE CASCADE, + CONSTRAINT fk_user_shares_created_by FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE, + FULLTEXT (src_path, dst_path) +); diff --git a/src/dav_fs.rs b/src/dav_fs.rs new file mode 100644 index 0000000..ddb29ff --- /dev/null +++ b/src/dav_fs.rs @@ -0,0 +1,751 @@ +use std::{ + collections::{BTreeMap, HashMap}, + fs::Metadata, + future::ready, + io::SeekFrom, + path::{Path, PathBuf}, + pin::Pin, + sync::Arc, + task::{Context, Poll}, + time::{SystemTime, UNIX_EPOCH}, +}; + +use axum::http::StatusCode; +use base64::{engine::general_purpose::STANDARD as base64std, Engine}; +use bytes::{Buf, Bytes, BytesMut}; +use futures::{future::BoxFuture, stream, Future, FutureExt, Stream, StreamExt, TryFutureExt}; +use log::{error, info}; +use sqlx::{query, MySqlPool}; +use tokio::{ + fs::{metadata, read_dir, File, ReadDir}, + io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt}, + sync::RwLock, +}; +use webdav_handler::{ + davpath::DavPath, + fs::{ + DavDirEntry, DavFile, DavFileSystem, DavMetaData, DavProp, FsError, FsFuture, FsResult, + FsStream, OpenOptions, ReadDirMeta, + }, +}; +use xmltree::{Element, XMLNode}; + +use crate::{error::Error, fs::FsUser}; + +const DAV_OC_PROP: &str = "http://owncloud.org/ns"; +const DAV_NC_PROP: &str = "http://nextcloud.org/ns"; + +#[derive(Clone)] +pub struct FilesFs { + pub user: FsUser, + pub db: MySqlPool, + pub incoming_share_cache: Arc>>, +} + +impl DavFileSystem for FilesFs { + fn open<'a>(&'a self, path: &'a DavPath, options: OpenOptions) -> FsFuture> { + info!("test: {}", path.to_string()); + Box::pin(async move { + let (meta, path) = self + .resolve_path(path) + .map_err(|_| FsError::GeneralFailure) + .await?; + + let mut tokio_options = tokio::fs::OpenOptions::new(); + tokio_options + .read(options.read) + .write(options.write) + .append(options.append) + .truncate(options.truncate) + .create(options.create) + .create_new(options.create_new); + + let file = OxiFile::new(path.clone(), &tokio_options).await?; + let file: Box = Box::new(file); + + Ok(file) + }) + } + + fn read_dir<'a>( + &'a self, + ppath: &'a DavPath, + meta: ReadDirMeta, + ) -> FsFuture>> { + info!("dir: {:?}", ppath.as_pathbuf()); + Box::pin(async move { + //let (is_share, path) = self + // .resolve_path(ppath) + // .map_err(|_| FsError::GeneralFailure) + // .await?; + //warn!("dirpath: {:?}", path); + //if !self.can_access(&path) { + // return Err(FsError::Forbidden); + //} + + let (meta, path) = self + .resolve_path(ppath) + .map_err(|_| FsError::GeneralFailure) + .await?; + + let entry_stream = DirEntryStream::read_dir(&path).await.map(|x| { + let y: Box = Box::new(x); + y + }); + + let entry_stream = entry_stream.chain(stream::iter(self.shares(&ppath).await.unwrap())); + let entry_stream: FsStream> = Box::pin(entry_stream); + + Ok(entry_stream) + }) + } + + fn metadata<'a>(&'a self, path: &'a DavPath) -> FsFuture> { + Box::pin(async move { + info!("metadata {:?}", path); + let (meta, path) = self + .resolve_path(path) + .map_err(|_| FsError::GeneralFailure) + .await?; + + if meta.exists { + let file = OxiFile::new(path.clone(), tokio::fs::OpenOptions::new().read(true)) + .await? + .metadata() + .await + .unwrap(); + + Ok(file) + } else { + Err(FsError::NotFound) + } + }) + } + + fn symlink_metadata<'a>(&'a self, path: &'a DavPath) -> FsFuture> { + // symlinks are currently not supported, so no difference to normal metadata + self.metadata(path) + } + + fn create_dir<'a>(&'a self, path: &'a DavPath) -> FsFuture<()> { + Box::pin(async move { + info!("create dir"); + let (path_meta, path) = self + .resolve_path(path) + .map_err(|_| FsError::GeneralFailure) + .await?; + + match path_meta.exists { + false => { + //TODO remove shares in db with src_path=path + tokio::fs::create_dir_all(path) + .await + .map_err(|_| FsError::GeneralFailure)?; + Ok(()) + } + true => Err(FsError::Exists), + } + }) + } + + fn remove_dir<'a>(&'a self, path: &'a DavPath) -> FsFuture<()> { + Box::pin(async move { + info!("remove dir"); + let (path_meta, path) = self + .resolve_path(path) + .map_err(|_| FsError::GeneralFailure) + .await?; + + match (path_meta.exists, path_meta.is_mount_point) { + (true, false) => { + //TODO remove shares in db with src_path.starts_with(path) + tokio::fs::remove_dir(path) + .await + .map_err(|_| FsError::GeneralFailure)?; + + Ok(()) + } + (true, true) => Err(FsError::NotImplemented), //TODO remove share in db with dst_path =path + _ => Err(FsError::NotFound), + } + }) + } + + fn remove_file<'a>(&'a self, path: &'a DavPath) -> FsFuture<()> { + Box::pin(async move { + info!("remove file"); + + let (path_meta, path) = self + .resolve_path(path) + .map_err(|_| FsError::GeneralFailure) + .await?; + + match (path_meta.exists, path_meta.is_mount_point) { + (true, false) => { + //TODO remove shares in db with src_path=path + tokio::fs::remove_file(path) + .await + .map_err(|_| FsError::GeneralFailure)?; + + Ok(()) + } + (true, true) => Err(FsError::NotImplemented), //TODO remove share in db with dst_path =path + _ => Err(FsError::NotFound), + } + }) + } + + fn rename<'a>(&'a self, from: &'a DavPath, to: &'a DavPath) -> FsFuture<()> { + Box::pin(async move { + info!("rename"); + let (from_meta, from) = self + .resolve_path(from) + .map_err(|_| FsError::GeneralFailure) + .await?; + let (to_meta, to) = self + .resolve_path(to) + .map_err(|_| FsError::GeneralFailure) + .await?; + + match (from_meta.exists, to_meta.exists) { + (true, false) if !from_meta.is_mount_point => { + tokio::fs::rename(from, to) + .await + .map_err(|_| FsError::GeneralFailure)?; + Ok(()) + } + (true, false) => Err(FsError::NotImplemented), //TODO rename dst in db + (false, _) => Err(FsError::NotFound), + (true, true) => Err(FsError::Exists), + } + }) + } + + fn copy<'a>(&'a self, from: &'a DavPath, to: &'a DavPath) -> FsFuture<()> { + Box::pin(async move { + info!("copy"); + let (from_meta, from) = self + .resolve_path(from) + .map_err(|_| FsError::GeneralFailure) + .await?; + let (to_meta, to) = self + .resolve_path(to) + .map_err(|_| FsError::GeneralFailure) + .await?; + match (from_meta.exists, to_meta.exists) { + (true, false) => { + tokio::fs::copy(from, to) + .await + .map_err(|_| FsError::GeneralFailure)?; + } + (false, _) => return Err(FsError::NotFound), + (true, true) => return Err(FsError::Exists), + } + Ok(()) + }) + } + + fn have_props<'a>( + &'a self, + path: &'a DavPath, + ) -> std::pin::Pin + Send + 'a>> { + Box::pin(ready(true)) + } + + fn patch_props<'a>( + &'a self, + path: &'a DavPath, + patch: Vec<(bool, DavProp)>, + ) -> FsFuture> { + Box::pin(async move { + error!("NOT IMPLEMENTED patch_props"); + Err(FsError::NotImplemented) + }) + } + + fn get_props<'a>(&'a self, path: &'a DavPath, do_content: bool) -> FsFuture> { + Box::pin(async move { + error!("NOT IMPLEMENTED get_props"); + Err(FsError::NotImplemented) + }) + } + + fn get_prop<'a>(&'a self, ppath: &'a DavPath, prop: DavProp) -> FsFuture { + fn element(prefix: &str, name: &str, children: Vec) -> Element { + Element { + prefix: Some(prefix.to_string()), + namespace: None, + namespaces: None, + name: name.to_string(), + attributes: HashMap::default(), + children, + } + } + + Box::pin(async move { + let (meta, path) = self + .resolve_path(ppath) + .map_err(|_| FsError::GeneralFailure) + .await?; + //if !self.can_access(&path) { + // return Err(FsError::Forbidden); + //} + // + const NC_PREFIX: &str = "nc"; + const OC_PREFIX: &str = "oc"; + + match ( + prop.name.as_str(), + prop.namespace.as_deref().unwrap_or_default(), + ) { + ("permissions", DAV_OC_PROP) => { + let val = match meta.is_share { + true => "SRGDNVCK", + false => "RGDNVCK", + }; + return Ok(element( + OC_PREFIX, + "permissions", + vec![XMLNode::Text(val.to_string())], + )); + } + ("rich-workspace", DAV_NC_PROP) if false => { + return Ok(element( + NC_PREFIX, + "rich-workspace", + vec![XMLNode::Text( + "# Hello World\nLorem Ipsum, si dolor amet.".to_string(), + )], + )) + } + ("has-preview", DAV_NC_PROP) => { + return Ok(element( + NC_PREFIX, + "has-preview", + vec![XMLNode::Text("false".to_string())], + )) + } + ("mount-type", DAV_NC_PROP) => { + let val = match meta.is_share { + true => "shared", + false => "", + }; + return Ok(element( + NC_PREFIX, + "mount-type", + vec![XMLNode::Text(val.to_string())], + )); + } + ("sharees", DAV_NC_PROP) if false => { + return Ok(element( + NC_PREFIX, + "sharees", + vec![XMLNode::Element(element( + "sharee", + "nc", + vec![ + XMLNode::Element(element( + "id", + "nc", + vec![XMLNode::Text("1".to_string())], + )), + XMLNode::Element(element( + "display-name", + "nc", + vec![XMLNode::Text("testuser".to_string())], + )), + XMLNode::Element(element( + "type", + "nc", + vec![XMLNode::Text("0".to_string())], + )), + ], + ))], + )); + } + ("is-encrypted", DAV_NC_PROP) => { + return Ok(element( + NC_PREFIX, + "is-encrypted", + vec![XMLNode::Text("false".to_string())], + )) + } + _ => (), + } + Err(FsError::NotFound) + }) + } + + fn get_quota(&self) -> FsFuture<(u64, Option)> { + info!("quota"); + Box::pin(async move { + error!("NOT IMPLEMENTED get_quota"); + Err(FsError::NotImplemented) + }) + } +} + +impl FilesFs { + async fn fill_cache(&self) -> Result<(), Error> { + if self.incoming_share_cache.read().await.is_empty() { + let res = query!( + "SELECT src_user, src_path, dst_path FROM user_shares WHERE dst_user = ?", + self.user.0 + ) + .fetch_all(&self.db) + .await?; + + let mut cache = self.incoming_share_cache.write().await; + res.into_iter().for_each(|x| { + cache.insert( + PathBuf::from(x.dst_path), + (FsUser(x.src_user), PathBuf::from(x.src_path)), + ); + }); + } + + Ok(()) + } + + async fn shares(&self, rel_root: &DavPath) -> Result>, Error> { + let rel_dst_home = rel_root.as_pathbuf(); + let rel_dst_home = rel_dst_home + .strip_prefix("/") + .map(|x| x.to_owned()) + .unwrap_or_else(|_| rel_dst_home); + + self.fill_cache().await?; + + let cache = self.incoming_share_cache.read().await; + + let mut cursor = cache.lower_bound(std::ops::Bound::Included(&rel_dst_home)); + + let mut share_dir_entries: Vec> = vec![]; + + while let Some((dst_path, (src_user, src_path))) = + cursor + .key_value() + .and_then(|x| match x.0.parent() == Some(&rel_dst_home) { + true => Some(x), + false => None, + }) + { + let name = dst_path.strip_prefix(&rel_dst_home).unwrap(); + + let mut p = PathBuf::new(); + p.push(src_user.home_dir()); + p.push(src_path); + + let meta = metadata(&p).await.unwrap(); + + share_dir_entries.push(Box::new(OxiDirEntry { + name: name.to_str().unwrap_or_default().into(), + len: meta.len(), + dir: p.is_dir(), + etag: etag(&p, &meta), + modified: meta.modified().ok(), + created: meta.created().ok(), + })); + + cursor.move_next(); + } + + Ok(share_dir_entries) + } + + async fn resolve_path(&self, rel_root: &DavPath) -> Result<(PathMeta, PathBuf), Error> { + let rel_dst_home = rel_root.as_pathbuf(); + let rel_dst_home = rel_dst_home + .strip_prefix("/") + .map(|x| x.to_owned()) + .unwrap_or_else(|_| rel_dst_home); + + self.fill_cache().await?; + + let cache = self.incoming_share_cache.read().await; + + let cursor = cache.lower_bound(std::ops::Bound::Included(&rel_dst_home)); + let share = cursor + .key_value() + .and_then(|x| rel_dst_home.strip_prefix(x.0).map(|y| (y, x.1)).ok()); + + if let Some((rel_mnt, (src_user, src_path))) = share { + let mut p = PathBuf::new(); + p.push(src_user.home_dir()); + p.push(src_path); + p.push(rel_mnt); + + Ok(( + PathMeta { + is_share: true, + is_mount_point: rel_mnt == rel_dst_home, + owner: *src_user, + exists: p.exists(), + }, + p, + )) + } else { + let mut p = PathBuf::new(); + p.push(&self.user.home_dir()); + p.push(rel_dst_home.clone()); + + Ok(( + PathMeta { + is_share: false, + is_mount_point: false, + owner: self.user, + exists: p.exists(), + }, + p, + )) + } + } +} + +#[derive(Debug)] +pub struct PathMeta { + is_share: bool, + is_mount_point: bool, + owner: FsUser, + exists: bool, +} + +pub struct DirEntryStream<'a> { + entry_stream: ReadDir, + entry_stream_completed: bool, + intermediate: Vec>, +} + +impl<'a> DirEntryStream<'a> { + pub async fn read_dir(path: &Path) -> DirEntryStream<'a> { + Self { + entry_stream: read_dir(path).await.unwrap(), + entry_stream_completed: false, + intermediate: vec![], + } + } +} + +impl<'a> Stream for DirEntryStream<'a> { + type Item = OxiDirEntry; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let entry_stream_item = match self.entry_stream_completed { + false => self.entry_stream.poll_next_entry(cx), + true => Poll::Ready(Ok(None)), + }; + match entry_stream_item { + Poll::Ready(Ok(Some(entry))) => self.intermediate.push(Box::pin(async move { + let meta = entry.metadata().await.unwrap(); + OxiDirEntry { + name: entry.file_name().to_str().unwrap_or_default().into(), + len: meta.len(), + dir: entry.file_type().await.unwrap().is_dir(), + etag: etag(&entry.path(), &meta), + modified: meta.modified().ok(), + created: meta.created().ok(), + } + })), + Poll::Ready(Ok(None)) => { + self.entry_stream_completed = true; + } + _ => (), + } + for (i, intermediate) in self.intermediate.iter_mut().enumerate() { + if let Poll::Ready(x) = intermediate.poll_unpin(cx) { + let intermediate_fut = self.intermediate.remove(i); + drop(intermediate_fut); + return Poll::Ready(Some(x)); + } + } + match self.intermediate.is_empty() { + true => Poll::Ready(None), + false => Poll::Pending, + } + } +} + +#[derive(Clone, Debug)] +pub struct OxiDirEntry { + name: Box, + len: u64, + dir: bool, + etag: String, + modified: Option, + created: Option, +} + +impl DavDirEntry for OxiDirEntry { + fn name(&self) -> Vec { + self.name.as_bytes().to_vec() + } + + fn metadata(&self) -> FsFuture> { + Box::pin(async move { + let meta: Box = Box::new(self.clone()); + Ok(meta) + }) + } +} + +impl DavMetaData for OxiDirEntry { + fn len(&self) -> u64 { + self.len + } + + fn modified(&self) -> FsResult { + self.modified.ok_or(FsError::GeneralFailure) + } + + fn created(&self) -> FsResult { + self.created.ok_or(FsError::GeneralFailure) + } + + fn is_dir(&self) -> bool { + self.dir + } + + fn etag(&self) -> Option { + Some(self.etag.to_string()) + } +} + +#[derive(Debug)] +pub struct OxiFile { + path: PathBuf, + file: Option, +} + +impl OxiFile { + pub async fn new(path: PathBuf, options: &tokio::fs::OpenOptions) -> FsResult { + let file = options + .open(&path) + .await + .map_err(|_| FsError::GeneralFailure)?; + Ok(Self { + path, + file: Some(file), + }) + } +} + +impl DavFile for OxiFile { + fn metadata(&mut self) -> FsFuture> { + Box::pin(async move { + let meta = metadata(&self.path).await.unwrap(); + + let etag = etag(&self.path, &meta); + + let oxi_meta = OxiFileMeta { + len: meta.len(), + is_dir: meta.is_dir(), + etag, + }; + let oxi_meta: Box = Box::new(oxi_meta); + Ok(oxi_meta) + }) + } + + fn write_bytes(&mut self, buf: Bytes) -> FsFuture<()> { + async move { + let mut file = self.file.take().unwrap(); + let res = file.write_all(&buf).await; + self.file = Some(file); + res.map_err(|e| e.into()) + } + .boxed() + } + + fn write_buf(&mut self, mut buf: Box) -> FsFuture<()> { + async move { + let mut file = self.file.take().unwrap(); + while buf.remaining() > 0 { + match file.write(buf.chunk()).await { + Ok(n) => buf.advance(n), + Err(e) => { + self.file = Some(file); + return Err(e.into()); + } + } + } + self.file = Some(file); + Ok(()) + } + .boxed() + } + + fn read_bytes(&mut self, count: usize) -> FsFuture { + async move { + let mut file = self.file.take().unwrap(); + let mut buf = BytesMut::with_capacity(count); + let res = unsafe { + buf.set_len(count); + file.read(&mut buf).await.map(|n| { + buf.set_len(n); + buf.freeze() + }) + }; + self.file = Some(file); + res.map_err(|e| e.into()) + } + .boxed() + } + + fn seek(&mut self, pos: SeekFrom) -> FsFuture { + async move { + let mut file = self.file.take().unwrap(); + let res = file.seek(pos).await; + self.file = Some(file); + res.map_err(|e| e.into()) + } + .boxed() + } + + fn flush(&mut self) -> FsFuture<()> { + async move { + let mut file = self.file.take().unwrap(); + let res = file.flush().await; + self.file = Some(file); + res.map_err(|e| e.into()) + } + .boxed() + } +} + +#[derive(Clone, Debug)] +pub struct OxiFileMeta { + len: u64, + is_dir: bool, + etag: String, +} + +impl DavMetaData for OxiFileMeta { + fn len(&self) -> u64 { + self.len + } + + fn modified(&self) -> FsResult { + Err(FsError::NotImplemented) + } + + fn is_dir(&self) -> bool { + self.is_dir + } + + fn etag(&self) -> Option { + Some(self.etag.to_string()) + } +} + +fn etag(path: &Path, meta: &Metadata) -> String { + let modified = meta + .modified() + .ok() + .and_then(|x| x.duration_since(UNIX_EPOCH).ok()) + .map(|x| x.as_secs().to_ne_bytes()) + .unwrap_or_default() + .to_vec(); + let path = path.to_str().unwrap_or_default().as_bytes(); + let hash: [u8; 16] = md5::compute([path, &modified].concat()).into(); + base64std.encode(hash) +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..0866bb4 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,46 @@ +use axum::{http::StatusCode, response::IntoResponse, Json}; +use log::error; +use serde::Serialize; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum Error { + #[error("internal server error")] + InternalServerError, + + #[error("bad request")] + BadRequest, + + #[error("csrf check failed")] + CsrfCheckFailed, + + #[error("sqlx error: {0:?}")] + Sqlx(#[from] sqlx::Error), + + #[error("base64 error: {0:?}")] + Base64User(base64::DecodeError), +} + +impl IntoResponse for Error { + fn into_response(self) -> axum::response::Response { + error!("{:?}", self); + match self { + Self::CsrfCheckFailed => ( + StatusCode::PRECONDITION_FAILED, + Json(JsonError { + message: "CSRF check failed".into(), + }), + ) + .into_response(), + Self::Sqlx(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(), + Self::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR.into_response(), + Self::Base64User(_) => StatusCode::BAD_REQUEST.into_response(), + Self::BadRequest => StatusCode::BAD_REQUEST.into_response(), + } + } +} + +#[derive(Serialize)] +struct JsonError { + message: Box, +} diff --git a/src/fs.rs b/src/fs.rs new file mode 100644 index 0000000..e03bef9 --- /dev/null +++ b/src/fs.rs @@ -0,0 +1,13 @@ +use std::path::PathBuf; + +#[derive(Clone, Copy, Debug)] +pub struct FsUser(pub u32); + +impl FsUser { + pub fn home_dir(&self) -> PathBuf { + PathBuf::from(format!("./data/files/{}", self.0)) + } + pub fn upload_dir(&self) -> PathBuf { + PathBuf::from(format!("./data/upload/{}", self.0)) + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..54bcd1c --- /dev/null +++ b/src/main.rs @@ -0,0 +1,418 @@ +#![feature(btree_cursors)] + +use std::{env, path::PathBuf, sync::Arc}; + +use axum::{ + async_trait, + body::Body, + debug_handler, + error_handling::HandleErrorLayer, + extract::{FromRequestParts, Path, Query, Request, State}, + http::{ + header::{AUTHORIZATION, CONTENT_TYPE, USER_AGENT}, + request::Parts, + uri::PathAndQuery, + HeaderMap, StatusCode, Uri, + }, + response::{IntoResponse, Redirect, Response}, + routing::{any, get, head}, + BoxError, Json, Router, +}; +use axum_oidc::{ + error::MiddlewareError, EmptyAdditionalClaims, OidcAuthLayer, OidcClaims, OidcLoginLayer, +}; +use base64::{engine::general_purpose::STANDARD as base64std, Engine}; +use bytes::Bytes; +use dav_fs::FilesFs; +use dotenvy::dotenv; +use fs::FsUser; +use log::debug; +use ocs::{OcsJson, OcsXml}; +use rand::{distributions, Rng}; +use serde::{Deserialize, Serialize}; +use sha3::{Digest, Sha3_256}; +use sqlx::{query, query_as, FromRow, MySqlPool}; +use tokio::{fs::File, io::AsyncReadExt, net::TcpListener}; +use tower::ServiceBuilder; +use tower_http::trace::TraceLayer; +use tower_sessions::{cookie::SameSite, MemoryStore, SessionManagerLayer}; +use webdav_handler::{memfs::MemFs, memls::MemLs, DavConfig, DavHandler}; + +use crate::error::Error; + +mod dav_fs; +mod error; +mod fs; +mod ocs; +mod upload_fs; + +type HResult = Result; + +#[derive(Clone)] +pub struct AppState { + dav_server: Arc, + db: MySqlPool, + share_password_salt: Arc, +} + +#[tokio::main] +async fn main() { + dotenv().ok(); + env_logger::init(); + + let database_url = env::var("DATABASE_URL").expect("DATABASE_URL"); + + let database_pool = MySqlPool::connect(&database_url) + .await + .expect("working database connection"); + + let application_base = env::var("APPLICATION_BASE").expect("APPLICATION_BASE"); + let application_base = + Uri::from_maybe_shared(application_base).expect("valid APPLICATION_BASE"); + let issuer = env::var("ISSUER").expect("ISSUER"); + let client_id = env::var("CLIENT_ID").expect("CLIENT_ID"); + let client_secret = env::var("CLIENT_SECRET").ok(); + let scopes: Vec = env::var("SCOPES") + .expect("SCOPES") + .split(' ') + .map(String::from) + .collect::>(); + + let session_store = MemoryStore::default(); + let session_service = ServiceBuilder::new() + .layer(HandleErrorLayer::new(|_: BoxError| async { + StatusCode::BAD_REQUEST + })) + .layer(SessionManagerLayer::new(session_store).with_same_site(SameSite::Lax)); + + let oidc_login_service = ServiceBuilder::new() + .layer(HandleErrorLayer::new(|e: MiddlewareError| async { + debug!("auth layer error {:?}", e); + e.into_response(); //TODO return this response + StatusCode::INTERNAL_SERVER_ERROR + })) + .layer(OidcLoginLayer::::new()); + + let oidc_auth_service = ServiceBuilder::new() + .layer(HandleErrorLayer::new(|e: MiddlewareError| async { + debug!("auth layer error {:?}", e); + e.into_response(); //TODO return this response + StatusCode::INTERNAL_SERVER_ERROR + })) + .layer( + OidcAuthLayer::::discover_client( + application_base, + issuer, + client_id, + client_secret, + scopes, + ) + .await + .unwrap(), + ); + + let state = AppState { + dav_server: Arc::new( + DavHandler::builder() + .filesystem(MemFs::new()) + .locksystem(MemLs::new()) + .build_handler(), + ), + db: database_pool, + share_password_salt: "".into(), + }; + + let app = Router::new() + .route("/index.php/login/flow", get(login_flow)) + .layer(oidc_login_service) + .route("/index.php/204", get(connectivity_check)) + .route("/status.php", get(status)) + //.route("/remote.php/dav", any(remote_dav)) + .route( + "/remote.php/dav", + head(|| async { StatusCode::OK.into_response() }), + ) + .nest_service( + "/remote.php/dav/files/:user_id", + any(user_dav).with_state(state.clone()), + ) + .nest_service( + "/remote.php/dav/upload/:user_id", + any(upload_dav).with_state(state.clone()), + ) + .nest("/ocs/v2.php", ocs::router(state.clone())) + .route("/index.php/avatar/:user_id/512", get(avatar_512)) + .route("/index.php/avatar/:user_id/:size", get(|| async { "" })) + .layer(oidc_auth_service) + .layer(session_service) + .layer(TraceLayer::new_for_http()) + .with_state(state); + + let listener = TcpListener::bind("[::]:8080").await.expect("valid address"); + axum::serve(listener, app).await.unwrap() +} + +async fn connectivity_check() -> impl IntoResponse { + StatusCode::NO_CONTENT +} + +async fn avatar_512() -> Response { + let mut buf = vec![]; + let mut image_file = File::open("static/ferris.png").await.unwrap(); + image_file.read_to_end(&mut buf).await; + let buf = Bytes::from(buf); + Response::builder() + .header(CONTENT_TYPE, "image/png") + .body(Body::from(buf)) + .unwrap() +} + +#[derive(Deserialize)] +struct Format { + format: Option, +} + +async fn ocs_default(Query(query): Query) -> Response { + if query.format.as_deref() == Some("json") { + OcsJson::not_implemented(()).into_response() + } else { + OcsXml::not_implemented(()).into_response() + } +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct Status { + installed: bool, + maintenance: bool, + needs_db_upgrade: bool, + version: Box, + version_string: Box, + edition: Box, + productname: Box, + extended_support: bool, +} + +async fn status() -> Json { + debug!("GET /status.php"); + Json(Status { + installed: true, + maintenance: false, + needs_db_upgrade: false, + version: "27.1.3".into(), + version_string: "27.1.3".into(), + edition: "".into(), + productname: "pfzettos Nextcloud Backend".into(), + extended_support: false, + }) +} + +async fn user_dav( + State(state): State, + user: User, + Path(user_id): Path, + mut req: Request, +) -> Response { + if user_id != user.id { + return StatusCode::UNAUTHORIZED.into_response(); + } + + let user_prefix = format!("/remote.php/dav/files/{}", user.id); + + let path = match req.uri().path_and_query() { + Some(x) => format!( + "{}{}?{}", + user_prefix, + x.path(), + x.query().unwrap_or_default() + ), + None => format!("{}{}", user_prefix, req.uri().path()), + } + .parse() + .unwrap(); + + let mut parts = req.uri().clone().into_parts(); + parts.path_and_query = Some(path); + *req.uri_mut() = Uri::from_parts(parts).unwrap(); + + let dav_config = DavConfig::new() + .strip_prefix(user_prefix) + .autoindex(true, None) + .filesystem(Box::new(FilesFs { + user: FsUser(user.id), + db: state.db.clone(), + incoming_share_cache: Arc::default(), + })); + + state + .dav_server + .handle_with(dav_config, req) + .await + .into_response() +} + +async fn upload_dav( + State(state): State, + user: User, + Path(user_id): Path, + req: Request, +) -> Response { + if user_id != user.id { + return StatusCode::UNAUTHORIZED.into_response(); + } + + let dav_config = DavConfig::new() + .autoindex(true, None) + .filesystem(MemFs::new()); + + state + .dav_server + .handle_with(dav_config, req) + .await + .into_response() +} + +async fn login_flow( + State(state): State, + headers: HeaderMap, + OidcClaims(claims): OidcClaims, +) -> HResult { + let user_id = query!( + "SELECT id FROM users WHERE oidc_id = ?", + claims.subject().to_string() + ) + .fetch_optional(&state.db) + .await?; + + let user_id = match user_id { + Some(row) => row.id, + None => { + let mut transaction = state.db.begin().await?; + query!( + "INSERT INTO users (oidc_id, name) VALUES (?, ?)", + claims.subject().to_string(), + claims + .preferred_username() + .map(|x| x.to_string()) + .unwrap_or_default() + ) + .execute(&mut *transaction) + .await?; + + let id = query!("SELECT LAST_INSERT_ID() AS id") + .fetch_one(&mut *transaction) + .await? + .id as u32; + transaction.commit().await?; + id + } + }; + + let user_token = rand::thread_rng() + .sample_iter(distributions::Alphanumeric) + .take(64) + .map(char::from) + .collect::(); + let user_token_hash = { + let mut token_hasher = Sha3_256::default(); + token_hasher.update(&user_token); + token_hasher.finalize().to_vec() + }; + + let user_agent = headers + .get(USER_AGENT) + .and_then(|x| x.to_str().ok()) + .unwrap_or(""); + + query!( + "INSERT INTO user_tokens (user_id, name, hash) VALUES (?, ?, ?)", + user_id, + user_agent, + user_token_hash + ) + .execute(&state.db) + .await?; + + let mut transaction = state.db.begin().await?; + query!( + "INSERT INTO user_tokens (user_id, name, hash) VALUES (?, ?, ?)", + user_id, + user_agent, + user_token_hash + ) + .execute(&mut *transaction) + .await?; + + let token_id = query!("SELECT LAST_INSERT_ID() AS id") + .fetch_one(&mut *transaction) + .await? + .id as u32; + transaction.commit().await?; + + Ok(Redirect::temporary(&format!( + "nc://login/server:http://10.50.10.2:8080&user:{}&password:{}", + token_id, user_token + ))) +} + +impl AsRef for AppState { + fn as_ref(&self) -> &MySqlPool { + &self.db + } +} + +#[derive(FromRow, Clone)] +pub struct User { + id: u32, + oidc_id: Box, + name: Box, +} + +impl User { + pub fn home_dir(&self) -> PathBuf { + PathBuf::from(format!("./files/{}/", self.id)) + } + pub fn upload_dir(&self) -> PathBuf { + PathBuf::from(format!("./upload/{}/", self.id)) + } +} + +#[async_trait] +impl + Send + Sync> FromRequestParts for User { + type Rejection = Result; + + async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { + let headers = HeaderMap::from_request_parts(parts, state) + .await + .map_err(|_| Err(Error::InternalServerError))?; + let authorizazion = headers + .get(AUTHORIZATION) + .ok_or(Ok(StatusCode::UNAUTHORIZED))?; + if let Some(basic) = authorizazion + .to_str() + .unwrap_or_default() + .strip_prefix("Basic ") + { + let basic = base64std + .decode(basic) + .map_err(|e| Err(Error::Base64User(e)))?; + let basic = std::str::from_utf8(&basic).unwrap_or_default(); + let (username, password) = basic.split_once(':').unwrap_or_default(); + + let password_hash = { + let mut hasher = Sha3_256::default(); + hasher.update(password); + hasher.finalize().to_vec() + }; + + let user = query_as!(User, "SELECT users.id, users.oidc_id, users.name FROM user_tokens INNER JOIN users ON user_tokens.user_id=users.id WHERE user_tokens.id = ? AND user_tokens.hash = ?", username, password_hash).fetch_optional(state.as_ref()).await.map_err(|x|Err(x.into()))?; + + match user { + Some(user) => Ok(user), + None => Err(Ok(StatusCode::UNAUTHORIZED)), + } + } else { + Err(Ok(StatusCode::UNAUTHORIZED)) + } + } +} diff --git a/src/ocs.rs b/src/ocs.rs new file mode 100644 index 0000000..360181c --- /dev/null +++ b/src/ocs.rs @@ -0,0 +1,159 @@ +use axum::{ + http::header::CONTENT_TYPE, + response::{IntoResponse, Response}, + routing::{get, post}, + Router, +}; +use serde::Serialize; + +use crate::AppState; + +pub mod core; +pub mod files_sharing; +pub mod provisioning_api; + +pub fn router(state: AppState) -> Router { + Router::new() + .route("/cloud/capabilities", get(core::get_status)) + .route("/cloud/users", get(provisioning_api::get_users)) + .route("/cloud/user", get(provisioning_api::get_user)) + .route( + "/apps/files_sharing/api/v1/shares", + get(files_sharing::share::get_shares).post(files_sharing::share::create), + ) + .route( + "/apps/files_sharing/api/v1/sharees", + get(files_sharing::sharees::search), + ) + .with_state(state) +} + +#[derive(Serialize)] +pub struct OcsJson { + ocs: Ocs, +} + +#[derive(Serialize)] +pub struct OcsXml(Ocs); + +#[derive(Serialize)] +#[serde(rename = "ocs")] +pub struct Ocs { + pub meta: OcsMeta, + pub data: T, +} + +#[derive(Serialize)] +pub struct OcsMeta { + pub status: Box, + pub statuscode: u16, + pub message: Box, + pub totalitems: Option>, + pub itemsperpage: Option>, +} + +impl OcsJson { + pub fn ok(data: T) -> Self { + Self { + ocs: Ocs { + meta: OcsMeta { + status: "ok".into(), + statuscode: 200, + message: "ok".into(), + totalitems: None, + itemsperpage: None, + }, + data, + }, + } + } + pub fn not_found(data: T) -> Self { + Self { + ocs: Ocs { + meta: OcsMeta { + status: "not found".into(), + statuscode: 200, + message: "not found".into(), + totalitems: None, + itemsperpage: None, + }, + data, + }, + } + } + pub fn not_implemented(data: T) -> Self { + Self { + ocs: Ocs { + meta: OcsMeta { + status: "not found".into(), + statuscode: 200, + message: "not found".into(), + totalitems: None, + itemsperpage: None, + }, + data, + }, + } + } +} + +impl OcsXml { + pub fn ok(data: T) -> Self { + Self(Ocs { + meta: OcsMeta { + status: "ok".into(), + statuscode: 200, + message: "ok".into(), + totalitems: None, + itemsperpage: None, + }, + data, + }) + } + pub fn not_found(data: T) -> Self { + Self(Ocs { + meta: OcsMeta { + status: "not found".into(), + statuscode: 200, + message: "not found".into(), + totalitems: None, + itemsperpage: None, + }, + data, + }) + } + pub fn not_implemented(data: T) -> Self { + Self(Ocs { + meta: OcsMeta { + status: "not implemented".into(), + statuscode: 501, + message: "not implemented".into(), + totalitems: None, + itemsperpage: None, + }, + data, + }) + } +} + +impl IntoResponse for OcsJson { + fn into_response(self) -> axum::response::Response { + let body = serde_json::to_string(&self).unwrap(); + Response::builder() + .header(CONTENT_TYPE, "application/json") + .body(body) + .unwrap() + .into_response() + } +} + +impl IntoResponse for OcsXml { + fn into_response(self) -> axum::response::Response { + let body = serde_xml_rs::to_string(&self).unwrap(); + Response::builder() + .header(CONTENT_TYPE, "application/xml; charset=utf-8") + .body(body) + .unwrap() + .into_response() + } +} diff --git a/src/ocs/core.rs b/src/ocs/core.rs new file mode 100644 index 0000000..ba6a38c --- /dev/null +++ b/src/ocs/core.rs @@ -0,0 +1,192 @@ +use axum::http::HeaderMap; +use serde::Serialize; + +use crate::{error::Error, HResult}; + +use super::OcsJson; + +// https://docs.nextcloud.com/server/latest/developer_manual/_static/openapi.html#/operations/core-ocs-get-capabilities +pub async fn get_status(headers: HeaderMap) -> HResult> { + if let Some(Ok("true")) = headers.get("OCS-ApiRequest").map(|x| x.to_str()) { + Ok(OcsJson::ok(CapabilitiesData { + version: CapabilitiesVersion { + major: 0, + minor: 1, + micro: 0, + string: "0.1.0".into(), + edition: "".into(), + extended_support: false, + }, + capabilities: Capabilities { + dav: DavCapabilities { + bulkupload: "1.0".into(), + chunking: "1.0".into(), + }, + user_status: UserStatusCapabilities::default(), + weather_status: WeatherStatusCapabilities::default(), + files: FilesCapabilities::default(), + files_sharing: FilesSharingCapabilities { + api_enabled: true, + default_permissions: 31, + group_sharing: false, + public: FilesSharingPublicCapabilities { + enabled: true, + multiple_links: true, + send_mail: false, + upload: true, + upload_files_drop: true, + ..Default::default() + }, + resharing: true, + ..Default::default() + }, + }, + })) + } else { + Err(Error::CsrfCheckFailed) + } +} + +#[derive(Serialize)] +pub struct CapabilitiesData { + version: CapabilitiesVersion, + capabilities: Capabilities, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CapabilitiesVersion { + major: i64, + minor: i64, + micro: i64, + string: Box, + edition: Box, + extended_support: bool, +} + +#[derive(Serialize)] +pub struct Capabilities { + dav: DavCapabilities, + user_status: UserStatusCapabilities, + weather_status: WeatherStatusCapabilities, + files: FilesCapabilities, + files_sharing: FilesSharingCapabilities, +} + +#[derive(Serialize)] +pub struct DavCapabilities { + bulkupload: Box, + chunking: Box, +} + +#[derive(Serialize, Default)] +pub struct UserStatusCapabilities { + enabled: bool, + restore: bool, + supports_emoji: bool, +} + +#[derive(Serialize, Default)] +pub struct WeatherStatusCapabilities { + enabled: bool, +} + +#[derive(Serialize)] +pub struct FilesCapabilities { + bigfilechunking: bool, + blacklisted_files: Box<[Box]>, + comments: bool, + undelete: bool, + version_deletion: bool, + version_labeling: bool, + versioning: bool, +} + +impl Default for FilesCapabilities { + fn default() -> Self { + Self { + bigfilechunking: true, + blacklisted_files: Default::default(), + comments: false, + undelete: false, + version_deletion: false, + version_labeling: false, + versioning: false, + } + } +} + +#[derive(Serialize, Default)] +pub struct FilesSharingCapabilities { + api_enabled: bool, + default_permissions: u32, + federation: FilesSharingFederationCapabilities, + group_sharing: bool, + public: FilesSharingPublicCapabilities, + resharing: bool, + sharebymail: FilesSharingSharebymailCapabilities, + sharee: FilesSharingShareeCapabilities, + user: FilesSharingUserCapabilities, +} +#[derive(Serialize, Default)] +pub struct FilesSharingFederationCapabilities { + expire_date: Expire, + expire_date_supported: Expire, + incoming: bool, + outgoing: bool, +} +#[derive(Serialize, Default)] +pub struct FilesSharingPublicCapabilities { + enabled: bool, + expire_date: Expire, + expire_date_internal: Expire, + expire_date_remote: Expire, + + multiple_links: bool, + + send_mail: bool, + upload: bool, + upload_files_drop: bool, +} +#[derive(Serialize, Default)] +pub struct FilesSharingSharebymailCapabilities { + enabled: bool, + expire_date: Expire, + send_passowrd_by_mail: bool, +} +#[derive(Serialize, Default)] +pub struct FilesSharingUserCapabilities { + expire_date: Expire, + send_mail: bool, +} +#[derive(Serialize, Default)] +pub struct FilesSharingShareeCapabilities { + always_show_unique: bool, + query_lookup_default: bool, +} + +#[derive(Serialize, Default)] +pub struct Expire { + enabled: bool, +} + +/* +//TODO: doesn't work +pub async fn get_avatar(Path((user_id, size)): Path<(u32, u32)>) -> impl IntoResponse { + let image = Reader::open("static/ferris.png").unwrap().decode().unwrap(); + image.resize(size, size, FilterType::Nearest); + let mut buf = vec![]; + image.write_to(&mut Cursor::new(&mut buf), ImageFormat::Png); + + Response::builder() + .status(StatusCode::OK) + .header(CONTENT_TYPE, "image/png") + .header( + CONTENT_DISPOSITION, + format!("inline; filename=\"avatar.{size}.png\""), + ) + .header("X-NC-IsCustomAvatar", "1") + .body(Body::from(buf)) + .unwrap() +} +*/ diff --git a/src/ocs/files_sharing.rs b/src/ocs/files_sharing.rs new file mode 100644 index 0000000..57179e3 --- /dev/null +++ b/src/ocs/files_sharing.rs @@ -0,0 +1,187 @@ +use serde::{Deserialize, Serialize}; + +pub mod share; +pub mod sharees; + +#[derive(Debug, Default, PartialEq, Eq)] +pub enum ShareType { + #[default] + Unknown, + User, + Group, + Usergroup, + Link, + Email, + #[deprecated] + Contact, + Remote, + Circle, + Guest, + RemoteGroup, + Room, + UserRoom, + Deck, + DeckUser, + Sciencemesh, +} + +impl<'de> Deserialize<'de> for ShareType { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let value = i32::deserialize(deserializer)?; + Ok(match value { + 0 => Self::User, + 1 => Self::Group, + 2 => Self::Usergroup, + 3 => Self::Link, + 4 => Self::Email, + 5 => Self::Contact, + 6 => Self::Remote, + 7 => Self::Circle, + 8 => Self::Guest, + 9 => Self::RemoteGroup, + 10 => Self::Room, + 11 => Self::UserRoom, + 12 => Self::Deck, + 13 => Self::DeckUser, + 15 => Self::Sciencemesh, + _ => Self::Unknown, + }) + } +} + +impl Serialize for ShareType { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let value = match self { + Self::Unknown => -1, + Self::User => 0, + Self::Group => 1, + Self::Usergroup => 2, + Self::Link => 3, + Self::Email => 4, + Self::Contact => 5, + Self::Remote => 6, + Self::Circle => 7, + Self::Guest => 8, + Self::RemoteGroup => 9, + Self::Room => 10, + Self::UserRoom => 11, + Self::Deck => 12, + Self::DeckUser => 13, + Self::Sciencemesh => 15, + }; + value.serialize(serializer) + } +} + +impl ShareType { + pub fn all() -> Box<[Self]> { + vec![ + Self::User, + Self::Group, + Self::Usergroup, + Self::Link, + Self::Email, + Self::Remote, + Self::Circle, + Self::Guest, + Self::RemoteGroup, + Self::Room, + Self::UserRoom, + Self::Deck, + Self::DeckUser, + Self::Sciencemesh, + ] + .into() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum SharePermission { + Read = 1, + Update = 2, + Create = 4, + Delete = 8, + Share = 16, +} + +#[derive(Debug, Default, Clone)] +pub struct SharePermissions(pub Box<[SharePermission]>); + +impl SharePermissions { + pub fn rus() -> Self { + Self( + vec![ + SharePermission::Read, + SharePermission::Update, + SharePermission::Share, + ] + .into(), + ) + } +} + +impl From for SharePermissions { + fn from(value: u8) -> Self { + let mut permissions = vec![]; + if value & 1 == 1 { + permissions.push(SharePermission::Read); + } + if value >> 1 & 1 == 1 { + permissions.push(SharePermission::Update); + } + if value >> 2 & 1 == 1 { + permissions.push(SharePermission::Create); + } + if value >> 3 & 1 == 1 { + permissions.push(SharePermission::Delete); + } + if value >> 4 & 1 == 1 { + permissions.push(SharePermission::Share); + } + + Self(permissions.into()) + } +} +impl From<&SharePermissions> for u8 { + fn from(value: &SharePermissions) -> Self { + let mut out = 0; + for p in value.0.iter() { + match p { + SharePermission::Read => out += 1, + SharePermission::Update => out += 2, + SharePermission::Create => out += 4, + SharePermission::Delete => out += 8, + SharePermission::Share => out += 16, + } + } + out + } +} + +impl<'de> Deserialize<'de> for SharePermissions { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let value = u8::deserialize(deserializer) + .map(|x| x.into()) + .unwrap_or_else(|_| Self::rus()); + + Ok(value) + } +} + +impl Serialize for SharePermissions { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + u8::from(self).serialize(serializer) + } +} diff --git a/src/ocs/files_sharing/share.rs b/src/ocs/files_sharing/share.rs new file mode 100644 index 0000000..c87881f --- /dev/null +++ b/src/ocs/files_sharing/share.rs @@ -0,0 +1,154 @@ +use axum::{ + extract::State, + response::{IntoResponse, Response}, + Form, +}; +use serde::{Deserialize, Serialize}; +use sqlx::query; +use time::{macros::format_description, Time}; + +use crate::{error::Error, ocs::OcsXml, AppState, HResult, User}; + +use super::{SharePermission, SharePermissions, ShareType}; + +#[derive(Deserialize, Debug)] +pub struct CreateShareData { + attributes: Option>, + #[serde(default, rename = "expireDate")] + expire_date: Option>, + #[serde(default)] + label: Box, + #[serde(default)] + note: Box, + #[serde(default)] + password: Box, + path: Option>, + #[serde(default = "SharePermissions::rus")] + permissions: SharePermissions, + #[serde(default, rename = "publicUpload")] + public_upload: bool, + #[serde(default, rename = "shareType")] + share_type: ShareType, + #[serde(rename = "shareWith")] + share_with: Option>, +} + +pub async fn create( + State(state): State, + user: User, + Form(form): Form, +) -> HResult { + match form.share_type { + ShareType::User => Ok(create_user_share(&state, &user, &form) + .await? + .into_response()), + _ => todo!(), + } +} + +async fn create_user_share( + state: &AppState, + user: &User, + data: &CreateShareData, +) -> HResult> { + // shareWith is required + let share_with = match &data.share_with { + Some(x) => x.as_ref(), + None => return Err(Error::BadRequest), + }; + + // check if user_to is valid + if query!("SELECT id FROM users WHERE id = ?", share_with) + .fetch_optional(&state.db) + .await? + .is_none() + { + return Err(Error::BadRequest); + } + + //TODO: parse expire_date + let expires_at: Option