Changes
This commit is contained in:
parent
7db9c374cc
commit
9eac960763
26 changed files with 3119 additions and 266 deletions
21
config/config.go
Normal file
21
config/config.go
Normal 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
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"`
|
||||||
}
|
}
|
||||||
|
|
76
entities/device/keyController.go
Normal file
76
entities/device/keyController.go
Normal 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
|
||||||
|
}
|
|
@ -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"`
|
|
||||||
}
|
|
|
@ -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"`
|
||||||
|
Origin string `json:"origin,omitempty"`
|
||||||
|
Timestamp int64 `json:"origin_server_ts,omitempty"`
|
||||||
|
EventType string `json:"type,omitempty"`
|
||||||
|
StateKey string `json:"state_key,omitempty"`
|
||||||
Content string `json:"content,omitempty"`
|
Content string `json:"content,omitempty"`
|
||||||
ParentId string `json:"parent,omitempty"`
|
PrevEventHashes map[string]EventHash `json:"prev_events,omitempty"`
|
||||||
Depth int `json:"depth,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"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
8
entities/event/transaction.go
Normal file
8
entities/event/transaction.go
Normal 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"`
|
||||||
|
}
|
|
@ -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
|
94
entities/general/generalController.go
Normal file
94
entities/general/generalController.go
Normal 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)
|
||||||
|
}
|
|
@ -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"`
|
|
||||||
//State map[string]event.Event `json:"state,omitempty"`
|
|
||||||
Members []string `json:"members,omitempty"`
|
|
||||||
Version string `json:"version,omitempty"`
|
Version string `json:"version,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Topic string `json:"topic,omitempty"`
|
||||||
|
Members []string `json:"members,omitempty"`
|
||||||
|
Servers []string
|
||||||
|
Events map[string]*event.Event `json:"events,omitempty"`
|
||||||
|
Visibility string `json:"visibility,omitempty"`
|
||||||
|
IsDirect bool `json:"is_direct,omitempty"`
|
||||||
|
Federated bool `json:"federated,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type createRoomRequest struct {
|
||||||
|
Visibility string `json:"visibility,omitempty"`
|
||||||
|
RoomAliasName string `json:"room_alias_name,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Topic string `json:"topic,omitempty"`
|
||||||
|
Invite string `json:"invite,omitempty"`
|
||||||
|
Invite3pid invite3pid `json:"invite_3pid,omitempty"`
|
||||||
|
RoomVersion string `json:"room_version,omitempty"`
|
||||||
|
CreationContent creationContent `json:"creation_content,omitempty"`
|
||||||
|
InitialState []event.StateEvent `json:"initial_state,omitempty"`
|
||||||
|
Preset string `json:"preset,omitempty"`
|
||||||
|
IsDirect bool `json:"is_direct,omitempty"`
|
||||||
|
PowerLevelContentOverride string `json:"power_level_content_override,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type createRoomResponse struct {
|
||||||
|
RoomId string `json:"room_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type getRoomMemberRequest struct{}
|
||||||
|
|
||||||
|
type getRoomMemberResponse struct {
|
||||||
|
Chunk []*event.Event `json:"chunk,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type joinRoomUserRequest struct {
|
||||||
|
ThirdPartySigned thirdPartySigned `json:"third_party_signed,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type joinRoomUserResponse struct {
|
||||||
|
RoomId string `json:"room_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type leaveRoomUserRequest struct{}
|
||||||
|
|
||||||
|
type leaveRoomUserResponse struct{}
|
||||||
|
|
||||||
|
type makeJoinRequest struct{}
|
||||||
|
|
||||||
|
type makeJoinResponse struct {
|
||||||
|
RoomVersion string `json:"room_version,omitempty"`
|
||||||
|
Event event.Event `json:"event,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type joinRoomServerRequest struct {
|
||||||
|
Sender string `json:"sender,omitempty"`
|
||||||
|
Origin string `json:"origin,omitempty"`
|
||||||
|
Timestamp int64 `json:"origin_server_ts,omitempty"`
|
||||||
|
EventType string `json:"type,omitempty"`
|
||||||
|
StateKey string `json:"state_key,omitempty"`
|
||||||
|
Content event.MemberEventContent `json:"content,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type joinRoomServerResponse struct {
|
||||||
|
Origin string `json:"origin,omitempty"`
|
||||||
|
AuthChain []*event.Event `json:"auth_chain,omitempty"`
|
||||||
|
State []*event.Event `json:"state,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type makeLeaveRequest struct{}
|
||||||
|
|
||||||
|
type makeLeaveResponse struct{}
|
||||||
|
|
||||||
|
type leaveRoomServerRequest struct{}
|
||||||
|
|
||||||
|
type leaveRoomServerResponse struct{}
|
||||||
|
|
||||||
|
type invite3pid struct {
|
||||||
|
IdServer string `json:"id_server,omitempty"`
|
||||||
|
IdAccessToken string `json:"id_access_token,omitempty"`
|
||||||
|
Medium string `json:"medium,omitempty"`
|
||||||
|
Address string `json:"address,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type creationContent struct {
|
||||||
|
Federated bool `json:"m.federate,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type unsignedData struct {
|
||||||
|
Age int `json:"age,omitempty"`
|
||||||
|
RedactedBecause *event.Event `json:"redacted_because,omitempty"`
|
||||||
|
TransactionId string `json:"transaction_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type invite struct {
|
||||||
|
DisplayName string `json:"display_name,omitempty"`
|
||||||
|
Signed thirdPartySigned `json:"signed,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type thirdPartySigned struct {
|
||||||
|
Sender string `json:"sender,omitempty"`
|
||||||
|
MXID string `json:"mxid,omitempty"`
|
||||||
|
Signatures signatures `json:"signatures,omitempty"`
|
||||||
|
Token string `json:"token,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type signatures struct{}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
for _, userId := range room.Members {
|
||||||
err = CreateRoomMember(room.Id, userId)
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"`
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
package transaction
|
|
||||||
|
|
||||||
func New() (transaction *Transaction) {
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -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"`
|
||||||
|
|
|
@ -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,41 +107,19 @@ 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()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Unable to renew AccesToken: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = device.UpdateDevice(userDevice, newUser.Id)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Database Error"}); err != nil {
|
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
response := registerResponse{
|
response := registerResponse{
|
||||||
UserId: newUser.Id,
|
UserId: newUser.Id,
|
||||||
AccessToken: userDevice.AccessToken,
|
AccessToken: userDevice.AccessToken,
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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
1
go.mod
|
@ -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
29
go.sum
|
@ -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
91
main.go
|
@ -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
BIN
sqlite.db
Binary file not shown.
|
@ -35,6 +35,7 @@ func initDeviceTable() (err error) {
|
||||||
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,
|
||||||
|
stateKey TEXT,
|
||||||
content TEXT,
|
content TEXT,
|
||||||
|
depth INTEGER,
|
||||||
|
hash TEXT,
|
||||||
|
signature TEXT
|
||||||
|
)`)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
statement.Exec()
|
||||||
|
|
||||||
|
statement, err = DB.Prepare(`CREATE TABLE IF NOT EXISTS parent (
|
||||||
|
eventId TEXT,
|
||||||
parentId TEXT,
|
parentId TEXT,
|
||||||
depth INTEGER
|
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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue