Compare commits

..

16 Commits

Author SHA1 Message Date
Lennart
4592afac10 version 0.9.7 2025-09-18 21:11:44 +02:00
Lennart
e7ab7c2987 ical: Fix import UID grouping 2025-09-18 21:08:00 +02:00
Lennart
242f7b9076 calendar export: Fix overrides 2025-09-18 20:38:54 +02:00
Lennart
cb1356acad ical: Fix data model to allow calendar objects with overrides
#125
2025-09-18 20:38:37 +02:00
Lennart
55dadbb06b update Rust to 1.90 2025-09-18 16:45:48 +02:00
Lennart
4dd12bfe52 version 0.9.6 2025-09-17 11:35:20 +02:00
Lennart
5e004a6edc calendar import: Enable import to existing calendars (if no objects are overwritten) 2025-09-17 11:33:49 +02:00
Lennart
03e550c2f8 add some debug logging for invalid data in put_event
#125
2025-09-17 10:18:46 +02:00
Lennart
b2f5d5486c version 0.9.5 2025-09-17 10:06:07 +02:00
Lennart
db674d5895 Allow setting HTTP payload limit and set default to 4MB
#124
2025-09-17 10:06:07 +02:00
Lennart K
bc98d1be42 document thing to watch out for with Kubernetes #122 2025-09-16 15:34:31 +02:00
Lennart
4bb8cae9ea docs: Fix typo for env var configuration 2025-09-14 18:55:33 +02:00
Lennart
3774b358a5 version 0.9.4 2025-09-10 23:23:12 +02:00
Lennart
c6b612e5a0 Update dependencies 2025-09-10 23:20:40 +02:00
Lennart
91586ee797 migrate quick-xml to 0.38
fixes #120
2025-09-05 15:24:34 +02:00
Lennart K
87adf94947 Update Cargo.toml and Dockerfile 2025-09-04 13:05:14 +02:00
27 changed files with 532 additions and 253 deletions

363
Cargo.lock generated
View File

