Matrix/entities/room/roomController.go
2020-10-12 16:16:28 +02:00

770 lines
21 KiB
Go

package room
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
"time"
"git.nutfactory.org/hoernschen/Matrix/config"
"git.nutfactory.org/hoernschen/Matrix/entities/event"
"git.nutfactory.org/hoernschen/Matrix/entities/user"
"git.nutfactory.org/hoernschen/Matrix/utils"
"github.com/cenkalti/backoff/v4"
"github.com/gorilla/mux"
)
func New(
version string,
name string,
topic string,
visibility string,
isDirect bool,
federated bool,
creatorId string,
) (err error, newRoom *Room) {
err, roomId := utils.CreateUUID()
if err != nil {
return
}
id := generateRoomId(roomId)
newRoom = &Room{
Id: id,
Version: version,
Name: name,
Topic: topic,
Members: []string{creatorId},
Events: make(map[string]*event.Event),
Visibility: visibility,
IsDirect: isDirect,
Federated: federated,
}
return
}
func CreateRoomHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
request := CreateRoomRequest{}
errResponse := utils.CheckRequest(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
token, errResponse := utils.GetAccessToken(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
foundUser, err := user.ReadUserFromAccessToken(token)
if err != nil || foundUser == nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorCode: "M_UNKNOWN_TOKEN", ErrorMessage: fmt.Sprintf("%s", err)}); err != nil {
panic(err)
}
return
}
decoder := json.NewDecoder(r.Body)
err = decoder.Decode(&request)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Could not parse JSON: %s", err)}); err != nil {
panic(err)
}
return
}
err, newRoom := New(
request.RoomVersion,
request.Name,
request.Topic,
request.Visibility,
request.IsDirect,
request.CreationContent.Federated,
foundUser.Id,
)
err = CreateRoom(newRoom)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Database Error: %s", err)}); err != nil {
panic(err)
}
return
}
createEventContent := event.CreateEventContent{
Creator: foundUser.Id,
Federated: request.CreationContent.Federated,
RoomVersion: newRoom.Version,
}
createEventContentBytes, _ := json.Marshal(createEventContent)
err, createEvent := event.New(
newRoom.Id,
foundUser.Id,
config.Homeserver,
time.Now().Unix(),
"m.room.create",
"",
string(createEventContentBytes),
"",
)
if err == nil {
err = event.CreateEvent(createEvent, "")
}
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Error Event-Creation: %s", err)}); err != nil {
panic(err)
}
return
}
memberEventContent := event.MemberEventContent{
DisplayName: foundUser.Name,
IsDirect: request.IsDirect,
Membership: "join",
}
memberEventContentBytes, _ := json.Marshal(memberEventContent)
err, memberEvent := event.New(
newRoom.Id,
foundUser.Id,
config.Homeserver,
time.Now().Unix(),
"m.room.member",
foundUser.Id,
string(memberEventContentBytes),
"",
)
if err == nil {
err = event.CreateEvent(memberEvent, "")
}
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Error Event-Creation: %s", err)}); err != nil {
panic(err)
}
return
}
userPowerLevel := make(map[string]int)
userPowerLevel[foundUser.Id] = 100
powerLevelEventContent := event.PowerLevelsEventContent{
Ban: 50,
EventsDefault: 0,
Invite: 50,
Kick: 50,
Redact: 50,
StateDefault: 50,
Users: userPowerLevel,
UsersDefault: 0,
Notifications: event.Notifications{
Room: 50,
},
}
powerLevelEventContentBytes, _ := json.Marshal(powerLevelEventContent)
err, powerLevelEvent := event.New(
newRoom.Id,
foundUser.Id,
config.Homeserver,
time.Now().Unix(),
"m.room.power_levels",
"",
string(powerLevelEventContentBytes),
"",
)
if err == nil {
err = event.CreateEvent(powerLevelEvent, "")
}
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Error Event-Creation: %s", err)}); err != nil {
panic(err)
}
return
}
joinRule := "invite"
historyVisibilty := "shared"
guestAccess := "can_join"
if request.Preset == "public_chat" {
joinRule = "public"
guestAccess = "forbidden"
}
joinRuleEventContent := event.JoinRuleEventContent{
JoinRule: joinRule,
}
joinRuleEventContentBytes, _ := json.Marshal(joinRuleEventContent)
err, joinRulesEvent := event.New(
newRoom.Id,
foundUser.Id,
config.Homeserver,
time.Now().Unix(),
"m.room.join_rules",
"",
string(joinRuleEventContentBytes),
"",
)
if err == nil {
err = event.CreateEvent(joinRulesEvent, "")
}
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Error Event-Creation: %s", err)}); err != nil {
panic(err)
}
return
}
historyVisiblilityEventContent := event.HistoryVisibilityEventContent{
HistoryVisibility: historyVisibilty,
}
historyVisiblilityContentBytes, _ := json.Marshal(historyVisiblilityEventContent)
err, historyVisibilityEvent := event.New(
newRoom.Id,
foundUser.Id,
config.Homeserver,
time.Now().Unix(),
"m.room.history_visibility",
"",
string(historyVisiblilityContentBytes),
"",
)
if err == nil {
err = event.CreateEvent(historyVisibilityEvent, "")
}
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Error Event-Creation: %s", err)}); err != nil {
panic(err)
}
return
}
guestAccessEventContent := event.GuestAccessEventContent{
GuestAccess: guestAccess,
}
guestAccessContentBytes, _ := json.Marshal(guestAccessEventContent)
err, guestAccessEvent := event.New(
newRoom.Id,
foundUser.Id,
config.Homeserver,
time.Now().Unix(),
"m.room.guest_access",
"",
string(guestAccessContentBytes),
"",
)
if err == nil {
err = event.CreateEvent(guestAccessEvent, "")
}
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Error Event-Creation: %s", err)}); err != nil {
panic(err)
}
return
}
nameEventContent := event.NameEventContent{
Name: newRoom.Name,
}
nameContentBytes, _ := json.Marshal(nameEventContent)
err, nameEvent := event.New(
newRoom.Id,
foundUser.Id,
config.Homeserver,
time.Now().Unix(),
"m.room.name",
"",
string(nameContentBytes),
"",
)
if err == nil {
err = event.CreateEvent(nameEvent, "")
}
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Error Event-Creation: %s", err)}); err != nil {
panic(err)
}
return
}
topicEventContent := event.TopicEventContent{
Topic: newRoom.Topic,
}
topicContentBytes, _ := json.Marshal(topicEventContent)
err, topicEvent := event.New(
newRoom.Id,
foundUser.Id,
config.Homeserver,
time.Now().Unix(),
"m.room.topic",
"",
string(topicContentBytes),
"",
)
if err == nil {
err = event.CreateEvent(topicEvent, "")
}
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Error Event-Creation: %s", err)}); err != nil {
panic(err)
}
return
}
response := CreateRoomResponse{RoomId: newRoom.Id}
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(response); err != nil {
panic(err)
}
}
func GetRoomMemberHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
request := getRoomMemberRequest{}
errResponse := utils.CheckRequest(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
token, errResponse := utils.GetAccessToken(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
foundUser, err := user.ReadUserFromAccessToken(token)
if err != nil || foundUser == nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorCode: "M_UNKNOWN_TOKEN", ErrorMessage: fmt.Sprintf("%s", err)}); err != nil {
panic(err)
}
return
}
decoder := json.NewDecoder(r.Body)
err = decoder.Decode(&request)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Could not parse JSON: %s", err)}); err != nil {
panic(err)
}
return
}
vars := mux.Vars(r)
roomId := vars["roomId"]
if roomId == "" {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Missing Params"}); err != nil {
panic(err)
}
return
}
event.ReadStateEvents(roomId, "m.room.member")
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode("Not Implemented"); err != nil {
panic(err)
}
}
func JoinRoomUserHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
request := JoinRoomUserRequest{}
errResponse := utils.CheckRequest(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
token, errResponse := utils.GetAccessToken(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
foundUser, err := user.ReadUserFromAccessToken(token)
if err != nil || foundUser == nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorCode: "M_UNKNOWN_TOKEN", ErrorMessage: fmt.Sprintf("%s", err)}); err != nil {
panic(err)
}
return
}
decoder := json.NewDecoder(r.Body)
err = decoder.Decode(&request)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Could not parse JSON: %s", err)}); err != nil {
panic(err)
}
return
}
vars := mux.Vars(r)
roomId := vars["roomId"]
if roomId == "" {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Missing Parameter"}); err != nil {
panic(err)
}
return
}
foundRoom, err := ReadRoom(roomId)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Database Error: %s", err)}); err != nil {
panic(err)
}
return
}
var joinEvent *event.Event
if foundRoom == nil {
memberEventContent := event.MemberEventContent{
DisplayName: foundUser.Name,
IsDirect: true,
Membership: "join",
}
memberEventContentBytes, _ := json.Marshal(memberEventContent)
err, memberEvent := event.New(
roomId,
foundUser.Id,
config.Homeserver,
time.Now().Unix(),
"m.room.member",
foundUser.Id,
string(memberEventContentBytes),
"",
)
if err == nil {
err = event.CreateEvent(memberEvent, "")
}
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Error Event-Creation: %s", err)}); err != nil {
panic(err)
}
return
}
httpString := "https"
server := strings.Split(roomId, ":")[1]
requestUrl := fmt.Sprintf("%s://%s/_matrix/federation/v1/make_join/%s/%s", httpString, server, roomId, foundUser.Id)
res, err := http.Get(requestUrl)
makeJoinRes := makeJoinResponse{}
decoder = json.NewDecoder(res.Body)
err = decoder.Decode(&makeJoinRes)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Could not parse JSON: %s", err)}); err != nil {
panic(err)
}
return
}
err = CreateRoom(&Room{Id: roomId, Version: makeJoinRes.RoomVersion})
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Database Error: %s", err)}); err != nil {
panic(err)
}
return
}
err, joinEvent = event.New(
roomId,
makeJoinRes.Event.Sender,
makeJoinRes.Event.Origin,
makeJoinRes.Event.Timestamp,
makeJoinRes.Event.EventType,
makeJoinRes.Event.StateKey,
makeJoinRes.Event.Content,
"",
)
requestUrl = fmt.Sprintf("%s://%s/_matrix/federation/v2/send_join/%s/%s", httpString, server, roomId, joinEvent.Id)
reqBody, err := json.Marshal(joinEvent)
if err != nil {
return
}
client := &http.Client{}
req, err := http.NewRequest(http.MethodPut, requestUrl, bytes.NewBuffer(reqBody))
if err != nil {
return
}
res, err = client.Do(req)
joinRes := joinRoomServerResponse{}
decoder = json.NewDecoder(res.Body)
err = decoder.Decode(&joinRes)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Could not parse JSON: %s", err)}); err != nil {
panic(err)
}
return
}
err = event.HandleEvents(joinRes.State)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Error Handling Events: %s", err)}); err != nil {
panic(err)
}
return
}
} else {
memberEventContent := event.MemberEventContent{
DisplayName: foundUser.Name,
IsDirect: true,
Membership: "join",
}
memberEventContentBytes, _ := json.Marshal(memberEventContent)
err, joinEvent = event.New(
roomId,
foundUser.Id,
config.Homeserver,
time.Now().Unix(),
"m.room.member",
foundUser.Id,
string(memberEventContentBytes),
"",
)
}
err, txnId := utils.CreateUUID()
err = event.CreateEvent(joinEvent, txnId)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Database Error: %s", err)}); err != nil {
panic(err)
}
return
}
transaction := &event.Transaction{
Id: txnId,
Origin: config.Homeserver,
Timestamp: time.Now().Unix(),
PDUS: []*event.Event{joinEvent},
}
servers, err := event.ReadServers(roomId)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Database Error: %s", err)}); err != nil {
panic(err)
}
return
}
for _, server := range servers {
//if server != config.Homeserver {
operation := func() error {
return event.SendTransaction(transaction, server)
}
notify := func(err error, duration time.Duration) {
log.Printf("Error Sending Transaction, retrying in %ss: %s", duration/1000000000, err)
}
go backoff.RetryNotify(operation, backoff.NewExponentialBackOff(), notify)
//}
}
err = CreateRoomMember(roomId, foundUser.Id)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Database Error: %s", err)}); err != nil {
panic(err)
}
return
}
response := JoinRoomUserResponse{RoomId: roomId}
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(response); err != nil {
panic(err)
}
}
func GetPrepInfoToJoinHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
request := makeJoinRequest{}
errResponse := utils.CheckRequest(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&request)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Could not parse JSON: %s", err)}); err != nil {
panic(err)
}
return
}
vars := mux.Vars(r)
roomId := vars["roomId"]
userId := vars["userId"]
if roomId == "" || userId == "" {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Missing Parameter"}); err != nil {
panic(err)
}
return
}
homeserver := strings.Split(userId, ":")
if len(homeserver) <= 1 {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Missing Homeserver in UserId"}); err != nil {
panic(err)
}
return
}
foundRoom, err := ReadRoom(roomId)
if err != nil || foundRoom == nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorCode: "MISSING_ROOM", ErrorMessage: fmt.Sprintf("%s", err)}); err != nil {
panic(err)
}
return
}
memberEventContent := event.MemberEventContent{
Membership: "join",
}
memberEventContentBytes, err := json.Marshal(memberEventContent)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Could not parse JSON: %s", err)}); err != nil {
panic(err)
}
return
}
response := makeJoinResponse{
RoomVersion: foundRoom.Version,
Event: event.Event{
Sender: userId,
Origin: homeserver[1],
Timestamp: time.Now().Unix(),
EventType: "m.room.member",
StateKey: userId,
Content: string(memberEventContentBytes),
},
}
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(response); err != nil {
panic(err)
}
}
// TODO: TEST
func JoinRoomServerHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
request := event.Event{}
errResponse := utils.CheckRequest(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&request)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Could not parse JSON: %s", err)}); err != nil {
panic(err)
}
return
}
vars := mux.Vars(r)
roomId := vars["roomId"]
eventId := vars["eventId"]
if roomId == "" || eventId == "" {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Missing Parameter"}); err != nil {
panic(err)
}
return
}
foundRoom, err := ReadRoom(roomId)
if err != nil || foundRoom == nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorCode: "MISSING_ROOM", ErrorMessage: fmt.Sprintf("%s", err)}); err != nil {
panic(err)
}
return
}
request.RoomId = roomId
request.Id = eventId
memberEventContent := event.MemberEventContent{}
err = json.Unmarshal([]byte(request.Content), &memberEventContent)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Could not parse JSON: %s", err)}); err != nil {
panic(err)
}
return
}
if memberEventContent.Membership != "join" {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Wrong Membership"}); err != nil {
panic(err)
}
return
}
if err == nil {
err = event.CreateEvent(&request, "")
}
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Error Event-Creation: %s", err)}); err != nil {
panic(err)
}
return
}
CreateRoomMember(roomId, request.StateKey)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Database Error: %s", err)}); err != nil {
panic(err)
}
return
}
authChain, err := event.GetAuthChain(&request)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Error Creating Auth Chain: %s", err)}); err != nil {
panic(err)
}
return
}
stateEvents, err := event.ReadStateEventsFromRoom(roomId)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Database Error: %s", err)}); err != nil {
panic(err)
}
return
}
response := joinRoomServerResponse{
Origin: config.Homeserver,
AuthChain: authChain,
State: stateEvents,
}
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(response); err != nil {
panic(err)
}
}
func generateRoomId(id string) string {
return fmt.Sprintf("!%s:%s", id, config.Homeserver)
}