847 lines
23 KiB
Go
847 lines
23 KiB
Go
package event
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"math/rand"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"git.nutfactory.org/hoernschen/Matrix/config"
|
|
"git.nutfactory.org/hoernschen/Matrix/entities/device"
|
|
"git.nutfactory.org/hoernschen/Matrix/entities/user"
|
|
"git.nutfactory.org/hoernschen/Matrix/utils"
|
|
|
|
"github.com/cenkalti/backoff/v4"
|
|
"github.com/gorilla/mux"
|
|
)
|
|
|
|
func New(
|
|
roomId string,
|
|
sender string,
|
|
origin string,
|
|
timestamp int64,
|
|
eventType string,
|
|
stateKey string,
|
|
content string,
|
|
txnId string,
|
|
) (err error, newEvent *Event) {
|
|
err, eventId := utils.CreateUUID()
|
|
if err != nil {
|
|
return
|
|
}
|
|
id := generateEventId(eventId)
|
|
newEvent = &Event{
|
|
Id: id,
|
|
RoomId: roomId,
|
|
Sender: sender,
|
|
Origin: origin,
|
|
Timestamp: timestamp,
|
|
EventType: eventType,
|
|
StateKey: stateKey,
|
|
Content: content,
|
|
Unsigned: UnsignedData{
|
|
TransactionId: txnId,
|
|
},
|
|
}
|
|
newEvent.AuthEventHashes, err = GetAuthEvents(newEvent)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if eventType != "m.room.create" {
|
|
var depth int
|
|
newEvent.PrevEventHashes, depth, err = ReadEventsWithoutChild(roomId)
|
|
if err != nil {
|
|
return
|
|
}
|
|
newEvent.Depth = depth + 1
|
|
}
|
|
|
|
newEvent.AuthEventHashes, err = GetAuthEvents(newEvent)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
newEventBytesForHash, err := json.Marshal(newEvent)
|
|
if err != nil {
|
|
return
|
|
}
|
|
err, newEvent.Hashes.SHA256 = utils.Hash(newEventBytesForHash)
|
|
if err != nil {
|
|
return
|
|
}
|
|
newEvent.Unsigned = UnsignedData{}
|
|
newEventBytesForSign, err := json.Marshal(newEvent)
|
|
if err != nil {
|
|
return
|
|
}
|
|
newEvent.Signatures = utils.SignContent(newEventBytesForSign)
|
|
newEvent.Unsigned = UnsignedData{
|
|
TransactionId: txnId,
|
|
}
|
|
return
|
|
}
|
|
|
|
func SendMessageHandler(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
|
request := SendMessageRequest{}
|
|
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"]
|
|
eventType := vars["eventType"]
|
|
txnId := vars["txnId"]
|
|
if roomId == "" || eventType == "" || txnId == "" {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Missing Parameter"}); err != nil {
|
|
panic(err)
|
|
}
|
|
return
|
|
}
|
|
content, _ := json.Marshal(request)
|
|
err, newEvent := New(
|
|
roomId,
|
|
foundUser.Id,
|
|
config.Homeserver,
|
|
time.Now().Unix(),
|
|
eventType,
|
|
foundUser.Id,
|
|
string(content),
|
|
txnId,
|
|
)
|
|
if err != nil {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Error Creating Event: %s", err)}); err != nil {
|
|
panic(err)
|
|
}
|
|
return
|
|
}
|
|
err = CreateEvent(newEvent, 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 := &Transaction{
|
|
Id: txnId,
|
|
Origin: config.Homeserver,
|
|
Timestamp: time.Now().Unix(),
|
|
PDUS: []*Event{newEvent},
|
|
}
|
|
servers, err := 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 {
|
|
log.Printf("Send Transaction to %s", server)
|
|
|
|
operation := func() error {
|
|
return SendTransaction(transaction, server, config.HttpString, config.AuthentificationCheck)
|
|
}
|
|
notify := func(err error, duration time.Duration) {
|
|
log.Printf("Error Sending Transaction, retrying in %ss: %s", duration/1000000000, err)
|
|
}
|
|
backoff.RetryNotify(operation, backoff.NewExponentialBackOff(), notify)
|
|
|
|
}
|
|
}
|
|
response := createEventResponse{
|
|
EventId: newEvent.Id,
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func CreateStateEventHandler(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
|
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
|
|
}
|
|
vars := mux.Vars(r)
|
|
roomId := vars["roomId"]
|
|
eventType := vars["eventType"]
|
|
stateKey := vars["stateKey"]
|
|
if roomId == "" || eventType == "" {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Missing Parameter"}); err != nil {
|
|
panic(err)
|
|
}
|
|
return
|
|
}
|
|
buf := new(bytes.Buffer)
|
|
buf.ReadFrom(r.Body)
|
|
content := buf.String()
|
|
err, newEvent := New(
|
|
roomId,
|
|
foundUser.Id,
|
|
config.Homeserver,
|
|
time.Now().Unix(),
|
|
eventType,
|
|
stateKey,
|
|
content,
|
|
"",
|
|
)
|
|
if err != nil {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Error Creating Event: %s", err)}); err != nil {
|
|
panic(err)
|
|
}
|
|
return
|
|
}
|
|
err = CreateEvent(newEvent, "")
|
|
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, txnId := utils.CreateUUID()
|
|
transaction := &Transaction{
|
|
Id: txnId,
|
|
Origin: config.Homeserver,
|
|
Timestamp: time.Now().Unix(),
|
|
PDUS: []*Event{newEvent},
|
|
}
|
|
servers, err := 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 {
|
|
operation := func() error {
|
|
return SendTransaction(transaction, server, config.HttpString, config.AuthentificationCheck)
|
|
}
|
|
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)
|
|
}
|
|
|
|
response := createEventResponse{
|
|
EventId: newEvent.Id,
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func GetEventUserHandler(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
|
request := getEventRequest{}
|
|
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"]
|
|
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
|
|
}
|
|
foundEvent, err := ReadEvent(eventId)
|
|
if err != nil || foundEvent == nil {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorCode: "M_NOT_FOUND", ErrorMessage: fmt.Sprintf("Event not found. %s", err)}); err != nil {
|
|
panic(err)
|
|
}
|
|
return
|
|
}
|
|
response := getEventResponse{
|
|
Content: foundEvent.Content,
|
|
EventType: foundEvent.EventType,
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func GetStateEventHandler(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
|
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
|
|
}
|
|
vars := mux.Vars(r)
|
|
roomId := vars["roomId"]
|
|
eventType := vars["eventType"]
|
|
stateKey := vars["stateKey"]
|
|
if roomId == "" || eventType == "" {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Missing Parameter"}); err != nil {
|
|
panic(err)
|
|
}
|
|
return
|
|
}
|
|
foundEvent, err := ReadStateEvent(roomId, eventType, stateKey)
|
|
if err != nil || foundEvent == nil {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorCode: "M_NOT_FOUND", ErrorMessage: fmt.Sprintf("Event not found. %s", err)}); err != nil {
|
|
panic(err)
|
|
}
|
|
return
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
if err := json.NewEncoder(w).Encode(foundEvent.Content); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func SyncEventsServerHandler(w http.ResponseWriter, r *http.Request) {
|
|
packetLossNumber := rand.Intn(100)
|
|
if packetLossNumber > config.Packetloss {
|
|
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
|
buf := new(bytes.Buffer)
|
|
buf.ReadFrom(r.Body)
|
|
request := syncEventsServerRequest{}
|
|
errResponse := utils.CheckRequest(r)
|
|
if errResponse != nil {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
|
|
panic(err)
|
|
}
|
|
return
|
|
}
|
|
_ = utils.CheckAuthHeader(r, buf.String())
|
|
/*if errResponse != nil {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
|
|
panic(err)
|
|
}
|
|
return
|
|
}*/
|
|
err := json.Unmarshal(buf.Bytes(), &request)
|
|
if err != nil {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("SyncEventsServerHandler Could not parse JSON Request: %s", err)}); err != nil {
|
|
panic(err)
|
|
}
|
|
return
|
|
}
|
|
vars := mux.Vars(r)
|
|
txnId := vars["txnId"]
|
|
if txnId == "" {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "SyncEventsServerHandler Missing Parameter"}); err != nil {
|
|
panic(err)
|
|
}
|
|
return
|
|
}
|
|
response := syncEventsServerResponse{
|
|
PDUs: make(map[string]pduProcessingResult),
|
|
}
|
|
missingEventIds := make(map[string][]string)
|
|
for _, pdu := range request.PDUs {
|
|
signatureValid := CheckSignature(*pdu)
|
|
if !signatureValid {
|
|
log.Printf("Wrong Signature for Event %s", pdu.Id)
|
|
response.PDUs[pdu.Id] = pduProcessingResult{ProcessingError: "Signature not valid"}
|
|
//continue
|
|
}
|
|
authEventsValid, err := CheckAuthEvents(pdu)
|
|
if !authEventsValid || err != nil {
|
|
log.Printf("Wrong Auth Events for Event %s", pdu.Id)
|
|
response.PDUs[pdu.Id] = pduProcessingResult{ProcessingError: fmt.Sprintf("Error in Auth Check: %s", err)}
|
|
//continue
|
|
}
|
|
missingParentIds, err := CheckParents(pdu)
|
|
if len(missingParentIds) > 0 || err != nil {
|
|
response.PDUs[pdu.Id] = pduProcessingResult{ProcessingError: fmt.Sprintf("Error in Parents Check: %s", err)}
|
|
for _, parentId := range missingParentIds {
|
|
missingEventIds[pdu.RoomId] = append(missingEventIds[pdu.RoomId], parentId)
|
|
}
|
|
}
|
|
|
|
err = HandleEvent(pdu, txnId)
|
|
if err != nil {
|
|
response.PDUs[pdu.Id] = pduProcessingResult{ProcessingError: fmt.Sprintf("Error in Event-Handling: %s", err)}
|
|
continue
|
|
}
|
|
|
|
response.PDUs[pdu.Id] = pduProcessingResult{}
|
|
}
|
|
|
|
if len(missingEventIds) > 0 {
|
|
for roomId, eventIds := range missingEventIds {
|
|
Backfill(eventIds, roomId, request.Origin, config.HttpString, config.AuthentificationCheck)
|
|
}
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
|
panic(err)
|
|
}
|
|
} else {
|
|
log.Println("Blocking Response")
|
|
}
|
|
}
|
|
|
|
func BackfillHandler(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
|
|
|
errResponse := utils.CheckAuthHeader(r, "")
|
|
|
|
if errResponse != nil {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
if err := json.NewEncoder(w).Encode(errResponse); 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
|
|
}
|
|
|
|
limit := 50
|
|
eventIds, ok := r.URL.Query()["v"]
|
|
if !ok || eventIds[0] == "" {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Missing Parameter"}); err != nil {
|
|
panic(err)
|
|
}
|
|
return
|
|
}
|
|
limitString := r.URL.Query().Get("limit")
|
|
if limitString != "" {
|
|
limit, _ = strconv.Atoi(limitString)
|
|
}
|
|
pdus := []*Event{}
|
|
for len(pdus) < limit {
|
|
newEventIds := []string{}
|
|
for _, eventId := range eventIds {
|
|
foundEvent, err := ReadEvent(eventId)
|
|
if err != nil || foundEvent == nil {
|
|
continue
|
|
}
|
|
|
|
for newEventId, _ := range foundEvent.PrevEventHashes {
|
|
newEventIds = append(newEventIds, newEventId)
|
|
}
|
|
pdus = append(pdus, foundEvent)
|
|
}
|
|
eventIds = newEventIds
|
|
}
|
|
response := backfillResponse{
|
|
Origin: config.Homeserver,
|
|
Timestamp: time.Now().Unix(),
|
|
PDUs: pdus,
|
|
}
|
|
w.WriteHeader(http.StatusOK)
|
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func SendTransaction(transaction *Transaction, homeserver string, httpString string, authentification bool) (err error) {
|
|
requestUrl := fmt.Sprintf("%s://%s/_matrix/federation/v1/send/%s?", httpString, homeserver, transaction.Id)
|
|
request := syncEventsServerRequest{
|
|
Origin: transaction.Origin,
|
|
Timestamp: transaction.Timestamp,
|
|
PDUs: transaction.PDUS,
|
|
}
|
|
reqBody, err := json.Marshal(request)
|
|
if err != nil {
|
|
return
|
|
}
|
|
client := &http.Client{Timeout: 2 * time.Second}
|
|
req, err := http.NewRequest(http.MethodPut, requestUrl, bytes.NewBuffer(reqBody))
|
|
if err != nil {
|
|
return
|
|
}
|
|
if authentification {
|
|
var authHeader string
|
|
authHeader, err = utils.CreateAuthHeader(http.MethodPut, requestUrl, homeserver, string(reqBody))
|
|
if err != nil {
|
|
return
|
|
}
|
|
req.Header["Authorization"] = []string{authHeader}
|
|
}
|
|
req.Header["Content-Type"] = []string{"application/json"}
|
|
r, err := client.Do(req)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if r.StatusCode != http.StatusOK {
|
|
utils.HandleHTTPError(r)
|
|
} else {
|
|
buf := new(bytes.Buffer)
|
|
buf.ReadFrom(r.Body)
|
|
content := buf.String()
|
|
if content == "" {
|
|
err = errors.New("Missing Response")
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func Backfill(eventIds []string, roomId string, homeserver string, httpString string, authentification bool) (err error) {
|
|
requestUrl := fmt.Sprintf("%s://%s/_matrix/federation/v1/backfill/%s?", httpString, homeserver, roomId)
|
|
for _, eventId := range eventIds {
|
|
requestUrl = fmt.Sprintf("%sv=%s&", requestUrl, eventId)
|
|
}
|
|
client := &http.Client{Timeout: 2 * time.Second}
|
|
var req *http.Request
|
|
req, err = http.NewRequest(http.MethodGet, requestUrl, bytes.NewBuffer(nil))
|
|
if err != nil {
|
|
return
|
|
}
|
|
if authentification {
|
|
var authHeader string
|
|
authHeader, err = utils.CreateAuthHeader(http.MethodGet, requestUrl, homeserver, "")
|
|
if err != nil {
|
|
return
|
|
}
|
|
req.Header["Authorization"] = []string{authHeader}
|
|
}
|
|
req.Header["Content-Type"] = []string{"application/json"}
|
|
r, err := client.Do(req)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if r.StatusCode != http.StatusOK {
|
|
errResponse := utils.HandleHTTPError(r)
|
|
log.Fatalf("%s (%s)", errResponse.ErrorMessage, errResponse.ErrorCode)
|
|
}
|
|
response := backfillResponse{}
|
|
defer r.Body.Close()
|
|
decoder := json.NewDecoder(r.Body)
|
|
err = decoder.Decode(&response)
|
|
if err != nil {
|
|
return
|
|
}
|
|
var missingEventIds []string
|
|
for _, pdu := range response.PDUs {
|
|
for i, eventId := range missingEventIds {
|
|
if pdu.Id == eventId {
|
|
missingEventIds = append(missingEventIds[:i], missingEventIds[i+1:]...)
|
|
}
|
|
}
|
|
signatureValid := CheckSignature(*pdu)
|
|
if !signatureValid {
|
|
log.Printf("Wrong Signature for Event %s", pdu.Id)
|
|
missingEventIds = append(missingEventIds, pdu.Id)
|
|
continue
|
|
}
|
|
authEventsValid, err := CheckAuthEvents(pdu)
|
|
if !authEventsValid || err != nil {
|
|
log.Printf("Wrong Auth Events for Event %s", pdu.Id)
|
|
missingEventIds = append(missingEventIds, pdu.Id)
|
|
}
|
|
missingParentIds, err := CheckParents(pdu)
|
|
if len(missingParentIds) > 0 || err != nil {
|
|
for _, parentId := range missingParentIds {
|
|
missingEventIds = append(missingEventIds, parentId)
|
|
}
|
|
}
|
|
|
|
err = HandleEvent(pdu, "")
|
|
if err != nil {
|
|
log.Printf("Error in Event-Handling: %s", err)
|
|
continue
|
|
}
|
|
}
|
|
|
|
if len(missingEventIds) > 0 {
|
|
Backfill(missingEventIds, roomId, homeserver, config.HttpString, config.AuthentificationCheck)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func generateEventId(id string) string {
|
|
return fmt.Sprintf("$%s:%s", id, config.Homeserver)
|
|
}
|
|
|
|
func GetAuthChain(newEvent *Event) (authChain []*Event, err error) {
|
|
if !config.AuthentificationCheck {
|
|
return
|
|
}
|
|
createEvent, err := ReadStateEvent(newEvent.RoomId, "m.room.create", "")
|
|
if err != nil {
|
|
return
|
|
}
|
|
if createEvent != nil {
|
|
authChain = append(authChain, createEvent)
|
|
}
|
|
|
|
powerLevelEvent, err := ReadStateEvent(newEvent.RoomId, "m.room.power_levels", "")
|
|
if err != nil {
|
|
return
|
|
}
|
|
if powerLevelEvent != nil {
|
|
authChain = append(authChain, powerLevelEvent)
|
|
}
|
|
|
|
stateKey := newEvent.Sender
|
|
if newEvent.EventType == "m.room.member" {
|
|
stateKey = newEvent.StateKey
|
|
}
|
|
|
|
memberEvent, err := ReadStateEvent(newEvent.RoomId, "m.room.member", stateKey)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if memberEvent != nil {
|
|
authChain = append(authChain, memberEvent)
|
|
}
|
|
|
|
joinRuleEvent, err := ReadStateEvent(newEvent.RoomId, "m.room.join_rules", "")
|
|
if err != nil {
|
|
return
|
|
}
|
|
if joinRuleEvent != nil && newEvent.EventType == "m.room.member" {
|
|
authChain = append(authChain, joinRuleEvent)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func GetAuthEvents(newEvent *Event) (authEventHashes map[string]EventHash, err error) {
|
|
authEventHashes = make(map[string]EventHash)
|
|
authChain, err := GetAuthChain(newEvent)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for _, authEvent := range authChain {
|
|
authEventHashes[authEvent.Id] = authEvent.Hashes
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func CheckEventHash(id string, hash EventHash) (correct bool, eventFound bool, err error) {
|
|
foundEvent, err := ReadEvent(id)
|
|
correct = true
|
|
eventFound = true
|
|
if err != nil {
|
|
return
|
|
}
|
|
if foundEvent == nil {
|
|
eventFound = false
|
|
return
|
|
}
|
|
if hash.SHA256 != foundEvent.Hashes.SHA256 {
|
|
correct = false
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func CheckParents(eventToCheck *Event) (missingParentIds []string, err error) {
|
|
if config.Consensus {
|
|
for key, hash := range eventToCheck.PrevEventHashes {
|
|
correctHash, foundEvent, err := CheckEventHash(key, hash)
|
|
if !correctHash || !foundEvent || err != nil {
|
|
missingParentIds = append(missingParentIds, key)
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func CheckAuthEvents(eventToCheck *Event) (correct bool, err error) {
|
|
correct = true
|
|
if !config.AuthentificationCheck {
|
|
return
|
|
}
|
|
authEvents, err := GetAuthEvents(eventToCheck)
|
|
if err != nil {
|
|
return
|
|
}
|
|
for key, hash := range authEvents {
|
|
if eventToCheck.AuthEventHashes[key].SHA256 != hash.SHA256 {
|
|
//correct = false
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func CheckSignature(eventToCheck Event) (correct bool) {
|
|
if !config.Signing {
|
|
correct = true
|
|
return
|
|
}
|
|
correct = false
|
|
signatures := eventToCheck.Signatures
|
|
eventToCheck.Unsigned = UnsignedData{}
|
|
eventToCheck.Signatures = nil
|
|
jsonString, err := json.Marshal(eventToCheck)
|
|
if err != nil {
|
|
return
|
|
}
|
|
for id, signature := range signatures[eventToCheck.Origin] {
|
|
verifyKey, err := device.GetVerifyKey(eventToCheck.Origin, id)
|
|
if err == nil && verifyKey != nil {
|
|
correct = utils.VerifySignature(verifyKey, jsonString, signature)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func HandleEvents(events []*Event, txnId string) (err error) {
|
|
for _, eventToHandle := range events {
|
|
err = HandleEvent(eventToHandle, txnId)
|
|
}
|
|
return
|
|
}
|
|
|
|
func HandleEvent(eventToHandle *Event, txnId string) (err error) {
|
|
log.Printf("EventType: %s", eventToHandle.EventType)
|
|
foundEvent, err := ReadEvent(eventToHandle.Id)
|
|
if foundEvent == nil && err == nil {
|
|
err = CreateEvent(eventToHandle, txnId)
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
if eventToHandle.EventType == "m.room.message" {
|
|
log.Printf("%s: %s", eventToHandle.Sender, eventToHandle.Content)
|
|
} else if eventToHandle.EventType == "m.room.member" {
|
|
message := MemberEventContent{}
|
|
err = json.Unmarshal([]byte(eventToHandle.Content), &message)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if message.Membership == "join" {
|
|
log.Printf("Join %s to Room %s", eventToHandle.StateKey, eventToHandle.RoomId)
|
|
}
|
|
err = CreateRoomMember(eventToHandle.RoomId, eventToHandle.StateKey, eventToHandle.Origin)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|