Import upstream v0.16.22, stripped

Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f
Enterprise-only files removed or emptied: 63
Enterprise-only snippets removed: 117 in 50 files
Dangling module declarations removed: 5
Cargo edits turning enterprise off: 14
Verification: clean
Enterprise feature gates left for rebuilt features: 19 in 18 files

Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
This commit is contained in:
2026-09-18 10:21:56 -07:00
commit 7dae9b29fd
1650 changed files with 485521 additions and 0 deletions
+203
View File
@@ -0,0 +1,203 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::utils::{server::TestServer, webdav::GenerateTestDavResource};
use dav_proto::schema::property::{DavProperty, WebDavProperty};
use groupware::DavResourceName;
use hyper::StatusCode;
pub async fn test(test: &TestServer) {
let client = test.account("[email protected]").webdav_client();
for resource_type in [
DavResourceName::File,
DavResourceName::Cal,
DavResourceName::Card,
] {
println!(
"Running LOCK/UNLOCK tests ({})...",
resource_type.base_path()
);
let base_path = format!("{}/john%40example.com", resource_type.base_path());
// Test 1: Creating a collection under an unmapped resource without providing a lock token should fail
let path = format!("{base_path}/do-not-write");
let response = client
.lock_create(&path, "super-owner", true, "infinity", "Second-123")
.await
.with_status(StatusCode::CREATED);
let lock_token = response
.with_value(
"D:prop.D:lockdiscovery.D:activelock.D:owner.href",
"super-owner",
)
.with_value("D:prop.D:lockdiscovery.D:activelock.D:depth", "infinity")
.with_value(
"D:prop.D:lockdiscovery.D:activelock.D:timeout",
"Second-123",
)
.lock_token()
.to_string();
// Test 2: Refreshing a lock token with an invalid a lock token should fail
client
.lock_refresh(&path, "urn:stalwart:davlock:1234", "infinity", "Second-456")
.await
.with_status(StatusCode::PRECONDITION_FAILED);
// Test 3: Refreshing a lock token with valid a lock token should succeed
client
.lock_refresh(&path, &lock_token, "infinity", "Second-456")
.await
.with_status(StatusCode::OK)
.with_value(
"D:prop.D:lockdiscovery.D:activelock.D:owner.href",
"super-owner",
)
.with_any_value(
"D:prop.D:lockdiscovery.D:activelock.D:timeout",
["Second-456", "Second-455"],
);
// Test 3: Creating a collection under an unmapped resource with a lock token should fail
client
.request_with_headers("MKCOL", &path, [], "")
.await
.with_status(StatusCode::LOCKED)
.with_value("D:error.D:lock-token-submitted.D:href", &path);
// Test 4: Creating a collection under a mapped resource with a lock token should succeed
client
.request_with_headers(
"MKCOL",
&path,
[("if", format!("(<{lock_token}>)").as_str())],
"",
)
.await
.with_status(StatusCode::CREATED);
// Test 5: Creating a lock under an infinity locked resource should fail
let file_path = format!("{path}/file.txt");
client
.lock_create(&file_path, "super-owner", true, "0", "Second-123")
.await
.with_status(StatusCode::LOCKED)
.with_value("D:error.D:lock-token-submitted.D:href", &path);
// Test 6: Creating a file under a locked resource without a lock token should fail
let contents = resource_type.generate();
client
.request("PUT", &file_path, &contents)
.await
.with_status(StatusCode::LOCKED)
.with_value("D:error.D:lock-token-submitted.D:href", &path);
// Test 7: Creating a file under a locked resource with a lock token should succeed
client
.request_with_headers(
"PUT",
&file_path,
[("if", format!("(<{lock_token}>)").as_str())],
&contents,
)
.await
.with_status(StatusCode::CREATED);
// Test 8: Locks should be included in propfind responses
let response = client
.propfind(&path, [DavProperty::WebDav(WebDavProperty::LockDiscovery)])
.await;
for href in [path.clone() + "/", file_path] {
let props = response.properties(&href);
props
.get(DavProperty::WebDav(WebDavProperty::LockDiscovery))
.with_some_values([
"D:activelock.D:owner.href:super-owner",
"D:activelock.D:depth:infinity",
format!("D:activelock.D:locktoken.D:href:{lock_token}").as_str(),
format!("D:activelock.D:lockroot.D:href:{path}").as_str(),
"D:activelock.D:locktype.D:write",
"D:activelock.D:lockscope.D:exclusive",
])
.with_any_values([
"D:activelock.D:timeout:Second-456",
"D:activelock.D:timeout:Second-455",
]);
}
// Test 9: Delete with and without a lock token
client
.request("DELETE", &path, "")
.await
.with_status(StatusCode::LOCKED)
.with_value("D:error.D:lock-token-submitted.D:href", &path);
client
.request_with_headers(
"DELETE",
&path,
[("if", format!("(<{lock_token}>)").as_str())],
"",
)
.await
.with_status(StatusCode::NO_CONTENT);
// Test 10: Unlock with and without a lock token
client
.unlock(&path, "urn:stalwart:davlock:1234")
.await
.with_status(StatusCode::CONFLICT)
.with_value("D:error.D:lock-token-matches-request-uri", "");
client
.unlock(&path, &lock_token)
.await
.with_status(StatusCode::NO_CONTENT);
// Test 11: Locking with a large dead property should fail
let path = format!("{base_path}/invalid-lock");
client
.lock_create(
&path,
(0..=test.server.core.groupware.dead_property_size.unwrap() + 1)
.map(|_| "a")
.collect::<String>()
.as_str(),
true,
"infinity",
"Second-123",
)
.await
.with_status(StatusCode::PAYLOAD_TOO_LARGE);
// Test 12: Too many locks should fail
for i in 0..test.server.core.groupware.max_locks_per_user {
client
.lock_create(
&format!("{base_path}/invalid-lock-{i}"),
"super-owner",
true,
"infinity",
"Second-123",
)
.await
.with_status(StatusCode::CREATED);
}
client
.lock_create(
&format!("{base_path}/invalid-lock-greedy"),
"super-owner",
true,
"infinity",
"Second-123",
)
.await
.with_status(StatusCode::TOO_MANY_REQUESTS);
}
client.delete_default_containers().await;
test.assert_is_empty().await;
}