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

View file

@ -3,5 +3,18 @@ package device
type Key struct { type Key struct {
Id string `json:"id,omitempty"` Id string `json:"id,omitempty"`
Type string `json:"type,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 package event
type Event struct { type Event struct {
Id string `json:"id,omitempty"` Id string `json:"event_id,omitempty"`
RoomId string `json:"roomId,omitempty"` RoomId string `json:"room_id,omitempty"`
EventType string `json:"eventType,omitempty"` Sender string `json:"sender,omitempty"`
Content string `json:"content,omitempty"` Origin string `json:"origin,omitempty"`
ParentId string `json:"parent,omitempty"` Timestamp int64 `json:"origin_server_ts,omitempty"`
Depth int `json:"depth,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 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 return
} }

View file

@ -2,15 +2,16 @@ package event
import ( import (
"fmt" "fmt"
"strings"
"nutfactory.org/Matrix/utils/database" "nutfactory.org/Matrix/utils/database"
) )
func CreateEvent(event *Event, txnId string) (err error) { func CreateRoomMember(roomId string, userId string) (err error) {
sqlStmt := fmt.Sprintf(`INSERT INTO event sqlStmt := fmt.Sprintf(`INSERT INTO roomMember
(id, roomId, txnId, eventType, content, parentId, depth) (roomId, userId, server)
VALUES VALUES
(?, ?, ?, ?, ?, ?, ?)`) (?, ?, ?)`)
tx, err := database.DB.Begin() tx, err := database.DB.Begin()
if err != nil { if err != nil {
@ -23,7 +24,7 @@ func CreateEvent(event *Event, txnId string) (err error) {
} }
defer stmt.Close() 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 { if err != nil {
return return
} }
@ -31,11 +32,129 @@ func CreateEvent(event *Event, txnId string) (err error) {
return 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) { func CreateEventsFromTransaction(txnId string, pdus map[string]*Event) (err error) {
sqlStmt := fmt.Sprintf(`INSERT INTO event 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 VALUES
(?, ?, ?, ?, ?, ?, ?)`) (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
tx, err := database.DB.Begin() tx, err := database.DB.Begin()
if err != nil { if err != nil {
@ -49,7 +168,35 @@ func CreateEventsFromTransaction(txnId string, pdus map[string]*Event) (err erro
defer stmt.Close() defer stmt.Close()
for _, pdu := range pdus { 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 { if err != nil {
return return
} }
@ -59,8 +206,186 @@ func CreateEventsFromTransaction(txnId string, pdus map[string]*Event) (err erro
return 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) { 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 FROM event
WHERE id = '%s'`, id) WHERE id = '%s'`, id)
@ -73,13 +398,34 @@ func ReadEvent(id string) (foundEvent *Event, err error) {
if rows.Next() { if rows.Next() {
foundEvent = &Event{} foundEvent = &Event{}
var signature string
err = rows.Scan(&foundEvent.Id, err = rows.Scan(&foundEvent.Id,
&foundEvent.RoomId, &foundEvent.RoomId,
&foundEvent.Unsigned.TransactionId,
&foundEvent.Sender,
&foundEvent.Origin,
&foundEvent.Timestamp,
&foundEvent.EventType, &foundEvent.EventType,
&foundEvent.Content, &foundEvent.Content,
&foundEvent.ParentId,
&foundEvent.Depth, &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 { if err != nil {
return return
} }
@ -88,8 +434,120 @@ func ReadEvent(id string) (foundEvent *Event, err error) {
return 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) { 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 FROM event
WHERE roomId = '%s'`, roomId) WHERE roomId = '%s'`, roomId)
@ -104,24 +562,98 @@ func ReadEventsFromRoom(roomId string) (events map[string]*Event, err error) {
for rows.Next() { for rows.Next() {
foundEvent := &Event{} foundEvent := &Event{}
var signature string
err = rows.Scan(&foundEvent.Id, err = rows.Scan(&foundEvent.Id,
&foundEvent.RoomId, &foundEvent.RoomId,
&foundEvent.Unsigned.TransactionId,
&foundEvent.Sender,
&foundEvent.Origin,
&foundEvent.Timestamp,
&foundEvent.EventType, &foundEvent.EventType,
&foundEvent.Content, &foundEvent.Content,
&foundEvent.ParentId,
&foundEvent.Depth, &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 { if err != nil {
return 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 events[foundEvent.Id] = foundEvent
} }
return return
} }
func ReadEventsFromTransaction(txnId string) (events map[string]*Event, err error) { func ReadStateEventsFromRoom(roomId string) (events []*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 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 FROM event
WHERE txnId = '%s'`, txnId) WHERE txnId = '%s'`, txnId)
@ -132,22 +664,41 @@ func ReadEventsFromTransaction(txnId string) (events map[string]*Event, err erro
defer rows.Close() defer rows.Close()
events = make(map[string]*Event)
for rows.Next() { for rows.Next() {
foundEvent := &Event{} foundEvent := &Event{}
err = rows.Scan( var signature string
&foundEvent.Id, err = rows.Scan(&foundEvent.Id,
&foundEvent.RoomId, &foundEvent.RoomId,
&foundEvent.Unsigned.TransactionId,
&foundEvent.Sender,
&foundEvent.Origin,
&foundEvent.Timestamp,
&foundEvent.EventType, &foundEvent.EventType,
&foundEvent.Content, &foundEvent.Content,
&foundEvent.ParentId,
&foundEvent.Depth, &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 { if err != nil {
return 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 return
@ -155,11 +706,8 @@ func ReadEventsFromTransaction(txnId string) (events map[string]*Event, err erro
func UpdateEvent(event *Event) (err error) { func UpdateEvent(event *Event) (err error) {
sqlStmt := fmt.Sprintf(`UPDATE event SET sqlStmt := fmt.Sprintf(`UPDATE event SET
roomId = ?,
eventType = ?, eventType = ?,
content = ?, content = ?,
parentId = ?,
depth = ?
WHERE id = ?`) WHERE id = ?`)
tx, err := database.DB.Begin() tx, err := database.DB.Begin()
@ -174,11 +722,8 @@ func UpdateEvent(event *Event) (err error) {
defer stmt.Close() defer stmt.Close()
_, err = stmt.Exec( _, err = stmt.Exec(
event.RoomId,
event.EventType, event.EventType,
event.Content, event.Content,
event.ParentId,
event.Depth,
event.Id, event.Id,
) )
if err != nil { if err != nil {
@ -203,6 +748,52 @@ func DeleteEvent(id string) (err error) {
return 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() tx.Commit()
return 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 ( import (
"fmt" "fmt"
"nutfactory.org/Matrix/entities/event"
"nutfactory.org/Matrix/utils/database" "nutfactory.org/Matrix/utils/database"
) )
@ -50,7 +49,7 @@ func ReadTransaction(id string) (foundTransaction *Transaction, err error) {
if err != nil { if err != nil {
return return
} }
foundTransaction.PDUS, err = event.ReadEventsFromTransaction(foundTransaction.Id) foundTransaction.PDUS, err = ReadEventsFromTransaction(foundTransaction.Id)
} }
return 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 package room
import ( import "nutfactory.org/Matrix/entities/event"
"nutfactory.org/Matrix/entities/event"
)
type Room struct { type Room struct {
Id string `json:"id,omitempty"` Id string `json:"id,omitempty"`
Messages map[string]*event.Event `json:"messages,omitempty"` Version string `json:"version,omitempty"`
//State map[string]event.Event `json:"state,omitempty"` Name string `json:"name,omitempty"`
Members []string `json:"members,omitempty"` Topic string `json:"topic,omitempty"`
Version string `json:"version,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 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 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" "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 sqlStmt := fmt.Sprintf(`INSERT INTO room
(id, version) (id, version, visibility, name, topic, isDirect, federated)
VALUES VALUES
(?, ?)`) (?, ?, ?, ?, ?, ?, ?)`)
tx, err := database.DB.Begin() tx, err := database.DB.Begin()
if err != nil { if err != nil {
@ -24,42 +24,31 @@ func CreateRoom(room *Room, userId string) (err error) {
} }
defer stmt.Close() 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 { if err != nil {
return return
} }
tx.Commit() tx.Commit()
err = CreateRoomMember(room.Id, userId) for _, userId := range room.Members {
err = CreateRoomMember(room.Id, userId)
}
return return
} }
func CreateRoomMember(roomId string, userId string) (err error) { func CreateRoomMember(roomId string, userId string) (err error) {
sqlStmt := fmt.Sprintf(`INSERT INTO roomMember return event.CreateRoomMember(roomId, userId)
(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
} }
func ReadRoom(id string) (foundRoom *Room, err error) { 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 FROM room
WHERE id = '%s'`, id) WHERE id = '%s'`, id)
@ -72,11 +61,19 @@ func ReadRoom(id string) (foundRoom *Room, err error) {
if rows.Next() { if rows.Next() {
foundRoom = &Room{} 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 { if err != nil {
return return
} }
foundRoom.Messages, err = event.ReadEventsFromRoom(foundRoom.Id) foundRoom.Events, err = event.ReadEventsFromRoom(foundRoom.Id)
if err != nil { if err != nil {
return return
} }
@ -90,34 +87,17 @@ func ReadRoom(id string) (foundRoom *Room, err error) {
} }
func ReadRoomMembers(roomId string) (roomMembers []string, err error) { func ReadRoomMembers(roomId string) (roomMembers []string, err error) {
queryStmt := fmt.Sprintf(`SELECT userId return event.ReadRoomMembers(roomId)
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 UpdateRoom(room *Room) (err error) { func UpdateRoom(room *Room) (err error) {
sqlStmt := fmt.Sprintf(`UPDATE room SET sqlStmt := fmt.Sprintf(`UPDATE room SET
version = ? version = ?,
visibility = ?,
name = ?,
topic = ?,
isDirect = ?,
federated = ?
WHERE id = ?`) WHERE id = ?`)
tx, err := database.DB.Begin() tx, err := database.DB.Begin()
@ -131,7 +111,15 @@ func UpdateRoom(room *Room) (err error) {
} }
defer stmt.Close() 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 { if err != nil {
return 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"` 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 { type registerRequest struct {
Auth authentificationData `json:"auth,omitempty"` Auth authentificationData `json:"auth,omitempty"`
Username string `json:"username,omitempty"` Username string `json:"username,omitempty"`
@ -58,12 +66,6 @@ type changePasswordRequest struct {
Auth authentificationData 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 { type identifier struct {
IdentifierType string `json:"type,omitempty"` IdentifierType string `json:"type,omitempty"`
User string `json:"user,omitempty"` User string `json:"user,omitempty"`

View file

@ -2,36 +2,72 @@ package user
import ( import (
"encoding/json" "encoding/json"
"log" "fmt"
"net/http" "net/http"
"nutfactory.org/Matrix/config"
"nutfactory.org/Matrix/entities/device" "nutfactory.org/Matrix/entities/device"
"nutfactory.org/Matrix/utils" "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)) err, hashedPassword := utils.Hash([]byte(password))
if err != nil { if err != nil {
return return
} }
id := generateUserId(username)
newUser = &User{ newUser = &User{
Id: id, Id: id,
Name: name, Name: name,
Password: password, Password: hashedPassword,
Devices: devices, Devices: make(map[string]*device.Device),
} }
return 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") 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) 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) 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") w.Header().Set("Content-Type", "application/json; charset=UTF-8")
request := registerRequest{} request := registerRequest{}
errResponse := utils.CheckRequest(r) errResponse := utils.CheckRequest(r)
@ -46,7 +82,7 @@ func Register(w http.ResponseWriter, r *http.Request) {
err := decoder.Decode(&request) err := decoder.Decode(&request)
if err != nil { if err != nil {
w.WriteHeader(http.StatusBadRequest) 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) panic(err)
} }
return return
@ -59,12 +95,7 @@ func Register(w http.ResponseWriter, r *http.Request) {
} }
return return
} }
// TODO: Use New Function err, newUser := New(request.Username, request.Username, request.Password)
newUser := &User{
Id: request.Username,
Name: request.Username,
Password: request.Password,
}
foundUser, err := ReadUser(newUser.Id) foundUser, err := ReadUser(newUser.Id)
if foundUser != nil { if foundUser != nil {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
@ -76,40 +107,18 @@ func Register(w http.ResponseWriter, r *http.Request) {
err = CreateUser(newUser) err = CreateUser(newUser)
if err != nil { if err != nil {
w.WriteHeader(http.StatusBadRequest) 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) panic(err)
} }
return return
} }
userDevice, err := device.ReadDevice(request.DeviceId) userDevice, errResponse := createUserDevice(request.DeviceId, request.DeviceName, newUser.Id)
if userDevice != nil { if errResponse != nil {
err = userDevice.RenewAccesToken() w.WriteHeader(http.StatusBadRequest)
if err != nil { if err := json.NewEncoder(w).Encode(errResponse); err != nil {
log.Fatalf("Unable to renew AccesToken: %s", err) panic(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
} }
return
} }
response := registerResponse{ response := registerResponse{
UserId: newUser.Id, 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") w.Header().Set("Content-Type", "application/json; charset=UTF-8")
request := loginRequest{} request := loginRequest{}
errResponse := utils.CheckRequest(r) errResponse := utils.CheckRequest(r)
@ -137,7 +146,7 @@ func Login(w http.ResponseWriter, r *http.Request) {
err := decoder.Decode(&request) err := decoder.Decode(&request)
if err != nil { if err != nil {
w.WriteHeader(http.StatusBadRequest) 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) panic(err)
} }
return return
@ -150,40 +159,91 @@ func Login(w http.ResponseWriter, r *http.Request) {
} }
return 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.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode("Test"); err != nil { if err := json.NewEncoder(w).Encode(response); err != nil {
panic(err) 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") 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) 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 //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.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode("Not Implemented"); err != nil { 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) { func checkLoginType(loginType string) (errResponse *utils.ErrorResponse) {
if loginType != "m.login.password" { 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
} }
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 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) { func UpdateUser(user *User) (err error) {
sqlStmt := fmt.Sprintf(`UPDATE user SET sqlStmt := fmt.Sprintf(`UPDATE user SET
name = ?, name = ?,

1
go.mod
View file

@ -3,6 +3,7 @@ module nutfactory.org/Matrix
go 1.14 go 1.14
require ( require (
github.com/cenkalti/backoff/v4 v4.1.0
github.com/gorilla/mux v1.8.0 github.com/gorilla/mux v1.8.0
github.com/mattn/go-sqlite3 v1.14.3 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 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 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 h1:j7a/xn1U6TKA/PHHxqZuzh64CdtRc7rU9M+AvkOl5bA=
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= 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 package main
import ( import (
"encoding/json" "crypto/tls"
"log" "log"
"net/http" "net/http"
"os" "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/entities/user"
"nutfactory.org/Matrix/utils/database" "nutfactory.org/Matrix/utils/database"
"nutfactory.org/Matrix/utils/router" "nutfactory.org/Matrix/utils/router"
@ -14,73 +19,65 @@ import (
var keyPath = "./ssl.key" var keyPath = "./ssl.key"
var certPath = "./ssl.crt" var certPath = "./ssl.crt"
//var htmlPath = "./html/"
var routes = router.Routes{ var routes = router.Routes{
// General // General
router.Route{"ResolveServerName", "GET", "/.well-known/matrix/server", Test}, router.Route{"ResolveServerName", "GET", "/.well-known/matrix/server", general.ResolveServerName},
router.Route{"GetServerImplementation", "GET", "/_matrix/federation/v1/version", Test}, router.Route{"GetServerImplementation", "GET", "/_matrix/federation/v1/version", general.GetServerImplementation},
router.Route{"Reset", "GET", "/reset", general.Reset},
// Keys // Keys
router.Route{"GetSigningKey", "GET", "/_matrix/key/v2/server/{keyId}", Test}, router.Route{"GetSigningKey", "GET", "/_matrix/key/v2/server/{keyId}", device.GetServerSigningKeyHandler},
router.Route{"GetSigningKeyFromServer", "GET", "/_matrix/key/v2/query/{serverName}/{keyId}", Test}, router.Route{"GetSigningKey", "GET", "/_matrix/key/v2/server", device.GetServerSigningKeyHandler},
router.Route{"GetSigningKeyFromMultipleServer", "GET", "/_matrix/key/v2/query", Test},
// Users // Users
router.Route{"CheckUsernameAvailability", "GET", "/_matrix/client/r0/register/available", user.CheckUsernameAvailability}, router.Route{"CheckUsernameAvailability", "GET", "/_matrix/client/r0/register/available", user.CheckUsernameAvailabilityHandler},
router.Route{"Register", "POST", "/_matrix/client/r0/register", user.Register}, router.Route{"Register", "POST", "/_matrix/client/r0/register", user.RegisterHandler},
router.Route{"Login", "POST", "/_matrix/client/r0/login", user.Login}, router.Route{"Login", "POST", "/_matrix/client/r0/login", user.LoginHandler},
router.Route{"Logout", "POST", "/_matrix/client/r0/logout", user.Logout}, router.Route{"Logout", "POST", "/_matrix/client/r0/logout", user.LogoutHandler},
router.Route{"Deactivate", "POST", "/_matrix/client/r0/account/deactivate", user.Deactivate}, router.Route{"Sync", "GET", "/_matrix/client/r0/sync", user.SyncHandler},
router.Route{"ChangePassword", "POST", "/_matrix/client/r0/account/password", user.ChangePassword},
router.Route{"Sync", "GET", "/_matrix/client/r0/sync", user.Sync},
// Rooms // Rooms
router.Route{"CreateRoom", "POST", "/_matrix/client/r0/createRoom", Test}, router.Route{"CreateRoom", "POST", "/_matrix/client/r0/createRoom", room.CreateRoomHandler},
router.Route{"GetRoomMembers", "GET", "/_matrix/client/r0/rooms/{roomId}/members", Test}, router.Route{"GetRoomMembers", "GET", "/_matrix/client/r0/rooms/{roomId}/members", room.GetRoomMemberHandler},
router.Route{"JoinRoomUser", "POST", "/_matrix/client/r0/rooms/{roomId}/join", Test}, router.Route{"JoinRoomUser", "POST", "/_matrix/client/r0/rooms/{roomId}/join", room.JoinRoomUserHandler},
router.Route{"LeaveRoomUser", "POST", "/_matrix/client/r0/rooms/{roomId}/leave", Test},
router.Route{"GetPrepInfoToJoin", "GET", "/_matrix/federation/v1/make_join/{roomId}/{userId}", 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}", Test}, router.Route{"JoinRoomServer", "PUT", "/_matrix/federation/v2/send_join/{roomId}/{eventId}", room.JoinRoomServerHandler},
router.Route{"GetPrepInfoToLeave", "GET", "/_matrix/federation/v1/make_leave/{roomId}/{userId}", Test},
router.Route{"LeaveRoomServer", "PUT", "/_matrix/federation/v2/send_leave/{roomId}/{eventId}", Test},
// Events // Events
router.Route{"CreateEvent", "PUT", "/_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}", 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}", Test}, router.Route{"CreateStateEvent", "PUT", "/_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}", event.CreateStateEventHandler},
router.Route{"ChangeEvent", "PUT", "/_matrix/client/r0/rooms/{roomId}/redact/{eventId}/{txnId}", Test}, router.Route{"GetEventUser", "GET", "/_matrix/client/r0/rooms/{roomId}/event/{eventId}", event.GetEventUserHandler},
router.Route{"GetEvents", "GET", "/_matrix/client/r0/rooms/{roomId}/messages", Test}, router.Route{"GetStateEvent", "GET", "/_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}", event.GetStateEventHandler},
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{"GetStateEventsServer", "GET", "/_matrix/federation/v1/state/{roomId}", Test}, router.Route{"SyncEventsServer", "PUT", "/_matrix/federation/v1/send/{txnId}", event.SyncEventsServerHandler},
router.Route{"GetEventServer", "GET", "/_matrix/federation/v1/event/{eventId}", Test}, router.Route{"Backfill", "GET", "/_matrix/federation/v1/backfill/{roomId}", event.BackfillHandler},
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)
}
} }
func main() { 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") os.Remove("sqlite.db")
_ = database.InitDB("sqlite.db") if err := database.InitDB("sqlite.db"); err != nil {
log.Fatal(err)
}
defer database.DB.Close() defer database.DB.Close()
// TODO: Set Default Config Params here
config.HttpString = "https"
router := router.NewRouter(routes) 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) httpErr := http.ListenAndServeTLS(":443", certPath, keyPath, router)
if httpErr != nil { if httpErr != nil {
log.Fatal(httpErr) 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") log.Printf("Init Device Table")
statement, err := DB.Prepare(`CREATE TABLE IF NOT EXISTS device ( statement, err := DB.Prepare(`CREATE TABLE IF NOT EXISTS device (
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
name TEXT, name TEXT,
accessToken TEXT,
userId TEXT userId TEXT
)`) )`)
if err != nil { if err != nil {
@ -137,10 +138,35 @@ func initEventTable() (err error) {
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
roomId TEXT, roomId TEXT,
txnId TEXT, txnId TEXT,
sender TEXT,
origin TEXT,
timestamp INTEGER,
eventType TEXT, eventType TEXT,
content TEXT, stateKey TEXT,
parentId TEXT, content TEXT,
depth INTEGER 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 { if err != nil {
return return
@ -202,7 +228,12 @@ func initRoomTable() (err error) {
log.Printf("Init Room Table") log.Printf("Init Room Table")
statement, err := DB.Prepare(`CREATE TABLE IF NOT EXISTS room ( statement, err := DB.Prepare(`CREATE TABLE IF NOT EXISTS room (
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
version TEXT version TEXT,
visibility TEXT,
name TEXT,
topic TEXT,
isDirect INT,
federated INT
)`) )`)
if err != nil { if err != nil {
return return
@ -211,6 +242,7 @@ func initRoomTable() (err error) {
statement, err = DB.Prepare(`CREATE TABLE IF NOT EXISTS roomMember ( statement, err = DB.Prepare(`CREATE TABLE IF NOT EXISTS roomMember (
userId TEXT, userId TEXT,
roomId TEXT, roomId TEXT,
server TEXT,
PRIMARY KEY (userId, roomId) PRIMARY KEY (userId, roomId)
)`) )`)
if err != nil { if err != nil {

View file

@ -1,11 +1,14 @@
package utils package utils
import ( import (
"crypto/ed25519"
"crypto/rand" "crypto/rand"
"crypto/sha256" "crypto/sha256"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"log" "log"
"nutfactory.org/Matrix/config"
) )
func CreateToken() (err error, token string) { func CreateToken() (err error, token string) {
@ -15,7 +18,7 @@ func CreateToken() (err error, token string) {
log.Fatal(err) log.Fatal(err)
return return
} }
token = string(b) token = fmt.Sprintf("%x", b)
return return
} }
@ -26,7 +29,7 @@ func CreateUUID() (err error, uuid string) {
log.Fatal(err) log.Fatal(err)
return 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 return
} }
@ -40,3 +43,23 @@ func Hash(s []byte) (err error, hashString string) {
hashString = base64.StdEncoding.EncodeToString(hash) hashString = base64.StdEncoding.EncodeToString(hash)
return 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 package utils
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"strings" "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 { type ErrorResponse struct {
ErrorCode string `json:"errcode,omitempty"` ErrorCode string `json:"errcode,omitempty"`
ErrorMessage string `json:"error,omitempty"` ErrorMessage string `json:"error,omitempty"`
@ -19,6 +32,72 @@ func CheckRequest(r *http.Request) (response *ErrorResponse) {
return 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 { func IsJSONString(s string) bool {
var js string var js string
return json.Unmarshal([]byte(s), &js) == nil return json.Unmarshal([]byte(s), &js) == nil