@ -5,15 +5,18 @@ use std::{
} ;
use log ::{ debug , warn } ;
use rocket ::{ get, options , post , put , State } ;
use rocket ::{ delete, get, options , post , put , State } ;
use ruma_client_api ::{
error ::{ Error , ErrorKind } ,
r0 ::{
account ::{ get_username_availability , register } ,
alias ::get_alias ,
alias ::{ create_alias , delete_alias , get_alias } ,
capabilities ::get_capabilities ,
config ::{ get_global_account_data , set_global_account_data } ,
directory ::{ self , get_public_rooms , get_public_rooms_filtered } ,
directory ::{
self , get_public_rooms , get_public_rooms_filtered , get_room_visibility ,
set_room_visibility ,
} ,
filter ::{ self , create_filter , get_filter } ,
keys ::{ claim_keys , get_keys , upload_keys } ,
media ::{ create_content , get_content , get_content_thumbnail , get_media_config } ,
@ -28,6 +31,7 @@ use ruma_client_api::{
} ,
push ::{ get_pushrules_all , set_pushrule , set_pushrule_enabled } ,
read_marker ::set_read_marker ,
redact ::redact_event ,
room ::{ self , create_room } ,
session ::{ get_login_types , login , logout } ,
state ::{
@ -45,10 +49,10 @@ use ruma_client_api::{
} ;
use ruma_events ::{
collections ::only ::Event as EduEvent ,
room ::{ guest_access, history_visibility , join_rules , member } ,
room ::{ canonical_alias, guest_access, history_visibility , join_rules , member , redaction } ,
EventJson , EventType ,
} ;
use ruma_identifiers ::{ Room Id, RoomVersionId , UserId } ;
use ruma_identifiers ::{ Room AliasId, Room Id, RoomVersionId , UserId } ;
use serde_json ::{ json , value ::RawValue } ;
use crate ::{ server_server , utils , Database , MatrixResult , Ruma } ;
@ -517,6 +521,7 @@ pub fn set_displayname_route(
. unwrap ( ) ,
None ,
Some ( user_id . to_string ( ) ) ,
None ,
& db . globals ,
)
. unwrap ( ) ;
@ -564,7 +569,7 @@ pub fn set_avatar_url_route(
) -> MatrixResult < set_avatar_url ::Response > {
let user_id = body . user_id . as_ref ( ) . expect ( "user is authenticated" ) ;
if let avatar_url = & body . avatar_url {
if let Some ( avatar_url ) = & body . avatar_url {
if ! avatar_url . starts_with ( "mxc://" ) {
debug ! ( "Request contains an invalid avatar_url." ) ;
return MatrixResult ( Err ( Error {
@ -579,7 +584,7 @@ pub fn set_avatar_url_route(
}
db . users
. set_avatar_url ( & user_id , Some ( body . avatar_url . clone ( ) ) )
. set_avatar_url ( & user_id , body . avatar_url . clone ( ) )
. unwrap ( ) ;
// Send a new membership event into all joined rooms
@ -591,7 +596,7 @@ pub fn set_avatar_url_route(
user_id . clone ( ) ,
EventType ::RoomMember ,
serde_json ::to_value ( ruma_events ::room ::member ::MemberEventContent {
avatar_url : Some ( body. avatar_url . clone ( ) ) ,
avatar_url : body. avatar_url . clone ( ) ,
.. serde_json ::from_value ::< EventJson < _ > > (
db . rooms
. room_state ( & room_id )
@ -608,6 +613,7 @@ pub fn set_avatar_url_route(
. unwrap ( ) ,
None ,
Some ( user_id . to_string ( ) ) ,
None ,
& db . globals ,
)
. unwrap ( ) ;
@ -669,7 +675,7 @@ pub fn get_profile_route(
MatrixResult ( Err ( Error {
kind : ErrorKind ::NotFound ,
message : "Profile was not found." . to_owned ( ) ,
status_code : http ::StatusCode ::BAD_REQUEST ,
status_code : http ::StatusCode ::NOT_FOUND ,
} ) )
}
@ -906,8 +912,32 @@ pub fn create_room_route(
let room_id = RoomId ::new ( db . globals . server_name ( ) ) . expect ( "host is valid" ) ;
let user_id = body . user_id . as_ref ( ) . expect ( "user is authenticated" ) ;
// TODO: Create alias and check if it already exists
let alias = if let Some ( localpart ) = & body . room_alias_name {
// TODO: Check for invalid characters and maximum length
if let Ok ( alias ) =
RoomAliasId ::try_from ( format! ( "#{}:{}" , localpart , db . globals . server_name ( ) ) )
{
if db . rooms . id_from_alias ( & alias ) . unwrap ( ) . is_some ( ) {
return MatrixResult ( Err ( Error {
kind : ErrorKind ::Unknown ,
message : "Alias already exists." . to_owned ( ) ,
status_code : http ::StatusCode ::BAD_REQUEST ,
} ) ) ;
}
Some ( alias )
} else {
return MatrixResult ( Err ( Error {
kind : ErrorKind ::Unknown ,
message : "Invalid alias." . to_owned ( ) ,
status_code : http ::StatusCode ::BAD_REQUEST ,
} ) ) ;
}
} else {
None
} ;
// 1. The room create event
db . rooms
. append_pdu (
room_id . clone ( ) ,
@ -915,21 +945,22 @@ pub fn create_room_route(
EventType ::RoomCreate ,
serde_json ::to_value ( ruma_events ::room ::create ::CreateEventContent {
creator : user_id . clone ( ) ,
federate : body
federate : body . creation_content . as_ref ( ) . map_or ( true , | c | c . federate ) ,
predecessor : body
. creation_content
. and_then ( | c | c . federate )
. unwrap_or ( true ) ,
predecessor : None , // TODO: Check creation_content.predecessor once ruma has that
. as_ref ( )
. and_then ( | c | c . predecessor . clone ( ) ) ,
room_version : RoomVersionId ::version_5 ( ) ,
} )
. unwrap ( ) ,
None ,
Some ( "" . to_owned ( ) ) ,
None ,
& db . globals ,
)
. unwrap ( ) ;
// Join room
// 2. Let the room creator join
db . rooms
. append_pdu (
room_id . clone ( ) ,
@ -945,6 +976,7 @@ pub fn create_room_route(
. unwrap ( ) ,
None ,
Some ( user_id . to_string ( ) ) ,
None ,
& db . globals ,
)
. unwrap ( ) ;
@ -956,7 +988,7 @@ pub fn create_room_route(
room ::Visibility ::Public = > create_room ::RoomPreset ::PublicChat ,
} ) ;
// 0 . Power levels
// 3 . Power levels
let mut users = BTreeMap ::new ( ) ;
users . insert ( user_id . clone ( ) , 100. into ( ) ) ;
for invite_user_id in & body . invite {
@ -991,12 +1023,13 @@ pub fn create_room_route(
power_levels_content ,
None ,
Some ( "" . to_owned ( ) ) ,
None ,
& db . globals ,
)
. unwrap ( ) ;
// 1 . Events set by preset
// 1 .1 Join Rules
// 4 . Events set by preset
// 4 .1 Join Rules
db . rooms
. append_pdu (
room_id . clone ( ) ,
@ -1016,11 +1049,12 @@ pub fn create_room_route(
} ,
None ,
Some ( "" . to_owned ( ) ) ,
None ,
& db . globals ,
)
. unwrap ( ) ;
// 1 .2 History Visibility
// 4 .2 History Visibility
db . rooms
. append_pdu (
room_id . clone ( ) ,
@ -1032,11 +1066,12 @@ pub fn create_room_route(
. unwrap ( ) ,
None ,
Some ( "" . to_owned ( ) ) ,
None ,
& db . globals ,
)
. unwrap ( ) ;
// 1 .3 Guest Access
// 4 .3 Guest Access
db . rooms
. append_pdu (
room_id . clone ( ) ,
@ -1056,11 +1091,12 @@ pub fn create_room_route(
} ,
None ,
Some ( "" . to_owned ( ) ) ,
None ,
& db . globals ,
)
. unwrap ( ) ;
// 2 . Events listed in initial_state
// 5 . Events listed in initial_state
for create_room ::InitialStateEvent {
event_type ,
state_key ,
@ -1071,16 +1107,17 @@ pub fn create_room_route(
. append_pdu (
room_id . clone ( ) ,
user_id . clone ( ) ,
EventType::from ( event_type ) ,
event_type. clone ( ) ,
serde_json ::from_str ( content . get ( ) ) . unwrap ( ) ,
None ,
state_key . clone ( ) ,
None ,
& db . globals ,
)
. unwrap ( ) ;
}
// 3 . Events implied by name and topic
// 6 . Events implied by name and topic
if let Some ( name ) = & body . name {
db . rooms
. append_pdu (
@ -1093,6 +1130,7 @@ pub fn create_room_route(
. unwrap ( ) ,
None ,
Some ( "" . to_owned ( ) ) ,
None ,
& db . globals ,
)
. unwrap ( ) ;
@ -1110,12 +1148,13 @@ pub fn create_room_route(
. unwrap ( ) ,
None ,
Some ( "" . to_owned ( ) ) ,
None ,
& db . globals ,
)
. unwrap ( ) ;
}
// 4 . Events implied by invite (and TODO: invite_3pid)
// 7 . Events implied by invite (and TODO: invite_3pid)
for user in & body . invite {
db . rooms
. append_pdu (
@ -1132,43 +1171,119 @@ pub fn create_room_route(
. unwrap ( ) ,
None ,
Some ( user . to_string ( ) ) ,
None ,
& db . globals ,
)
. unwrap ( ) ;
}
// Homeserver specific stuff
if let Some ( alias ) = alias {
db . rooms
. set_alias ( & alias , Some ( & room_id ) , & db . globals )
. unwrap ( ) ;
}
if let Some ( room ::Visibility ::Public ) = body . visibility {
db . rooms . set_public ( & room_id , true ) . unwrap ( ) ;
}
MatrixResult ( Ok ( create_room ::Response { room_id } ) )
}
#[ put(
"/_matrix/client/r0/rooms/<_room_id>/redact/<_event_id>/<_txn_id>" ,
data = "<body>"
) ]
pub fn redact_event_route (
db : State < ' _ , Database > ,
body : Ruma < redact_event ::Request > ,
_room_id : String ,
_event_id : String ,
_txn_id : String ,
) -> MatrixResult < redact_event ::Response > {
let user_id = body . user_id . as_ref ( ) . expect ( "user is authenticated" ) ;
if let Ok ( event_id ) = db . rooms . append_pdu (
body . room_id . clone ( ) ,
user_id . clone ( ) ,
EventType ::RoomRedaction ,
serde_json ::to_value ( redaction ::RedactionEventContent {
reason : body . reason . clone ( ) ,
} )
. unwrap ( ) ,
None ,
None ,
Some ( body . event_id . clone ( ) ) ,
& db . globals ,
) {
MatrixResult ( Ok ( redact_event ::Response { event_id } ) )
} else {
MatrixResult ( Err ( Error {
kind : ErrorKind ::Unknown ,
message : "Failed to redact event." . to_owned ( ) ,
status_code : http ::StatusCode ::BAD_REQUEST ,
} ) )
}
}
#[ put( " /_matrix/client/r0/directory/room/<_room_alias> " , data = " <body> " ) ]
pub fn create_alias_route (
db : State < ' _ , Database > ,
body : Ruma < create_alias ::Request > ,
_room_alias : String ,
) -> MatrixResult < create_alias ::Response > {
if db . rooms . id_from_alias ( & body . room_alias ) . unwrap ( ) . is_some ( ) {
return MatrixResult ( Err ( Error {
kind : ErrorKind ::Unknown ,
message : "Alias already exists" . to_owned ( ) ,
status_code : http ::StatusCode ::BAD_REQUEST ,
} ) ) ;
}
db . rooms
. set_alias ( & body . room_alias , Some ( & body . room_id ) , & db . globals )
. unwrap ( ) ;
MatrixResult ( Ok ( create_alias ::Response ) )
}
#[ delete( " /_matrix/client/r0/directory/room/<_room_alias> " , data = " <body> " ) ]
pub fn delete_alias_route (
db : State < ' _ , Database > ,
body : Ruma < delete_alias ::Request > ,
_room_alias : String ,
) -> MatrixResult < delete_alias ::Response > {
db . rooms
. set_alias ( & body . room_alias , None , & db . globals )
. unwrap ( ) ;
MatrixResult ( Ok ( delete_alias ::Response ) )
}
#[ get( " /_matrix/client/r0/directory/room/<_room_alias> " , data = " <body> " ) ]
pub fn get_alias_route (
db : State < ' _ , Database > ,
body : Ruma < get_alias ::Request > ,
_room_alias : String ,
) -> MatrixResult < get_alias ::Response > {
warn ! ( "TODO: get_alias_route" ) ;
let room_id = if body . room_alias . server_name ( ) = = db . globals . server_name ( ) {
match body . room_alias . alias ( ) {
"conduit" = > "!lgOCCXQKtXOAPlAlG5:conduit.rs" ,
_ = > {
debug ! ( "Room alias not found." ) ;
return MatrixResult ( Err ( Error {
kind : ErrorKind ::NotFound ,
message : "Room not found." . to_owned ( ) ,
status_code : http ::StatusCode ::BAD_REQUEST ,
} ) ) ;
}
if body . room_alias . server_name ( ) = = db . globals . server_name ( ) {
if let Some ( room_id ) = db . rooms . id_from_alias ( & body . room_alias ) . unwrap ( ) {
MatrixResult ( Ok ( get_alias ::Response {
room_id ,
servers : vec ! [ db . globals . server_name ( ) . to_owned ( ) ] ,
} ) )
} else {
debug ! ( "Room alias not found." ) ;
return MatrixResult ( Err ( Error {
kind : ErrorKind ::NotFound ,
message : "Room with alias not found." . to_owned ( ) ,
status_code : http ::StatusCode ::BAD_REQUEST ,
} ) ) ;
}
} else {
todo! ( "ask remote server" ) ;
}
. try_into ( )
. unwrap ( ) ;
MatrixResult ( Ok ( get_alias ::Response {
room_id ,
servers : vec ! [ "conduit.rs" . to_owned ( ) ] ,
} ) )
}
#[ post( " /_matrix/client/r0/rooms/<_room_id>/join " , data = " <body> " ) ]
@ -1220,6 +1335,7 @@ pub fn join_room_by_id_route(
serde_json ::to_value ( event ) . unwrap ( ) ,
None ,
Some ( user_id . to_string ( ) ) ,
None ,
& db . globals ,
)
. unwrap ( ) ;
@ -1237,17 +1353,21 @@ pub fn join_room_by_id_or_alias_route(
) -> MatrixResult < join_room_by_id_or_alias ::Response > {
let room_id = match RoomId ::try_from ( body . room_id_or_alias . clone ( ) ) {
Ok ( room_id ) = > room_id ,
Err ( room_alias ) = > {
if room_alias . server_name ( ) = = db . globals . server_name ( ) {
Err ( _ ) = > {
if let Some ( room_id ) = db
. rooms
. id_from_alias ( & body . room_id_or_alias . clone ( ) . try_into ( ) . unwrap ( ) )
. unwrap ( )
{
room_id
} else {
// Ask creator server of the room to join TODO ask someone else when not available
//server_server::send_request(data, destination, request)
return MatrixResult ( Err ( Error {
kind : ErrorKind ::NotFound ,
message : "Room alias not found." . to_owned ( ) ,
status_code : http ::StatusCode ::BAD_REQUEST ,
} ) ) ;
} else {
// Ask creator server of the room to join TODO ask someone else when not available
//server_server::send_request(data, destination, request)
todo! ( ) ;
}
}
} ;
@ -1285,6 +1405,7 @@ pub fn leave_room_route(
json ! ( { "membership" : "leave" } ) ,
None ,
Some ( user_id . to_string ( ) ) ,
None ,
& db . globals ,
)
. unwrap ( ) ;
@ -1328,6 +1449,7 @@ pub fn invite_user_route(
serde_json ::to_value ( event ) . unwrap ( ) ,
None ,
Some ( user_id . to_string ( ) ) ,
None ,
& db . globals ,
)
. unwrap ( ) ;
@ -1342,6 +1464,35 @@ pub fn invite_user_route(
}
}
#[ put( " /_matrix/client/r0/directory/list/room/<_room_id> " , data = " <body> " ) ]
pub async fn set_room_visibility_route (
db : State < ' _ , Database > ,
body : Ruma < set_room_visibility ::Request > ,
_room_id : String ,
) -> MatrixResult < set_room_visibility ::Response > {
match body . visibility {
room ::Visibility ::Public = > db . rooms . set_public ( & body . room_id , true ) . unwrap ( ) ,
room ::Visibility ::Private = > db . rooms . set_public ( & body . room_id , false ) . unwrap ( ) ,
}
MatrixResult ( Ok ( set_room_visibility ::Response ) )
}
#[ get( " /_matrix/client/r0/directory/list/room/<_room_id> " , data = " <body> " ) ]
pub async fn get_room_visibility_route (
db : State < ' _ , Database > ,
body : Ruma < get_room_visibility ::Request > ,
_room_id : String ,
) -> MatrixResult < get_room_visibility ::Response > {
MatrixResult ( Ok ( get_room_visibility ::Response {
visibility : if db . rooms . is_public_room ( & body . room_id ) . unwrap ( ) {
room ::Visibility ::Public
} else {
room ::Visibility ::Private
} ,
} ) )
}
#[ get( " /_matrix/client/r0/publicRooms " , data = " <body> " ) ]
pub async fn get_public_rooms_route (
db : State < ' _ , Database > ,
@ -1399,9 +1550,10 @@ pub async fn get_public_rooms_filtered_route(
) -> MatrixResult < get_public_rooms_filtered ::Response > {
let mut chunk = db
. rooms
. all_rooms ( )
. into_iter ( )
. public_rooms ( )
. map ( | room_id | {
let room_id = room_id . unwrap ( ) ;
let state = db . rooms . room_state ( & room_id ) . unwrap ( ) ;
directory ::PublicRoomsChunk {
@ -1414,7 +1566,7 @@ pub async fn get_public_rooms_filtered_route(
. deserialize ( )
. unwrap ( )
. alias
} ) .map ( | a | a . to_string ( ) ) ,
} ) ,
name : state . get ( & ( EventType ::RoomName , "" . to_owned ( ) ) ) . map ( | s | {
serde_json ::from_value ::< EventJson < ruma_events ::room ::name ::NameEventContent > > (
s . content . clone ( ) ,
@ -1565,6 +1717,7 @@ pub fn create_message_event_route(
serde_json ::from_str ( body . json_body . unwrap ( ) . get ( ) ) . unwrap ( ) ,
Some ( unsigned ) ,
None ,
None ,
& db . globals ,
) {
MatrixResult ( Ok ( create_message_event ::Response { event_id } ) )
@ -1590,14 +1743,49 @@ pub fn create_state_event_for_key_route(
) -> MatrixResult < create_state_event_for_key ::Response > {
let user_id = body . user_id . as_ref ( ) . expect ( "user is authenticated" ) ;
// Reponse of with/without key is the same
let content =
serde_json ::from_str ::< serde_json ::Value > ( body . json_body . clone ( ) . unwrap ( ) . get ( ) ) . unwrap ( ) ;
if body . event_type = = EventType ::RoomCanonicalAlias {
let canonical_alias = serde_json ::from_value ::<
EventJson < canonical_alias ::CanonicalAliasEventContent > ,
> ( content . clone ( ) )
. unwrap ( )
. deserialize ( )
. unwrap ( ) ;
let mut aliases = canonical_alias . alt_aliases ;
if let Some ( alias ) = canonical_alias . alias {
aliases . push ( alias ) ;
}
for alias in aliases {
if alias . server_name ( ) ! = db . globals . server_name ( )
| | db
. rooms
. id_from_alias ( & alias )
. unwrap ( )
. filter ( | room | room = = & body . room_id ) // Make sure it's the right room
. is_none ( )
{
return MatrixResult ( Err ( Error {
kind : ErrorKind ::Unknown ,
message : "You are only allowed to send canonical_alias events when it's aliases already exists" . to_owned ( ) ,
status_code : http ::StatusCode ::BAD_REQUEST ,
} ) ) ;
}
}
}
if let Ok ( event_id ) = db . rooms . append_pdu (
body . room_id . clone ( ) ,
user_id . clone ( ) ,
body . event_type . clone ( ) ,
serde_json ::from_str ( body . json_body . clone ( ) . unwrap ( ) . get ( ) ) . unwrap ( ) ,
cont ent,
None ,
Some ( body . state_key . clone ( ) ) ,
None ,
& db . globals ,
) {
MatrixResult ( Ok ( create_state_event_for_key ::Response { event_id } ) )
@ -1620,26 +1808,43 @@ pub fn create_state_event_for_empty_key_route(
_room_id : String ,
_event_type : String ,
) -> MatrixResult < create_state_event_for_empty_key ::Response > {
let user_id = body . user_id . as_ref ( ) . expect ( "user is authenticated" ) ;
// This just calls create_state_event_for_key_route
let Ruma {
body :
create_state_event_for_empty_key ::Request {
room_id ,
event_type ,
data ,
} ,
user_id ,
device_id ,
json_body ,
} = body ;
// Reponse of with/without key is the same
if let Ok ( event_id ) = db . rooms . append_pdu (
body . room_id . clone ( ) ,
user_id . clone ( ) ,
body . event_type . clone ( ) ,
serde_json ::from_str ( body . json_body . unwrap ( ) . get ( ) ) . unwrap ( ) ,
None ,
Some ( "" . to_owned ( ) ) ,
& db . globals ,
) {
MatrixResult ( Ok ( create_state_event_for_empty_key ::Response { event_id } ) )
} else {
MatrixResult ( Err ( Error {
kind : ErrorKind ::Unknown ,
message : "Failed to send event." . to_owned ( ) ,
status_code : http ::StatusCode ::BAD_REQUEST ,
} ) )
}
let response = create_state_event_for_key_route (
db ,
Ruma {
body : create_state_event_for_key ::Request {
room_id ,
event_type ,
data ,
state_key : "" . to_owned ( ) ,
} ,
user_id ,
device_id ,
json_body ,
} ,
_room_id ,
_event_type ,
"" . to_owned ( ) ,
) ;
MatrixResult ( match response . 0 {
Ok ( create_state_event_for_key ::Response { event_id } ) = > {
Ok ( create_state_event_for_empty_key ::Response { event_id } )
}
Err ( e ) = > Err ( e ) ,
} )
}
#[ get( " /_matrix/client/r0/rooms/<_room_id>/state " , data = " <body> " ) ]
@ -1933,17 +2138,19 @@ pub fn sync_route(
let mut invited_rooms = BTreeMap ::new ( ) ;
for room_id in db . rooms . rooms_invited ( & user_id ) {
let room_id = room_id . unwrap ( ) ;
let events = db
. rooms
. pdus_since ( & room_id , since )
. unwrap ( )
. map ( | pdu | pdu . unwrap ( ) . to_stripped_state_event ( ) )
. collect ( ) ;
invited_rooms . insert (
room_id ,
room_id . clone ( ) ,
sync_events ::InvitedRoom {
invite_state : sync_events ::InviteState { events } ,
invite_state : sync_events ::InviteState {
events : db
. rooms
. room_state ( & room_id )
. unwrap ( )
. into_iter ( )
. map ( | ( _ , pdu ) | pdu . to_stripped_state_event ( ) )
. collect ( ) ,
} ,
} ,
) ;
}
@ -2046,12 +2253,12 @@ pub fn get_message_events_route(
. map ( | pdu | pdu . to_room_event ( ) )
. collect ::< Vec < _ > > ( ) ;
MatrixResult ( Ok ( get_message_events ::Response {
MatrixResult ( Ok ( dbg! ( get_message_events ::Response {
start : Some ( body . from . clone ( ) ) ,
end : prev_batch ,
chunk : room_events ,
state : Vec ::new ( ) ,
} ) )
} ) ) )
} else {
MatrixResult ( Err ( Error {
kind : ErrorKind ::Unknown ,