mirror of
https://forgejo.ellis.link/continuwuation/continuwuity/
synced 2026-07-12 08:38:52 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9aacbf2650 | |||
| b1a3bcfb88 | |||
| 3c2274b9e7 |
+34
@@ -0,0 +1,34 @@
|
||||
# Roadmap for PDU versioning
|
||||
|
||||
Currently, PDUs are one monolithic `PduEvent` struct which applies a one-size-fits-all approach to PDUs, however as we
|
||||
support a wide range of room versions, this only just about currently works, and will cause more and more problems as
|
||||
time progresses. This branch will split the `PduEvent` struct into multiple versioned structs that represent the proper
|
||||
format for appropriate room versions, and will drop the `Event` trait in favour of Ruma's `ruma::state_res::Event`
|
||||
trait.
|
||||
|
||||
This will require a lot of work, and since I forget everything the moment I look out the window, here be a list (waow):
|
||||
|
||||
- [x] Split `PduEvent` into multiple versioned structs, implementing `ruma::state_res::Event` for each of them.
|
||||
- [ ] Refactor out all references to `PduEvent`.
|
||||
- [ ] Refactor out all references to `conduwuit_core::event::Event` trait in favour of `ruma::state_res::Event`.
|
||||
- [ ] Centralise PDU struct creation in a way that allows us to greater control the deserialization process.
|
||||
- Developers can't be trusted to read documentation so we can't store the PDU metadata (things like event ID and
|
||||
rejection status) in the PDU struct itself, as this may end up being leaked to consumers. Instead, PDU metadata
|
||||
should be stored seprately from the PDU struct itself, but needs to be strongly associated with it (i.e. fetched
|
||||
at the same time, always returned together, etc). This may take a form similar to
|
||||
`StoredPdu<T: Event> { pdu: T, metadata: PduMetadata }`.
|
||||
- [ ] Stop storing generated data in the `unsigned` field, but instead calculate it on demand(?).
|
||||
- Storing generated data in the `unsigned` field results in:
|
||||
- History visibility cannot be enforced on `prev_content` or `redacted_because`.
|
||||
- `age` is manually calculated but only sometimes.
|
||||
- `membership` is not provided at all.
|
||||
- `prev_content` can leak redacted state event content ([#1103]).
|
||||
- `prev_content` cannot easily be populated for backfilled events, leaving them contextless.
|
||||
- It is easy to accidentally leak `transaction_id`.
|
||||
|
||||
Once `PduEvent` is gone, we can then do the following lovely things:
|
||||
|
||||
- Drop our local state resolution logic and event authorisation logic and instead use Ruma's, which naturally receives
|
||||
more love and attention. Our current `Event` trait and `PduEvent` implementations are incompatible with what Ruma
|
||||
needs, and do not store the appropriate information required.
|
||||
- There's something else but I forgot just trust me
|
||||
@@ -1,9 +1,11 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use assign::assign;
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Pdu, Result, debug_info, debug_warn, err,
|
||||
matrix::{event::gen_event_id, pdu::PartialPdu},
|
||||
pdu::{AnyPDU, metadata::PduMetadata, new_pdu},
|
||||
utils::{self, FutureBoolExt, future::ReadyEqExt},
|
||||
warn,
|
||||
};
|
||||
@@ -216,7 +218,7 @@ pub async fn remote_leave_room<S: ::std::hash::BuildHasher>(
|
||||
room_id: &RoomId,
|
||||
reason: Option<String>,
|
||||
mut servers: HashSet<OwnedServerName, S>,
|
||||
) -> Result<Pdu> {
|
||||
) -> Result<AnyPDU> {
|
||||
let mut make_leave_response_and_server =
|
||||
Err!(BadServerResponse("No remote server available to assist in leaving {room_id}."));
|
||||
|
||||
@@ -406,9 +408,14 @@ pub async fn remote_leave_room<S: ::std::hash::BuildHasher>(
|
||||
.outlier
|
||||
.add_pdu_outlier(&event_id, &leave_event);
|
||||
|
||||
let leave_pdu = Pdu::from_id_val(&event_id, leave_event).map_err(|e| {
|
||||
err!(BadServerResponse("Invalid leave PDU received during federated leave: {e:?}"))
|
||||
})?;
|
||||
let leave_pdu = new_pdu(
|
||||
room_version_rules.into(),
|
||||
PduMetadata::new(event_id),
|
||||
serde_json::to_value(leave_event)?,
|
||||
)?;
|
||||
// let leave_pdu = Pdu::from_id_val(&event_id, leave_event).map_err(|e| {
|
||||
// err!(BadServerResponse("Invalid leave PDU received during federated leave:
|
||||
// {e:?}")) })?;
|
||||
|
||||
Ok(leave_pdu)
|
||||
}
|
||||
|
||||
@@ -143,8 +143,6 @@ fn as_mut_pdu(&mut self) -> &mut Pdu { unimplemented!("not a mutable Pdu") }
|
||||
|
||||
fn into_pdu(self) -> Pdu;
|
||||
|
||||
fn is_owned(&self) -> bool;
|
||||
|
||||
//
|
||||
// Canonical properties
|
||||
//
|
||||
@@ -182,12 +180,12 @@ fn as_mut_pdu(&mut self) -> &mut Pdu { unimplemented!("not a mutable Pdu") }
|
||||
fn state_key(&self) -> Option<&str>;
|
||||
|
||||
/// The event type.
|
||||
// #[deprecated]
|
||||
fn kind(&self) -> &TimelineEventType;
|
||||
|
||||
/// Metadata container; peer-trusted only.
|
||||
fn unsigned(&self) -> Option<&RawJsonValue>;
|
||||
|
||||
//#[deprecated]
|
||||
#[inline]
|
||||
fn event_type(&self) -> &TimelineEventType { self.kind() }
|
||||
}
|
||||
|
||||
+195
-12
@@ -1,19 +1,25 @@
|
||||
mod count;
|
||||
pub mod hashes;
|
||||
mod id;
|
||||
pub mod metadata;
|
||||
mod partial;
|
||||
mod raw_id;
|
||||
mod redact;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod unsigned;
|
||||
pub mod v2;
|
||||
pub mod v3;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use assign::assign;
|
||||
use ruma::{
|
||||
CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId,
|
||||
OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, UInt, UserId, events::TimelineEventType,
|
||||
OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerSignatures, UInt, UserId, event_id,
|
||||
events::TimelineEventType, room_version_rules::RoomVersionRules, state_res::Event,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
|
||||
pub use self::{
|
||||
@@ -23,10 +29,193 @@
|
||||
partial::PartialPdu,
|
||||
raw_id::*,
|
||||
};
|
||||
use super::{Event, StateKey};
|
||||
use crate::Result;
|
||||
use super::StateKey;
|
||||
use crate::{
|
||||
Err, Result,
|
||||
pdu::{hashes::Hashes, metadata::PduMetadata},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum EventFormatVersion {
|
||||
/// V1 represents the PDU format for rooms v1 and v2.
|
||||
V1,
|
||||
/// V2 represents the PDU format for rooms v3-v11.
|
||||
V2,
|
||||
/// V3 represents the PDU format for rooms v12+.
|
||||
V3,
|
||||
}
|
||||
|
||||
impl From<RoomVersionRules> for EventFormatVersion {
|
||||
fn from(rules: RoomVersionRules) -> Self {
|
||||
// TODO: this is hacky because ruma doesn't actually define event versions in an
|
||||
// enum, but does define consts with the appropriate rules.
|
||||
if rules.event_format.require_event_id {
|
||||
Self::V1
|
||||
} else if rules.event_format.require_room_create_room_id {
|
||||
Self::V2
|
||||
} else {
|
||||
Self::V3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait that allows conversion from a generic PDU to a concrete PDU type.
|
||||
pub trait ConcretePDU: ruma::state_res::Event<Id = OwnedEventId> {
|
||||
/// The concrete type for this PDU (`v2::PDU`, `v3::PDU`, etc)
|
||||
type Concrete;
|
||||
|
||||
/// Get a reference to the concrete PDU type.
|
||||
fn to_concrete(&self) -> &Self::Concrete;
|
||||
|
||||
/// Get the value of the concrete PDU type, consuming.
|
||||
fn into_concrete(self) -> Self::Concrete;
|
||||
}
|
||||
|
||||
pub fn new_pdu<E, C, P, JSON>(
|
||||
format: EventFormatVersion,
|
||||
metadata: PduMetadata,
|
||||
json: serde_json::Value,
|
||||
) -> Result<E>
|
||||
where
|
||||
E: ConcretePDU<Concrete = C>,
|
||||
JSON: DeserializeOwned,
|
||||
{
|
||||
match format {
|
||||
| EventFormatVersion::V2 =>
|
||||
Ok(assign!(v2::PDU::try_from(json)?, {internal_metadata: metadata})),
|
||||
| EventFormatVersion::V3 =>
|
||||
Ok(assign!(v3::PDU::try_from(json)?, {internal_metadata: metadata})),
|
||||
| _ => Err!("Unsupported event format: {format:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder type that specifies every field present on any PDU type, allowing
|
||||
/// it to be used to create any PDU version with the appropriate function.
|
||||
///
|
||||
/// See `create_pdu`.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct CommonPDUBuilder {
|
||||
pub auth_events: Option<Vec<OwnedEventId>>,
|
||||
pub content: Option<Box<RawJsonValue>>,
|
||||
pub depth: Option<UInt>,
|
||||
pub hashes: Option<Hashes>,
|
||||
pub origin_server_ts: Option<MilliSecondsSinceUnixEpoch>,
|
||||
pub prev_events: Option<Vec<OwnedEventId>>,
|
||||
pub redacts: Option<OwnedEventId>,
|
||||
pub room_id: Option<OwnedRoomId>,
|
||||
pub sender: Option<OwnedUserId>,
|
||||
pub signatures: Option<ServerSignatures>,
|
||||
pub state_key: Option<String>,
|
||||
pub event_type: Option<TimelineEventType>,
|
||||
pub unsigned: Option<Box<RawJsonValue>>,
|
||||
pub internal_metadata: Option<PduMetadata>,
|
||||
}
|
||||
|
||||
/// Creates a new PDU based on the given version and builder.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use conduwuit_core::pdu::{CommonPDUBuilder, create_pdu}
|
||||
///
|
||||
/// let room_version_rules = RoomVersionRules::V10; // This usually comes dynamically.
|
||||
/// let builder = CommonPDUBuilder {
|
||||
/// auth_events: Some(vec![event_id!("$auth_event_id").to_owned()]),
|
||||
/// content: Some(Box::new(serde_json::json!({"key": "value"}))),
|
||||
/// depth: Some(1),
|
||||
/// origin_server_ts: Some(MilliSecondsSinceUnixEpoch(123456789)),
|
||||
/// prev_events: Some(vec![event_id!("$prev_event_id").to_owned()]),
|
||||
/// room_id: Some(room_id!("!room_id").to_owned()),
|
||||
/// sender: Some(user_id!("@sender:example.com").to_owned()),
|
||||
/// event_type: Some(TimelineEventType::RoomMessage),
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
/// let pdu = create_pdu(
|
||||
/// room_version_rules.into(),
|
||||
/// builder,
|
||||
/// ).expect("PDU creation must succeed");
|
||||
/// ```
|
||||
pub fn create_pdu<C>(version: EventFormatVersion, builder: CommonPDUBuilder) -> Result<C> {
|
||||
match version {
|
||||
| EventFormatVersion::V2 => Ok(create_v2_pdu(builder)),
|
||||
| EventFormatVersion::V3 => Ok(create_v3_pdu(builder)),
|
||||
| _ => Err!("Unsupported event format: {version:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_v2_pdu<C>(builder: CommonPDUBuilder) -> v2::PDU {
|
||||
v2::PDU {
|
||||
auth_events: builder
|
||||
.auth_events
|
||||
.expect("auth_events must be provided for a V2 PDU"),
|
||||
content: builder
|
||||
.content
|
||||
.expect("content must be provided for a V2 PDU"),
|
||||
depth: builder.depth.expect("depth must be provided for a V2 PDU"),
|
||||
hashes: builder.hashes.unwrap_or_default(), /* We allow default here since we might not
|
||||
* have hashed yet */
|
||||
origin_server_ts: builder
|
||||
.origin_server_ts
|
||||
.expect("origin_server_ts must be provided for a V2 PDU"),
|
||||
prev_events: builder
|
||||
.prev_events
|
||||
.expect("prev_events must be provided for a V2 PDU"),
|
||||
redacts: builder.redacts,
|
||||
room_id: builder
|
||||
.room_id
|
||||
.expect("room_id must be provided for a V2 PDU"),
|
||||
sender: builder
|
||||
.sender
|
||||
.expect("sender must be provided for a V2 PDU"),
|
||||
signatures: builder.signatures.unwrap_or_default(), /* We allow default here since we
|
||||
* might not have signed yet */
|
||||
state_key: builder.state_key,
|
||||
event_type: builder
|
||||
.event_type
|
||||
.expect("event_type must be provided for a V2 PDU"),
|
||||
unsigned: builder.unsigned,
|
||||
internal_metadata: builder
|
||||
.internal_metadata
|
||||
.unwrap_or_else(|| PduMetadata::new(event_id!("$unknown").to_owned())),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_v3_pdu(builder: CommonPDUBuilder) -> v3::PDU {
|
||||
v3::PDU {
|
||||
auth_events: builder
|
||||
.auth_events
|
||||
.expect("auth_events must be provided for a V3 PDU"),
|
||||
content: builder
|
||||
.content
|
||||
.expect("content must be provided for a V3 PDU"),
|
||||
depth: builder.depth.expect("depth must be provided for a V3 PDU"),
|
||||
hashes: builder.hashes.unwrap_or_default(), /* We allow default here since we might not
|
||||
* have hashed yet */
|
||||
origin_server_ts: builder
|
||||
.origin_server_ts
|
||||
.expect("origin_server_ts must be provided for a V3 PDU"),
|
||||
prev_events: builder
|
||||
.prev_events
|
||||
.expect("prev_events must be provided for a V3 PDU"),
|
||||
room_id: builder.room_id,
|
||||
sender: builder
|
||||
.sender
|
||||
.expect("sender must be provided for a V3 PDU"),
|
||||
signatures: builder.signatures.unwrap_or_default(), /* We allow default here since we
|
||||
* might not have signed yet */
|
||||
state_key: builder.state_key,
|
||||
event_type: builder
|
||||
.event_type
|
||||
.expect("event_type must be provided for a V3 PDU"),
|
||||
unsigned: builder.unsigned,
|
||||
internal_metadata: builder
|
||||
.internal_metadata
|
||||
.unwrap_or_else(|| PduMetadata::new(event_id!("$unknown").to_owned())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Persistent Data Unit (Event)
|
||||
#[deprecated(note = "Use `v2::PDU` or `v3::PDU` or `ruma::state_res::Event` instead")]
|
||||
#[derive(Clone, Deserialize, Serialize, Debug)]
|
||||
pub struct Pdu {
|
||||
pub event_id: OwnedEventId,
|
||||
@@ -85,7 +274,7 @@ pub fn from_id_val(event_id: &EventId, mut json: CanonicalJsonObject) -> Result<
|
||||
}
|
||||
}
|
||||
|
||||
impl Event for Pdu {
|
||||
impl crate::Event for Pdu {
|
||||
#[inline]
|
||||
fn auth_events(&self) -> impl DoubleEndedIterator<Item = &EventId> + Clone + Send + '_ {
|
||||
self.auth_events.iter().map(AsRef::as_ref)
|
||||
@@ -151,12 +340,9 @@ fn as_pdu(&self) -> &Pdu { self }
|
||||
|
||||
#[inline]
|
||||
fn into_pdu(self) -> Pdu { self }
|
||||
|
||||
#[inline]
|
||||
fn is_owned(&self) -> bool { true }
|
||||
}
|
||||
|
||||
impl Event for &Pdu {
|
||||
impl crate::Event for &Pdu {
|
||||
#[inline]
|
||||
fn auth_events(&self) -> impl DoubleEndedIterator<Item = &EventId> + Clone + Send + '_ {
|
||||
self.auth_events.iter().map(AsRef::as_ref)
|
||||
@@ -219,9 +405,6 @@ fn as_pdu(&self) -> &Pdu { self }
|
||||
|
||||
#[inline]
|
||||
fn into_pdu(self) -> Pdu { self.clone() }
|
||||
|
||||
#[inline]
|
||||
fn is_owned(&self) -> bool { false }
|
||||
}
|
||||
|
||||
/// Prevent derived equality which wouldn't limit itself to event_id
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// The content hash map for a PDU.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, Default)]
|
||||
pub struct Hashes {
|
||||
/// The SHA256 content hash.
|
||||
#[serde(default, skip_serializing_if = "String::is_empty")]
|
||||
pub sha256: String,
|
||||
|
||||
/// Any other hashes present, if any.
|
||||
#[serde(flatten, default, skip_serializing_if = "HashMap::is_empty")]
|
||||
pub other: HashMap<String, String>,
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
use ruma::{EventId, OwnedEventId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct PduMetadata {
|
||||
/// This event's calculated event ID.
|
||||
pub event_id: OwnedEventId,
|
||||
|
||||
/// Indicates whether the event has been rejected under auth rules.
|
||||
pub rejected: bool,
|
||||
}
|
||||
|
||||
impl PduMetadata {
|
||||
/// Creates a new PduMetadata instance with the minimum required fields.
|
||||
pub fn new(event_id: OwnedEventId) -> Self { Self { event_id, rejected: false } }
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
use ruma::{
|
||||
CanonicalJsonObject, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId,
|
||||
OwnedUserId, RoomId, ServerSignatures, UInt, UserId, events::TimelineEventType,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
|
||||
use crate::{
|
||||
Pdu,
|
||||
pdu::{hashes::Hashes, metadata::PduMetadata},
|
||||
};
|
||||
|
||||
/// Represents a persistent data unit (PDU) version 2, introduced in room
|
||||
/// version 3, up until room version 11.
|
||||
///
|
||||
/// Spec: https://spec.matrix.org/v1.19/rooms/v3/#event-format
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct PDU {
|
||||
/// The event IDs which authorise this event. Between 0 and 10 entries.
|
||||
pub auth_events: Vec<OwnedEventId>,
|
||||
/// This event's content.
|
||||
pub content: Box<RawJsonValue>,
|
||||
/// The depth of this event.
|
||||
pub depth: UInt,
|
||||
/// The content hashes for this event.
|
||||
pub hashes: Hashes,
|
||||
/// Timestamp in milliseconds on origin homeserver when this event was
|
||||
/// created.
|
||||
pub origin_server_ts: MilliSecondsSinceUnixEpoch,
|
||||
/// The event IDs which immediately precede this event. Between 0 and 20
|
||||
/// entries.
|
||||
pub prev_events: Vec<OwnedEventId>,
|
||||
/// For redaction events, the ID of the event being redacted.
|
||||
///
|
||||
/// Not used in v11.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub redacts: Option<OwnedEventId>,
|
||||
/// The room in which this event resides.
|
||||
pub room_id: OwnedRoomId,
|
||||
/// The user who sent this event.
|
||||
pub sender: OwnedUserId,
|
||||
/// The signatures on this event.
|
||||
pub signatures: ServerSignatures,
|
||||
/// The state key for this PDU, if any.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub state_key: Option<String>,
|
||||
/// The type of this PDU.
|
||||
#[serde(rename = "type")]
|
||||
pub event_type: TimelineEventType,
|
||||
/// Additional data added by the origin server but not covered by the
|
||||
/// signatures.
|
||||
///
|
||||
/// This should be `None` when receiving from and transmitting over
|
||||
/// federation.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub unsigned: Option<Box<RawJsonValue>>,
|
||||
|
||||
/// Internal metadata that is important to the PDU but should not be
|
||||
/// serialized into the final event.
|
||||
///
|
||||
/// This field MUST NOT be sent to clients or federation.
|
||||
pub internal_metadata: PduMetadata,
|
||||
}
|
||||
|
||||
impl PDU {
|
||||
#[must_use]
|
||||
pub fn internal_metadata(&self) -> &PduMetadata { &self.internal_metadata }
|
||||
}
|
||||
|
||||
impl crate::pdu::ConcretePDU for PDU {
|
||||
type Concrete = PDU;
|
||||
|
||||
fn to_concrete(&self) -> &Self::Concrete { &self }
|
||||
|
||||
fn into_concrete(self) -> Self::Concrete { self }
|
||||
}
|
||||
|
||||
impl ruma::state_res::Event for PDU {
|
||||
type Id = OwnedEventId;
|
||||
|
||||
fn event_id(&self) -> &Self::Id { &self.internal_metadata.event_id }
|
||||
|
||||
fn room_id(&self) -> Option<&RoomId> { Some(&self.room_id) }
|
||||
|
||||
fn sender(&self) -> &UserId { &self.sender }
|
||||
|
||||
fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch { self.origin_server_ts }
|
||||
|
||||
fn event_type(&self) -> &TimelineEventType { &self.event_type }
|
||||
|
||||
fn content(&self) -> &RawJsonValue { self.content.as_ref() }
|
||||
|
||||
fn state_key(&self) -> Option<&str> { self.state_key.as_deref() }
|
||||
|
||||
fn prev_events(&self) -> Box<dyn DoubleEndedIterator<Item = &Self::Id> + '_> {
|
||||
Box::new(self.prev_events.iter())
|
||||
}
|
||||
|
||||
fn auth_events(&self) -> Box<dyn DoubleEndedIterator<Item = &Self::Id> + '_> {
|
||||
Box::new(self.auth_events.iter())
|
||||
}
|
||||
|
||||
fn redacts(&self) -> Option<&Self::Id> { self.redacts.as_ref() }
|
||||
|
||||
fn rejected(&self) -> bool { self.internal_metadata().rejected }
|
||||
}
|
||||
|
||||
impl crate::Event for PDU {
|
||||
fn as_pdu(&self) -> &Pdu { todo!() }
|
||||
|
||||
fn into_pdu(self) -> Pdu { todo!() }
|
||||
|
||||
fn auth_events(&self) -> impl DoubleEndedIterator<Item = &EventId> + Clone + Send + '_ {
|
||||
self.auth_events.iter().map(AsRef::as_ref)
|
||||
}
|
||||
|
||||
fn content(&self) -> &RawJsonValue { self.content.as_ref() }
|
||||
|
||||
fn event_id(&self) -> &EventId { &self.internal_metadata.event_id }
|
||||
|
||||
fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch { self.origin_server_ts }
|
||||
|
||||
fn prev_events(&self) -> impl DoubleEndedIterator<Item = &EventId> + Clone + Send + '_ {
|
||||
self.prev_events.iter().map(AsRef::as_ref)
|
||||
}
|
||||
|
||||
fn redacts(&self) -> Option<&EventId> { self.redacts.as_deref() }
|
||||
|
||||
fn room_id(&self) -> Option<&RoomId> { Some(&self.room_id) }
|
||||
|
||||
fn room_id_or_hash(&self) -> OwnedRoomId { self.room_id.clone() }
|
||||
|
||||
fn sender(&self) -> &UserId { &self.sender }
|
||||
|
||||
fn state_key(&self) -> Option<&str> { self.state_key.as_deref() }
|
||||
|
||||
fn kind(&self) -> &TimelineEventType { self.event_type() }
|
||||
|
||||
fn unsigned(&self) -> Option<&RawJsonValue> { self.unsigned.as_deref() }
|
||||
|
||||
fn event_type(&self) -> &TimelineEventType { &self.event_type }
|
||||
}
|
||||
|
||||
impl TryFrom<&RawJsonValue> for PDU {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(value: &RawJsonValue) -> Result<Self, Self::Error> {
|
||||
serde_json::from_str(value.get()).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Box<RawJsonValue>> for PDU {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(value: Box<RawJsonValue>) -> Result<Self, Self::Error> {
|
||||
Self::try_from(value.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<serde_json::Value> for PDU {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
|
||||
serde_json::from_value(value).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<CanonicalJsonObject> for PDU {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(value: CanonicalJsonObject) -> Result<Self, Self::Error> {
|
||||
serde_json::from_value(serde_json::to_value(value)?).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
use ruma::{
|
||||
CanonicalJsonObject, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId,
|
||||
OwnedUserId, RoomId, ServerSignatures, UInt, UserId,
|
||||
events::{StateEventType, TimelineEventType},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
|
||||
use crate::{
|
||||
Pdu,
|
||||
pdu::{hashes::Hashes, metadata::PduMetadata},
|
||||
};
|
||||
|
||||
/// Represents a persistent data unit (PDU) version 3, introduced in room
|
||||
/// version 12.
|
||||
///
|
||||
/// Spec: https://spec.matrix.org/v1.19/rooms/v12/#event-format
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct PDU {
|
||||
/// The event IDs which authorise this event. Between 0 and 10 entries.
|
||||
pub auth_events: Vec<OwnedEventId>,
|
||||
/// This event's content.
|
||||
pub content: Box<RawJsonValue>,
|
||||
/// The depth of this event.
|
||||
pub depth: UInt,
|
||||
/// The content hashes for this event.
|
||||
pub hashes: Hashes,
|
||||
/// Timestamp in milliseconds on origin homeserver when this event was
|
||||
/// created.
|
||||
pub origin_server_ts: MilliSecondsSinceUnixEpoch,
|
||||
/// The event IDs which immediately precede this event. Between 0 and 20
|
||||
/// entries.
|
||||
pub prev_events: Vec<OwnedEventId>,
|
||||
/// The room in which this event resides.
|
||||
/// May be None if the event is a create event.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub room_id: Option<OwnedRoomId>,
|
||||
/// The user who sent this event.
|
||||
pub sender: OwnedUserId,
|
||||
/// The signatures on this event.
|
||||
pub signatures: ServerSignatures,
|
||||
/// The state key for this PDU, if any.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub state_key: Option<String>,
|
||||
/// The type of this PDU.
|
||||
#[serde(rename = "type")]
|
||||
pub event_type: TimelineEventType,
|
||||
/// Additional data added by the origin server but not covered by the
|
||||
/// signatures.
|
||||
///
|
||||
/// This should be `None` when receiving from and transmitting over
|
||||
/// federation.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub unsigned: Option<Box<RawJsonValue>>,
|
||||
|
||||
/// Internal metadata that is important to the PDU but should not be
|
||||
/// serialized into the final event.
|
||||
///
|
||||
/// This field MUST NOT be sent to clients or federation.
|
||||
pub internal_metadata: PduMetadata,
|
||||
}
|
||||
|
||||
impl PDU {
|
||||
#[must_use]
|
||||
pub fn internal_metadata(&self) -> &PduMetadata { &self.internal_metadata }
|
||||
}
|
||||
|
||||
impl crate::pdu::ConcretePDU<PDU> for PDU {
|
||||
fn to_concrete(&self) -> &PDU { &self }
|
||||
|
||||
fn into_concrete(self) -> PDU { self }
|
||||
}
|
||||
|
||||
impl ruma::state_res::Event for PDU {
|
||||
type Id = OwnedEventId;
|
||||
|
||||
fn event_id(&self) -> &Self::Id { &self.internal_metadata.event_id }
|
||||
|
||||
fn room_id(&self) -> Option<&RoomId> { self.room_id.as_deref() }
|
||||
|
||||
fn sender(&self) -> &UserId { &self.sender }
|
||||
|
||||
fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch { self.origin_server_ts }
|
||||
|
||||
fn event_type(&self) -> &TimelineEventType { &self.event_type }
|
||||
|
||||
fn content(&self) -> &RawJsonValue { self.content.as_ref() }
|
||||
|
||||
fn state_key(&self) -> Option<&str> { self.state_key.as_deref() }
|
||||
|
||||
fn prev_events(&self) -> Box<dyn DoubleEndedIterator<Item = &Self::Id> + '_> {
|
||||
Box::new(self.prev_events.iter())
|
||||
}
|
||||
|
||||
fn auth_events(&self) -> Box<dyn DoubleEndedIterator<Item = &Self::Id> + '_> {
|
||||
Box::new(self.auth_events.iter())
|
||||
}
|
||||
|
||||
fn redacts(&self) -> Option<&Self::Id> { None }
|
||||
|
||||
fn rejected(&self) -> bool { self.internal_metadata().rejected }
|
||||
}
|
||||
|
||||
impl crate::Event for PDU {
|
||||
fn as_pdu(&self) -> &Pdu { todo!() }
|
||||
|
||||
fn into_pdu(self) -> Pdu { todo!() }
|
||||
|
||||
fn auth_events(&self) -> impl DoubleEndedIterator<Item = &EventId> + Clone + Send + '_ {
|
||||
self.auth_events.iter().map(AsRef::as_ref)
|
||||
}
|
||||
|
||||
fn content(&self) -> &RawJsonValue { self.content.as_ref() }
|
||||
|
||||
fn event_id(&self) -> &EventId { &self.internal_metadata.event_id }
|
||||
|
||||
fn origin_server_ts(&self) -> MilliSecondsSinceUnixEpoch { self.origin_server_ts }
|
||||
|
||||
fn prev_events(&self) -> impl DoubleEndedIterator<Item = &EventId> + Clone + Send + '_ {
|
||||
self.prev_events.iter().map(AsRef::as_ref)
|
||||
}
|
||||
|
||||
fn redacts(&self) -> Option<&EventId> { None }
|
||||
|
||||
fn room_id(&self) -> Option<&RoomId> { self.room_id.as_deref() }
|
||||
|
||||
fn room_id_or_hash(&self) -> OwnedRoomId {
|
||||
if self.event_type == StateEventType::RoomCreate.into()
|
||||
&& self.state_key().is_some_and(str::is_empty)
|
||||
{
|
||||
// Calculate the room ID for a create event.
|
||||
RoomId::new_v2(
|
||||
self.internal_metadata()
|
||||
.event_id
|
||||
.as_str()
|
||||
.strip_prefix("$")
|
||||
.expect("event ID must start with $ sigil"),
|
||||
)
|
||||
.expect("must be able to create a room ID from create event reference hash")
|
||||
} else {
|
||||
// Otherwise, all other events have a room ID field.
|
||||
self.room_id.clone().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn sender(&self) -> &UserId { &self.sender }
|
||||
|
||||
fn state_key(&self) -> Option<&str> { self.state_key.as_deref() }
|
||||
|
||||
fn kind(&self) -> &TimelineEventType { self.event_type() }
|
||||
|
||||
fn unsigned(&self) -> Option<&RawJsonValue> { self.unsigned.as_deref() }
|
||||
|
||||
fn event_type(&self) -> &TimelineEventType { &self.event_type }
|
||||
}
|
||||
|
||||
impl TryFrom<&RawJsonValue> for PDU {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(value: &RawJsonValue) -> Result<Self, Self::Error> {
|
||||
serde_json::from_str(value.get()).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Box<RawJsonValue>> for PDU {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(value: Box<RawJsonValue>) -> Result<Self, Self::Error> {
|
||||
Self::try_from(value.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<serde_json::Value> for PDU {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
|
||||
serde_json::from_value(value).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<CanonicalJsonObject> for PDU {
|
||||
type Error = crate::Error;
|
||||
|
||||
fn try_from(value: CanonicalJsonObject) -> Result<Self, Self::Error> {
|
||||
serde_json::from_value(serde_json::to_value(value)?).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{cmp, collections::HashMap};
|
||||
|
||||
use conduwuit::{smallstr::SmallString, trace};
|
||||
use conduwuit::{pdu::AnyPDU, smallstr::SmallString, trace};
|
||||
use conduwuit_core::{
|
||||
Err, Error, Result, err,
|
||||
matrix::{
|
||||
@@ -84,7 +84,7 @@ pub async fn create_event(
|
||||
sender: &UserId,
|
||||
room_id: Option<&RoomId>,
|
||||
_mutex_lock: &RoomMutexGuard,
|
||||
) -> Result<(PduEvent, RoomVersionRules)> {
|
||||
) -> Result<AnyPDU> {
|
||||
let PartialPdu {
|
||||
event_type,
|
||||
content,
|
||||
@@ -282,7 +282,7 @@ pub async fn create_hash_and_sign_event(
|
||||
sender: &UserId,
|
||||
room_id: Option<&RoomId>,
|
||||
mutex_lock: &RoomMutexGuard,
|
||||
) -> Result<(PduEvent, CanonicalJsonObject)> {
|
||||
) -> Result<AnyPDU> {
|
||||
if !self.services.globals.user_is_local(sender) {
|
||||
return Err!(Request(Forbidden("Sender must be a local user")));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user