diff --git a/Cargo.toml b/Cargo.toml index 8d5885b4..dec70d2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -209,7 +209,7 @@ explicit_outlives_requirements = "warn" # unreachable_pub = "warn" unused_extern_crates = "warn" unused_import_braces = "warn" -# unused_lifetimes = "warn" +unused_lifetimes = "warn" unused_qualifications = "warn" dead_code = "warn" diff --git a/DEPLOY.md b/DEPLOY.md index d56f4dbc..1fc9c242 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -248,7 +248,7 @@ server { location /_matrix/ { # TCP - proxy_pass http://127.0.0.1:6167$request_uri; + proxy_pass http://127.0.0.1:6167; # UNIX socket #proxy_pass http://backend; diff --git a/DIFFERENCES.md b/DIFFERENCES.md index 3cf7894f..75ed0166 100644 --- a/DIFFERENCES.md +++ b/DIFFERENCES.md @@ -72,4 +72,5 @@ - Add admin command to bulk delete media via a codeblock list of MXC URLs. - Add admin command to delete both the thumbnail and media MXC URLs from an event ID (e.g. from an abuse report) - Add `!admin` as a way to call the Conduit admin bot -- Add support for listening on multiple TCP ports \ No newline at end of file +- Add support for listening on multiple TCP ports +- Add admin command to list all the rooms a local user is joined in \ No newline at end of file diff --git a/docker/README.md b/docker/README.md index 36e6bbf3..eb2de450 100644 --- a/docker/README.md +++ b/docker/README.md @@ -70,7 +70,7 @@ docker run -d -p 8448:6167 \ or you can use [docker-compose](#docker-compose). -The `-d` flag lets the container run in detached mode. You now need to supply a `conduit.toml` config file, an example can be found [here](../conduit-example.toml). +The `-d` flag lets the container run in detached mode. You now need to supply a `conduit.toml` config file, an example can be found [here](../conduwuit-example.toml). You can pass in different env vars to change config values on the fly. You can even configure Conduit completely by using env vars, but for that you need to pass `-e CONDUIT_CONFIG=""` into your container. For an overview of possible values, please take a look at the `docker-compose.yml` file. @@ -131,7 +131,7 @@ So...step by step: 1. Copy [`docker-compose.for-traefik.yml`](docker-compose.for-traefik.yml) (or [`docker-compose.with-traefik.yml`](docker-compose.with-traefik.yml)) and [`docker-compose.override.yml`](docker-compose.override.yml) from the repository and remove `.for-traefik` (or `.with-traefik`) from the filename. 2. Open both files and modify/adjust them to your needs. Meaning, change the `CONDUIT_SERVER_NAME` and the volume host mappings according to your needs. -3. Create the `conduit.toml` config file, an example can be found [here](../conduit-example.toml), or set `CONDUIT_CONFIG=""` and configure Conduit per env vars. +3. Create the `conduit.toml` config file, an example can be found [here](../conduwuit-example.toml), or set `CONDUIT_CONFIG=""` and configure Conduit per env vars. 4. Uncomment the `element-web` service if you want to host your own Element Web Client and create a `element_config.json`. 5. Create the files needed by the `well-known` service. diff --git a/nix/README.md b/nix/README.md index 474bd4b8..98f1bc69 100644 --- a/nix/README.md +++ b/nix/README.md @@ -15,8 +15,8 @@ https://attic.kennel.juneis.dog/conduwuit conduwuit:lYPVh7o1hLu1idH4Xt2QHaRa49WRGSAqzcfFd94aOTw= ``` -You can now use the usual Nix commands to interact with Conduit's flake. For -example, `nix run gitlab:famedly/conduit` will run Conduit (though you'll need +You can now use the usual Nix commands to interact with conduwuit's flake. For +example, `nix run github:girlbossceo/conduwuit` will run conduwuit (though you'll need to provide configuration and such manually as usual). If your NixOS configuration is defined as a flake, you can depend on this flake @@ -25,7 +25,7 @@ add the following to your `inputs`: ```nix conduit = { - url = "gitlab:famedly/conduit"; + url = "github:girlbossceo/conduwuit"; # Assuming you have an input for nixpkgs called `nixpkgs`. If you experience # build failures while using this, try commenting/deleting this line. This @@ -38,7 +38,7 @@ Next, make sure you're passing your flake inputs to the `specialArgs` argument of `nixpkgs.lib.nixosSystem` [as explained here][specialargs]. This guide will assume you've named the group `flake-inputs`. -Now you can configure Conduit and a reverse proxy for it. Add the following to +Now you can configure conduwuit and a reverse proxy for it. Add the following to a new Nix file and include it in your configuration: ```nix @@ -144,7 +144,7 @@ in ]; locations."/_matrix/" = { - proxyPass = "http://backend_conduit$request_uri"; + proxyPass = "http://backend_conduit"; proxyWebsockets = true; extraConfig = '' proxy_set_header Host $host; diff --git a/src/api/client_server/sync.rs b/src/api/client_server/sync.rs index 05847c3f..bc5eacc0 100644 --- a/src/api/client_server/sync.rs +++ b/src/api/client_server/sync.rs @@ -1492,6 +1492,9 @@ pub async fn sync_events_v4_route( let mut known_subscription_rooms = BTreeSet::new(); for (room_id, room) in &body.room_subscriptions { + if !services().rooms.metadata.exists(room_id)? { + continue; + } let todo_room = todo_rooms .entry(room_id.clone()) .or_insert((BTreeSet::new(), 0, u64::MAX)); diff --git a/src/api/server_server.rs b/src/api/server_server.rs index fafc52a6..0393f7fa 100644 --- a/src/api/server_server.rs +++ b/src/api/server_server.rs @@ -1963,6 +1963,13 @@ pub async fn get_devices_route( return Err(Error::bad_config("Federation is disabled.")); } + if body.user_id.server_name() != services().globals.server_name() { + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Tried to access user from other server.", + )); + } + let sender_servername = body .sender_servername .as_ref() @@ -2044,7 +2051,7 @@ pub async fn get_profile_information_route( if body.user_id.server_name() != services().globals.server_name() { return Err(Error::BadRequest( - ErrorKind::NotFound, + ErrorKind::InvalidParam, "User does not belong to this server", )); } @@ -2085,6 +2092,17 @@ pub async fn get_keys_route(body: Ruma) -> Result { Ok(()) } - fn insert_batch<'a>(&self, iter: &mut dyn Iterator, Vec)>) -> Result<()> { + fn insert_batch(&self, iter: &mut dyn Iterator, Vec)>) -> Result<()> { for (key, value) in iter { self.db.rocks.put_cf(&self.cf(), key, value)?; } @@ -247,7 +247,7 @@ impl KvTree for RocksDbEngineTree<'_> { Ok(new) } - fn increment_batch<'a>(&self, iter: &mut dyn Iterator>) -> Result<()> { + fn increment_batch(&self, iter: &mut dyn Iterator>) -> Result<()> { let lock = self.write_lock.write().unwrap(); for key in iter { diff --git a/src/database/key_value/rooms/search.rs b/src/database/key_value/rooms/search.rs index 9aceaa63..128b3019 100644 --- a/src/database/key_value/rooms/search.rs +++ b/src/database/key_value/rooms/search.rs @@ -5,7 +5,7 @@ use crate::{database::KeyValueDatabase, service, services, utils, Result}; type SearchPdusResult<'a> = Result> + 'a>, Vec)>>; impl service::rooms::search::Data for KeyValueDatabase { - fn index_pdu<'a>(&self, shortroomid: u64, pdu_id: &[u8], message_body: &str) -> Result<()> { + fn index_pdu(&self, shortroomid: u64, pdu_id: &[u8], message_body: &str) -> Result<()> { let mut batch = message_body .split_terminator(|c: char| !c.is_alphanumeric()) .filter(|s| !s.is_empty()) diff --git a/src/database/key_value/rooms/state.rs b/src/database/key_value/rooms/state.rs index f17d37bb..6865d7b9 100644 --- a/src/database/key_value/rooms/state.rs +++ b/src/database/key_value/rooms/state.rs @@ -49,7 +49,7 @@ impl service::rooms::state::Data for KeyValueDatabase { .collect() } - fn set_forward_extremities<'a>( + fn set_forward_extremities( &self, room_id: &RoomId, event_ids: Vec, diff --git a/src/database/key_value/rooms/timeline.rs b/src/database/key_value/rooms/timeline.rs index d097aaf1..d76e211d 100644 --- a/src/database/key_value/rooms/timeline.rs +++ b/src/database/key_value/rooms/timeline.rs @@ -330,7 +330,7 @@ fn count_to_id( .rooms .short .get_shortroomid(room_id)? - .expect("room exists") + .ok_or_else(|| Error::bad_database("Looked for bad shortroomid in timeline"))? .to_be_bytes() .to_vec(); let mut pdu_id = prefix.clone(); diff --git a/src/service/admin/mod.rs b/src/service/admin/mod.rs index 1439cc28..0ea67abf 100644 --- a/src/service/admin/mod.rs +++ b/src/service/admin/mod.rs @@ -179,6 +179,9 @@ enum UserCommand { /// - List local users in the database List, + + /// - Lists all the rooms (local and remote) that the specified user is joined in + ListJoinedRooms { user_id: Box }, } #[cfg_attr(test, derive(Debug))] @@ -1005,6 +1008,52 @@ impl Service { ) } } + UserCommand::ListJoinedRooms { user_id } => { + if user_id.server_name() != services().globals.server_name() { + return Ok(RoomMessageEventContent::text_plain( + "User does not belong to our server.", + )); + } + + let mut rooms: Vec<(OwnedRoomId, u64, String)> = vec![]; // room ID, members joined, room name + + for room_id in services().rooms.state_cache.rooms_joined(&user_id) { + let room_id = room_id?; + rooms.push(Self::get_room_info(room_id)); + } + + if rooms.is_empty() { + return Ok(RoomMessageEventContent::text_plain( + "User is not in any rooms.", + )); + } + + rooms.sort_by_key(|r| r.1); + rooms.reverse(); + + let output_plain = format!( + "Rooms {user_id} Joined:\n{}", + rooms + .iter() + .map(|(id, members, name)| format!( + "{id}\tMembers: {members}\tName: {name}" + )) + .collect::>() + .join("\n") + ); + let output_html = format!( + "\n\t\t\n{}
Rooms {user_id} Joined
idmembersname
", + rooms + .iter() + .fold(String::new(), |mut output, (id, members, name)| { + writeln!(output, "{}\t{}\t{}", escape_html(id.as_ref()), + members, + escape_html(name)).unwrap(); + output + }) + ); + RoomMessageEventContent::text_html(output_plain, output_html) + } }, AdminCommand::Rooms(command) => match command { RoomCommand::Moderation(command) => match command { diff --git a/src/service/rooms/event_handler/mod.rs b/src/service/rooms/event_handler/mod.rs index 0c83ee45..b8dde553 100644 --- a/src/service/rooms/event_handler/mod.rs +++ b/src/service/rooms/event_handler/mod.rs @@ -190,7 +190,22 @@ impl Service { } if errors >= 5 { - break; + // Timeout other events + match services() + .globals + .bad_event_ratelimiter + .write() + .unwrap() + .entry((*prev_id).to_owned()) + { + hash_map::Entry::Vacant(e) => { + e.insert((Instant::now(), 1)); + } + hash_map::Entry::Occupied(mut e) => { + *e.get_mut() = (Instant::now(), e.get().1 + 1) + } + } + continue; } if let Some((pdu, json)) = eventid_info.remove(&*prev_id) { diff --git a/src/service/rooms/search/mod.rs b/src/service/rooms/search/mod.rs index b6f35e79..139169ef 100644 --- a/src/service/rooms/search/mod.rs +++ b/src/service/rooms/search/mod.rs @@ -11,7 +11,7 @@ pub struct Service { impl Service { #[tracing::instrument(skip(self))] - pub fn index_pdu<'a>(&self, shortroomid: u64, pdu_id: &[u8], message_body: &str) -> Result<()> { + pub fn index_pdu(&self, shortroomid: u64, pdu_id: &[u8], message_body: &str) -> Result<()> { self.db.index_pdu(shortroomid, pdu_id, message_body) } diff --git a/src/service/rooms/timeline/mod.rs b/src/service/rooms/timeline/mod.rs index 38b038e0..a96d8748 100644 --- a/src/service/rooms/timeline/mod.rs +++ b/src/service/rooms/timeline/mod.rs @@ -224,7 +224,7 @@ impl Service { /// /// Returns pdu id #[tracing::instrument(skip(self, pdu, pdu_json, leaves))] - pub async fn append_pdu<'a>( + pub async fn append_pdu( &self, pdu: &PduEvent, mut pdu_json: CanonicalJsonObject, @@ -318,6 +318,28 @@ impl Service { let mut pdu_id = shortroomid.to_be_bytes().to_vec(); pdu_id.extend_from_slice(&count2.to_be_bytes()); + // https://spec.matrix.org/v1.9/rooms/v11/#moving-the-redacts-property-of-mroomredaction-events-to-a-content-property + // For backwards-compatibility with older clients, + // servers should add a redacts property to the top level of m.room.redaction events in when serving such events over the Client-Server API. + if pdu.kind == TimelineEventType::RoomRedaction + && services().rooms.state.get_room_version(&pdu.room_id)? == RoomVersionId::V11 + { + #[derive(Deserialize)] + struct Redaction { + redacts: Option, + } + + let content = serde_json::from_str::(pdu.content.get()) + .map_err(|_| Error::bad_database("Invalid content in redaction pdu."))?; + + if let Some(redact_id) = &content.redacts { + pdu_json.insert( + "redacts".to_owned(), + CanonicalJsonValue::String(redact_id.to_string()), + ); + } + } + // Insert pdu self.db.append_pdu(&pdu_id, pdu, &pdu_json, count2)?; @@ -1019,7 +1041,7 @@ impl Service { /// Append the incoming event setting the state snapshot to the state from the /// server that sent the event. #[tracing::instrument(skip_all)] - pub async fn append_incoming_pdu<'a>( + pub async fn append_incoming_pdu( &self, pdu: &PduEvent, pdu_json: CanonicalJsonObject,