From 4b28146ee7837451511a660cfb83130373ca38d3 Mon Sep 17 00:00:00 2001
From: Nyaaori <+@nyaaori.cat>
Date: Mon, 1 Nov 2021 08:57:27 +0000
Subject: [PATCH] Support room version 3

---
 src/client_server/membership.rs |  3 ++-
 src/database/globals.rs         |  2 +-
 src/database/rooms.rs           | 23 +++++++++++++++++++++++
 src/pdu.rs                      | 16 ++++++++++++----
 src/server_server.rs            |  6 +++---
 5 files changed, 41 insertions(+), 9 deletions(-)

diff --git a/src/client_server/membership.rs b/src/client_server/membership.rs
index 0f440f48..65107a3c 100644
--- a/src/client_server/membership.rs
+++ b/src/client_server/membership.rs
@@ -975,7 +975,8 @@ pub(crate) async fn invite_helper<'a>(
         let pub_key_map = RwLock::new(BTreeMap::new());
 
         // We do not add the event_id field to the pdu here because of signature and hashes checks
-        let (event_id, value) = match crate::pdu::gen_event_id_canonical_json(&response.event) {
+        let (event_id, value) = match crate::pdu::gen_event_id_canonical_json(&response.event, &db)
+        {
             Ok(t) => t,
             Err(_) => {
                 // Event could not be converted to canonical json
diff --git a/src/database/globals.rs b/src/database/globals.rs
index c2ce8a5a..b1afd96c 100644
--- a/src/database/globals.rs
+++ b/src/database/globals.rs
@@ -151,7 +151,7 @@ impl Globals {
         // Supported and stable room versions
         let stable_room_versions = vec![RoomVersionId::V6];
         // Experimental, partially supported room versions
-        let unstable_room_versions = vec![RoomVersionId::V4, RoomVersionId::V5];
+        let unstable_room_versions = vec![RoomVersionId::V3, RoomVersionId::V4, RoomVersionId::V5];
 
         let s = Self {
             globals,
diff --git a/src/database/rooms.rs b/src/database/rooms.rs
index 0bccc84f..31333658 100644
--- a/src/database/rooms.rs
+++ b/src/database/rooms.rs
@@ -3437,4 +3437,27 @@ impl Rooms {
 
         Ok(())
     }
+
+    /// Returns the room's version.
+    #[tracing::instrument(skip(self))]
+    pub fn get_room_version(&self, room_id: &RoomId) -> RoomVersionId {
+        let create_event = self
+            .room_state_get(room_id, &StateEventType::RoomCreate, "")
+            .unwrap();
+
+        let create_event_content: Option<RoomCreateEventContent> = create_event
+            .as_ref()
+            .map(|create_event| {
+                serde_json::from_str(create_event.content.get()).map_err(|e| {
+                    warn!("Invalid create event: {}", e);
+                    Error::bad_database("Invalid create event in db.")
+                })
+            })
+            .transpose()
+            .unwrap();
+
+        create_event_content
+            .map(|create_event| create_event.room_version)
+            .expect("Invalid room version")
+    }
 }
diff --git a/src/pdu.rs b/src/pdu.rs
index 3b905336..6e2bf5aa 100644
--- a/src/pdu.rs
+++ b/src/pdu.rs
@@ -1,11 +1,11 @@
-use crate::Error;
+use crate::{Database, Error};
 use ruma::{
     events::{
         room::member::RoomMemberEventContent, AnyEphemeralRoomEvent, AnyRoomEvent, AnyStateEvent,
         AnyStrippedStateEvent, AnySyncRoomEvent, AnySyncStateEvent, RoomEventType, StateEvent,
     },
     serde::{CanonicalJsonObject, CanonicalJsonValue, Raw},
-    state_res, EventId, MilliSecondsSinceUnixEpoch, RoomId, RoomVersionId, UInt, UserId,
+    state_res, EventId, MilliSecondsSinceUnixEpoch, RoomId, UInt, UserId,
 };
 use serde::{Deserialize, Serialize};
 use serde_json::{
@@ -332,16 +332,24 @@ impl Ord for PduEvent {
 /// Returns a tuple of the new `EventId` and the PDU as a `BTreeMap<String, CanonicalJsonValue>`.
 pub(crate) fn gen_event_id_canonical_json(
     pdu: &RawJsonValue,
+    db: &Database,
 ) -> crate::Result<(Box<EventId>, CanonicalJsonObject)> {
-    let value = serde_json::from_str(pdu.get()).map_err(|e| {
+    let value: CanonicalJsonObject = serde_json::from_str(pdu.get()).map_err(|e| {
         warn!("Error parsing incoming event {:?}: {:?}", pdu, e);
         Error::BadServerResponse("Invalid PDU in server response")
     })?;
 
+    let room_id = value
+        .get("room_id")
+        .and_then(|id| RoomId::parse(id.as_str()?).ok())
+        .expect("Invalid room id in event");
+
+    let room_version_id = db.rooms.get_room_version(&room_id);
+
     let event_id = format!(
         "${}",
         // Anything higher than version3 behaves the same
-        ruma::signatures::reference_hash(&value, &RoomVersionId::V6)
+        ruma::signatures::reference_hash(&value, &room_version_id)
             .expect("ruma can calculate reference hashes")
     )
     .try_into()
diff --git a/src/server_server.rs b/src/server_server.rs
index 6d589475..e95c4c0f 100644
--- a/src/server_server.rs
+++ b/src/server_server.rs
@@ -659,7 +659,7 @@ pub async fn send_transaction_message_route(
 
     for pdu in &body.pdus {
         // We do not add the event_id field to the pdu here because of signature and hashes checks
-        let (event_id, value) = match crate::pdu::gen_event_id_canonical_json(pdu) {
+        let (event_id, value) = match crate::pdu::gen_event_id_canonical_json(pdu, &db) {
             Ok(t) => t,
             Err(_) => {
                 // Event could not be converted to canonical json
@@ -1859,7 +1859,7 @@ pub(crate) fn fetch_and_handle_outliers<'a>(
                     Ok(res) => {
                         warn!("Got {} over federation", next_id);
                         let (calculated_event_id, value) =
-                            match crate::pdu::gen_event_id_canonical_json(&res.pdu) {
+                            match crate::pdu::gen_event_id_canonical_json(&res.pdu, &db) {
                                 Ok(t) => t,
                                 Err(_) => {
                                     back_off((*next_id).to_owned());
@@ -2820,7 +2820,7 @@ async fn create_join_event(
     // let mut auth_cache = EventMap::new();
 
     // We do not add the event_id field to the pdu here because of signature and hashes checks
-    let (event_id, value) = match crate::pdu::gen_event_id_canonical_json(pdu) {
+    let (event_id, value) = match crate::pdu::gen_event_id_canonical_json(pdu, &db) {
         Ok(t) => t,
         Err(_) => {
             // Event could not be converted to canonical json