From 88cf043f944f73f3c31d30c7f8cb5e773f4f3c73 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timo=20K=C3=B6sters?= <timo@koesters.xyz>
Date: Sun, 30 May 2021 21:55:43 +0200
Subject: [PATCH 1/2] fix: deactivate accounts that should be deactivated

---
 src/client_server/account.rs | 12 +++----
 src/client_server/media.rs   | 10 ++++--
 src/database.rs              | 21 ++++++++++-
 src/database/media.rs        | 69 +++++++++++++++++++++++-------------
 src/database/users.rs        | 23 +++++++-----
 5 files changed, 91 insertions(+), 44 deletions(-)

diff --git a/src/client_server/account.rs b/src/client_server/account.rs
index 24b04d59..0cf30a07 100644
--- a/src/client_server/account.rs
+++ b/src/client_server/account.rs
@@ -179,12 +179,11 @@ pub async fn register_route(
     let password = if is_guest {
         None
     } else {
-        body.password.clone()
-    }
-    .unwrap_or_default();
+        body.password.as_deref()
+    };
 
     // Create user
-    db.users.create(&user_id, &password)?;
+    db.users.create(&user_id, password)?;
 
     // Initial data
     db.account_data.update(
@@ -233,7 +232,7 @@ pub async fn register_route(
         let conduit_user = UserId::parse_with_server_name("conduit", db.globals.server_name())
             .expect("@conduit:server_name is valid");
 
-        db.users.create(&conduit_user, "")?;
+        db.users.create(&conduit_user, None)?;
 
         let room_id = RoomId::new(db.globals.server_name());
 
@@ -547,7 +546,8 @@ pub async fn change_password_route(
         return Err(Error::Uiaa(uiaainfo));
     }
 
-    db.users.set_password(&sender_user, &body.new_password)?;
+    db.users
+        .set_password(&sender_user, Some(&body.new_password))?;
 
     if body.logout_devices {
         // Logout all devices except the current one
diff --git a/src/client_server/media.rs b/src/client_server/media.rs
index f9350e0f..74ca6c84 100644
--- a/src/client_server/media.rs
+++ b/src/client_server/media.rs
@@ -38,7 +38,11 @@ pub async fn create_content_route(
     );
     db.media.create(
         mxc.clone(),
-        &body.filename.as_deref(),
+        &body
+            .filename
+            .as_ref()
+            .map(|filename| "inline; filename=".to_owned() + filename)
+            .as_deref(),
         &body.content_type.as_deref(),
         &body.file,
     )?;
@@ -64,7 +68,7 @@ pub async fn get_content_route(
     let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
 
     if let Some(FileMeta {
-        filename,
+        content_disposition,
         content_type,
         file,
     }) = db.media.get(&mxc)?
@@ -72,7 +76,7 @@ pub async fn get_content_route(
         Ok(get_content::Response {
             file,
             content_type,
-            content_disposition: filename,
+            content_disposition,
         }
         .into())
     } else if &*body.server_name != db.globals.server_name() && body.allow_remote {
diff --git a/src/database.rs b/src/database.rs
index 561b5612..7a55b030 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -11,7 +11,7 @@ pub mod transaction_ids;
 pub mod uiaa;
 pub mod users;
 
-use crate::{Error, Result};
+use crate::{utils, Error, Result};
 use directories::ProjectDirs;
 use futures::StreamExt;
 use log::{error, info};
@@ -246,6 +246,25 @@ impl Database {
             info!("Migration: 0 -> 1 finished");
         }
 
+        if db.globals.database_version()? < 2 {
+            // We accidentally inserted hashed versions of "" into the db instead of just ""
+            for userid_password in db.users.userid_password.iter() {
+                let (userid, password) = userid_password?;
+
+                let password = utils::string_from_bytes(&password);
+
+                if password.map_or(false, |password| {
+                    argon2::verify_encoded(&password, b"").unwrap_or(false)
+                }) {
+                    db.users.userid_password.insert(userid, b"")?;
+                }
+            }
+
+            db.globals.bump_database_version(2)?;
+
+            info!("Migration: 1 -> 2 finished");
+        }
+
         // This data is probably outdated
         db.rooms.edus.presenceid_presence.clear()?;
 
diff --git a/src/database/media.rs b/src/database/media.rs
index 37fcb741..28ef88a2 100644
--- a/src/database/media.rs
+++ b/src/database/media.rs
@@ -4,14 +4,14 @@ use crate::{utils, Error, Result};
 use std::mem;
 
 pub struct FileMeta {
-    pub filename: Option<String>,
+    pub content_disposition: Option<String>,
     pub content_type: Option<String>,
     pub file: Vec<u8>,
 }
 
 #[derive(Clone)]
 pub struct Media {
-    pub(super) mediaid_file: sled::Tree, // MediaId = MXC + WidthHeight + Filename + ContentType
+    pub(super) mediaid_file: sled::Tree, // MediaId = MXC + WidthHeight + ContentDisposition + ContentType
 }
 
 impl Media {
@@ -19,7 +19,7 @@ impl Media {
     pub fn create(
         &self,
         mxc: String,
-        filename: &Option<&str>,
+        content_disposition: &Option<&str>,
         content_type: &Option<&str>,
         file: &[u8],
     ) -> Result<()> {
@@ -28,7 +28,12 @@ impl Media {
         key.extend_from_slice(&0_u32.to_be_bytes()); // Width = 0 if it's not a thumbnail
         key.extend_from_slice(&0_u32.to_be_bytes()); // Height = 0 if it's not a thumbnail
         key.push(0xff);
-        key.extend_from_slice(filename.as_ref().map(|f| f.as_bytes()).unwrap_or_default());
+        key.extend_from_slice(
+            content_disposition
+                .as_ref()
+                .map(|f| f.as_bytes())
+                .unwrap_or_default(),
+        );
         key.push(0xff);
         key.extend_from_slice(
             content_type
@@ -46,7 +51,7 @@ impl Media {
     pub fn upload_thumbnail(
         &self,
         mxc: String,
-        filename: &Option<String>,
+        content_disposition: &Option<String>,
         content_type: &Option<String>,
         width: u32,
         height: u32,
@@ -57,7 +62,12 @@ impl Media {
         key.extend_from_slice(&width.to_be_bytes());
         key.extend_from_slice(&height.to_be_bytes());
         key.push(0xff);
-        key.extend_from_slice(filename.as_ref().map(|f| f.as_bytes()).unwrap_or_default());
+        key.extend_from_slice(
+            content_disposition
+                .as_ref()
+                .map(|f| f.as_bytes())
+                .unwrap_or_default(),
+        );
         key.push(0xff);
         key.extend_from_slice(
             content_type
@@ -92,20 +102,24 @@ impl Media {
                 })
                 .transpose()?;
 
-            let filename_bytes = parts
+            let content_disposition_bytes = parts
                 .next()
                 .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?;
 
-            let filename = if filename_bytes.is_empty() {
+            let content_disposition = if content_disposition_bytes.is_empty() {
                 None
             } else {
-                Some(utils::string_from_bytes(filename_bytes).map_err(|_| {
-                    Error::bad_database("Filename in mediaid_file is invalid unicode.")
-                })?)
+                Some(
+                    utils::string_from_bytes(content_disposition_bytes).map_err(|_| {
+                        Error::bad_database(
+                            "Content Disposition in mediaid_file is invalid unicode.",
+                        )
+                    })?,
+                )
             };
 
             Ok(Some(FileMeta {
-                filename,
+                content_disposition,
                 content_type,
                 file: file.to_vec(),
             }))
@@ -169,21 +183,22 @@ impl Media {
                 })
                 .transpose()?;
 
-            let filename_bytes = parts
+            let content_disposition_bytes = parts
                 .next()
                 .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?;
 
-            let filename = if filename_bytes.is_empty() {
+            let content_disposition = if content_disposition_bytes.is_empty() {
                 None
             } else {
                 Some(
-                    utils::string_from_bytes(filename_bytes)
-                        .map_err(|_| Error::bad_database("Filename in db is invalid."))?,
+                    utils::string_from_bytes(content_disposition_bytes).map_err(|_| {
+                        Error::bad_database("Content Disposition in db is invalid.")
+                    })?,
                 )
             };
 
             Ok(Some(FileMeta {
-                filename,
+                content_disposition,
                 content_type,
                 file: file.to_vec(),
             }))
@@ -202,16 +217,20 @@ impl Media {
                 })
                 .transpose()?;
 
-            let filename_bytes = parts
+            let content_disposition_bytes = parts
                 .next()
                 .ok_or_else(|| Error::bad_database("Media ID in db is invalid."))?;
 
-            let filename = if filename_bytes.is_empty() {
+            let content_disposition = if content_disposition_bytes.is_empty() {
                 None
             } else {
-                Some(utils::string_from_bytes(filename_bytes).map_err(|_| {
-                    Error::bad_database("Filename in mediaid_file is invalid unicode.")
-                })?)
+                Some(
+                    utils::string_from_bytes(content_disposition_bytes).map_err(|_| {
+                        Error::bad_database(
+                            "Content Disposition in mediaid_file is invalid unicode.",
+                        )
+                    })?,
+                )
             };
 
             if let Ok(image) = image::load_from_memory(&file) {
@@ -219,7 +238,7 @@ impl Media {
                 let original_height = image.height();
                 if width > original_width || height > original_height {
                     return Ok(Some(FileMeta {
-                        filename,
+                        content_disposition,
                         content_type,
                         file: file.to_vec(),
                     }));
@@ -286,14 +305,14 @@ impl Media {
                 self.mediaid_file.insert(thumbnail_key, &*thumbnail_bytes)?;
 
                 Ok(Some(FileMeta {
-                    filename,
+                    content_disposition,
                     content_type,
                     file: thumbnail_bytes.to_vec(),
                 }))
             } else {
                 // Couldn't parse file to generate thumbnail, send original
                 Ok(Some(FileMeta {
-                    filename,
+                    content_disposition,
                     content_type,
                     file: file.to_vec(),
                 }))
diff --git a/src/database/users.rs b/src/database/users.rs
index a5b87754..52e6e33b 100644
--- a/src/database/users.rs
+++ b/src/database/users.rs
@@ -49,7 +49,7 @@ impl Users {
     }
 
     /// Create a new user account on this homeserver.
-    pub fn create(&self, user_id: &UserId, password: &str) -> Result<()> {
+    pub fn create(&self, user_id: &UserId, password: Option<&str>) -> Result<()> {
         self.set_password(user_id, password)?;
         Ok(())
     }
@@ -110,15 +110,20 @@ impl Users {
     }
 
     /// Hash and set the user's password to the Argon2 hash
-    pub fn set_password(&self, user_id: &UserId, password: &str) -> Result<()> {
-        if let Ok(hash) = utils::calculate_hash(&password) {
-            self.userid_password.insert(user_id.to_string(), &*hash)?;
-            Ok(())
+    pub fn set_password(&self, user_id: &UserId, password: Option<&str>) -> Result<()> {
+        if let Some(password) = password {
+            if let Ok(hash) = utils::calculate_hash(&password) {
+                self.userid_password.insert(user_id.to_string(), &*hash)?;
+                Ok(())
+            } else {
+                Err(Error::BadRequest(
+                    ErrorKind::InvalidParam,
+                    "Password does not meet the requirements.",
+                ))
+            }
         } else {
-            Err(Error::BadRequest(
-                ErrorKind::InvalidParam,
-                "Password does not meet the requirements.",
-            ))
+            self.userid_password.insert(user_id.to_string(), "")?;
+            Ok(())
         }
     }
 

From e1e529d818510b335cba3db02656616ee4eb7267 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timo=20K=C3=B6sters?= <timo@koesters.xyz>
Date: Sun, 30 May 2021 22:01:12 +0200
Subject: [PATCH 2/2] fix: don't apply push rules for users of other
 homeservers

---
 src/database/rooms.rs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/database/rooms.rs b/src/database/rooms.rs
index 6b17f398..703314e6 100644
--- a/src/database/rooms.rs
+++ b/src/database/rooms.rs
@@ -725,8 +725,9 @@ impl Rooms {
             .users
             .iter()
             .filter_map(|r| r.ok())
-            .filter(|user_id| self.is_joined(&user_id, &pdu.room_id).unwrap_or(false))
+            .filter(|user_id| user_id.server_name() == db.globals.server_name())
             .filter(|user_id| !db.users.is_deactivated(user_id).unwrap_or(false))
+            .filter(|user_id| self.is_joined(&user_id, &pdu.room_id).unwrap_or(false))
         {
             // Don't notify the user of their own events
             if user == pdu.sender {