Skip to main content

m4a_matrix_core/
builder.rs

1use std::collections::HashMap;
2
3use ruma_common::{CanonicalJsonObject, OwnedEventId, OwnedRoomId, UserId};
4use ruma_events::{StateEventType, TimelineEventType};
5use ruma_signatures::{hash_and_sign_event, reference_hash};
6use ruma_state_res::{auth_types_for_event, check_state_dependent_auth_rules, check_state_independent_auth_rules, StateMap};
7use serde_json::{json, value::RawValue, Value};
8
9use crate::{Pdu, Signer, ROOM_VERSION};
10
11#[derive(Debug, thiserror::Error)]
12pub enum BuildError {
13    #[error("unsupported room version")]
14    Version,
15    #[error("bad event field: {0}")]
16    Field(String),
17    #[error("signing failed: {0}")]
18    Sign(String),
19    #[error("auth rules refuse the event: {0}")]
20    Auth(String),
21}
22
23/// A finished event: its computed id and the signed wire JSON (no `event_id` inside).
24#[derive(Clone, Debug)]
25pub struct SignedEvent {
26    pub event_id: OwnedEventId,
27    pub json: CanonicalJsonObject,
28}
29
30/// Builds the timeline of one NEW room, event by event: fills `prev_events`, `depth` and the
31/// `auth_events` the spec selects from the current state, hashes and signs, computes the id, and
32/// refuses an event the auth rules would reject.
33pub struct RoomBuilder<'a> {
34    room_id: OwnedRoomId,
35    signer: &'a Signer,
36    rules: ruma_common::room_version_rules::RoomVersionRules,
37    ts: u64,
38    depth: u64,
39    last: Option<OwnedEventId>,
40    state: StateMap<OwnedEventId>,
41    pdus: HashMap<OwnedEventId, Pdu>,
42    events: Vec<SignedEvent>,
43}
44
45impl<'a> RoomBuilder<'a> {
46    pub fn new(room_id: OwnedRoomId, signer: &'a Signer, start_ts_ms: u64) -> Result<Self, BuildError> {
47        let rules = ROOM_VERSION.rules().ok_or(BuildError::Version)?;
48        Ok(Self { room_id, signer, rules, ts: start_ts_ms, depth: 0, last: None, state: StateMap::new(), pdus: HashMap::new(), events: Vec::new() })
49    }
50
51    /// Appends one event. `state_key` makes it a state event.
52    pub fn push(&mut self, sender: &str, kind: &str, state_key: Option<&str>, content: Value) -> Result<&SignedEvent, BuildError> {
53        let sender_id = UserId::parse(sender).map_err(|e| BuildError::Field(e.to_string()))?;
54        let raw = RawValue::from_string(content.to_string()).map_err(|e| BuildError::Field(e.to_string()))?;
55        let tl_kind = TimelineEventType::from(kind.to_owned());
56        let auth_events: Vec<OwnedEventId> = auth_types_for_event(&tl_kind, &sender_id, state_key, &raw, &self.rules.authorization)
57            .map_err(BuildError::Auth)?
58            .iter()
59            .filter_map(|k| self.state.get(k).cloned())
60            .collect();
61        self.depth += 1;
62        self.ts += 1;
63        let mut obj = json!({
64            "room_id": self.room_id,
65            "sender": sender,
66            "type": kind,
67            "content": content,
68            "origin_server_ts": self.ts,
69            "prev_events": self.last.iter().collect::<Vec<_>>(),
70            "auth_events": auth_events,
71            "depth": self.depth,
72        });
73        if let Some(sk) = state_key {
74            obj["state_key"] = json!(sk);
75        }
76        let mut obj: CanonicalJsonObject = serde_json::from_value(obj).map_err(|e| BuildError::Field(e.to_string()))?;
77        hash_and_sign_event(self.signer.server(), self.signer, &mut obj, &self.rules.redaction).map_err(|e| BuildError::Sign(e.to_string()))?;
78        let id = format!("${}", reference_hash(&obj, &self.rules).map_err(|e| BuildError::Sign(e.to_string()))?);
79        let event_id = OwnedEventId::try_from(id).map_err(|e| BuildError::Field(e.to_string()))?;
80        let pdu = Pdu::from_wire(event_id.clone(), &obj).map_err(BuildError::Field)?;
81
82        // The auth rules, as the spec orders them: independent of state, then against the state
83        // the event names (its auth events), then against the room state before it.
84        let by_id = |id: &ruma_common::EventId| self.pdus.get(id).cloned();
85        check_state_independent_auth_rules(&self.rules.authorization, &pdu, by_id).map_err(BuildError::Auth)?;
86        let fetch = |t: &StateEventType, k: &str| self.state.get(&(t.clone(), k.to_owned())).and_then(|id| self.pdus.get(id).cloned());
87        check_state_dependent_auth_rules(&self.rules.authorization, &pdu, fetch).map_err(BuildError::Auth)?;
88
89        if let Some(sk) = &pdu.state_key {
90            self.state.insert((StateEventType::from(pdu.kind.to_string()), sk.clone()), event_id.clone());
91        }
92        self.last = Some(event_id.clone());
93        self.pdus.insert(event_id.clone(), pdu);
94        self.events.push(SignedEvent { event_id, json: obj });
95        Ok(self.events.last().expect("just pushed"))
96    }
97
98    pub fn events(&self) -> &[SignedEvent] {
99        &self.events
100    }
101    pub fn state(&self) -> &StateMap<OwnedEventId> {
102        &self.state
103    }
104    pub fn pdus(&self) -> &HashMap<OwnedEventId, Pdu> {
105        &self.pdus
106    }
107    pub fn rules(&self) -> &ruma_common::room_version_rules::RoomVersionRules {
108        &self.rules
109    }
110}