Merge remote-tracking branch 'upstream/main' into conduwuit-changes

This commit is contained in:
strawberry 2024-09-17 17:55:49 -04:00
commit 9900d06765
6 changed files with 88 additions and 22 deletions

View File

@ -23,7 +23,9 @@ use ruma_events::{
}; };
use serde::{de::Error as _, Deserialize, Serialize}; use serde::{de::Error as _, Deserialize, Serialize};
use super::{v5, DeviceLists, UnreadNotificationsCount}; #[cfg(feature = "unstable-msc4186")]
use super::v5;
use super::{DeviceLists, UnreadNotificationsCount};
const METADATA: Metadata = metadata! { const METADATA: Metadata = metadata! {
method: POST, method: POST,
@ -928,6 +930,7 @@ impl Typing {
} }
} }
#[cfg(feature = "unstable-msc4186")]
impl From<v5::Request> for Request { impl From<v5::Request> for Request {
fn from(value: v5::Request) -> Self { fn from(value: v5::Request) -> Self {
Self { Self {
@ -952,6 +955,7 @@ impl From<v5::Request> for Request {
} }
} }
#[cfg(feature = "unstable-msc4186")]
impl From<v5::request::List> for SyncRequestList { impl From<v5::request::List> for SyncRequestList {
fn from(value: v5::request::List) -> Self { fn from(value: v5::request::List) -> Self {
Self { Self {
@ -974,12 +978,14 @@ impl From<v5::request::List> for SyncRequestList {
} }
} }
#[cfg(feature = "unstable-msc4186")]
impl From<v5::request::RoomDetails> for RoomDetailsConfig { impl From<v5::request::RoomDetails> for RoomDetailsConfig {
fn from(value: v5::request::RoomDetails) -> Self { fn from(value: v5::request::RoomDetails) -> Self {
Self { required_state: value.required_state, timeline_limit: value.timeline_limit } Self { required_state: value.required_state, timeline_limit: value.timeline_limit }
} }
} }
#[cfg(feature = "unstable-msc4186")]
impl From<v5::request::ListFilters> for SyncRequestListFilters { impl From<v5::request::ListFilters> for SyncRequestListFilters {
fn from(value: v5::request::ListFilters) -> Self { fn from(value: v5::request::ListFilters) -> Self {
Self { Self {
@ -990,6 +996,7 @@ impl From<v5::request::ListFilters> for SyncRequestListFilters {
} }
} }
#[cfg(feature = "unstable-msc4186")]
impl From<v5::request::RoomSubscription> for RoomSubscription { impl From<v5::request::RoomSubscription> for RoomSubscription {
fn from(value: v5::request::RoomSubscription) -> Self { fn from(value: v5::request::RoomSubscription) -> Self {
Self { Self {
@ -1000,6 +1007,7 @@ impl From<v5::request::RoomSubscription> for RoomSubscription {
} }
} }
#[cfg(feature = "unstable-msc4186")]
impl From<v5::request::Extensions> for ExtensionsConfig { impl From<v5::request::Extensions> for ExtensionsConfig {
fn from(value: v5::request::Extensions) -> Self { fn from(value: v5::request::Extensions) -> Self {
Self { Self {
@ -1014,6 +1022,7 @@ impl From<v5::request::Extensions> for ExtensionsConfig {
} }
} }
#[cfg(feature = "unstable-msc4186")]
impl From<v5::request::ToDevice> for ToDeviceConfig { impl From<v5::request::ToDevice> for ToDeviceConfig {
fn from(value: v5::request::ToDevice) -> Self { fn from(value: v5::request::ToDevice) -> Self {
Self { Self {
@ -1026,18 +1035,21 @@ impl From<v5::request::ToDevice> for ToDeviceConfig {
} }
} }
#[cfg(feature = "unstable-msc4186")]
impl From<v5::request::E2EE> for E2EEConfig { impl From<v5::request::E2EE> for E2EEConfig {
fn from(value: v5::request::E2EE) -> Self { fn from(value: v5::request::E2EE) -> Self {
Self { enabled: value.enabled } Self { enabled: value.enabled }
} }
} }
#[cfg(feature = "unstable-msc4186")]
impl From<v5::request::AccountData> for AccountDataConfig { impl From<v5::request::AccountData> for AccountDataConfig {
fn from(value: v5::request::AccountData) -> Self { fn from(value: v5::request::AccountData) -> Self {
Self { enabled: value.enabled, lists: value.lists, rooms: value.rooms } Self { enabled: value.enabled, lists: value.lists, rooms: value.rooms }
} }
} }
#[cfg(feature = "unstable-msc4186")]
impl From<v5::request::Receipts> for ReceiptsConfig { impl From<v5::request::Receipts> for ReceiptsConfig {
fn from(value: v5::request::Receipts) -> Self { fn from(value: v5::request::Receipts) -> Self {
Self { Self {
@ -1048,6 +1060,7 @@ impl From<v5::request::Receipts> for ReceiptsConfig {
} }
} }
#[cfg(feature = "unstable-msc4186")]
impl From<v5::request::ReceiptsRoom> for RoomReceiptConfig { impl From<v5::request::ReceiptsRoom> for RoomReceiptConfig {
fn from(value: v5::request::ReceiptsRoom) -> Self { fn from(value: v5::request::ReceiptsRoom) -> Self {
match value { match value {
@ -1057,6 +1070,7 @@ impl From<v5::request::ReceiptsRoom> for RoomReceiptConfig {
} }
} }
#[cfg(feature = "unstable-msc4186")]
impl From<v5::request::Typing> for TypingConfig { impl From<v5::request::Typing> for TypingConfig {
fn from(value: v5::request::Typing) -> Self { fn from(value: v5::request::Typing) -> Self {
Self { enabled: value.enabled, lists: value.lists, rooms: value.rooms } Self { enabled: value.enabled, lists: value.lists, rooms: value.rooms }

View File

@ -18,6 +18,11 @@ Improvements:
- Stabilize support for muting in VoIP calls, according to Matrix 1.11 - Stabilize support for muting in VoIP calls, according to Matrix 1.11
- All the root `Any*EventContent` types now have a `EventContentFromType` implementations - All the root `Any*EventContent` types now have a `EventContentFromType` implementations
automatically derived by the `event_enum!` macro. automatically derived by the `event_enum!` macro.
- `CallMemberEventContent` now supports two different formats: Session memberships and Legacy memberships.
The new format (Session) is required to reliably display the call member count (reliable call member events).
`CallMemberEventContent` is now an enum to model the two different formats.
- `CallMemberStateKey` (instead of `OwnedUserId`) is now used as the state key type for `CallMemberEventContent`.
This guarantees correct formatting of the event key.
Breaking changes: Breaking changes:

View File

@ -11,7 +11,7 @@ mod member_state_key;
pub use focus::*; pub use focus::*;
pub use member_data::*; pub use member_data::*;
pub use member_state_key::*; pub use member_state_key::*;
use ruma_common::MilliSecondsSinceUnixEpoch; use ruma_common::{MilliSecondsSinceUnixEpoch, OwnedDeviceId};
use ruma_macros::{EventContent, StringEnum}; use ruma_macros::{EventContent, StringEnum};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -56,7 +56,7 @@ impl CallMemberEventContent {
/// Creates a new [`CallMemberEventContent`] with [`SessionMembershipData`]. /// Creates a new [`CallMemberEventContent`] with [`SessionMembershipData`].
pub fn new( pub fn new(
application: Application, application: Application,
device_id: String, device_id: OwnedDeviceId,
focus_active: ActiveFocus, focus_active: ActiveFocus,
foci_preferred: Vec<Focus>, foci_preferred: Vec<Focus>,
created_ts: Option<MilliSecondsSinceUnixEpoch>, created_ts: Option<MilliSecondsSinceUnixEpoch>,
@ -234,8 +234,8 @@ mod tests {
use assert_matches2::assert_matches; use assert_matches2::assert_matches;
use ruma_common::{ use ruma_common::{
device_id, user_id, MilliSecondsSinceUnixEpoch as TS, OwnedEventId, OwnedRoomId, device_id, owned_device_id, user_id, MilliSecondsSinceUnixEpoch as TS, OwnedEventId,
OwnedUserId, OwnedRoomId, OwnedUserId,
}; };
use serde_json::{from_value as from_json_value, json, Value as JsonValue}; use serde_json::{from_value as from_json_value, json, Value as JsonValue};
@ -257,7 +257,7 @@ mod tests {
call_id: "123456".to_owned(), call_id: "123456".to_owned(),
scope: CallScope::Room, scope: CallScope::Room,
}), }),
device_id: "ABCDE".to_owned(), device_id: owned_device_id!("ABCDE"),
expires: Duration::from_secs(3600), expires: Duration::from_secs(3600),
foci_active: vec![Focus::Livekit(LivekitFocus { foci_active: vec![Focus::Livekit(LivekitFocus {
alias: "1".to_owned(), alias: "1".to_owned(),
@ -274,7 +274,7 @@ mod tests {
call_id: "123456".to_owned(), call_id: "123456".to_owned(),
scope: CallScope::Room, scope: CallScope::Room,
}), }),
"ABCDE".to_owned(), owned_device_id!("ABCDE"),
ActiveFocus::Livekit(ActiveLivekitFocus { ActiveFocus::Livekit(ActiveLivekitFocus {
focus_selection: FocusSelection::OldestMembership, focus_selection: FocusSelection::OldestMembership,
}), }),
@ -354,7 +354,7 @@ mod tests {
call_id: "123456".to_owned(), call_id: "123456".to_owned(),
scope: CallScope::Room, scope: CallScope::Room,
}), }),
"THIS_DEVICE".to_owned(), owned_device_id!("THIS_DEVICE"),
ActiveFocus::Livekit(ActiveLivekitFocus { ActiveFocus::Livekit(ActiveLivekitFocus {
focus_selection: FocusSelection::OldestMembership, focus_selection: FocusSelection::OldestMembership,
}), }),
@ -404,7 +404,7 @@ mod tests {
call_id: "123456".to_owned(), call_id: "123456".to_owned(),
scope: CallScope::Room, scope: CallScope::Room,
}), }),
device_id: "THIS_DEVICE".to_owned(), device_id: owned_device_id!("THIS_DEVICE"),
expires: Duration::from_secs(3600), expires: Duration::from_secs(3600),
foci_active: vec![Focus::Livekit(LivekitFocus { foci_active: vec![Focus::Livekit(LivekitFocus {
alias: "room1".to_owned(), alias: "room1".to_owned(),
@ -418,7 +418,7 @@ mod tests {
call_id: "".to_owned(), call_id: "".to_owned(),
scope: CallScope::Room, scope: CallScope::Room,
}), }),
device_id: "OTHER_DEVICE".to_owned(), device_id: owned_device_id!("OTHER_DEVICE"),
expires: Duration::from_secs(3600), expires: Duration::from_secs(3600),
foci_active: vec![Focus::Livekit(LivekitFocus { foci_active: vec![Focus::Livekit(LivekitFocus {
alias: "room2".to_owned(), alias: "room2".to_owned(),
@ -526,7 +526,7 @@ mod tests {
call_id: "".to_owned(), call_id: "".to_owned(),
scope: CallScope::Room, scope: CallScope::Room,
}), }),
device_id: "THIS_DEVICE".to_owned(), device_id: owned_device_id!("THIS_DEVICE"),
foci_preferred: [Focus::Livekit(LivekitFocus { foci_preferred: [Focus::Livekit(LivekitFocus {
alias: "room1".to_owned(), alias: "room1".to_owned(),
service_url: "https://livekit1.com".to_owned(), service_url: "https://livekit1.com".to_owned(),

View File

@ -5,7 +5,7 @@
use std::time::Duration; use std::time::Duration;
use as_variant::as_variant; use as_variant::as_variant;
use ruma_common::MilliSecondsSinceUnixEpoch; use ruma_common::{DeviceId, MilliSecondsSinceUnixEpoch, OwnedDeviceId};
use ruma_macros::StringEnum; use ruma_macros::StringEnum;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tracing::warn; use tracing::warn;
@ -41,7 +41,7 @@ impl<'a> MembershipData<'a> {
} }
/// The device id of this membership. /// The device id of this membership.
pub fn device_id(&self) -> &String { pub fn device_id(&self) -> &DeviceId {
match self { match self {
MembershipData::Legacy(data) => &data.device_id, MembershipData::Legacy(data) => &data.device_id,
MembershipData::Session(data) => &data.device_id, MembershipData::Session(data) => &data.device_id,
@ -121,7 +121,7 @@ pub struct LegacyMembershipData {
/// The device id of this membership. /// The device id of this membership.
/// ///
/// The same user can join with their phone/computer. /// The same user can join with their phone/computer.
pub device_id: String, pub device_id: OwnedDeviceId,
/// The duration in milliseconds relative to the time this membership joined /// The duration in milliseconds relative to the time this membership joined
/// during which the membership is valid. /// during which the membership is valid.
@ -190,7 +190,7 @@ pub struct LegacyMembershipDataInit {
/// The device id of this membership. /// The device id of this membership.
/// ///
/// The same user can join with their phone/computer. /// The same user can join with their phone/computer.
pub device_id: String, pub device_id: OwnedDeviceId,
/// The duration in milliseconds relative to the time this membership joined /// The duration in milliseconds relative to the time this membership joined
/// during which the membership is valid. /// during which the membership is valid.
@ -236,7 +236,7 @@ pub struct SessionMembershipData {
/// The device id of this membership. /// The device id of this membership.
/// ///
/// The same user can join with their phone/computer. /// The same user can join with their phone/computer.
pub device_id: String, pub device_id: OwnedDeviceId,
/// A list of the foci that this membership proposes to use. /// A list of the foci that this membership proposes to use.
pub foci_preferred: Vec<Focus>, pub foci_preferred: Vec<Focus>,

View File

@ -16,11 +16,11 @@ pub struct CallMemberStateKey {
impl CallMemberStateKey { impl CallMemberStateKey {
/// Constructs a new CallMemberStateKey there are three possible formats: /// Constructs a new CallMemberStateKey there are three possible formats:
/// - "_{UserId}_{DeviceId}" example: "_@test:user.org_DEVICE". `device_id`: Some`, `underscore: /// - `_{UserId}_{DeviceId}` example: `_@test:user.org_DEVICE`. `device_id: Some`, `underscore:
/// true` /// true`
/// - "{UserId}_{DeviceId}" example: "@test:user.org_DEVICE". `device_id`: Some`, `underscore: /// - `{UserId}_{DeviceId}` example: `@test:user.org_DEVICE`. `device_id: Some`, `underscore:
/// false` /// false`
/// - "{UserId}" example example: "@test:user.org". `device_id`: None`, underscore is ignored: /// - `{UserId}` example: `@test:user.org`. `device_id: None`, underscore is ignored:
/// `underscore: false|true` /// `underscore: false|true`
/// ///
/// Dependent on the parameters the correct CallMemberStateKey will be constructed. /// Dependent on the parameters the correct CallMemberStateKey will be constructed.

View File

@ -174,12 +174,20 @@ fn try_from_multipart_mixed_response<T: AsRef<[u8]>>(
let mut full_boundary = Vec::with_capacity(boundary.len() + 4); let mut full_boundary = Vec::with_capacity(boundary.len() + 4);
full_boundary.extend_from_slice(b"\r\n--"); full_boundary.extend_from_slice(b"\r\n--");
full_boundary.extend_from_slice(boundary); full_boundary.extend_from_slice(boundary);
let full_boundary_no_crlf = full_boundary.strip_prefix(b"\r\n").unwrap();
let mut boundaries = memchr::memmem::find_iter(body, &full_boundary); let mut boundaries = memchr::memmem::find_iter(body, &full_boundary);
let metadata_start = boundaries.next().ok_or_else(|| { let metadata_start = if body.starts_with(full_boundary_no_crlf) {
MultipartMixedDeserializationError::MissingBodyParts { expected: 2, found: 0 } // If there is no preamble before the first boundary, it may omit the
})? + full_boundary.len(); // preceding CRLF.
full_boundary_no_crlf.len()
} else {
boundaries.next().ok_or_else(|| MultipartMixedDeserializationError::MissingBodyParts {
expected: 2,
found: 0,
})? + full_boundary.len()
};
let metadata_end = boundaries.next().ok_or_else(|| { let metadata_end = boundaries.next().ok_or_else(|| {
MultipartMixedDeserializationError::MissingBodyParts { expected: 2, found: 0 } MultipartMixedDeserializationError::MissingBodyParts { expected: 2, found: 0 }
})?; })?;
@ -417,6 +425,15 @@ mod tests {
.unwrap(); .unwrap();
try_from_multipart_mixed_response(response).unwrap_err(); try_from_multipart_mixed_response(response).unwrap_err();
// Boundary without CRLF with preamble.
let body = "foo--abcdef\r\n\r\n{}\r\n--abcdef\r\n\r\nsome plain text\r\n--abcdef--";
let response = http::Response::builder()
.header(http::header::CONTENT_TYPE, "multipart/mixed; boundary=abcdef")
.body(body)
.unwrap();
try_from_multipart_mixed_response(response).unwrap_err();
} }
#[test] #[test]
@ -483,6 +500,36 @@ mod tests {
assert_eq!(file_content.content_type.unwrap(), "text/plain"); assert_eq!(file_content.content_type.unwrap(), "text/plain");
assert_eq!(file_content.content_disposition, None); assert_eq!(file_content.content_disposition, None);
// No leading CRLF (and no preamble)
let body = "--abcdef\r\n\r\n{}\r\n--abcdef\r\n\r\nsome plain text\r\n--abcdef--";
let response = http::Response::builder()
.header(http::header::CONTENT_TYPE, "multipart/mixed; boundary=abcdef")
.body(body)
.unwrap();
let (_metadata, content) = try_from_multipart_mixed_response(response).unwrap();
assert_matches!(content, FileOrLocation::File(file_content));
assert_eq!(file_content.file, b"some plain text");
assert_eq!(file_content.content_type, None);
assert_eq!(file_content.content_disposition, None);
// Boundary text in preamble, but no leading CRLF, so it should be
// ignored.
let body =
"foo--abcdef\r\n--abcdef\r\n\r\n{}\r\n--abcdef\r\n\r\nsome plain text\r\n--abcdef--";
let response = http::Response::builder()
.header(http::header::CONTENT_TYPE, "multipart/mixed; boundary=abcdef")
.body(body)
.unwrap();
let (_metadata, content) = try_from_multipart_mixed_response(response).unwrap();
assert_matches!(content, FileOrLocation::File(file_content));
assert_eq!(file_content.file, b"some plain text");
assert_eq!(file_content.content_type, None);
assert_eq!(file_content.content_disposition, None);
// No body part headers. // No body part headers.
let body = "\r\n--abcdef\r\n\r\n{}\r\n--abcdef\r\n\r\nsome plain text\r\n--abcdef--"; let body = "\r\n--abcdef\r\n\r\n{}\r\n--abcdef\r\n\r\nsome plain text\r\n--abcdef--";
let response = http::Response::builder() let response = http::Response::builder()