@@ -32,12 +32,6 @@ version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]] [[package]]
name = "android_system_properties" name = "android_system_properties"
version = "0.1.5" version = "0.1.5"
@@ -248,11 +242,11 @@ dependencies = [
[[package]] [[package]]
name = "async-io" name = "async-io"
version = "2.5.0" version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc"
dependencies = [ dependencies = [
"async-lock", "autocfg",
"cfg-if", "cfg-if",
"concurrent-queue", "concurrent-queue",
"futures-io", "futures-io",
@@ -261,7 +255,7 @@ dependencies = [
"polling", "polling",
"rustix", "rustix",
"slab", "slab",
"windows-sys 0.60.2", "windows-sys 0.61.0",
] ]
[[package]] [[package]]
@@ -476,9 +470,9 @@ dependencies = [
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.9.3" version = "2.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@@ -540,10 +534,11 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.34" version = "1.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44"
dependencies = [ dependencies = [
"find-msvc-tools",
"shlex", "shlex",
] ]
@@ -561,17 +556,16 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.41" version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
dependencies = [ dependencies = [
"android-tzdata",
"iana-time-zone", "iana-time-zone",
"js-sys", "js-sys",
"num-traits", "num-traits",
"serde", "serde",
"wasm-bindgen", "wasm-bindgen",
"windows-link", "windows-link 0.2.0",
] ]
[[package]] [[package]]
@@ -595,9 +589,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.46" version = "4.5.47"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57" checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@@ -605,9 +599,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.46" version = "4.5.47"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41" checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@@ -617,9 +611,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.45" version = "4.5.47"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@@ -842,9 +836,9 @@ dependencies = [
[[package]] [[package]]
name = "deranged" name = "deranged"
version = "0.4.0" version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc"
dependencies = [ dependencies = [
"powerfmt", "powerfmt",
"serde", "serde",
@@ -1008,12 +1002,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.13" version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.60.2", "windows-sys 0.61.0",
] ]
[[package]] [[package]]
@@ -1090,6 +1084,12 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "find-msvc-tools"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d"
[[package]] [[package]]
name = "flume" name = "flume"
version = "0.11.1" version = "0.11.1"
@@ -1288,7 +1288,7 @@ dependencies = [
"js-sys", "js-sys",
"libc", "libc",
"r-efi", "r-efi",
"wasi 0.14.3+wasi-0.2.4", "wasi 0.14.7+wasi-0.2.4",
"wasm-bindgen", "wasm-bindgen",
] ]
@@ -1339,7 +1339,7 @@ dependencies = [
"futures-core", "futures-core",
"futures-sink", "futures-sink",
"http", "http",
"indexmap 2.11.0", "indexmap 2.11.4",
"slab", "slab",
"tokio", "tokio",
"tokio-util", "tokio-util",
@@ -1363,6 +1363,12 @@ dependencies = [
"foldhash", "foldhash",
] ]
[[package]]
name = "hashbrown"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
[[package]] [[package]]
name = "hashlink" name = "hashlink"
version = "0.10.0" version = "0.10.0"
@@ -1551,9 +1557,9 @@ dependencies = [
[[package]] [[package]]
name = "hyper-util" name = "hyper-util"
version = "0.1.16" version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"bytes", "bytes",
@@ -1575,9 +1581,9 @@ dependencies = [
[[package]] [[package]]
name = "iana-time-zone" name = "iana-time-zone"
version = "0.1.63" version = "0.1.64"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
dependencies = [ dependencies = [
"android_system_properties", "android_system_properties",
"core-foundation-sys", "core-foundation-sys",
@@ -1600,7 +1606,7 @@ dependencies = [
[[package]] [[package]]
name = "ical" name = "ical"
version = "0.11.0" version = "0.11.0"
source = "git+https://github.com/lennart-k/ical-rs#9035f46cd1d9d0a42e9727914301edc32bbcc126" source = "git+https://github.com/lennart-k/ical-rs#38e4201d5f653b07c9800cccec93996f542267b4"
dependencies = [ dependencies = [
"chrono", "chrono",
"chrono-tz", "chrono-tz",
@@ -1737,13 +1743,14 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.11.0" version = "2.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.15.5", "hashbrown 0.16.0",
"serde", "serde",
"serde_core",
] ]
[[package]] [[package]]
@@ -1811,9 +1818,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.77" version = "0.3.80"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"wasm-bindgen", "wasm-bindgen",
@@ -1851,9 +1858,9 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
[[package]] [[package]]
name = "libredox" name = "libredox"
version = "0.1.9" version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"libc", "libc",
@@ -1873,9 +1880,9 @@ dependencies = [
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.9.4" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
[[package]] [[package]]
name = "litemap" name = "litemap"
@@ -1896,9 +1903,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.27" version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
dependencies = [ dependencies = [
"value-bag", "value-bag",
] ]
@@ -2498,16 +2505,16 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]] [[package]]
name = "polling" name = "polling"
version = "3.10.0" version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829" checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"concurrent-queue", "concurrent-queue",
"hermit-abi", "hermit-abi",
"pin-project-lite", "pin-project-lite",
"rustix", "rustix",
"windows-sys 0.60.2", "windows-sys 0.61.0",
] ]
[[package]] [[package]]
@@ -2545,11 +2552,11 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
version = "3.3.0" version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983"
dependencies = [ dependencies = [
"toml_edit", "toml_edit 0.23.6",
] ]
[[package]] [[package]]
@@ -2599,9 +2606,9 @@ dependencies = [
[[package]] [[package]]
name = "quick-xml" name = "quick-xml"
version = "0.37.5" version = "0.38.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
@@ -3017,7 +3024,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical" name = "rustical"
version = "0.9.3" version = "0.9.7"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"argon2", "argon2",
@@ -3048,7 +3055,7 @@ dependencies = [
"serde", "serde",
"sqlx", "sqlx",
"tokio", "tokio",
"toml 0.9.5", "toml 0.9.7",
"tower", "tower",
"tower-http", "tower-http",
"tower-sessions", "tower-sessions",
@@ -3060,7 +3067,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_caldav" name = "rustical_caldav"
version = "0.9.3" version = "0.9.7"
dependencies = [ dependencies = [
"async-std", "async-std",
"async-trait", "async-trait",
@@ -3100,7 +3107,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_carddav" name = "rustical_carddav"
version = "0.9.3" version = "0.9.7"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"axum", "axum",
@@ -3132,7 +3139,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_dav" name = "rustical_dav"
version = "0.9.3" version = "0.9.7"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"axum", "axum",
@@ -3157,7 +3164,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_dav_push" name = "rustical_dav_push"
version = "0.9.3" version = "0.9.7"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"axum", "axum",
@@ -3182,7 +3189,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_frontend" name = "rustical_frontend"
version = "0.9.3" version = "0.9.7"
dependencies = [ dependencies = [
"askama", "askama",
"askama_web", "askama_web",
@@ -3215,7 +3222,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_ical" name = "rustical_ical"
version = "0.9.3" version = "0.9.7"
dependencies = [ dependencies = [
"axum", "axum",
"chrono", "chrono",
@@ -3233,7 +3240,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_oidc" name = "rustical_oidc"
version = "0.9.3" version = "0.9.7"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"axum", "axum",
@@ -3248,7 +3255,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_store" name = "rustical_store"
version = "0.9.3" version = "0.9.7"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@@ -3282,7 +3289,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_store_sqlite" name = "rustical_store_sqlite"
version = "0.9.3" version = "0.9.7"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"chrono", "chrono",
@@ -3303,7 +3310,7 @@ dependencies = [
[[package]] [[package]]
name = "rustical_xml" name = "rustical_xml"
version = "0.9.3" version = "0.9.7"
dependencies = [ dependencies = [
"quick-xml", "quick-xml",
"thiserror 2.0.16", "thiserror 2.0.16",
@@ -3312,15 +3319,15 @@ dependencies = [
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "1.0.8" version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.60.2", "windows-sys 0.61.0",
] ]
[[package]] [[package]]
@@ -3349,9 +3356,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.103.4" version = "0.103.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb"
dependencies = [ dependencies = [
"ring", "ring",
"rustls-pki-types", "rustls-pki-types",
@@ -3425,16 +3432,17 @@ dependencies = [
[[package]] [[package]]
name = "semver" name = "semver"
version = "1.0.26" version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.219" version = "1.0.225"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" checksum = "fd6c24dee235d0da097043389623fb913daddf92c76e9f5a1db88607a0bcbd1d"
dependencies = [ dependencies = [
"serde_core",
"serde_derive", "serde_derive",
] ]
@@ -3449,10 +3457,19 @@ dependencies = [
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_core"
version = "1.0.219" version = "1.0.225"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" checksum = "659356f9a0cb1e529b24c01e43ad2bdf520ec4ceaf83047b83ddcc2251f96383"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.225"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ea936adf78b1f766949a4977b91d2f5595825bd6ec079aa9543ad2685fc4516"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -3461,24 +3478,26 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.143" version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
"ryu", "ryu",
"serde", "serde",
"serde_core",
] ]
[[package]] [[package]]
name = "serde_path_to_error" name = "serde_path_to_error"
version = "0.1.17" version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457"
dependencies = [ dependencies = [
"itoa", "itoa",
"serde", "serde",
"serde_core",
] ]
[[package]] [[package]]
@@ -3501,11 +3520,11 @@ dependencies = [
[[package]] [[package]]
name = "serde_spanned" name = "serde_spanned"
version = "1.0.0" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee"
dependencies = [ dependencies = [
"serde", "serde_core",
] ]
[[package]] [[package]]
@@ -3530,7 +3549,7 @@ dependencies = [
"chrono", "chrono",
"hex", "hex",
"indexmap 1.9.3", "indexmap 1.9.3",
"indexmap 2.11.0", "indexmap 2.11.4",
"schemars 0.9.0", "schemars 0.9.0",
"schemars 1.0.4", "schemars 1.0.4",
"serde", "serde",
@@ -3690,7 +3709,7 @@ dependencies = [
"futures-util", "futures-util",
"hashbrown 0.15.5", "hashbrown 0.15.5",
"hashlink", "hashlink",
"indexmap 2.11.0", "indexmap 2.11.4",
"log", "log",
"memchr", "memchr",
"once_cell", "once_cell",
@@ -3994,12 +4013,11 @@ dependencies = [
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.41" version = "0.3.43"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" checksum = "83bde6f1ec10e72d583d91623c939f623002284ef622b87de38cfd546cbf2031"
dependencies = [ dependencies = [
"deranged", "deranged",
"itoa",
"num-conv", "num-conv",
"powerfmt", "powerfmt",
"serde", "serde",
@@ -4009,15 +4027,15 @@ dependencies = [
[[package]] [[package]]
name = "time-core" name = "time-core"
version = "0.1.4" version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
[[package]] [[package]]
name = "time-macros" name = "time-macros"
version = "0.2.22" version = "0.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
dependencies = [ dependencies = [
"num-conv", "num-conv",
"time-core", "time-core",
@@ -4082,9 +4100,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio-rustls" name = "tokio-rustls"
version = "0.26.2" version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" checksum = "05f63835928ca123f1bef57abbcd23bb2ba0ac9ae1235f1e65bda0d06e7786bd"
dependencies = [ dependencies = [
"rustls", "rustls",
"tokio", "tokio",
@@ -4123,19 +4141,19 @@ dependencies = [
"serde", "serde",
"serde_spanned 0.6.9", "serde_spanned 0.6.9",
"toml_datetime 0.6.11", "toml_datetime 0.6.11",
"toml_edit", "toml_edit 0.22.27",
] ]
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.9.5" version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0"
dependencies = [ dependencies = [
"indexmap 2.11.0", "indexmap 2.11.4",
"serde", "serde_core",
"serde_spanned 1.0.0", "serde_spanned 1.0.2",
"toml_datetime 0.7.0", "toml_datetime 0.7.2",
"toml_parser", "toml_parser",
"toml_writer", "toml_writer",
"winnow", "winnow",
@@ -4152,11 +4170,11 @@ dependencies = [
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.7.0" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1"
dependencies = [ dependencies = [
"serde", "serde_core",
] ]
[[package]] [[package]]
@@ -4165,7 +4183,7 @@ version = "0.22.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [ dependencies = [
"indexmap 2.11.0", "indexmap 2.11.4",
"serde", "serde",
"serde_spanned 0.6.9", "serde_spanned 0.6.9",
"toml_datetime 0.6.11", "toml_datetime 0.6.11",
@@ -4174,10 +4192,22 @@ dependencies = [
] ]
[[package]] [[package]]
name = "toml_parser" name = "toml_edit"
version = "1.0.2" version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b"
dependencies = [
"indexmap 2.11.4",
"toml_datetime 0.7.2",
"toml_parser",
"winnow",
]
[[package]]
name = "toml_parser"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627"
dependencies = [ dependencies = [
"winnow", "winnow",
] ]
@@ -4190,9 +4220,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
[[package]] [[package]]
name = "toml_writer" name = "toml_writer"
version = "1.0.2" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109"
[[package]] [[package]]
name = "tonic" name = "tonic"
@@ -4228,7 +4258,7 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-util", "futures-util",
"indexmap 2.11.0", "indexmap 2.11.4",
"pin-project-lite", "pin-project-lite",
"slab", "slab",
"sync_wrapper", "sync_wrapper",
@@ -4461,9 +4491,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.18" version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
[[package]] [[package]]
name = "unicode-normalization" name = "unicode-normalization"
@@ -4518,9 +4548,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.18.0" version = "1.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2"
dependencies = [ dependencies = [
"getrandom 0.3.3", "getrandom 0.3.3",
"js-sys", "js-sys",
@@ -4590,9 +4620,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.14.3+wasi-0.2.4" version = "0.14.7+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c"
dependencies = [
"wasip2",
]
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
dependencies = [ dependencies = [
"wit-bindgen", "wit-bindgen",
] ]
@@ -4605,21 +4644,22 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.100" version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"once_cell", "once_cell",
"rustversion", "rustversion",
"wasm-bindgen-macro", "wasm-bindgen-macro",
"wasm-bindgen-shared",
] ]
[[package]] [[package]]
name = "wasm-bindgen-backend" name = "wasm-bindgen-backend"
version = "0.2.100" version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"log", "log",
@@ -4631,9 +4671,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-futures" name = "wasm-bindgen-futures"
version = "0.4.50" version = "0.4.53"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" checksum = "a0b221ff421256839509adbb55998214a70d829d3a28c69b4a6672e9d2a42f67"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys", "js-sys",
@@ -4644,9 +4684,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.100" version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@@ -4654,9 +4694,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.100" version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -4667,18 +4707,18 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.100" version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.77" version = "0.3.80"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" checksum = "fbe734895e869dc429d78c4b433f8d17d95f8d05317440b4fad5ab2d33e596dc"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
@@ -4715,22 +4755,22 @@ dependencies = [
[[package]] [[package]]
name = "winapi-util" name = "winapi-util"
version = "0.1.10" version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [ dependencies = [
"windows-sys 0.60.2", "windows-sys 0.61.0",
] ]
[[package]] [[package]]
name = "windows-core" name = "windows-core"
version = "0.61.2" version = "0.62.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c"
dependencies = [ dependencies = [
"windows-implement", "windows-implement",
"windows-interface", "windows-interface",
"windows-link", "windows-link 0.2.0",
"windows-result", "windows-result",
"windows-strings", "windows-strings",
] ]
@@ -4764,21 +4804,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]] [[package]]
name = "windows-result" name = "windows-link"
version = "0.3.4" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
[[package]]
name = "windows-result"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f"
dependencies = [ dependencies = [
"windows-link", "windows-link 0.2.0",
] ]
[[package]] [[package]]
name = "windows-strings" name = "windows-strings"
version = "0.4.2" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda"
dependencies = [ dependencies = [
"windows-link", "windows-link 0.2.0",
] ]
[[package]] [[package]]
@@ -4817,6 +4863,15 @@ dependencies = [
"windows-targets 0.53.3", "windows-targets 0.53.3",
] ]
[[package]]
name = "windows-sys"
version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa"
dependencies = [
"windows-link 0.2.0",
]
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.48.5" version = "0.48.5"
@@ -4854,7 +4909,7 @@ version = "0.53.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
dependencies = [ dependencies = [
"windows-link", "windows-link 0.1.3",
"windows_aarch64_gnullvm 0.53.0", "windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0", "windows_aarch64_msvc 0.53.0",
"windows_i686_gnu 0.53.0", "windows_i686_gnu 0.53.0",
@@ -5014,9 +5069,9 @@ dependencies = [
[[package]] [[package]]
name = "wit-bindgen" name = "wit-bindgen"
version = "0.45.0" version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]] [[package]]
name = "writeable" name = "writeable"
@@ -5067,18 +5122,18 @@ dependencies = [
[[package]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.8.26" version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
dependencies = [ dependencies = [
"zerocopy-derive", "zerocopy-derive",
] ]
[[package]] [[package]]
name = "zerocopy-derive" name = "zerocopy-derive"
version = "0.8.26" version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@@ -2,9 +2,10 @@
members = ["crates/*"] members = ["crates/*"]
[workspace.package] [workspace.package]
version = "0.9.3" version = "0.9.7"
edition = "2024" edition = "2024"
description = "A CalDAV server" description = "A CalDAV server"
documentation = "https://lennart-k.github.io/rustical/"
repository = "https://github.com/lennart-k/rustical" repository = "https://github.com/lennart-k/rustical"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"
@@ -16,7 +17,7 @@ description.workspace = true
repository.workspace = true repository.workspace = true
license.workspace = true license.workspace = true
resolver = "2" resolver = "2"
publish = false publish = true
[features] [features]
debug = ["opentelemetry"] debug = ["opentelemetry"]
@@ -61,7 +62,7 @@ tokio = { version = "1", features = [
url = "2.5" url = "2.5"
base64 = "0.22" base64 = "0.22"
thiserror = "2.0" thiserror = "2.0"
quick-xml = { version = "0.37" } quick-xml = { version = "0.38" }
rust-embed = "8.5" rust-embed = "8.5"
tower-sessions = "0.14" tower-sessions = "0.14"
futures-core = "0.3.31" futures-core = "0.3.31"

View File

@@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM rust:1.89-alpine AS chef FROM --platform=$BUILDPLATFORM rust:1.90-alpine AS chef
ARG TARGETPLATFORM ARG TARGETPLATFORM
ARG BUILDPLATFORM ARG BUILDPLATFORM
@@ -45,4 +45,5 @@ CMD ["/usr/local/bin/rustical"]
ENV RUSTICAL_DATA_STORE__SQLITE__DB_URL=/var/lib/rustical/db.sqlite3 ENV RUSTICAL_DATA_STORE__SQLITE__DB_URL=/var/lib/rustical/db.sqlite3
LABEL org.opencontainers.image.authors="Lennart K github.com/lennart-k" LABEL org.opencontainers.image.authors="Lennart K github.com/lennart-k"
LABEL org.opencontainers.image.licenses="AGPL-3.0-or-later"
EXPOSE 4000 EXPOSE 4000

View File

@@ -68,19 +68,32 @@ pub async fn route_get<C: CalendarStore, S: SubscriptionStore>(
for object in &objects { for object in &objects {
vtimezones.extend(object.get_vtimezones()); vtimezones.extend(object.get_vtimezones());
match object.get_data() { match object.get_data() {
CalendarObjectComponent::Event(EventObject { CalendarObjectComponent::Event(
event, EventObject {
timezones: object_timezones, event,
.. timezones: object_timezones,
}) => { ..
},
overrides,
) => {
timezones.extend(object_timezones); timezones.extend(object_timezones);
ical_calendar_builder = ical_calendar_builder.add_event(event.clone()); ical_calendar_builder = ical_calendar_builder.add_event(event.clone());
for _override in overrides {
ical_calendar_builder =
ical_calendar_builder.add_event(_override.event.clone());
}
} }
CalendarObjectComponent::Todo(TodoObject(todo)) => { CalendarObjectComponent::Todo(TodoObject(todo), overrides) => {
ical_calendar_builder = ical_calendar_builder.add_todo(todo.clone()); ical_calendar_builder = ical_calendar_builder.add_todo(todo.clone());
for _override in overrides {
ical_calendar_builder = ical_calendar_builder.add_todo(_override.0.clone());
}
} }
CalendarObjectComponent::Journal(JournalObject(journal)) => { CalendarObjectComponent::Journal(JournalObject(journal), overrides) => {
ical_calendar_builder = ical_calendar_builder.add_journal(journal.clone()); ical_calendar_builder = ical_calendar_builder.add_journal(journal.clone());
for _override in overrides {
ical_calendar_builder = ical_calendar_builder.add_journal(_override.0.clone());
}
} }
} }
} }

View File

@@ -9,6 +9,7 @@ use ical::{
generator::Emitter, generator::Emitter,
parser::{Component, ComponentMut}, parser::{Component, ComponentMut},
}; };
use rustical_dav::header::Overwrite;
use rustical_ical::{CalendarObject, CalendarObjectType}; use rustical_ical::{CalendarObject, CalendarObjectType};
use rustical_store::{ use rustical_store::{
Calendar, CalendarMetadata, CalendarStore, SubscriptionStore, auth::Principal, Calendar, CalendarMetadata, CalendarStore, SubscriptionStore, auth::Principal,
@@ -21,6 +22,7 @@ pub async fn route_import<C: CalendarStore, S: SubscriptionStore>(
Path((principal, cal_id)): Path<(String, String)>, Path((principal, cal_id)): Path<(String, String)>,
user: Principal, user: Principal,
State(resource_service): State<CalendarResourceService<C, S>>, State(resource_service): State<CalendarResourceService<C, S>>,
overwrite: Overwrite,
body: String, body: String,
) -> Result<Response, Error> { ) -> Result<Response, Error> {
if !user.is_principal(&principal) { if !user.is_principal(&principal) {
@@ -100,7 +102,9 @@ pub async fn route_import<C: CalendarStore, S: SubscriptionStore>(
}; };
let cal_store = resource_service.cal_store; let cal_store = resource_service.cal_store;
cal_store.import_calendar(new_cal, objects, false).await?; cal_store
.import_calendar(new_cal, objects, overwrite.is_true())
.await?;
Ok(StatusCode::OK.into_response()) Ok(StatusCode::OK.into_response())
} }

View File

@@ -11,7 +11,7 @@ use rustical_ical::CalendarObject;
use rustical_store::CalendarStore; use rustical_store::CalendarStore;
use rustical_store::auth::Principal; use rustical_store::auth::Principal;
use std::str::FromStr; use std::str::FromStr;
use tracing::instrument; use tracing::{debug, instrument};
#[instrument(skip(cal_store))] #[instrument(skip(cal_store))]
pub async fn get_event<C: CalendarStore>( pub async fn get_event<C: CalendarStore>(
@@ -78,9 +78,10 @@ pub async fn put_event<C: CalendarStore>(
true true
}; };
let object = match CalendarObject::from_ics(body) { let object = match CalendarObject::from_ics(body.clone()) {
Ok(obj) => obj, Ok(obj) => obj,
Err(_) => { Err(_) => {
debug!("invalid calendar data:\n{body}");
return Err(Error::PreconditionFailed(Precondition::ValidCalendarData)); return Err(Error::PreconditionFailed(Precondition::ValidCalendarData));
} }
}; };

View File

@@ -66,6 +66,9 @@ impl<PN: XmlDeserialize> XmlDeserialize for PropElement<PN> {
Event::Text(_) | Event::CData(_) => { Event::Text(_) | Event::CData(_) => {
return Err(XmlError::UnsupportedEvent("Not expecting text here")); return Err(XmlError::UnsupportedEvent("Not expecting text here"));
} }
Event::GeneralRef(_) => {
return Err(::rustical_xml::XmlError::UnsupportedEvent("GeneralRef"));
}
Event::Decl(_) | Event::Comment(_) | Event::DocType(_) | Event::PI(_) => { /* ignore */ Event::Decl(_) | Event::Comment(_) | Event::DocType(_) | Event::PI(_) => { /* ignore */
} }
Event::End(_end) => { Event::End(_end) => {

View File

@@ -29,12 +29,9 @@ impl XmlSerialize for TagList {
}); });
let has_prefix = prefix.is_some(); let has_prefix = prefix.is_some();
let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat()); let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat());
let qname = tagname
.as_ref()
.map(|tagname| ::quick_xml::name::QName(tagname.as_bytes()));
if let Some(qname) = &qname { if let Some(tagname) = tagname.as_ref() {
let mut bytes_start = BytesStart::from(qname.to_owned()); let mut bytes_start = BytesStart::new(tagname);
if !has_prefix && let Some(ns) = &ns { if !has_prefix && let Some(ns) = &ns {
bytes_start.push_attribute((b"xmlns".as_ref(), ns.as_ref())); bytes_start.push_attribute((b"xmlns".as_ref(), ns.as_ref()));
} }
@@ -49,8 +46,8 @@ impl XmlSerialize for TagList {
el.write_empty()?; el.write_empty()?;
} }
if let Some(qname) = &qname { if let Some(tagname) = tagname.as_ref() {
writer.write_event(Event::End(BytesEnd::from(qname.to_owned())))?; writer.write_event(Event::End(BytesEnd::new(tagname)))?;
} }
Ok(()) Ok(())
} }

View File

@@ -15,6 +15,10 @@ pub struct EventObject {
} }
impl EventObject { impl EventObject {
pub fn get_uid(&self) -> &str {
self.event.get_uid()
}
pub fn get_dtstart(&self) -> Result<Option<CalDateTime>, Error> { pub fn get_dtstart(&self) -> Result<Option<CalDateTime>, Error> {
if let Some(dtstart) = self.event.get_dtstart() { if let Some(dtstart) = self.event.get_dtstart() {
Ok(Some(CalDateTime::parse_prop(dtstart, &self.timezones)?)) Ok(Some(CalDateTime::parse_prop(dtstart, &self.timezones)?))
@@ -92,6 +96,7 @@ impl EventObject {
&self, &self,
start: Option<DateTime<Utc>>, start: Option<DateTime<Utc>>,
end: Option<DateTime<Utc>>, end: Option<DateTime<Utc>>,
overrides: &[EventObject],
) -> Result<Vec<IcalEvent>, Error> { ) -> Result<Vec<IcalEvent>, Error> {
if let Some(mut rrule_set) = self.recurrence_ruleset()? { if let Some(mut rrule_set) = self.recurrence_ruleset()? {
if let Some(start) = start { if let Some(start) = start {
@@ -107,13 +112,30 @@ impl EventObject {
.get_dtend()? .get_dtend()?
.map(|dtend| dtend.as_datetime().into_owned() - dtstart.as_datetime().into_owned()); .map(|dtend| dtend.as_datetime().into_owned() - dtstart.as_datetime().into_owned());
for date in dates { 'recurrence: for date in dates {
let date = CalDateTime::from(date); let date = CalDateTime::from(date);
let dateformat = if dtstart.is_date() { let dateformat = if dtstart.is_date() {
date.format_date() date.format_date()
} else { } else {
date.format() date.format()
}; };
for _override in overrides {
if let Some(override_id) = &_override
.event
.get_recurrence_id()
.as_ref()
.expect("overrides have a recurrence id")
.value
&& override_id == &dateformat
{
// We have an override for this occurence
//
events.push(_override.event.clone());
continue 'recurrence;
}
}
let mut ev = self.event.clone().mutable(); let mut ev = self.event.clone().mutable();
ev.remove_property("RRULE"); ev.remove_property("RRULE");
ev.remove_property("RDATE"); ev.remove_property("RDATE");
@@ -229,10 +251,18 @@ END:VEVENT\r\n",
#[test] #[test]
fn test_expand_recurrence() { fn test_expand_recurrence() {
let event = CalendarObject::from_ics(ICS.to_string()).unwrap(); let event = CalendarObject::from_ics(ICS.to_string()).unwrap();
let event = event.event().unwrap(); let (event, overrides) = if let crate::CalendarObjectComponent::Event(
main_event,
overrides,
) = event.get_data()
{
(main_event, overrides)
} else {
panic!()
};
let events: Vec<String> = event let events: Vec<String> = event
.expand_recurrence(None, None) .expand_recurrence(None, None, overrides)
.unwrap() .unwrap()
.into_iter() .into_iter()
.map(|event| Emitter::generate(&event)) .map(|event| Emitter::generate(&event))

View File

@@ -3,3 +3,9 @@ use ical::parser::ical::component::IcalJournal;
#[derive(Debug, Clone, From)] #[derive(Debug, Clone, From)]
pub struct JournalObject(pub IcalJournal); pub struct JournalObject(pub IcalJournal);
impl JournalObject {
pub fn get_uid(&self) -> &str {
self.0.get_uid()
}
}

View File

@@ -56,18 +56,75 @@ impl rustical_xml::ValueDeserialize for CalendarObjectType {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum CalendarObjectComponent { pub enum CalendarObjectComponent {
Event(EventObject), Event(EventObject, Vec<EventObject>),
Todo(TodoObject), Todo(TodoObject, Vec<TodoObject>),
Journal(JournalObject), Journal(JournalObject, Vec<JournalObject>),
} }
impl Default for CalendarObjectComponent { impl CalendarObjectComponent {
fn default() -> Self { fn from_events(mut events: Vec<EventObject>) -> Result<Self, Error> {
Self::Event(EventObject::default()) let main_event = events
.extract_if(.., |event| event.event.get_recurrence_id().is_none())
.next()
.expect("there must be one main event");
let overrides = events;
for event in &overrides {
if event.get_uid() != main_event.get_uid() {
return Err(Error::InvalidData(
"Calendar object contains multiple UIDs".to_owned(),
));
}
if event.event.get_recurrence_id().is_none() {
return Err(Error::InvalidData(
"Calendar object can only contain one main component".to_owned(),
));
}
}
Ok(Self::Event(main_event, overrides))
}
fn from_todos(mut todos: Vec<TodoObject>) -> Result<Self, Error> {
let main_todo = todos
.extract_if(.., |todo| todo.0.get_recurrence_id().is_none())
.next()
.expect("there must be one main event");
let overrides = todos;
for todo in &overrides {
if todo.get_uid() != main_todo.get_uid() {
return Err(Error::InvalidData(
"Calendar object contains multiple UIDs".to_owned(),
));
}
if todo.0.get_recurrence_id().is_none() {
return Err(Error::InvalidData(
"Calendar object can only contain one main component".to_owned(),
));
}
}
Ok(Self::Todo(main_todo, overrides))
}
fn from_journals(mut journals: Vec<JournalObject>) -> Result<Self, Error> {
let main_journal = journals
.extract_if(.., |journal| journal.0.get_recurrence_id().is_none())
.next()
.expect("there must be one main event");
let overrides = journals;
for journal in &overrides {
if journal.get_uid() != main_journal.get_uid() {
return Err(Error::InvalidData(
"Calendar object contains multiple UIDs".to_owned(),
));
}
if journal.0.get_recurrence_id().is_none() {
return Err(Error::InvalidData(
"Calendar object can only contain one main component".to_owned(),
));
}
}
Ok(Self::Journal(main_journal, overrides))
} }
} }
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone)]
pub struct CalendarObject { pub struct CalendarObject {
data: CalendarObjectComponent, data: CalendarObjectComponent,
properties: Vec<Property>, properties: Vec<Property>,
@@ -84,16 +141,16 @@ impl CalendarObject {
"multiple calendars, only one allowed".to_owned(), "multiple calendars, only one allowed".to_owned(),
)); ));
} }
if cal.events.len()
+ cal.alarms.len() if !cal.events.is_empty() as u8
+ cal.todos.len() + !cal.todos.is_empty() as u8
+ cal.journals.len() + !cal.journals.is_empty() as u8
+ cal.free_busys.len() + !cal.free_busys.is_empty() as u8
!= 1 != 1
{ {
// https://datatracker.ietf.org/doc/html/rfc4791#section-4.1 // https://datatracker.ietf.org/doc/html/rfc4791#section-4.1
return Err(Error::InvalidData( return Err(Error::InvalidData(
"iCalendar object is only allowed to have exactly one component".to_owned(), "iCalendar object must have exactly one component type".to_owned(),
)); ));
} }
@@ -111,12 +168,27 @@ impl CalendarObject {
.map(|timezone| (timezone.get_tzid().to_owned(), timezone)) .map(|timezone| (timezone.get_tzid().to_owned(), timezone))
.collect(); .collect();
let data = if let Some(event) = cal.events.into_iter().next() { let data = if !cal.events.is_empty() {
CalendarObjectComponent::Event(EventObject { event, timezones }) CalendarObjectComponent::from_events(
} else if let Some(todo) = cal.todos.into_iter().next() { cal.events
CalendarObjectComponent::Todo(todo.into()) .into_iter()
} else if let Some(journal) = cal.journals.into_iter().next() { .map(|event| EventObject {
CalendarObjectComponent::Journal(journal.into()) event,
timezones: timezones.clone(),
})
.collect(),
)?
} else if !cal.todos.is_empty() {
CalendarObjectComponent::from_todos(
cal.todos.into_iter().map(|todo| todo.into()).collect(),
)?
} else if !cal.journals.is_empty() {
CalendarObjectComponent::from_journals(
cal.journals
.into_iter()
.map(|journal| journal.into())
.collect(),
)?
} else { } else {
return Err(Error::InvalidData( return Err(Error::InvalidData(
"iCalendar component type not supported :(".to_owned(), "iCalendar component type not supported :(".to_owned(),
@@ -141,9 +213,11 @@ impl CalendarObject {
pub fn get_id(&self) -> &str { pub fn get_id(&self) -> &str {
match &self.data { match &self.data {
CalendarObjectComponent::Todo(todo) => todo.0.get_uid(), // We've made sure before that the first component exists and all components share the
CalendarObjectComponent::Event(event) => event.event.get_uid(), // same UID
CalendarObjectComponent::Journal(journal) => journal.0.get_uid(), CalendarObjectComponent::Todo(todo, _) => todo.0.get_uid(),
CalendarObjectComponent::Event(event, _) => event.event.get_uid(),
CalendarObjectComponent::Journal(journal, _) => journal.0.get_uid(),
} }
} }
@@ -164,33 +238,40 @@ impl CalendarObject {
pub fn get_object_type(&self) -> CalendarObjectType { pub fn get_object_type(&self) -> CalendarObjectType {
match self.data { match self.data {
CalendarObjectComponent::Todo(_) => CalendarObjectType::Todo, CalendarObjectComponent::Todo(_, _) => CalendarObjectType::Todo,
CalendarObjectComponent::Event(_) => CalendarObjectType::Event, CalendarObjectComponent::Event(_, _) => CalendarObjectType::Event,
CalendarObjectComponent::Journal(_) => CalendarObjectType::Journal, CalendarObjectComponent::Journal(_, _) => CalendarObjectType::Journal,
} }
} }
pub fn get_first_occurence(&self) -> Result<Option<CalDateTime>, Error> { pub fn get_first_occurence(&self) -> Result<Option<CalDateTime>, Error> {
match &self.data { match &self.data {
CalendarObjectComponent::Event(event) => event.get_dtstart(), CalendarObjectComponent::Event(main_event, overrides) => Ok(overrides
.iter()
.chain([main_event].into_iter())
.map(|event| event.get_dtstart())
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.flatten()
.min()),
_ => Ok(None), _ => Ok(None),
} }
} }
pub fn get_last_occurence(&self) -> Result<Option<CalDateTime>, Error> { pub fn get_last_occurence(&self) -> Result<Option<CalDateTime>, Error> {
match &self.data { match &self.data {
CalendarObjectComponent::Event(event) => event.get_last_occurence(), CalendarObjectComponent::Event(main_event, overrides) => Ok(overrides
.iter()
.chain([main_event].into_iter())
.map(|event| event.get_last_occurence())
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.flatten()
.max()),
_ => Ok(None), _ => Ok(None),
} }
} }
pub fn event(&self) -> Option<&EventObject> {
match &self.data {
CalendarObjectComponent::Event(event) => Some(event),
_ => None,
}
}
pub fn expand_recurrence( pub fn expand_recurrence(
&self, &self,
start: Option<DateTime<Utc>>, start: Option<DateTime<Utc>>,
@@ -198,10 +279,10 @@ impl CalendarObject {
) -> Result<String, Error> { ) -> Result<String, Error> {
// Only events can be expanded // Only events can be expanded
match &self.data { match &self.data {
CalendarObjectComponent::Event(event) => { CalendarObjectComponent::Event(main_event, overrides) => {
let cal = IcalCalendar { let cal = IcalCalendar {
properties: self.properties.clone(), properties: self.properties.clone(),
events: event.expand_recurrence(start, end)?, events: main_event.expand_recurrence(start, end, overrides)?,
..Default::default() ..Default::default()
}; };
Ok(cal.generate()) Ok(cal.generate())

View File

@@ -3,3 +3,9 @@ use ical::parser::ical::component::IcalTodo;
#[derive(Debug, Clone, From)] #[derive(Debug, Clone, From)]
pub struct TodoObject(pub IcalTodo); pub struct TodoObject(pub IcalTodo);
impl TodoObject {
pub fn get_uid(&self) -> &str {
self.0.get_uid()
}
}

View File

@@ -0,0 +1,30 @@
use rustical_ical::CalendarObject;
const MULTI_VEVENT: &str = r#"
BEGIN:VCALENDAR
PRODID:-//Example Corp.//CalDAV Client//EN
VERSION:2.0
BEGIN:VEVENT
UID:2@example.com
SUMMARY:Weekly Meeting
DTSTAMP:20041210T183838Z
DTSTART:20041206T120000Z
DTEND:20041206T130000Z
RRULE:FREQ=WEEKLY
END:VEVENT
BEGIN:VEVENT
UID:2@example.com
SUMMARY:Weekly Meeting
RECURRENCE-ID:20041213T120000Z
DTSTAMP:20041210T183838Z
DTSTART:20041213T130000Z
DTEND:20041213T140000Z
END:VEVENT
END:VCALENDAR
"#;
#[test]
fn parse_calendar_object() {
let object = CalendarObject::from_ics(MULTI_VEVENT.to_string()).unwrap();
object.expand_recurrence(None, None).unwrap();
}

View File

@@ -34,12 +34,11 @@ impl Enum {
}); });
let has_prefix = prefix.is_some(); let has_prefix = prefix.is_some();
let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat()); let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat());
let qname = tagname.as_ref().map(|tagname| ::quick_xml::name::QName(tagname.as_bytes()));
const enum_untagged: bool = #enum_untagged; const enum_untagged: bool = #enum_untagged;
if let Some(qname) = &qname { if let Some(tagname) = tagname.as_ref() {
let mut bytes_start = BytesStart::from(qname.to_owned()); let mut bytes_start = BytesStart::new(tagname);
if !has_prefix { if !has_prefix {
if let Some(ns) = &ns { if let Some(ns) = &ns {
bytes_start.push_attribute((b"xmlns".as_ref(), ns.as_ref())); bytes_start.push_attribute((b"xmlns".as_ref(), ns.as_ref()));
@@ -50,8 +49,8 @@ impl Enum {
#(#variant_serializers);* #(#variant_serializers);*
if let Some(qname) = &qname { if let Some(tagname) = tagname.as_ref() {
writer.write_event(Event::End(BytesEnd::from(qname.to_owned())))?; writer.write_event(Event::End(BytesEnd::new(tagname)))?;
} }
Ok(()) Ok(())
} }

View File

@@ -66,6 +66,9 @@ impl Enum {
Event::CData(cdata) => { Event::CData(cdata) => {
return Err(::rustical_xml::XmlError::UnsupportedEvent("CDATA")); return Err(::rustical_xml::XmlError::UnsupportedEvent("CDATA"));
} }
Event::GeneralRef(_) => {
return Err(::rustical_xml::XmlError::UnsupportedEvent("GeneralRef"));
}
Event::Decl(_) => { /* <?xml ... ?> ignore this */ } Event::Decl(_) => { /* <?xml ... ?> ignore this */ }
Event::Comment(_) => { /* ignore */ } Event::Comment(_) => { /* ignore */ }
Event::DocType(_) => { /* ignore */ } Event::DocType(_) => { /* ignore */ }

View File

@@ -111,10 +111,9 @@ impl NamedStruct {
}); });
let has_prefix = prefix.is_some(); let has_prefix = prefix.is_some();
let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat()); let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat());
let qname = tagname.as_ref().map(|tagname| ::quick_xml::name::QName(tagname.as_bytes()));
if let Some(qname) = &qname { if let Some(tagname) = tagname.as_ref() {
let mut bytes_start = BytesStart::from(qname.to_owned()); let mut bytes_start = BytesStart::new(tagname);
if !has_prefix { if !has_prefix {
if let Some(ns) = &ns { if let Some(ns) = &ns {
bytes_start.push_attribute((b"xmlns".as_ref(), ns.as_ref())); bytes_start.push_attribute((b"xmlns".as_ref(), ns.as_ref()));
@@ -133,8 +132,8 @@ impl NamedStruct {
} }
if !#is_empty { if !#is_empty {
#(#tag_writers);* #(#tag_writers);*
if let Some(qname) = &qname { if let Some(tagname) = tagname.as_ref() {
writer.write_event(Event::End(BytesEnd::from(qname.to_owned())))?; writer.write_event(Event::End(BytesEnd::new(tagname)))?;
} }
} }
Ok(()) Ok(())

View File

@@ -148,6 +148,8 @@ impl NamedStruct {
} }
} }
let mut string = String::new();
if !empty { if !empty {
loop { loop {
let event = reader.read_event_into(&mut buf)?; let event = reader.read_event_into(&mut buf)?;
@@ -167,12 +169,23 @@ impl NamedStruct {
} }
} }
Event::Text(bytes_text) => { Event::Text(bytes_text) => {
let text = bytes_text.unescape()?; let text = bytes_text.decode()?;
#(#text_field_branches)* string.push_str(&text);
} }
Event::CData(cdata) => { Event::CData(cdata) => {
let text = String::from_utf8(cdata.to_vec())?; let text = String::from_utf8(cdata.to_vec())?;
#(#text_field_branches)* string.push_str(&text);
}
Event::GeneralRef(gref) => {
if let Some(char) = gref.resolve_char_ref()? {
string.push(char);
} else if let Some(text) =
quick_xml::escape::resolve_xml_entity(&gref.xml_content()?)
{
string.push_str(text);
} else {
return Err(XmlError::UnsupportedEvent("invalid XML ref"));
}
} }
Event::Decl(_) => { /* <?xml ... ?> ignore this */ } Event::Decl(_) => { /* <?xml ... ?> ignore this */ }
Event::Comment(_) => { /* ignore */ } Event::Comment(_) => { /* ignore */ }
@@ -185,6 +198,9 @@ impl NamedStruct {
} }
} }
let text = string;
#(#text_field_branches)*
Ok(Self { Ok(Self {
#(#builder_field_builds),* #(#builder_field_builds),*
}) })

View File

@@ -8,6 +8,8 @@ pub enum XmlError {
#[error(transparent)] #[error(transparent)]
QuickXmlError(#[from] quick_xml::Error), QuickXmlError(#[from] quick_xml::Error),
#[error(transparent)] #[error(transparent)]
QuickXmlEncodingError(#[from] quick_xml::encoding::EncodingError),
#[error(transparent)]
QuickXmlAttrError(#[from] quick_xml::events::attributes::AttrError), QuickXmlAttrError(#[from] quick_xml::events::attributes::AttrError),
#[error(transparent)] #[error(transparent)]
FromUtf8Error(#[from] FromUtf8Error), FromUtf8Error(#[from] FromUtf8Error),

View File

@@ -1,7 +1,7 @@
use crate::XmlRootTag; use crate::XmlRootTag;
use quick_xml::{ use quick_xml::{
events::{BytesStart, Event, attributes::Attribute}, events::{BytesStart, Event, attributes::Attribute},
name::{Namespace, QName}, name::Namespace,
}; };
use std::collections::HashMap; use std::collections::HashMap;
pub use xml_derive::XmlSerialize; pub use xml_derive::XmlSerialize;
@@ -76,9 +76,8 @@ impl XmlSerialize for () {
}); });
let has_prefix = prefix.is_some(); let has_prefix = prefix.is_some();
let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat()); let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat());
let qname = tagname.as_ref().map(|tagname| QName(tagname.as_bytes())); if let Some(tagname) = tagname.as_ref() {
if let Some(qname) = &qname { let mut bytes_start = BytesStart::new(tagname);
let mut bytes_start = BytesStart::from(qname.to_owned());
if !has_prefix && let Some(ns) = &ns { if !has_prefix && let Some(ns) = &ns {
bytes_start.push_attribute((b"xmlns".as_ref(), ns.as_ref())); bytes_start.push_attribute((b"xmlns".as_ref(), ns.as_ref()));
} }

View File

@@ -1,6 +1,6 @@
use crate::{XmlDeserialize, XmlError, XmlSerialize}; use crate::{XmlDeserialize, XmlError, XmlSerialize};
use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event}; use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
use quick_xml::name::{Namespace, QName}; use quick_xml::name::Namespace;
use std::collections::HashMap; use std::collections::HashMap;
use std::num::{ParseFloatError, ParseIntError}; use std::num::{ParseFloatError, ParseIntError};
use std::{convert::Infallible, io::BufRead}; use std::{convert::Infallible, io::BufRead};
@@ -77,20 +77,23 @@ impl<T: ValueDeserialize> XmlDeserialize for T {
loop { loop {
match reader.read_event_into(&mut buf)? { match reader.read_event_into(&mut buf)? {
Event::Text(bytes_text) => { Event::Text(bytes_text) => {
let text = bytes_text.unescape()?; let text = bytes_text.decode()?;
if !string.is_empty() { string.push_str(&text);
// Content already written
return Err(XmlError::UnsupportedEvent("content already written"));
}
string = text.to_string();
} }
Event::CData(cdata) => { Event::CData(cdata) => {
let text = String::from_utf8(cdata.to_vec())?; let text = String::from_utf8(cdata.to_vec())?;
if !string.is_empty() { string.push_str(&text);
// Content already written }
return Err(XmlError::UnsupportedEvent("content already written")); Event::GeneralRef(gref) => {
if let Some(char) = gref.resolve_char_ref()? {
string.push(char);
} else if let Some(text) =
quick_xml::escape::resolve_xml_entity(&gref.xml_content()?)
{
string.push_str(text);
} else {
return Err(XmlError::UnsupportedEvent("invalid XML ref"));
} }
string = text;
} }
Event::End(_) => break, Event::End(_) => break,
Event::Eof => return Err(XmlError::Eof), Event::Eof => return Err(XmlError::Eof),
@@ -123,17 +126,16 @@ impl<T: ValueSerialize> XmlSerialize for T {
}); });
let has_prefix = prefix.is_some(); let has_prefix = prefix.is_some();
let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat()); let tagname = tag.map(|tag| [&prefix.unwrap_or_default(), tag].concat());
let qname = tagname.as_ref().map(|tagname| QName(tagname.as_bytes())); if let Some(tagname) = tagname.as_ref() {
if let Some(qname) = &qname { let mut bytes_start = BytesStart::new(tagname);
let mut bytes_start = BytesStart::from(qname.to_owned());
if !has_prefix && let Some(ns) = &ns { if !has_prefix && let Some(ns) = &ns {
bytes_start.push_attribute((b"xmlns".as_ref(), ns.as_ref())); bytes_start.push_attribute((b"xmlns".as_ref(), ns.as_ref()));
} }
writer.write_event(Event::Start(bytes_start))?; writer.write_event(Event::Start(bytes_start))?;
} }
writer.write_event(Event::Text(BytesText::new(&self.serialize())))?; writer.write_event(Event::Text(BytesText::new(&self.serialize())))?;
if let Some(qname) = &qname { if let Some(tagname) = tagname {
writer.write_event(Event::End(BytesEnd::from(qname.to_owned())))?; writer.write_event(Event::End(BytesEnd::new(tagname)))?;
} }
Ok(()) Ok(())
} }

View File

@@ -275,7 +275,7 @@ fn test_xml_cdata() {
<document> <document>
<![CDATA[some text]]> <![CDATA[some text]]>
<href><![CDATA[some stuff]]></href> <href><![CDATA[some stuff]]></href>
<okay>&gt;</okay> <okay>nice&gt;text</okay>
</document> </document>
"#, "#,
) )
@@ -285,11 +285,25 @@ fn test_xml_cdata() {
Document { Document {
hello: "some text".to_owned(), hello: "some text".to_owned(),
href: "some stuff".to_owned(), href: "some stuff".to_owned(),
okay: ">".to_owned() okay: "nice>text".to_owned()
} }
); );
} }
#[test]
fn test_quickxml_bytesref() {
let gt = quick_xml::events::BytesRef::new("gt");
assert!(!gt.is_char_ref());
let result = if !gt.is_char_ref() {
quick_xml::escape::resolve_xml_entity(&gt.xml_content().unwrap())
.unwrap()
.to_string()
} else {
gt.xml_content().unwrap().to_string()
};
assert_eq!(result, ">");
}
#[test] #[test]
fn test_struct_xml_decl() { fn test_struct_xml_decl() {
#[derive(Debug, XmlDeserialize, XmlRootTag, PartialEq)] #[derive(Debug, XmlDeserialize, XmlRootTag, PartialEq)]
@@ -307,14 +321,14 @@ fn test_struct_xml_decl() {
let doc = Document::parse_str( let doc = Document::parse_str(
r#" r#"
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<document><child>Hello!</child></document>"#, <document><child>Hello!&amp;</child></document>"#,
) )
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
doc, doc,
Document { Document {
child: Child { child: Child {
text: "Hello!".to_owned() text: "Hello!&".to_owned()
} }
} }
); );

View File

@@ -9,7 +9,7 @@ docker run \
-p 4000:4000 \ -p 4000:4000 \
-v YOUR_DATA_DIR:/var/lib/rustical/ \ -v YOUR_DATA_DIR:/var/lib/rustical/ \
-v OPTIONAL_YOUR_CONFIG_TOML:/etc/rustical/config.toml \ # (1)! -v OPTIONAL_YOUR_CONFIG_TOML:/etc/rustical/config.toml \ # (1)!
-e RUSTICAL__CONFIG_OPTION="asd" \ # (2)! -e RUSTICAL_CONFIG_OPTION="asd" \ # (2)!
ghcr.io/lennart-k/rustical ghcr.io/lennart-k/rustical
``` ```

View File

@@ -0,0 +1,11 @@
# Notes
## Kubernetes setup
If you setup RustiCal with Kubernetes and call the deployment `rustical`
Kubernetes will by default expose some environment variables starting with `RUSTICAL_`
that will be rejected by RustiCal.
So for now the solutions are either not calling the deployment `rustical` or setting
`enableServiceLinks: false`, see <https://kubernetes.io/docs/tutorials/services/connect-applications-service/#accessing-the-service>.
For the corresponding issue see <https://github.com/lennart-k/rustical/issues/122>

View File

@@ -68,6 +68,7 @@ nav:
- Installation: - Installation:
- installation/index.md - installation/index.md
- Configuration: installation/configuration.md - Configuration: installation/configuration.md
- Notes: installation/notes.md
- Client Setup: setup/client.md - Client Setup: setup/client.md
- OpenID Connect: setup/oidc.md - OpenID Connect: setup/oidc.md
- Developers: - Developers:

View File

@@ -1,7 +1,7 @@
use crate::config::NextcloudLoginConfig; use crate::config::NextcloudLoginConfig;
use axum::Router; use axum::Router;
use axum::body::{Body, HttpBody}; use axum::body::{Body, HttpBody};
use axum::extract::Request; use axum::extract::{DefaultBodyLimit, Request};
use axum::middleware::Next; use axum::middleware::Next;
use axum::response::{Redirect, Response}; use axum::response::{Redirect, Response};
use axum::routing::{any, options}; use axum::routing::{any, options};
@@ -39,6 +39,7 @@ pub fn make_app<AS: AddressbookStore, CS: CalendarStore, S: SubscriptionStore>(
nextcloud_login_config: NextcloudLoginConfig, nextcloud_login_config: NextcloudLoginConfig,
dav_push_enabled: bool, dav_push_enabled: bool,
session_cookie_samesite_strict: bool, session_cookie_samesite_strict: bool,
payload_limit_mb: usize,
) -> Router<()> { ) -> Router<()> {
let birthday_store = Arc::new(ContactBirthdayStore::new(addr_store.clone())); let birthday_store = Arc::new(ContactBirthdayStore::new(addr_store.clone()));
let combined_cal_store = let combined_cal_store =
@@ -202,4 +203,5 @@ pub fn make_app<AS: AddressbookStore, CS: CalendarStore, S: SubscriptionStore>(
response response
}, },
)) ))
.layer(DefaultBodyLimit::max(payload_limit_mb * 1000 * 1000))
} }

View File

@@ -8,6 +8,7 @@ pub struct HttpConfig {
pub host: String, pub host: String,
pub port: u16, pub port: u16,
pub session_cookie_samesite_strict: bool, pub session_cookie_samesite_strict: bool,
pub payload_limit_mb: usize,
} }
impl Default for HttpConfig { impl Default for HttpConfig {
@@ -16,6 +17,7 @@ impl Default for HttpConfig {
host: "0.0.0.0".to_owned(), host: "0.0.0.0".to_owned(),
port: 4000, port: 4000,
session_cookie_samesite_strict: false, session_cookie_samesite_strict: false,
payload_limit_mb: 4,
} }
} }
} }

View File

@@ -117,6 +117,7 @@ async fn main() -> Result<()> {
config.nextcloud_login.clone(), config.nextcloud_login.clone(),
config.dav_push.enabled, config.dav_push.enabled,
config.http.session_cookie_samesite_strict, config.http.session_cookie_samesite_strict,
config.http.payload_limit_mb,
); );
let app = ServiceExt::<Request>::into_make_service( let app = ServiceExt::<Request>::into_make_service(
NormalizePathLayer::trim_trailing_slash().layer(app), NormalizePathLayer::trim_trailing_slash().layer(app),