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
+674
View File
@@ -0,0 +1,674 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::utils::{jmap::JmapUtils, server::TestServer};
use calcard::jscontact::JSContactProperty;
use jmap_proto::{
object::{addressbook::AddressBookProperty, share_notification::ShareNotificationProperty},
request::method::MethodObject,
};
use serde_json::json;
use types::id::Id;
pub async fn test(test: &TestServer) {
println!("Running Contacts ACL tests...");
let john = test.account("[email protected]");
let jane = test.account("[email protected]");
let john_id = john.id_string().to_string();
let jane_id = jane.id_string().to_string();
// Create test address books
let response = john
.jmap_create(
MethodObject::AddressBook,
[json!({
"name": "Test #1",
})],
Vec::<(&str, &str)>::new(),
)
.await;
let john_book_id = response.created(0).id().to_string();
let john_contact_id = john
.jmap_create(
MethodObject::ContactCard,
[json!({
"uid": "abc123",
"name": {
"full": "John's Simple Contact",
},
"addressBookIds": {
&john_book_id: true
},
})],
Vec::<(&str, &str)>::new(),
)
.await
.created(0)
.id()
.to_string();
let response = jane
.jmap_create(
MethodObject::AddressBook,
[json!({
"name": "Test #1",
})],
Vec::<(&str, &str)>::new(),
)
.await;
let jane_book_id = response.created(0).id().to_string();
let jane_contact_id = jane
.jmap_create(
MethodObject::ContactCard,
[json!({
"uid": "abc456",
"name": {
"full": "Jane's Simple Contact",
},
"addressBookIds": {
&jane_book_id: true
},
})],
Vec::<(&str, &str)>::new(),
)
.await
.created(0)
.id()
.to_string();
// Verify myRights
john.jmap_get(
MethodObject::AddressBook,
[
AddressBookProperty::Id,
AddressBookProperty::Name,
AddressBookProperty::MyRights,
AddressBookProperty::ShareWith,
],
[john_book_id.as_str()],
)
.await
.list()[0]
.assert_is_equal(json!({
"id": john_book_id,
"name": "Test #1",
"myRights": {
"mayRead": true,
"mayWrite": true,
"mayDelete": true,
"mayShare": true
},
"shareWith": {}
}));
// Obtain share notifications
let mut jane_share_change_id = jane
.jmap_get(
MethodObject::ShareNotification,
Vec::<&str>::new(),
Vec::<&str>::new(),
)
.await
.state()
.to_string();
// Make sure Jane has no access
assert_eq!(
jane.jmap_get_account(
john,
MethodObject::AddressBook,
Vec::<&str>::new(),
[john_book_id.as_str()],
)
.await
.method_response()
.typ(),
"forbidden"
);
// Share address book with Jane
john.jmap_update(
MethodObject::AddressBook,
[(
&john_book_id,
json!({
"shareWith": {
&jane_id : {
"mayRead": true,
}
}
}),
)],
Vec::<(&str, &str)>::new(),
)
.await
.updated(&john_book_id);
john.jmap_get(
MethodObject::AddressBook,
[
AddressBookProperty::Id,
AddressBookProperty::Name,
AddressBookProperty::ShareWith,
],
[john_book_id.as_str()],
)
.await
.list()[0]
.assert_is_equal(json!({
"id": john_book_id,
"name": "Test #1",
"shareWith": {
&jane_id : {
"mayRead": true,
"mayWrite": false,
"mayDelete": false,
"mayShare": false
}
}
}));
// Verify Jane can access the contact
jane.jmap_get_account(
john,
MethodObject::AddressBook,
[
AddressBookProperty::Id,
AddressBookProperty::Name,
AddressBookProperty::MyRights,
],
[john_book_id.as_str()],
)
.await
.list()[0]
.assert_is_equal(json!({
"id": john_book_id,
"name": "Test #1",
"myRights": {
"mayRead": true,
"mayWrite": false,
"mayDelete": false,
"mayShare": false
}
}));
jane.jmap_get_account(
john,
MethodObject::ContactCard,
[AddressBookProperty::Id, AddressBookProperty::Name],
[john_contact_id.as_str()],
)
.await
.list()[0]
.assert_is_equal(json!({
"id": john_contact_id,
"name": {
"full": "John's Simple Contact"
},
}));
// Verify Jane received a share notification
let response = jane
.jmap_changes(MethodObject::ShareNotification, &jane_share_change_id)
.await;
jane_share_change_id = response.new_state().to_string();
let changes = response.changes().collect::<Vec<_>>();
assert_eq!(changes.len(), 1);
let share_id = changes[0].as_created();
jane.jmap_get(
MethodObject::ShareNotification,
[
ShareNotificationProperty::Id,
ShareNotificationProperty::ChangedBy,
ShareNotificationProperty::ObjectType,
ShareNotificationProperty::ObjectAccountId,
ShareNotificationProperty::ObjectId,
ShareNotificationProperty::OldRights,
ShareNotificationProperty::NewRights,
ShareNotificationProperty::Name,
],
[share_id],
)
.await
.list()[0]
.assert_is_equal(json!({
"id": &share_id,
"changedBy": {
"principalId": &john_id,
"name": "John Doe",
"email": "[email protected]"
},
"objectType": "AddressBook",
"objectAccountId": &john_id,
"objectId": &john_book_id,
"oldRights": {
"mayRead": false,
"mayWrite": false,
"mayDelete": false,
"mayShare": false
},
"newRights": {
"mayRead": true,
"mayWrite": false,
"mayDelete": false,
"mayShare": false
},
"name": null
}));
// Updating and deleting should fail
assert_eq!(
jane.jmap_update_account(
john,
MethodObject::AddressBook,
[(&john_book_id, json!({}))],
Vec::<(&str, &str)>::new(),
)
.await
.not_updated(&john_book_id)
.description(),
"You are not allowed to modify this address book."
);
assert_eq!(
jane.jmap_destroy_account(
john,
MethodObject::AddressBook,
[&john_book_id],
Vec::<(&str, &str)>::new(),
)
.await
.not_destroyed(&john_book_id)
.description(),
"You are not allowed to delete this address book."
);
assert!(
jane.jmap_update_account(
john,
MethodObject::ContactCard,
[(&john_contact_id, json!({}))],
Vec::<(&str, &str)>::new(),
)
.await
.not_updated(&john_contact_id)
.description()
.contains("You are not allowed to modify address book"),
);
assert!(
jane.jmap_destroy_account(
john,
MethodObject::ContactCard,
[&john_contact_id],
Vec::<(&str, &str)>::new(),
)
.await
.not_destroyed(&john_contact_id)
.description()
.contains("You are not allowed to remove contacts from address book"),
);
// Grant Jane write access
john.jmap_update(
MethodObject::AddressBook,
[(
&john_book_id,
json!({
format!("shareWith/{jane_id}/mayWrite"): true,
format!("shareWith/{jane_id}/mayDelete"): true,
}),
)],
Vec::<(&str, &str)>::new(),
)
.await
.updated(&john_book_id);
jane.jmap_get_account(
john,
MethodObject::AddressBook,
[
AddressBookProperty::Id,
AddressBookProperty::Name,
AddressBookProperty::MyRights,
],
[john_book_id.as_str()],
)
.await
.list()[0]
.assert_is_equal(json!({
"id": john_book_id,
"name": "Test #1",
"myRights": {
"mayRead": true,
"mayWrite": true,
"mayDelete": true,
"mayShare": false
}
}));
// Verify Jane received a share notification with the updated rights
let response = jane
.jmap_changes(MethodObject::ShareNotification, &jane_share_change_id)
.await;
jane_share_change_id = response.new_state().to_string();
let changes = response.changes().collect::<Vec<_>>();
assert_eq!(changes.len(), 1);
let share_id = changes[0].as_created();
jane.jmap_get(
MethodObject::ShareNotification,
[
ShareNotificationProperty::Id,
ShareNotificationProperty::ChangedBy,
ShareNotificationProperty::ObjectType,
ShareNotificationProperty::ObjectAccountId,
ShareNotificationProperty::ObjectId,
ShareNotificationProperty::OldRights,
ShareNotificationProperty::NewRights,
ShareNotificationProperty::Name,
],
[share_id],
)
.await
.list()[0]
.assert_is_equal(json!({
"id": &share_id,
"changedBy": {
"principalId": &john_id,
"name": "John Doe",
"email": "[email protected]"
},
"objectType": "AddressBook",
"objectAccountId": &john_id,
"objectId": &john_book_id,
"oldRights": {
"mayRead": true,
"mayWrite": false,
"mayDelete": false,
"mayShare": false
},
"newRights": {
"mayRead": true,
"mayWrite": true,
"mayDelete": true,
"mayShare": false
},
"name": null
}));
// Creating a root folder should fail
assert_eq!(
jane.jmap_create_account(
john,
MethodObject::AddressBook,
[json!({
"name": "A new shared address book",
})],
Vec::<(&str, &str)>::new()
)
.await
.not_created(0)
.description(),
"Cannot create address books in a shared account."
);
// Copy Jane's contact into John's address book
let john_copied_contact_id = jane
.jmap_copy(
jane,
john,
MethodObject::ContactCard,
[(
&jane_contact_id,
json!({
"addressBookIds": {
&john_book_id: true
}
}),
)],
false,
)
.await
.copied(&jane_contact_id)
.id()
.to_string();
jane.jmap_get_account(
john,
MethodObject::ContactCard,
[
JSContactProperty::<Id>::Id,
JSContactProperty::AddressBookIds,
JSContactProperty::Name,
],
[john_copied_contact_id.as_str()],
)
.await
.list()[0]
.assert_is_equal(json!({
"id": john_copied_contact_id,
"name": {
"full": "Jane's Simple Contact"
},
"addressBookIds": {
&john_book_id: true
}
}));
// Destroy the copied contact
assert_eq!(
jane.jmap_destroy_account(
john,
MethodObject::ContactCard,
[john_copied_contact_id.as_str()],
Vec::<(&str, &str)>::new(),
)
.await
.destroyed()
.collect::<Vec<_>>(),
[&john_copied_contact_id]
);
// Update John's contact
jane.jmap_update_account(
john,
MethodObject::ContactCard,
[(
&john_contact_id,
json!({
"name": {
"full": "John's Updated Contact",
}
}),
)],
Vec::<(&str, &str)>::new(),
)
.await
.updated(&john_contact_id);
jane.jmap_get_account(
john,
MethodObject::ContactCard,
[JSContactProperty::<Id>::Id, JSContactProperty::Name],
[john_contact_id.as_str()],
)
.await
.list()[0]
.assert_is_equal(json!({
"id": john_contact_id,
"name": {
"full": "John's Updated Contact"
},
}));
// Update John's address book name
jane.jmap_update_account(
john,
MethodObject::AddressBook,
[(
&john_book_id,
json!({
"name": "Jane's version of John's Address Book",
"description": "This is John's address book, but Jane can edit it now"
}),
)],
Vec::<(&str, &str)>::new(),
)
.await
.updated(&john_book_id);
jane.jmap_get_account(
john,
MethodObject::AddressBook,
[
AddressBookProperty::Id,
AddressBookProperty::Name,
AddressBookProperty::Description,
],
[john_book_id.as_str()],
)
.await
.list()[0]
.assert_is_equal(json!({
"id": john_book_id,
"name": "Jane's version of John's Address Book",
"description": "This is John's address book, but Jane can edit it now"
}));
// John should still see the old name
john.jmap_get(
MethodObject::AddressBook,
[
AddressBookProperty::Id,
AddressBookProperty::Name,
AddressBookProperty::Description,
],
[john_book_id.as_str()],
)
.await
.list()[0]
.assert_is_equal(json!({
"id": john_book_id,
"name": "Test #1",
"description": null
}));
// Revoke Jane's access
john.jmap_update(
MethodObject::AddressBook,
[(
&john_book_id,
json!({
format!("shareWith/{jane_id}"): ()
}),
)],
Vec::<(&str, &str)>::new(),
)
.await
.updated(&john_book_id);
john.jmap_get(
MethodObject::AddressBook,
[
AddressBookProperty::Id,
AddressBookProperty::Name,
AddressBookProperty::ShareWith,
],
[john_book_id.as_str()],
)
.await
.list()[0]
.assert_is_equal(json!({
"id": john_book_id,
"name": "Test #1",
"shareWith": {}
}));
// Verify Jane can no longer access the address book or its contacts
assert_eq!(
jane.jmap_get_account(
john,
MethodObject::AddressBook,
Vec::<&str>::new(),
[john_book_id.as_str()],
)
.await
.method_response()
.typ(),
"forbidden"
);
// Verify Jane received a share notification with the updated rights
let response = jane
.jmap_changes(MethodObject::ShareNotification, &jane_share_change_id)
.await;
let changes = response.changes().collect::<Vec<_>>();
assert_eq!(changes.len(), 1);
let share_id = changes[0].as_created();
jane.jmap_get(
MethodObject::ShareNotification,
[
ShareNotificationProperty::Id,
ShareNotificationProperty::ChangedBy,
ShareNotificationProperty::ObjectType,
ShareNotificationProperty::ObjectAccountId,
ShareNotificationProperty::ObjectId,
ShareNotificationProperty::OldRights,
ShareNotificationProperty::NewRights,
ShareNotificationProperty::Name,
],
[share_id],
)
.await
.list()[0]
.assert_is_equal(json!({
"id": &share_id,
"changedBy": {
"principalId": &john_id,
"name": "John Doe",
"email": "[email protected]"
},
"objectType": "AddressBook",
"objectAccountId": &john_id,
"objectId": &john_book_id,
"oldRights": {
"mayRead": true,
"mayWrite": true,
"mayDelete": true,
"mayShare": false
},
"newRights": {
"mayRead": false,
"mayWrite": false,
"mayDelete": false,
"mayShare": false
},
"name": null
}));
// Grant Jane delete access once again
john.jmap_update(
MethodObject::AddressBook,
[(
&john_book_id,
json!({
format!("shareWith/{jane_id}/mayRead"): true,
format!("shareWith/{jane_id}/mayDelete"): true,
}),
)],
Vec::<(&str, &str)>::new(),
)
.await
.updated(&john_book_id);
// Verify Jane can delete the address book
assert_eq!(
jane.jmap_destroy_account(
john,
MethodObject::AddressBook,
[john_book_id.as_str()],
[("onDestroyRemoveContents", true)],
)
.await
.destroyed()
.collect::<Vec<_>>(),
[john_book_id.as_str()]
);
// Destroy all mailboxes
john.destroy_all_addressbooks().await;
jane.destroy_all_addressbooks().await;
test.assert_is_empty().await;
}
+216
View File
@@ -0,0 +1,216 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::utils::{
jmap::{ChangeType, JmapUtils},
server::TestServer,
};
use jmap_proto::{object::addressbook::AddressBookProperty, request::method::MethodObject};
use serde_json::json;
pub async fn test(test: &TestServer) {
println!("Running AddressBook tests...");
let account = test.account("[email protected]");
// Make sure the default address book exists
let response = account
.jmap_get(
MethodObject::AddressBook,
[
AddressBookProperty::Id,
AddressBookProperty::Name,
AddressBookProperty::Description,
AddressBookProperty::SortOrder,
AddressBookProperty::IsSubscribed,
AddressBookProperty::IsDefault,
],
Vec::<&str>::new(),
)
.await;
let list = response.list();
assert_eq!(list.len(), 1);
let default_addressbook_id = list[0].id().to_string();
assert_eq!(
list[0],
json!({
"name": "Stalwart Address Book ([email protected])",
"description": (),
"sortOrder": 0,
"isSubscribed": true,
"isDefault": true,
"id": default_addressbook_id,
})
);
let change_id = response.state();
// Create Address Book
let addressbook_id = account
.jmap_create(
MethodObject::AddressBook,
[json!({
"name": "Test address book",
"description": "My personal address book",
"sortOrder": 1,
"isSubscribed": true
})],
Vec::<(&str, &str)>::new(),
)
.await
.created(0)
.id()
.to_string();
// Validate changes
assert_eq!(
account
.jmap_changes(MethodObject::AddressBook, change_id)
.await
.changes()
.collect::<Vec<_>>(),
[ChangeType::Created(&addressbook_id)]
);
// Get Address Book
let response = account
.jmap_get(
MethodObject::AddressBook,
[
AddressBookProperty::Id,
AddressBookProperty::Name,
AddressBookProperty::Description,
AddressBookProperty::SortOrder,
AddressBookProperty::IsSubscribed,
AddressBookProperty::IsDefault,
],
[&addressbook_id],
)
.await;
assert_eq!(
response.list()[0],
json!({
"name": "Test address book",
"description": "My personal address book",
"sortOrder": 1,
"isSubscribed": true,
"isDefault": false,
"id": addressbook_id,
})
);
// Update Address Book and set it as default
account
.jmap_update(
MethodObject::AddressBook,
[(
addressbook_id.as_str(),
json!({
"name": "Updated address book",
"description": "My updated personal address book",
"sortOrder": 2,
"isSubscribed": false
}),
)],
[("onSuccessSetIsDefault", addressbook_id.as_str())],
)
.await
.updated(&addressbook_id);
// Validate changes
assert_eq!(
account
.jmap_get(
MethodObject::AddressBook,
[
AddressBookProperty::Id,
AddressBookProperty::Name,
AddressBookProperty::Description,
AddressBookProperty::SortOrder,
AddressBookProperty::IsSubscribed,
AddressBookProperty::IsDefault,
],
[&addressbook_id, &default_addressbook_id],
)
.await
.list(),
vec![
json!({
"name": "Updated address book",
"description": "My updated personal address book",
"sortOrder": 2,
"isSubscribed": false,
"isDefault": true,
"id": addressbook_id,
}),
json!({
"name": "Stalwart Address Book ([email protected])",
"description": (),
"sortOrder": 0,
"isSubscribed": true,
"isDefault": false,
"id": default_addressbook_id,
})
]
);
// Create a contact
let _ = account
.jmap_create(
MethodObject::ContactCard,
[json!({
"addressBookIds": {
&addressbook_id: true
},
"name": {
"components": [
{ "kind": "given", "value": "Joe" },
{ "kind": "surname", "value": "Bloggs" }
]
},
"emails": {
"0": {
"address": "[email protected]"
}
}
})],
Vec::<(&str, &str)>::new(),
)
.await
.created(0)
.id();
// Try destroying the address book (should fail)
assert_eq!(
account
.jmap_destroy(
MethodObject::AddressBook,
[&addressbook_id],
Vec::<(&str, &str)>::new(),
)
.await
.not_destroyed(&addressbook_id)
.typ(),
"addressBookHasContents"
);
// Destroy using force
assert_eq!(
account
.jmap_destroy(
MethodObject::AddressBook,
[&addressbook_id],
[("onDestroyRemoveContents", true)],
)
.await
.destroyed()
.collect::<Vec<_>>(),
vec![&addressbook_id]
);
// Destroy all mailboxes
account.destroy_all_addressbooks().await;
test.assert_is_empty().await;
}
File diff suppressed because it is too large Load Diff
+9
View File
@@ -0,0 +1,9 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
pub mod acl;
pub mod addressbook;
pub mod contact;