This commit is contained in:
hoernschen 2020-10-11 23:11:30 +02:00
parent 7db9c374cc
commit 9eac960763
26 changed files with 3119 additions and 266 deletions

21
config/config.go Normal file
View file

@ -0,0 +1,21 @@
package config
var ServerName string = "Hoernschen's Matrix Server"
var Version string = "0.1"
var Homeserver string
var Port string
var PrivateKey []byte
var PublicKey []byte
var KeyId string
var VerifyKeys map[string]map[string][]byte
// Parameters for Mesurements
// TODO: Implement correctly
var Packetloss float32
var UnavailableTill int
var AuthentificationCheck bool
var Signing bool
var Encryption bool
var HttpString string

View file

@ -2,15 +2,16 @@ package device
import (
"fmt"
"log"
"nutfactory.org/Matrix/utils/database"
)
func CreateDevice(device *Device, userId string) (err error) {
sqlStmt := fmt.Sprintf(`INSERT INTO device
(id, name, userId)
(id, name, accessToken, userId)
VALUES
(?, ?, ?)`)
(?, ?, ?, ?)`)
tx, err := database.DB.Begin()
if err != nil {
@ -23,7 +24,7 @@ func CreateDevice(device *Device, userId string) (err error) {
}
defer stmt.Close()
_, err = stmt.Exec(device.Id, device.Name, userId)
_, err = stmt.Exec(device.Id, device.Name, device.AccessToken, userId)
if err != nil {
return
}
@ -32,7 +33,7 @@ func CreateDevice(device *Device, userId string) (err error) {
}
func ReadDevice(id string) (foundDevice *Device, err error) {
queryStmt := fmt.Sprintf(`SELECT id, name
queryStmt := fmt.Sprintf(`SELECT id, name, accessToken
FROM device
WHERE id = '%s'`, id)
@ -45,7 +46,31 @@ func ReadDevice(id string) (foundDevice *Device, err error) {
if rows.Next() {
foundDevice = &Device{}
err = rows.Scan(&foundDevice.Id, &foundDevice.Name)
err = rows.Scan(&foundDevice.Id, &foundDevice.Name, &foundDevice.AccessToken)
if err != nil {
return
}
foundDevice.Keys, err = ReadKeysForDevice(foundDevice.Id)
}
return
}
func ReadDeviceFromAccessToken(accessToken string) (foundDevice *Device, err error) {
queryStmt := fmt.Sprintf(`SELECT id, name, accessToken
FROM device
WHERE accessToken = '%s'`, accessToken)
log.Printf(queryStmt)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
if rows.Next() {
foundDevice = &Device{}
err = rows.Scan(&foundDevice.Id, &foundDevice.Name, &foundDevice.AccessToken)
if err != nil {
return
}
@ -56,7 +81,7 @@ func ReadDevice(id string) (foundDevice *Device, err error) {
}
func ReadDevicesForUser(userId string) (devices map[string]*Device, err error) {
queryStmt := fmt.Sprintf(`SELECT id, name
queryStmt := fmt.Sprintf(`SELECT id, name, accessToken
FROM device
WHERE userId = '%s'`, userId)
@ -71,7 +96,7 @@ func ReadDevicesForUser(userId string) (devices map[string]*Device, err error) {
for rows.Next() {
foundDevice := &Device{}
err = rows.Scan(&foundDevice.Id, &foundDevice.Name)
err = rows.Scan(&foundDevice.Id, &foundDevice.Name, &foundDevice.AccessToken)
if err != nil {
return
}
@ -82,10 +107,10 @@ func ReadDevicesForUser(userId string) (devices map[string]*Device, err error) {
return
}
func UpdateDevice(device *Device, userId string) (err error) {
func UpdateDevice(device *Device) (err error) {
sqlStmt := fmt.Sprintf(`UPDATE device SET
name = ?,
userId = ?
accessToken = ?
WHERE id = ?`)
tx, err := database.DB.Begin()
@ -99,7 +124,7 @@ func UpdateDevice(device *Device, userId string) (err error) {
}
defer stmt.Close()
_, err = stmt.Exec(device.Name, userId, device.Id)
_, err = stmt.Exec(device.Name, device.AccessToken, device.Id)
if err != nil {
return
}

View file

@ -3,5 +3,18 @@ package device
type Key struct {
Id string `json:"id,omitempty"`
Type string `json:"type,omitempty"`
Key string `json:"key,omitempty"`
Key []byte `json:"key,omitempty"`
}
type serverKeys struct {
ServerName string `json:"server_name,omitempty"`
VerifyKeys map[string]verifyKey `json:"verify_keys,omitempty"`
OldVerifyKeys map[string]verifyKey `json:"old_verify_keys,omitempty"`
Signatures map[string]map[string]string `json:"signatures,omitempty"`
ValidUntil int64 `json:"valid_until_ts,omitempty"`
}
type verifyKey struct {
Key string `json:"key,omitempty"`
Expired int64 `json:"expired_ts,omitempty"`
}

View file

@ -0,0 +1,76 @@
package device
import (
"encoding/json"
"fmt"
"net/http"
"nutfactory.org/Matrix/config"
"nutfactory.org/Matrix/utils"
)
func InitServerSigningKey() (err error) {
publicKey, privateKey, err := utils.GenerateKeyPair()
if err != nil {
return
}
config.PublicKey = publicKey
config.PrivateKey = privateKey
config.KeyId = "ed25519:1"
return
}
func GetServerSigningKeyHandler(w http.ResponseWriter, r *http.Request) {
if config.PublicKey == nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Server Signing Key Missing"}); err != nil {
panic(err)
}
return
}
response := serverKeys{
ServerName: config.Homeserver,
VerifyKeys: make(map[string]verifyKey),
}
response.VerifyKeys[config.KeyId] = verifyKey{Key: string(config.PublicKey)}
content, err := json.Marshal(response)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Error creating Signatures: %s", err)}); err != nil {
panic(err)
}
return
}
response.Signatures = utils.SignContent(content)
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(response); err != nil {
panic(err)
}
}
func getVerifyKey(server string, id string) (key []byte, err error) {
if val, ok := config.VerifyKeys[server][id]; ok {
key = val
} else {
httpString := "https"
requestUrl := fmt.Sprintf("%s://%s/_matrix/key/v2/server", httpString, server)
var res *http.Response
res, err = http.Get(requestUrl)
if err != nil {
return
}
serverKeyRes := serverKeys{}
decoder := json.NewDecoder(res.Body)
err = decoder.Decode(&serverKeyRes)
config.VerifyKeys[server] = make(map[string][]byte)
for keyId, verifyKey := range serverKeyRes.VerifyKeys {
config.VerifyKeys[server][keyId] = []byte(verifyKey.Key)
if id == keyId {
key = []byte(verifyKey.Key)
}
}
}
return
}

View file

@ -1,8 +0,0 @@
package event
// TODO: Check if it can be deleted
type EDU struct {
Type string `json:"type,omitempty"`
Content string `json:"content,omitempty"`
}

View file

@ -1,10 +1,123 @@
package event
type Event struct {
Id string `json:"id,omitempty"`
RoomId string `json:"roomId,omitempty"`
EventType string `json:"eventType,omitempty"`
Content string `json:"content,omitempty"`
ParentId string `json:"parent,omitempty"`
Depth int `json:"depth,omitempty"`
Id string `json:"event_id,omitempty"`
RoomId string `json:"room_id,omitempty"`
Sender string `json:"sender,omitempty"`
Origin string `json:"origin,omitempty"`
Timestamp int64 `json:"origin_server_ts,omitempty"`
EventType string `json:"type,omitempty"`
StateKey string `json:"state_key,omitempty"`
Content string `json:"content,omitempty"`
PrevEventHashes map[string]EventHash `json:"prev_events,omitempty"`
Depth int `json:"depth,omitempty"`
AuthEventHashes map[string]EventHash `json:"auth_events,omitempty"`
Unsigned UnsignedData `json:"unsigned,omitempty"`
Hashes EventHash `json:"hashes,omitempty"`
Signatures map[string]map[string]string `json:"signatures,omitempty"`
}
type StateEvent struct {
EventType string `json:"type,omitempty"`
StateKey string `json:"state_key,omitempty"`
Content string `json:"content,omitempty"`
}
type UnsignedData struct {
Age int `json:"age,omitempty"`
TransactionId string `json:"transaction_id,omitempty"`
ReplaceState string `json:"replaces_state,omitempty"`
PrevSender string `json:"prev_sender,omitempty"`
PrevContent string `json:"prev_content,omitempty"`
RedactedBecause string `json:"redacted_because,omitempty"`
}
type EventHash struct {
SHA256 string `json:"sha256,omitempty"`
}
type CreateEventContent struct {
Creator string `json:"creator,omitempty"`
Federated bool `json:"m.federate,omitempty"`
RoomVersion string `json:"room_version,omitempty"`
}
type JoinRuleEventContent struct {
JoinRule string `json:"join_rule,omitempty"`
}
type HistoryVisibilityEventContent struct {
HistoryVisibility string `json:"history_visibility,omitempty"`
}
type GuestAccessEventContent struct {
GuestAccess string `json:"guest_access,omitempty"`
}
type NameEventContent struct {
Name string `json:"name,omitempty"`
}
type TopicEventContent struct {
Topic string `json:"topic,omitempty"`
}
type MemberEventContent struct {
AvatarUrl string `json:"avatar_url,omitempty"`
DisplayName string `json:"displayname,omitempty"`
Membership string `json:"membership,omitempty"`
IsDirect bool `json:"is_direct,omitempty"`
}
type PowerLevelsEventContent struct {
Ban int `json:"ban,omitempty"`
Events map[string]int `json:"events,omitempty"`
EventsDefault int `json:"events_default,omitempty"`
Invite int `json:"invite,omitempty"`
Kick int `json:"kick,omitempty"`
Redact int `json:"redact,omitempty"`
StateDefault int `json:"state_default,omitempty"`
Users map[string]int `json:"users,omitempty"`
UsersDefault int `json:"users_default,omitempty"`
Notifications Notifications `json:"notifications,omitempty"`
}
type Notifications struct {
Room int `json:"room,omitempty"`
}
type sendMessageRequest struct {
MessageType string `json:"msgtype,omitempty"`
Body string `json:"body,omitempty"`
}
type createEventResponse struct {
EventId string `json:"event_id,omitempty"`
}
type getEventRequest struct{}
type getEventResponse struct {
Content string `json:"content,omitempty"`
EventType string `json:"type,omitempty"`
}
type syncEventsServerRequest struct {
Origin string `json:"origin,omitempty"`
Timestamp int64 `json:"origin_server_ts,omitempty"`
PDUs []*Event `json:"pdus,omitempty"`
}
type syncEventsServerResponse struct {
PDUs map[string]pduProcessingResult `json:"pdus,omitempty"`
}
type backfillResponse struct {
Origin string `json:"origin,omitempty"`
Timestamp int64 `json:"origin_server_ts,omitempty"`
PDUs []*Event `json:"pdus,omitempty"`
}
type pduProcessingResult struct {
ProcessingError string `json:"error,omitempty"`
}

View file

@ -1,5 +1,803 @@
package event
func New() (event *Event) {
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"time"
"github.com/cenkalti/backoff/v4"
"github.com/gorilla/mux"
"nutfactory.org/Matrix/config"
"nutfactory.org/Matrix/entities/device"
"nutfactory.org/Matrix/entities/user"
"nutfactory.org/Matrix/utils"
)
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
}
buf := new(bytes.Buffer)
buf.ReadFrom(r.Body)
content := buf.String()
err, newEvent := New(
roomId,
foundUser.Id,
config.Homeserver,
time.Now().Unix(),
eventType,
foundUser.Id,
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 {
operation := func() error {
return 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)
}
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)
}
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) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
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
}
errResponse = utils.CheckAuthHeader(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)
txnId := vars["txnId"]
if txnId == "" {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "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)
}
}
foundEvent, err := ReadEvent(pdu.Id)
if foundEvent == nil && err == nil {
err = CreateEvent(pdu, txnId)
}
if err != nil {
response.PDUs[pdu.Id] = pduProcessingResult{ProcessingError: fmt.Sprintf("Database Error: %s", err)}
continue
}
err = HandleEvent(pdu)
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 {
operation := func() error {
return Backfill(eventIds, roomId, request.Origin)
}
notify := func(err error, duration time.Duration) {
log.Printf("Error Backfill, retrying in %s: %s", duration/1000000000, err)
}
go backoff.RetryNotify(operation, backoff.NewExponentialBackOff(), notify)
}
}
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(response); err != nil {
panic(err)
}
}
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"]
log.Printf("%s", eventIds)
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 {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Event not found"}); err != nil {
panic(err)
}
return
}
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) (err error) {
requestUrl := fmt.Sprintf("https://%s/_matrix/federation/v1/send/%s?", 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{}
req, err := http.NewRequest(http.MethodPut, requestUrl, bytes.NewBuffer(reqBody))
if err != nil {
return
}
_, err = client.Do(req)
return
}
func Backfill(eventIds []string, roomId string, homeserver string) (err error) {
requestUrl := fmt.Sprintf("https://%s/_matrix/federation/v1/backfill/%s?", homeserver, roomId)
for _, eventId := range eventIds {
requestUrl = fmt.Sprintf("%sv=%s&", requestUrl, eventId)
}
r, err := http.Get(requestUrl)
if err != nil {
return
}
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)
continue
}
missingParentIds, err := CheckParents(pdu)
if len(missingParentIds) > 0 || err != nil {
for _, parentId := range missingParentIds {
missingEventIds = append(missingEventIds, parentId)
}
}
foundEvent, err := ReadEvent(pdu.Id)
if foundEvent == nil && err == nil {
err = CreateEvent(pdu, "")
}
if err != nil {
continue
}
err = HandleEvent(pdu)
if err != nil {
log.Printf("Error in Event-Handling: %s", err)
continue
}
}
if len(missingEventIds) > 0 {
Backfill(missingEventIds, roomId, homeserver)
}
return
}
func generateEventId(id string) string {
return fmt.Sprintf("$%s:%s", id, config.Homeserver)
}
func GetAuthChain(newEvent *Event) (authChain []*Event, err error) {
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) {
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
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) {
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.Sender] {
key, err := device.ReadKey(id)
if err == nil {
correct = utils.VerifySignature([]byte(key.Key), jsonString, []byte(signature))
}
}
return
}
func HandleEvents(events []*Event) (err error) {
for _, eventToHandle := range events {
err = HandleEvent(eventToHandle)
}
return
}
func HandleEvent(eventToHandle *Event) (err error) {
if eventToHandle.EventType == "m.room.message" {
message := sendMessageRequest{}
err = json.Unmarshal([]byte(eventToHandle.Content), &message)
if err != nil {
return
}
if message.MessageType != "" && message.MessageType == "m.text" {
log.Printf("%s: %s", eventToHandle.Sender, message.Body)
}
} else if eventToHandle.EventType == "m.room.member" {
message := MemberEventContent{}
err = json.Unmarshal([]byte(eventToHandle.Content), &message)
if err != nil {
return
}
if message.Membership == "join" {
CreateRoomMember(eventToHandle.RoomId, eventToHandle.StateKey)
}
}
return
}

View file

@ -2,15 +2,16 @@ package event
import (
"fmt"
"strings"
"nutfactory.org/Matrix/utils/database"
)
func CreateEvent(event *Event, txnId string) (err error) {
sqlStmt := fmt.Sprintf(`INSERT INTO event
(id, roomId, txnId, eventType, content, parentId, depth)
func CreateRoomMember(roomId string, userId string) (err error) {
sqlStmt := fmt.Sprintf(`INSERT INTO roomMember
(roomId, userId, server)
VALUES
(?, ?, ?, ?, ?, ?, ?)`)
(?, ?, ?)`)
tx, err := database.DB.Begin()
if err != nil {
@ -23,7 +24,7 @@ func CreateEvent(event *Event, txnId string) (err error) {
}
defer stmt.Close()
_, err = stmt.Exec(event.Id, event.RoomId, txnId, event.EventType, event.Content, event.ParentId, event.Depth)
_, err = stmt.Exec(roomId, userId, strings.Split(userId, ":")[1])
if err != nil {
return
}
@ -31,11 +32,129 @@ func CreateEvent(event *Event, txnId string) (err error) {
return
}
func CreateParents(eventId string, parentIds map[string]EventHash) (err error) {
sqlStmt := fmt.Sprintf(`INSERT INTO parent
(eventId, parentId)
VALUES
(?, ?)`)
tx, err := database.DB.Begin()
if err != nil {
return
}
stmt, err := tx.Prepare(sqlStmt)
if err != nil {
return
}
defer stmt.Close()
for parentId, _ := range parentIds {
_, err = stmt.Exec(
eventId,
parentId,
)
if err != nil {
return
}
}
tx.Commit()
return
}
func CreateAuthEvents(eventId string, authEventIds map[string]EventHash) (err error) {
sqlStmt := fmt.Sprintf(`INSERT INTO authEvent
(eventId, authEventId)
VALUES
(?, ?)`)
tx, err := database.DB.Begin()
if err != nil {
return
}
stmt, err := tx.Prepare(sqlStmt)
if err != nil {
return
}
defer stmt.Close()
for authEventId, _ := range authEventIds {
_, err = stmt.Exec(
eventId,
authEventId,
)
if err != nil {
return
}
}
tx.Commit()
return
}
func CreateEvent(event *Event, txnId string) (err error) {
sqlStmt := fmt.Sprintf(`INSERT INTO event
(id, roomId, txnId, sender, origin, timestamp, eventType, stateKey, content, depth, hash, signature)
VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
tx, err := database.DB.Begin()
if err != nil {
return
}
stmt, err := tx.Prepare(sqlStmt)
if err != nil {
return
}
defer stmt.Close()
signatures := ""
for _, signature := range event.Signatures[event.Origin] {
signatures = signature
}
_, err = stmt.Exec(
event.Id,
event.RoomId,
txnId,
event.Sender,
event.Origin,
event.Timestamp,
event.EventType,
event.StateKey,
event.Content,
event.Depth,
event.Hashes.SHA256,
signatures,
)
if err != nil {
return
}
tx.Commit()
err = CreateParents(event.Id, event.PrevEventHashes)
if err != nil {
return
}
err = CreateAuthEvents(event.Id, event.AuthEventHashes)
if err != nil {
return
}
return
}
func CreateEventsFromTransaction(txnId string, pdus map[string]*Event) (err error) {
sqlStmt := fmt.Sprintf(`INSERT INTO event
(id, roomId, txnId, eventType, content, parentId, depth)
(id, roomId, txnId, sender, origin, timestamp, eventType, stateKey, content, depth, hash, signature)
VALUES
(?, ?, ?, ?, ?, ?, ?)`)
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
tx, err := database.DB.Begin()
if err != nil {
@ -49,7 +168,35 @@ func CreateEventsFromTransaction(txnId string, pdus map[string]*Event) (err erro
defer stmt.Close()
for _, pdu := range pdus {
_, err = stmt.Exec(pdu.Id, pdu.RoomId, txnId, pdu.EventType, pdu.Content, pdu.ParentId, pdu.Depth)
signatures := ""
for _, signature := range pdu.Signatures[pdu.Origin] {
signatures = signature
}
_, err = stmt.Exec(
pdu.Id,
pdu.RoomId,
txnId,
pdu.Sender,
pdu.Origin,
pdu.Timestamp,
pdu.EventType,
pdu.StateKey,
pdu.Content,
pdu.Depth,
pdu.Hashes.SHA256,
signatures,
)
if err != nil {
return
}
err = CreateParents(pdu.Id, pdu.PrevEventHashes)
if err != nil {
return
}
err = CreateAuthEvents(pdu.Id, pdu.AuthEventHashes)
if err != nil {
return
}
@ -59,8 +206,186 @@ func CreateEventsFromTransaction(txnId string, pdus map[string]*Event) (err erro
return
}
func ReadEventHash(id string) (hash string, err error) {
queryStmt := fmt.Sprintf(`SELECT hash
FROM event
WHERE id = '%s'`, id)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
if rows.Next() {
err = rows.Scan(hash)
if err != nil {
return
}
}
return
}
func ReadRoomMembers(roomId string) (roomMembers []string, err error) {
queryStmt := fmt.Sprintf(`SELECT userId
FROM roomMember
WHERE roomId = '%s'`, roomId)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
roomMembers = []string{}
for rows.Next() {
var foundUser string
err = rows.Scan(&foundUser)
if err != nil {
return
}
roomMembers = append(roomMembers, foundUser)
}
return
}
func ReadServers(roomId string) (servers []string, err error) {
queryStmt := fmt.Sprintf(`SELECT DISTINCT server
FROM roomMember
WHERE roomId = '%s'`, roomId)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
servers = []string{}
for rows.Next() {
var foundUser string
err = rows.Scan(&foundUser)
if err != nil {
return
}
servers = append(servers, foundUser)
}
return
}
func ReadParents(eventId string) (parents map[string]EventHash, err error) {
queryStmt := fmt.Sprintf(`SELECT e.id, e.hash
FROM event as e
join parent as p on e.id = p.parentId
WHERE p.eventId = '%s'`, eventId)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
parents = make(map[string]EventHash)
for rows.Next() {
var eventId string
var foundEvent EventHash
err = rows.Scan(&eventId,
&foundEvent.SHA256,
)
if err != nil {
return
}
parents[eventId] = foundEvent
}
return
}
func ReadEventsWithoutChild(roomId string) (events map[string]EventHash, depth int, err error) {
queryStmt := fmt.Sprintf(`SELECT e.id, e.hash, e.depth
FROM event as e
LEFT JOIN parent as p on e.id = p.parentId
WHERE p.eventId IS NULL AND e.roomId = '%s'`, roomId)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
events = make(map[string]EventHash)
for rows.Next() {
var eventId string
var foundEvent EventHash
var foundDepth int
err = rows.Scan(&eventId,
&foundEvent.SHA256,
&foundDepth,
)
if foundDepth > depth {
depth = foundDepth
}
if err != nil {
return
}
events[eventId] = foundEvent
}
return
}
func ReadAuthEvents(eventId string) (authEvents map[string]EventHash, err error) {
queryStmt := fmt.Sprintf(`SELECT e.id, e.hash
FROM event as e
join authEvent as a on e.id = a.authEventId
WHERE a.eventId = '%s'`, eventId)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
authEvents = make(map[string]EventHash)
for rows.Next() {
var eventId string
var foundEvent EventHash
err = rows.Scan(&eventId,
&foundEvent.SHA256,
)
if err != nil {
return
}
authEvents[eventId] = foundEvent
}
return
}
func ReadEvent(id string) (foundEvent *Event, err error) {
queryStmt := fmt.Sprintf(`SELECT id, roomId, eventType, content, parentId, depth
queryStmt := fmt.Sprintf(`SELECT id, roomId, txnId, sender, origin, timestamp, eventType, content, depth, hash, signature
FROM event
WHERE id = '%s'`, id)
@ -73,13 +398,34 @@ func ReadEvent(id string) (foundEvent *Event, err error) {
if rows.Next() {
foundEvent = &Event{}
var signature string
err = rows.Scan(&foundEvent.Id,
&foundEvent.RoomId,
&foundEvent.Unsigned.TransactionId,
&foundEvent.Sender,
&foundEvent.Origin,
&foundEvent.Timestamp,
&foundEvent.EventType,
&foundEvent.Content,
&foundEvent.ParentId,
&foundEvent.Depth,
&foundEvent.Hashes.SHA256,
&signature,
)
foundEvent.Signatures = make(map[string]map[string]string)
foundEvent.Signatures[foundEvent.Origin] = make(map[string]string)
foundEvent.Signatures[foundEvent.Origin]["ed25519:1"] = signature
if err != nil {
return
}
foundEvent.PrevEventHashes, err = ReadParents(foundEvent.Id)
if err != nil {
return
}
foundEvent.AuthEventHashes, err = ReadAuthEvents(foundEvent.Id)
if err != nil {
return
}
@ -88,8 +434,120 @@ func ReadEvent(id string) (foundEvent *Event, err error) {
return
}
func ReadStateEvent(roomId string, eventType string, stateKey string) (foundEvent *Event, err error) {
queryStmt := fmt.Sprintf(`SELECT id, roomId, txnId, sender, origin, timestamp, eventType, content, depth, hash, signature
FROM event
WHERE roomId = '%s'
AND eventType = '%s'
AND stateKey = '%s'`, roomId, eventType, stateKey)
if stateKey == "" {
queryStmt = fmt.Sprintf(`SELECT id, roomId, txnId, sender, origin, timestamp, eventType, content, depth, hash, signature
FROM event
WHERE roomId = '%s'
AND eventType = '%s'`, roomId, eventType)
}
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
if rows.Next() {
foundEvent = &Event{}
var signature string
err = rows.Scan(&foundEvent.Id,
&foundEvent.RoomId,
&foundEvent.Unsigned.TransactionId,
&foundEvent.Sender,
&foundEvent.Origin,
&foundEvent.Timestamp,
&foundEvent.EventType,
&foundEvent.Content,
&foundEvent.Depth,
&foundEvent.Hashes.SHA256,
&signature,
)
foundEvent.Signatures = make(map[string]map[string]string)
foundEvent.Signatures[foundEvent.Origin] = make(map[string]string)
foundEvent.Signatures[foundEvent.Origin]["ed25519:1"] = signature
if err != nil {
return
}
foundEvent.PrevEventHashes, err = ReadParents(foundEvent.Id)
if err != nil {
return
}
foundEvent.AuthEventHashes, err = ReadAuthEvents(foundEvent.Id)
if err != nil {
return
}
}
return
}
func ReadStateEvents(roomId string, eventType string) (foundEvents []*Event, err error) {
queryStmt := fmt.Sprintf(`SELECT id, roomId, txnId, sender, origin, timestamp, eventType, content, depth, hash, signature
FROM event
WHERE roomId = '%s'
AND eventType = '%s'`, roomId, eventType)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
foundEvent := &Event{}
var signature string
err = rows.Scan(&foundEvent.Id,
&foundEvent.RoomId,
&foundEvent.Unsigned.TransactionId,
&foundEvent.Sender,
&foundEvent.Origin,
&foundEvent.Timestamp,
&foundEvent.EventType,
&foundEvent.Content,
&foundEvent.Depth,
&foundEvent.Hashes.SHA256,
&signature,
)
foundEvent.Signatures = make(map[string]map[string]string)
foundEvent.Signatures[foundEvent.Origin] = make(map[string]string)
foundEvent.Signatures[foundEvent.Origin]["ed25519:1"] = signature
if err != nil {
return
}
foundEvent.PrevEventHashes, err = ReadParents(foundEvent.Id)
if err != nil {
return
}
foundEvent.AuthEventHashes, err = ReadAuthEvents(foundEvent.Id)
if err != nil {
return
}
foundEvents = append(foundEvents, foundEvent)
}
return
}
func ReadEventsFromRoom(roomId string) (events map[string]*Event, err error) {
queryStmt := fmt.Sprintf(`SELECT id, roomId, eventType, content, parentId, depth
queryStmt := fmt.Sprintf(`SELECT id, roomId, txnId, sender, origin, timestamp, eventType, content, depth, hash, signature
FROM event
WHERE roomId = '%s'`, roomId)
@ -104,24 +562,98 @@ func ReadEventsFromRoom(roomId string) (events map[string]*Event, err error) {
for rows.Next() {
foundEvent := &Event{}
var signature string
err = rows.Scan(&foundEvent.Id,
&foundEvent.RoomId,
&foundEvent.Unsigned.TransactionId,
&foundEvent.Sender,
&foundEvent.Origin,
&foundEvent.Timestamp,
&foundEvent.EventType,
&foundEvent.Content,
&foundEvent.ParentId,
&foundEvent.Depth,
&foundEvent.Hashes.SHA256,
&signature,
)
foundEvent.Signatures = make(map[string]map[string]string)
foundEvent.Signatures[foundEvent.Origin] = make(map[string]string)
foundEvent.Signatures[foundEvent.Origin]["ed25519:1"] = signature
if err != nil {
return
}
foundEvent.PrevEventHashes, err = ReadParents(foundEvent.Id)
if err != nil {
return
}
foundEvent.AuthEventHashes, err = ReadAuthEvents(foundEvent.Id)
if err != nil {
return
}
events[foundEvent.Id] = foundEvent
}
return
}
func ReadEventsFromTransaction(txnId string) (events map[string]*Event, err error) {
queryStmt := fmt.Sprintf(`SELECT id, roomId, eventType, content, parentId, depth
func ReadStateEventsFromRoom(roomId string) (events []*Event, err error) {
queryStmt := fmt.Sprintf(`SELECT id, roomId, txnId, sender, origin, timestamp, eventType, content, depth, hash, signature
FROM event
WHERE eventType <> 'm.room.message' AND roomId = '%s'`, roomId)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
foundEvent := &Event{}
var signature string
err = rows.Scan(&foundEvent.Id,
&foundEvent.RoomId,
&foundEvent.Unsigned.TransactionId,
&foundEvent.Sender,
&foundEvent.Origin,
&foundEvent.Timestamp,
&foundEvent.EventType,
&foundEvent.Content,
&foundEvent.Depth,
&foundEvent.Hashes.SHA256,
&signature,
)
foundEvent.Signatures = make(map[string]map[string]string)
foundEvent.Signatures[foundEvent.Origin] = make(map[string]string)
foundEvent.Signatures[foundEvent.Origin]["ed25519:1"] = signature
if err != nil {
return
}
foundEvent.PrevEventHashes, err = ReadParents(foundEvent.Id)
if err != nil {
return
}
foundEvent.AuthEventHashes, err = ReadAuthEvents(foundEvent.Id)
if err != nil {
return
}
events = append(events, foundEvent)
}
return
}
func ReadEventsFromTransaction(txnId string) (events []*Event, err error) {
queryStmt := fmt.Sprintf(`SELECT id, roomId, txnId, sender, origin, timestamp, eventType, content, depth, hash, signature
FROM event
WHERE txnId = '%s'`, txnId)
@ -132,22 +664,41 @@ func ReadEventsFromTransaction(txnId string) (events map[string]*Event, err erro
defer rows.Close()
events = make(map[string]*Event)
for rows.Next() {
foundEvent := &Event{}
err = rows.Scan(
&foundEvent.Id,
var signature string
err = rows.Scan(&foundEvent.Id,
&foundEvent.RoomId,
&foundEvent.Unsigned.TransactionId,
&foundEvent.Sender,
&foundEvent.Origin,
&foundEvent.Timestamp,
&foundEvent.EventType,
&foundEvent.Content,
&foundEvent.ParentId,
&foundEvent.Depth,
&foundEvent.Hashes.SHA256,
&signature,
)
foundEvent.Signatures = make(map[string]map[string]string)
foundEvent.Signatures[foundEvent.Origin] = make(map[string]string)
foundEvent.Signatures[foundEvent.Origin]["ed25519:1"] = signature
if err != nil {
return
}
events[foundEvent.Id] = foundEvent
foundEvent.PrevEventHashes, err = ReadParents(foundEvent.Id)
if err != nil {
return
}
foundEvent.AuthEventHashes, err = ReadAuthEvents(foundEvent.Id)
if err != nil {
return
}
events = append(events, foundEvent)
}
return
@ -155,11 +706,8 @@ func ReadEventsFromTransaction(txnId string) (events map[string]*Event, err erro
func UpdateEvent(event *Event) (err error) {
sqlStmt := fmt.Sprintf(`UPDATE event SET
roomId = ?,
eventType = ?,
content = ?,
parentId = ?,
depth = ?
WHERE id = ?`)
tx, err := database.DB.Begin()
@ -174,11 +722,8 @@ func UpdateEvent(event *Event) (err error) {
defer stmt.Close()
_, err = stmt.Exec(
event.RoomId,
event.EventType,
event.Content,
event.ParentId,
event.Depth,
event.Id,
)
if err != nil {
@ -203,6 +748,52 @@ func DeleteEvent(id string) (err error) {
return
}
err = DeleteParents(id)
if err != nil {
return
}
err = DeleteAuthEvents(id)
if err != nil {
return
}
tx.Commit()
return
}
func DeleteParents(eventId string) (err error) {
queryStmt := fmt.Sprintf(`DELETE FROM parent
WHERE eventId = '%s'`, eventId)
tx, err := database.DB.Begin()
if err != nil {
return
}
_, err = database.DB.Exec(queryStmt)
if err != nil {
return
}
tx.Commit()
return
}
func DeleteAuthEvents(eventId string) (err error) {
queryStmt := fmt.Sprintf(`DELETE FROM authEvent
WHERE eventId = '%s'`, eventId)
tx, err := database.DB.Begin()
if err != nil {
return
}
_, err = database.DB.Exec(queryStmt)
if err != nil {
return
}
tx.Commit()
return
}

View file

@ -0,0 +1,8 @@
package event
type Transaction struct {
Id string `json:"id,omitempty"`
Origin string `json:"origin,omitempty"`
Timestamp int64 `json:"timestamp,omitempty"`
PDUS []*Event `json:"pdus,omitempty"`
}

View file

@ -1,9 +1,8 @@
package transaction
package event
import (
"fmt"
"nutfactory.org/Matrix/entities/event"
"nutfactory.org/Matrix/utils/database"
)
@ -50,7 +49,7 @@ func ReadTransaction(id string) (foundTransaction *Transaction, err error) {
if err != nil {
return
}
foundTransaction.PDUS, err = event.ReadEventsFromTransaction(foundTransaction.Id)
foundTransaction.PDUS, err = ReadEventsFromTransaction(foundTransaction.Id)
}
return

View file

@ -0,0 +1,94 @@
package general
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"nutfactory.org/Matrix/config"
"nutfactory.org/Matrix/entities/device"
"nutfactory.org/Matrix/utils"
"nutfactory.org/Matrix/utils/database"
)
type resolveServerNameResponse struct {
Server string `json:"m.server,omitempty"`
}
type getServerImplementationResponse struct {
Server serverImplementation `json:"server,omitempty"`
}
type serverImplementation struct {
Name string `json:"name,omitempty"`
Version string `json:"version,omitempty"`
}
type resetBody struct {
Packetloss float32 `json:"packetloss,omitempty"`
UnavailableTill int `json:"unavailableTill,omitempty"`
AuthentificationCheck bool `json:"authentificationCheck,omitempty"`
Signing bool `json:"signing,omitempty"`
Encryption bool `json:"encryption,omitempty"`
}
func ResolveServerName(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
response := resolveServerNameResponse{Server: fmt.Sprintf("%s:%s", config.Homeserver, config.Port)}
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(response); err != nil {
panic(err)
}
}
func GetServerImplementation(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
response := getServerImplementationResponse{Server: serverImplementation{Name: config.ServerName, Version: config.Version}}
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(response); err != nil {
panic(err)
}
}
func Reset(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
request := resetBody{}
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
}
if err := device.InitServerSigningKey(); err != nil {
log.Fatal(err)
}
config.VerifyKeys = make(map[string]map[string][]byte)
os.Remove("sqlite.db")
if err := database.InitDB("sqlite.db"); err != nil {
log.Fatal(err)
}
config.Packetloss = request.Packetloss
config.UnavailableTill = request.UnavailableTill
config.AuthentificationCheck = request.AuthentificationCheck
config.Signing = request.Signing
config.Encryption = request.Signing
w.WriteHeader(http.StatusOK)
}

View file

@ -1,13 +1,114 @@
package room
import (
"nutfactory.org/Matrix/entities/event"
)
import "nutfactory.org/Matrix/entities/event"
type Room struct {
Id string `json:"id,omitempty"`
Messages map[string]*event.Event `json:"messages,omitempty"`
//State map[string]event.Event `json:"state,omitempty"`
Members []string `json:"members,omitempty"`
Version string `json:"version,omitempty"`
Id string `json:"id,omitempty"`
Version string `json:"version,omitempty"`
Name string `json:"name,omitempty"`
Topic string `json:"topic,omitempty"`
Members []string `json:"members,omitempty"`
Servers []string
Events map[string]*event.Event `json:"events,omitempty"`
Visibility string `json:"visibility,omitempty"`
IsDirect bool `json:"is_direct,omitempty"`
Federated bool `json:"federated,omitempty"`
}
type createRoomRequest struct {
Visibility string `json:"visibility,omitempty"`
RoomAliasName string `json:"room_alias_name,omitempty"`
Name string `json:"name,omitempty"`
Topic string `json:"topic,omitempty"`
Invite string `json:"invite,omitempty"`
Invite3pid invite3pid `json:"invite_3pid,omitempty"`
RoomVersion string `json:"room_version,omitempty"`
CreationContent creationContent `json:"creation_content,omitempty"`
InitialState []event.StateEvent `json:"initial_state,omitempty"`
Preset string `json:"preset,omitempty"`
IsDirect bool `json:"is_direct,omitempty"`
PowerLevelContentOverride string `json:"power_level_content_override,omitempty"`
}
type createRoomResponse struct {
RoomId string `json:"room_id,omitempty"`
}
type getRoomMemberRequest struct{}
type getRoomMemberResponse struct {
Chunk []*event.Event `json:"chunk,omitempty"`
}
type joinRoomUserRequest struct {
ThirdPartySigned thirdPartySigned `json:"third_party_signed,omitempty"`
}
type joinRoomUserResponse struct {
RoomId string `json:"room_id,omitempty"`
}
type leaveRoomUserRequest struct{}
type leaveRoomUserResponse struct{}
type makeJoinRequest struct{}
type makeJoinResponse struct {
RoomVersion string `json:"room_version,omitempty"`
Event event.Event `json:"event,omitempty"`
}
type joinRoomServerRequest struct {
Sender string `json:"sender,omitempty"`
Origin string `json:"origin,omitempty"`
Timestamp int64 `json:"origin_server_ts,omitempty"`
EventType string `json:"type,omitempty"`
StateKey string `json:"state_key,omitempty"`
Content event.MemberEventContent `json:"content,omitempty"`
}
type joinRoomServerResponse struct {
Origin string `json:"origin,omitempty"`
AuthChain []*event.Event `json:"auth_chain,omitempty"`
State []*event.Event `json:"state,omitempty"`
}
type makeLeaveRequest struct{}
type makeLeaveResponse struct{}
type leaveRoomServerRequest struct{}
type leaveRoomServerResponse struct{}
type invite3pid struct {
IdServer string `json:"id_server,omitempty"`
IdAccessToken string `json:"id_access_token,omitempty"`
Medium string `json:"medium,omitempty"`
Address string `json:"address,omitempty"`
}
type creationContent struct {
Federated bool `json:"m.federate,omitempty"`
}
type unsignedData struct {
Age int `json:"age,omitempty"`
RedactedBecause *event.Event `json:"redacted_because,omitempty"`
TransactionId string `json:"transaction_id,omitempty"`
}
type invite struct {
DisplayName string `json:"display_name,omitempty"`
Signed thirdPartySigned `json:"signed,omitempty"`
}
type thirdPartySigned struct {
Sender string `json:"sender,omitempty"`
MXID string `json:"mxid,omitempty"`
Signatures signatures `json:"signatures,omitempty"`
Token string `json:"token,omitempty"`
}
type signatures struct{}

View file

@ -1,5 +1,770 @@
package room
func New() (room *Room) {
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
"time"
"github.com/cenkalti/backoff/v4"
"github.com/gorilla/mux"
"nutfactory.org/Matrix/config"
"nutfactory.org/Matrix/entities/event"
"nutfactory.org/Matrix/entities/user"
"nutfactory.org/Matrix/utils"
)
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)
}

View file

@ -7,11 +7,11 @@ import (
"nutfactory.org/Matrix/utils/database"
)
func CreateRoom(room *Room, userId string) (err error) {
func CreateRoom(room *Room) (err error) {
sqlStmt := fmt.Sprintf(`INSERT INTO room
(id, version)
(id, version, visibility, name, topic, isDirect, federated)
VALUES
(?, ?)`)
(?, ?, ?, ?, ?, ?, ?)`)
tx, err := database.DB.Begin()
if err != nil {
@ -24,42 +24,31 @@ func CreateRoom(room *Room, userId string) (err error) {
}
defer stmt.Close()
_, err = stmt.Exec(room.Id, room.Version)
_, err = stmt.Exec(
room.Id,
room.Version,
room.Visibility,
room.Name,
room.Topic,
room.IsDirect,
room.Federated,
)
if err != nil {
return
}
tx.Commit()
err = CreateRoomMember(room.Id, userId)
for _, userId := range room.Members {
err = CreateRoomMember(room.Id, userId)
}
return
}
func CreateRoomMember(roomId string, userId string) (err error) {
sqlStmt := fmt.Sprintf(`INSERT INTO roomMember
(roomId, userId)
VALUES
(?, ?)`)
tx, err := database.DB.Begin()
if err != nil {
return
}
stmt, err := tx.Prepare(sqlStmt)
if err != nil {
return
}
defer stmt.Close()
_, err = stmt.Exec(roomId, userId)
if err != nil {
return
}
tx.Commit()
return
return event.CreateRoomMember(roomId, userId)
}
func ReadRoom(id string) (foundRoom *Room, err error) {
queryStmt := fmt.Sprintf(`SELECT id, version
queryStmt := fmt.Sprintf(`SELECT id, version, visibility, name, topic, isDirect, federated
FROM room
WHERE id = '%s'`, id)
@ -72,11 +61,19 @@ func ReadRoom(id string) (foundRoom *Room, err error) {
if rows.Next() {
foundRoom = &Room{}
err = rows.Scan(&foundRoom.Id, &foundRoom.Version)
err = rows.Scan(
&foundRoom.Id,
&foundRoom.Version,
&foundRoom.Visibility,
&foundRoom.Name,
&foundRoom.Topic,
&foundRoom.IsDirect,
&foundRoom.Federated,
)
if err != nil {
return
}
foundRoom.Messages, err = event.ReadEventsFromRoom(foundRoom.Id)
foundRoom.Events, err = event.ReadEventsFromRoom(foundRoom.Id)
if err != nil {
return
}
@ -90,34 +87,17 @@ func ReadRoom(id string) (foundRoom *Room, err error) {
}
func ReadRoomMembers(roomId string) (roomMembers []string, err error) {
queryStmt := fmt.Sprintf(`SELECT userId
FROM roomMember
WHERE roomId = '%s'`, roomId)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
roomMembers = []string{}
for rows.Next() {
var foundUser string
err = rows.Scan(&foundUser)
if err != nil {
return
}
roomMembers = append(roomMembers, foundUser)
}
return
return event.ReadRoomMembers(roomId)
}
func UpdateRoom(room *Room) (err error) {
sqlStmt := fmt.Sprintf(`UPDATE room SET
version = ?
version = ?,
visibility = ?,
name = ?,
topic = ?,
isDirect = ?,
federated = ?
WHERE id = ?`)
tx, err := database.DB.Begin()
@ -131,7 +111,15 @@ func UpdateRoom(room *Room) (err error) {
}
defer stmt.Close()
_, err = stmt.Exec(room.Version, room.Id)
_, err = stmt.Exec(
room.Version,
room.Visibility,
room.Name,
room.Topic,
room.IsDirect,
room.Federated,
room.Id,
)
if err != nil {
return
}

View file

@ -1,13 +0,0 @@
package transaction
import (
"nutfactory.org/Matrix/entities/event"
)
type Transaction struct {
Id string `json:"id,omitempty"`
Origin string `json:"origin,omitempty"`
Timestamp int `json:"timestamp,omitempty"`
PDUS map[string]*event.Event `json:"pdus,omitempty"`
//EDUS []event.EDU `json:"edus,omitempty"`
}

View file

@ -1,5 +0,0 @@
package transaction
func New() (transaction *Transaction) {
return
}

View file

@ -11,6 +11,14 @@ type User struct {
Devices map[string]*device.Device `json:"devices,omitempty"`
}
type availableRequest struct {
Username string `json:"username,omitempty"`
}
type availableResponse struct {
Available bool `json:"available,omitempty"`
}
type registerRequest struct {
Auth authentificationData `json:"auth,omitempty"`
Username string `json:"username,omitempty"`
@ -58,12 +66,6 @@ type changePasswordRequest struct {
Auth authentificationData
}
type errorResponse struct {
ErrorCode string `json:"errcode,omitempty"`
ErrorMessage string `json:"error,omitempty"`
RetryTime int `json:"retry_after_ms,omitempty"`
}
type identifier struct {
IdentifierType string `json:"type,omitempty"`
User string `json:"user,omitempty"`

View file

@ -2,36 +2,72 @@ package user
import (
"encoding/json"
"log"
"fmt"
"net/http"
"nutfactory.org/Matrix/config"
"nutfactory.org/Matrix/entities/device"
"nutfactory.org/Matrix/utils"
)
func New(id string, name, string, password string, devices map[string]*device.Device) (err error, newUser *User) {
func New(username string, name string, password string) (err error, newUser *User) {
err, hashedPassword := utils.Hash([]byte(password))
if err != nil {
return
}
id := generateUserId(username)
newUser = &User{
Id: id,
Name: name,
Password: password,
Devices: devices,
Password: hashedPassword,
Devices: make(map[string]*device.Device),
}
return
}
func CheckUsernameAvailability(w http.ResponseWriter, r *http.Request) {
func CheckUsernameAvailabilityHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
request := availableRequest{}
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
}
userId := generateUserId(request.Username)
foundUser, err := ReadUser(userId)
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
}
if foundUser != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorCode: "M_USER_IN_USE", ErrorMessage: "Username already in use"}); err != nil {
panic(err)
}
return
}
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode("Test"); err != nil {
if err := json.NewEncoder(w).Encode(availableResponse{Available: true}); err != nil {
panic(err)
}
}
func Register(w http.ResponseWriter, r *http.Request) {
func RegisterHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
request := registerRequest{}
errResponse := utils.CheckRequest(r)
@ -46,7 +82,7 @@ func Register(w http.ResponseWriter, r *http.Request) {
err := decoder.Decode(&request)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Could not parse JSON"}); err != nil {
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Could not parse JSON: %s", err)}); err != nil {
panic(err)
}
return
@ -59,12 +95,7 @@ func Register(w http.ResponseWriter, r *http.Request) {
}
return
}
// TODO: Use New Function
newUser := &User{
Id: request.Username,
Name: request.Username,
Password: request.Password,
}
err, newUser := New(request.Username, request.Username, request.Password)
foundUser, err := ReadUser(newUser.Id)
if foundUser != nil {
w.WriteHeader(http.StatusBadRequest)
@ -76,40 +107,18 @@ func Register(w http.ResponseWriter, r *http.Request) {
err = CreateUser(newUser)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Database Error"}); err != nil {
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Database Error: %s", err)}); err != nil {
panic(err)
}
return
}
userDevice, err := device.ReadDevice(request.DeviceId)
if userDevice != nil {
err = userDevice.RenewAccesToken()
if err != nil {
log.Fatalf("Unable to renew AccesToken: %s", err)
return
}
err = device.UpdateDevice(userDevice, newUser.Id)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Database Error"}); err != nil {
panic(err)
}
return
}
} else {
err, userDevice = device.New(request.DeviceName)
if err != nil {
log.Fatalf("Unable to create device: %s", err)
return
}
err = device.CreateDevice(userDevice, newUser.Id)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Database Error"}); err != nil {
panic(err)
}
return
userDevice, errResponse := createUserDevice(request.DeviceId, request.DeviceName, newUser.Id)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
response := registerResponse{
UserId: newUser.Id,
@ -122,7 +131,7 @@ func Register(w http.ResponseWriter, r *http.Request) {
}
}
func Login(w http.ResponseWriter, r *http.Request) {
func LoginHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
request := loginRequest{}
errResponse := utils.CheckRequest(r)
@ -137,7 +146,7 @@ func Login(w http.ResponseWriter, r *http.Request) {
err := decoder.Decode(&request)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Could not parse JSON"}); err != nil {
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Could not parse JSON: %s", err)}); err != nil {
panic(err)
}
return
@ -150,40 +159,91 @@ func Login(w http.ResponseWriter, r *http.Request) {
}
return
}
if request.Identifier.IdentifierType != "m.id.user" && request.Identifier.User == "" {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Username missing"}); err != nil {
panic(err)
}
return
}
userId := generateUserId(request.Identifier.User)
foundUser, err := ReadUser(userId)
if err != nil || foundUser == nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorCode: "M_FORBIDDEN"}); err != nil {
panic(err)
}
return
}
err, hashedPassword := utils.Hash([]byte(request.Password))
if err != nil || foundUser.Password != hashedPassword {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorCode: "M_FORBIDDEN"}); err != nil {
panic(err)
}
return
}
userDevice, errResponse := createUserDevice(request.DeviceId, request.DeviceName, request.Identifier.User)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
response := loginResponse{
UserId: foundUser.Id,
AccessToken: userDevice.AccessToken,
DeviceId: userDevice.Id,
}
response.DiscoveryInfo.Homeserver.BaseUrl = config.Homeserver
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode("Test"); err != nil {
if err := json.NewEncoder(w).Encode(response); err != nil {
panic(err)
}
}
func Logout(w http.ResponseWriter, r *http.Request) {
func LogoutHandler(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
}
accessToken, errResponse := utils.GetAccessToken(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
foundDevice, err := device.ReadDeviceFromAccessToken(accessToken)
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
}
foundDevice.AccessToken = ""
err = device.UpdateDevice(foundDevice)
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
}
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode("Test"); err != nil {
panic(err)
}
}
func Deactivate(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode("Not Implemented"); err != nil {
panic(err)
}
}
func ChangePassword(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode("Not Implemented"); err != nil {
panic(err)
}
}
//TODO: Check if necessary
func Sync(w http.ResponseWriter, r *http.Request) {
func SyncHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode("Not Implemented"); err != nil {
@ -193,8 +253,47 @@ func Sync(w http.ResponseWriter, r *http.Request) {
func checkLoginType(loginType string) (errResponse *utils.ErrorResponse) {
if loginType != "m.login.password" {
errResponse = &utils.ErrorResponse{ErrorCode: "M_FORBIDDEN", ErrorMessage: "Unsupported Auth Type"}
errResponse = &utils.ErrorResponse{ErrorCode: "M_UNKNOWN", ErrorMessage: "Bad login type."}
return
}
return
}
func generateUserId(username string) string {
return fmt.Sprintf("@%s:%s", username, config.Homeserver)
}
func createUserDevice(id string, name string, userId string) (userDevice *device.Device, errResponse *utils.ErrorResponse) {
userDevice, err := device.ReadDevice(id)
if err != nil {
errResponse = &utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Database Error: %s", err)}
return
}
if userDevice != nil {
err = userDevice.RenewAccesToken()
if name != "" {
userDevice.Name = name
}
if err != nil {
errResponse = &utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Unable to renew AccesToken: %s", err)}
return
}
err = device.UpdateDevice(userDevice)
if err != nil {
errResponse = &utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Database Error: %s", err)}
return
}
} else {
err, userDevice = device.New(name)
if err != nil {
errResponse = &utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Unable to create device: %s", err)}
return
}
err = device.CreateDevice(userDevice, userId)
if err != nil {
errResponse = &utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Database Error: %s", err)}
return
}
}
return
}

View file

@ -56,6 +56,31 @@ func ReadUser(id string) (foundUser *User, err error) {
return
}
func ReadUserFromAccessToken(accessToken string) (foundUser *User, err error) {
queryStmt := fmt.Sprintf(`SELECT u.id, u.name, u.password
FROM user as u
join device as d on u.id = d.userId
WHERE d.accessToken = '%s'`, accessToken)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
if rows.Next() {
foundUser = &User{}
err = rows.Scan(&foundUser.Id, &foundUser.Name, &foundUser.Password)
if err != nil {
return
}
foundUser.Devices, err = device.ReadDevicesForUser(foundUser.Id)
}
return
}
func UpdateUser(user *User) (err error) {
sqlStmt := fmt.Sprintf(`UPDATE user SET
name = ?,

1
go.mod
View file

@ -3,6 +3,7 @@ module nutfactory.org/Matrix
go 1.14
require (
github.com/cenkalti/backoff/v4 v4.1.0
github.com/gorilla/mux v1.8.0
github.com/mattn/go-sqlite3 v1.14.3
)

29
go.sum
View file

@ -1,4 +1,33 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/cenkalti/backoff v1.1.0 h1:QnvVp8ikKCDWOsFheytRCoYWYPO/ObCTBGxT19Hc+yE=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v4 v4.1.0 h1:c8LkOFQTzuO0WBM/ae5HdGQuZPfPxp7lqBRwQRm4fSc=
github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-sqlite3 v1.14.3 h1:j7a/xn1U6TKA/PHHxqZuzh64CdtRc7rU9M+AvkOl5bA=
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d h1:/iIZNFGxc/a7C3yWjGcnboV+Tkc7mxr+p6fDztwoxuM=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
honnef.co/go/tools v0.0.1-2020.1.5 h1:nI5egYTGJakVyOryqLs1cQO5dO0ksin5XXs2pspk75k=
honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=

91
main.go
View file

@ -1,11 +1,16 @@
package main
import (
"encoding/json"
"crypto/tls"
"log"
"net/http"
"os"
"nutfactory.org/Matrix/config"
"nutfactory.org/Matrix/entities/device"
"nutfactory.org/Matrix/entities/event"
"nutfactory.org/Matrix/entities/general"
"nutfactory.org/Matrix/entities/room"
"nutfactory.org/Matrix/entities/user"
"nutfactory.org/Matrix/utils/database"
"nutfactory.org/Matrix/utils/router"
@ -14,73 +19,65 @@ import (
var keyPath = "./ssl.key"
var certPath = "./ssl.crt"
//var htmlPath = "./html/"
var routes = router.Routes{
// General
router.Route{"ResolveServerName", "GET", "/.well-known/matrix/server", Test},
router.Route{"GetServerImplementation", "GET", "/_matrix/federation/v1/version", Test},
router.Route{"ResolveServerName", "GET", "/.well-known/matrix/server", general.ResolveServerName},
router.Route{"GetServerImplementation", "GET", "/_matrix/federation/v1/version", general.GetServerImplementation},
router.Route{"Reset", "GET", "/reset", general.Reset},
// Keys
router.Route{"GetSigningKey", "GET", "/_matrix/key/v2/server/{keyId}", Test},
router.Route{"GetSigningKeyFromServer", "GET", "/_matrix/key/v2/query/{serverName}/{keyId}", Test},
router.Route{"GetSigningKeyFromMultipleServer", "GET", "/_matrix/key/v2/query", Test},
router.Route{"GetSigningKey", "GET", "/_matrix/key/v2/server/{keyId}", device.GetServerSigningKeyHandler},
router.Route{"GetSigningKey", "GET", "/_matrix/key/v2/server", device.GetServerSigningKeyHandler},
// Users
router.Route{"CheckUsernameAvailability", "GET", "/_matrix/client/r0/register/available", user.CheckUsernameAvailability},
router.Route{"Register", "POST", "/_matrix/client/r0/register", user.Register},
router.Route{"Login", "POST", "/_matrix/client/r0/login", user.Login},
router.Route{"Logout", "POST", "/_matrix/client/r0/logout", user.Logout},
router.Route{"Deactivate", "POST", "/_matrix/client/r0/account/deactivate", user.Deactivate},
router.Route{"ChangePassword", "POST", "/_matrix/client/r0/account/password", user.ChangePassword},
router.Route{"Sync", "GET", "/_matrix/client/r0/sync", user.Sync},
router.Route{"CheckUsernameAvailability", "GET", "/_matrix/client/r0/register/available", user.CheckUsernameAvailabilityHandler},
router.Route{"Register", "POST", "/_matrix/client/r0/register", user.RegisterHandler},
router.Route{"Login", "POST", "/_matrix/client/r0/login", user.LoginHandler},
router.Route{"Logout", "POST", "/_matrix/client/r0/logout", user.LogoutHandler},
router.Route{"Sync", "GET", "/_matrix/client/r0/sync", user.SyncHandler},
// Rooms
router.Route{"CreateRoom", "POST", "/_matrix/client/r0/createRoom", Test},
router.Route{"GetRoomMembers", "GET", "/_matrix/client/r0/rooms/{roomId}/members", Test},
router.Route{"JoinRoomUser", "POST", "/_matrix/client/r0/rooms/{roomId}/join", Test},
router.Route{"LeaveRoomUser", "POST", "/_matrix/client/r0/rooms/{roomId}/leave", Test},
router.Route{"CreateRoom", "POST", "/_matrix/client/r0/createRoom", room.CreateRoomHandler},
router.Route{"GetRoomMembers", "GET", "/_matrix/client/r0/rooms/{roomId}/members", room.GetRoomMemberHandler},
router.Route{"JoinRoomUser", "POST", "/_matrix/client/r0/rooms/{roomId}/join", room.JoinRoomUserHandler},
router.Route{"GetPrepInfoToJoin", "GET", "/_matrix/federation/v1/make_join/{roomId}/{userId}", Test},
router.Route{"JoinRoomServer", "PUT", "/_matrix/federation/v2/send_join/{roomId}/{eventId}", Test},
router.Route{"GetPrepInfoToLeave", "GET", "/_matrix/federation/v1/make_leave/{roomId}/{userId}", Test},
router.Route{"LeaveRoomServer", "PUT", "/_matrix/federation/v2/send_leave/{roomId}/{eventId}", Test},
router.Route{"GetPrepInfoToJoin", "GET", "/_matrix/federation/v1/make_join/{roomId}/{userId}", room.GetPrepInfoToJoinHandler},
router.Route{"JoinRoomServer", "PUT", "/_matrix/federation/v2/send_join/{roomId}/{eventId}", room.JoinRoomServerHandler},
// Events
router.Route{"CreateEvent", "PUT", "/_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}", Test},
router.Route{"CreateStateEvent", "PUT", "/_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}", Test},
router.Route{"ChangeEvent", "PUT", "/_matrix/client/r0/rooms/{roomId}/redact/{eventId}/{txnId}", Test},
router.Route{"GetEvents", "GET", "/_matrix/client/r0/rooms/{roomId}/messages", Test},
router.Route{"GetStateEventsUser", "GET", "/_matrix/client/r0/rooms/{roomId}/state", Test},
router.Route{"GetEventUser", "GET", "/_matrix/client/r0/rooms/{roomId}/event/{eventId}", Test},
router.Route{"GetStateEvent", "GET", "/_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}", Test},
router.Route{"CreateEvent", "PUT", "/_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}", event.SendMessageHandler},
router.Route{"CreateStateEvent", "PUT", "/_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}", event.CreateStateEventHandler},
router.Route{"GetEventUser", "GET", "/_matrix/client/r0/rooms/{roomId}/event/{eventId}", event.GetEventUserHandler},
router.Route{"GetStateEvent", "GET", "/_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}", event.GetStateEventHandler},
router.Route{"GetStateEventsServer", "GET", "/_matrix/federation/v1/state/{roomId}", Test},
router.Route{"GetEventServer", "GET", "/_matrix/federation/v1/event/{eventId}", Test},
router.Route{"SyncEventsServer", "PUT", "/_matrix/federation/v1/send/{txnId}", Test},
router.Route{"Backfill", "GET", "/_matrix/federation/v1/backfill/{roomId}", Test},
router.Route{"GetMissingEvents", "POST", "/_matrix/federation/v1/get_missing_events/{roomId}", Test},
}
func Test(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode("Test"); err != nil {
panic(err)
}
router.Route{"SyncEventsServer", "PUT", "/_matrix/federation/v1/send/{txnId}", event.SyncEventsServerHandler},
router.Route{"Backfill", "GET", "/_matrix/federation/v1/backfill/{roomId}", event.BackfillHandler},
}
func main() {
// TODO: Remove later
// TODO: Change to something variable --> cli
// TODO: Implement Message Counter
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
config.Homeserver = "localhost"
config.Port = "80"
if err := device.InitServerSigningKey(); err != nil {
log.Fatal(err)
}
config.VerifyKeys = make(map[string]map[string][]byte)
os.Remove("sqlite.db")
_ = database.InitDB("sqlite.db")
if err := database.InitDB("sqlite.db"); err != nil {
log.Fatal(err)
}
defer database.DB.Close()
// TODO: Set Default Config Params here
config.HttpString = "https"
router := router.NewRouter(routes)
//router.PathPrefix("/").Handler(http.FileServer(http.Dir(htmlPath)))
// TODO: Serve on Port 443 and 80 without Redirect
httpErr := http.ListenAndServeTLS(":443", certPath, keyPath, router)
if httpErr != nil {
log.Fatal(httpErr)

BIN
sqlite.db

Binary file not shown.

View file

@ -34,7 +34,8 @@ func initDeviceTable() (err error) {
log.Printf("Init Device Table")
statement, err := DB.Prepare(`CREATE TABLE IF NOT EXISTS device (
id TEXT PRIMARY KEY,
name TEXT,
name TEXT,
accessToken TEXT,
userId TEXT
)`)
if err != nil {
@ -137,10 +138,35 @@ func initEventTable() (err error) {
id TEXT PRIMARY KEY,
roomId TEXT,
txnId TEXT,
sender TEXT,
origin TEXT,
timestamp INTEGER,
eventType TEXT,
content TEXT,
parentId TEXT,
depth INTEGER
stateKey TEXT,
content TEXT,
depth INTEGER,
hash TEXT,
signature TEXT
)`)
if err != nil {
return
}
statement.Exec()
statement, err = DB.Prepare(`CREATE TABLE IF NOT EXISTS parent (
eventId TEXT,
parentId TEXT,
PRIMARY KEY (eventId, parentId)
)`)
if err != nil {
return
}
statement.Exec()
statement, err = DB.Prepare(`CREATE TABLE IF NOT EXISTS authEvent (
eventId TEXT,
authEventId TEXT,
PRIMARY KEY (eventId, authEventId)
)`)
if err != nil {
return
@ -202,7 +228,12 @@ func initRoomTable() (err error) {
log.Printf("Init Room Table")
statement, err := DB.Prepare(`CREATE TABLE IF NOT EXISTS room (
id TEXT PRIMARY KEY,
version TEXT
version TEXT,
visibility TEXT,
name TEXT,
topic TEXT,
isDirect INT,
federated INT
)`)
if err != nil {
return
@ -211,6 +242,7 @@ func initRoomTable() (err error) {
statement, err = DB.Prepare(`CREATE TABLE IF NOT EXISTS roomMember (
userId TEXT,
roomId TEXT,
server TEXT,
PRIMARY KEY (userId, roomId)
)`)
if err != nil {

View file

@ -1,11 +1,14 @@
package utils
import (
"crypto/ed25519"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"fmt"
"log"
"nutfactory.org/Matrix/config"
)
func CreateToken() (err error, token string) {
@ -15,7 +18,7 @@ func CreateToken() (err error, token string) {
log.Fatal(err)
return
}
token = string(b)
token = fmt.Sprintf("%x", b)
return
}
@ -26,7 +29,7 @@ func CreateUUID() (err error, uuid string) {
log.Fatal(err)
return
}
uuid = fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
uuid = fmt.Sprintf("%x_%x_%x_%x_%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
return
}
@ -40,3 +43,23 @@ func Hash(s []byte) (err error, hashString string) {
hashString = base64.StdEncoding.EncodeToString(hash)
return
}
func GenerateKeyPair() (publicKey ed25519.PublicKey, privateKey ed25519.PrivateKey, err error) {
publicKey, privateKey, err = ed25519.GenerateKey(nil)
return
}
func Sign(message []byte) []byte {
return ed25519.Sign(config.PrivateKey, message)
}
func SignContent(content []byte) (signatures map[string]map[string]string) {
signatures = make(map[string]map[string]string)
signatures[config.Homeserver] = make(map[string]string)
signatures[config.Homeserver][config.KeyId] = string(Sign(content))
return
}
func VerifySignature(publicKey []byte, message []byte, signature []byte) bool {
return ed25519.Verify(publicKey, message, signature)
}

View file

@ -1,11 +1,24 @@
package utils
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"strings"
"nutfactory.org/Matrix/config"
)
type RequestSummary struct {
Method string `json:"method,omitempty"`
Uri string `json:"uri,omitempty"`
Origin string `json:"origin,omitempty"`
Destination string `json:"destination,omitempty"`
Content string `json:"content,omitempty"`
Signatures map[string]map[string]string `json:"signatures,omitempty"`
}
type ErrorResponse struct {
ErrorCode string `json:"errcode,omitempty"`
ErrorMessage string `json:"error,omitempty"`
@ -19,6 +32,72 @@ func CheckRequest(r *http.Request) (response *ErrorResponse) {
return
}
func CheckAuthHeader(r *http.Request) (response *ErrorResponse) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" || !strings.Contains(authHeader, "X-Matrix") {
response = &ErrorResponse{ErrorMessage: "Missing Authorization Header"}
return
}
keys := strings.Split(authHeader, ",")
origin := strings.Split(keys[0], "=")[1]
if !strings.Contains(keys[2], "ed25519") {
response = &ErrorResponse{ErrorMessage: "Missing ed25519 Signature Key"}
return
}
key := strings.Split(strings.Replace(strings.Split(keys[2], "=")[1], "\"", "", 2), ":")[1]
signature := strings.Replace(strings.Split(keys[2], "=")[1], "\"", "", 2)
buf := new(bytes.Buffer)
buf.ReadFrom(r.Body)
content := buf.String()
requestSummary := RequestSummary{
Method: r.Method,
Uri: r.RequestURI,
Origin: origin,
Destination: config.Homeserver,
Content: content,
}
requestSummaryString, err := json.Marshal(requestSummary)
if err != nil {
response = &ErrorResponse{ErrorMessage: "Error Creating Auth JSON String"}
return
}
correct := VerifySignature([]byte(key), requestSummaryString, []byte(signature))
if !correct {
response = &ErrorResponse{ErrorMessage: "Signature in Auth Header is incorrect"}
return
}
return
}
func CreateAuthHeader(method string, uri string, destination string, content string) (authHeader string, err error) {
requestSummary := RequestSummary{
Method: method,
Uri: uri,
Origin: config.Homeserver,
Destination: destination,
Content: content,
}
SigningContent, err := json.Marshal(requestSummary)
if err != nil {
return
}
authHeader = fmt.Sprintf("X-Matrix origin=%s,key=\"%s\",sig=\"%s\"", config.Homeserver, config.KeyId, Sign(SigningContent))
return
}
func GetAccessToken(r *http.Request) (token string, response *ErrorResponse) {
token = r.URL.Query().Get("access_token")
if token == "" {
token = r.Header.Get("Authorization")
if token == "" || !strings.Contains(token, "Bearer") {
response = &ErrorResponse{ErrorCode: "M_MISSING_TOKEN"}
} else {
token = strings.Split(token, " ")[1]
}
}
return
}
func IsJSONString(s string) bool {
var js string
return json.Unmarshal([]byte(s), &js) == nil