little update.
This commit is contained in:
parent
4d60e5918d
commit
260386bcac
28 changed files with 1070 additions and 50 deletions
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright (C) 2026 saces@c-base.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
package main
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
typedef void (*on_event_handler_ptr) (char*);
|
||||
typedef void (*on_message_handler_ptr) (char*);
|
||||
|
||||
static inline void call_c_on_event_handler(on_event_handler_ptr ptr, char* jsonStr) {
|
||||
(ptr)(jsonStr);
|
||||
}
|
||||
|
||||
static inline void call_c_on_message_handler(on_message_handler_ptr ptr, char* jsonStr) {
|
||||
(ptr)(jsonStr);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
var on_event_handler C.on_event_handler_ptr
|
||||
var on_message_handler C.on_message_handler_ptr
|
||||
|
||||
//export register_on_event_handler
|
||||
func register_on_event_handler(fn C.on_event_handler_ptr) {
|
||||
on_event_handler = fn
|
||||
}
|
||||
|
||||
//export register_on_message_handler
|
||||
func register_on_message_handler(fn C.on_message_handler_ptr) {
|
||||
on_message_handler = fn
|
||||
}
|
||||
125
libmxclient/determinant/mxpassfile/mxpassfile.go
Normal file
125
libmxclient/determinant/mxpassfile/mxpassfile.go
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
package mxpassfile
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// inspired by https://github.com/jackc/pgpassfile
|
||||
|
||||
// Entry represents a line in a MX passfile.
|
||||
type Entry struct {
|
||||
Matrixhost string
|
||||
Localpart string
|
||||
Domain string
|
||||
Token string
|
||||
}
|
||||
|
||||
// Passfile is the in memory data structure representing a MX passfile.
|
||||
type Passfile struct {
|
||||
Entries []*Entry
|
||||
}
|
||||
|
||||
// ReadPassfile reads the file at path and parses it into a Passfile.
|
||||
func readPassfile(path string) (*Passfile, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return ParsePassfile(f)
|
||||
}
|
||||
|
||||
// ParsePassfile reads r and parses it into a Passfile.
|
||||
func ParsePassfile(r io.Reader) (*Passfile, error) {
|
||||
passfile := &Passfile{}
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
for scanner.Scan() {
|
||||
entry := parseLine(scanner.Text())
|
||||
if entry != nil {
|
||||
passfile.Entries = append(passfile.Entries, entry)
|
||||
}
|
||||
}
|
||||
|
||||
return passfile, scanner.Err()
|
||||
}
|
||||
|
||||
// parseLine parses a line into an *Entry. It returns nil on comment lines or any other unparsable
|
||||
// line.
|
||||
func parseLine(line string) *Entry {
|
||||
const (
|
||||
tmpBackslash = "\r"
|
||||
tmpPipe = "\n"
|
||||
)
|
||||
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
if strings.HasPrefix(line, "#") {
|
||||
return nil
|
||||
}
|
||||
|
||||
line = strings.ReplaceAll(line, `\\`, tmpBackslash)
|
||||
line = strings.ReplaceAll(line, `\|`, tmpPipe)
|
||||
|
||||
parts := strings.Split(line, "|")
|
||||
if len(parts) != 4 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unescape escaped colons and backslashes
|
||||
for i := range parts {
|
||||
parts[i] = strings.ReplaceAll(parts[i], tmpBackslash, `\`)
|
||||
parts[i] = strings.ReplaceAll(parts[i], tmpPipe, `|`)
|
||||
parts[i] = strings.TrimSpace(parts[i])
|
||||
}
|
||||
|
||||
return &Entry{
|
||||
Matrixhost: parts[0],
|
||||
Localpart: parts[1],
|
||||
Domain: parts[2],
|
||||
Token: parts[3],
|
||||
}
|
||||
}
|
||||
|
||||
func isWild(s string) bool {
|
||||
if s == "" || s == "*" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func superCMP(s1, s2 string) bool {
|
||||
if isWild(s1) || isWild(s2) {
|
||||
return true
|
||||
}
|
||||
//fmt.Printf("sCMP: '%s' '%s'\n", s1, s2)
|
||||
return s1 == s2
|
||||
}
|
||||
|
||||
// FindPassword finds the password for the provided synapsehost, localpart, and domain. An empty
|
||||
// string will be returned if no match is found.
|
||||
func (pf *Passfile) FindPassword(matrixhost, localpart, domain string) string {
|
||||
for _, e := range pf.Entries {
|
||||
if (e.Matrixhost == "*" || e.Matrixhost == matrixhost) &&
|
||||
(e.Localpart == "*" || e.Localpart == localpart) &&
|
||||
(e.Domain == "*" || e.Domain == domain) {
|
||||
return e.Token
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (pf *Passfile) FindPasswordFill(matrixhost, localpart, domain string) *Entry {
|
||||
for _, e := range pf.Entries {
|
||||
if superCMP(e.Matrixhost, matrixhost) &&
|
||||
superCMP(e.Localpart, localpart) &&
|
||||
superCMP(e.Domain, domain) {
|
||||
return e
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
59
libmxclient/determinant/mxpassfile/mxpassfile_test.go
Normal file
59
libmxclient/determinant/mxpassfile/mxpassfile_test.go
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
package mxpassfile
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func tokenComp(t *testing.T, expected string, value string) {
|
||||
if value != expected {
|
||||
t.Fatalf(`token was "%s", expected "%s"`, value, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func unescape(s string) string {
|
||||
s = strings.Replace(s, `\:`, `:`, -1)
|
||||
s = strings.Replace(s, `\\`, `\`, -1)
|
||||
return s
|
||||
}
|
||||
|
||||
var passfile = [][]string{
|
||||
{"test1:5432", "larrydb", "larry", "whatstheidea"},
|
||||
{"test1:5432", "moedb", "moe", "imbecile"},
|
||||
{"test1:5432", "curlydb", "curly", "nyuknyuknyuk"},
|
||||
{"test2:5432", "*", "shemp", "heymoe"},
|
||||
{"test2:5432", "*", "*", `test\\ing\|er`},
|
||||
{"localhost", "*", "*", "sesam"},
|
||||
{"test3", "", "", "swordfish"}, // user will be filled later
|
||||
}
|
||||
|
||||
func TestParsePassFile(t *testing.T) {
|
||||
buf := bytes.NewBufferString(`# A comment
|
||||
test1:5432|larrydb|larry|whatstheidea
|
||||
test1:5432|moedb|moe|imbecile
|
||||
test1:5432|curlydb|curly|nyuknyuknyuk
|
||||
test2:5432|*|shemp|heymoe
|
||||
test2:5432|*|*|test\\ing\|er
|
||||
localhost|*|*|sesam
|
||||
`)
|
||||
|
||||
passfile, err := ParsePassfile(buf)
|
||||
if err != nil {
|
||||
t.Fatalf(`ParsePassfile returned error: "%v"`, err)
|
||||
}
|
||||
|
||||
if len(passfile.Entries) != 6 {
|
||||
t.Fatalf(`passfile.Entries is "%d", expected 6`, len(passfile.Entries))
|
||||
}
|
||||
|
||||
tokenComp(t, "whatstheidea", passfile.FindPassword("test1:5432", "larrydb", "larry"))
|
||||
tokenComp(t, "imbecile", passfile.FindPassword("test1:5432", "moedb", "moe"))
|
||||
tokenComp(t, `test\ing|er`, passfile.FindPassword("test2:5432", "something", "else"))
|
||||
tokenComp(t, "sesam", passfile.FindPassword("localhost", "foo", "bare"))
|
||||
|
||||
tokenComp(t, "", passfile.FindPassword("wrong:5432", "larrydb", "larry"))
|
||||
tokenComp(t, "", passfile.FindPassword("test1:wrong", "larrydb", "larry"))
|
||||
tokenComp(t, "", passfile.FindPassword("test1:5432", "wrong", "larry"))
|
||||
tokenComp(t, "", passfile.FindPassword("test1:5432", "larrydb", "wrong"))
|
||||
}
|
||||
9
libmxclient/determinant/mxpassfile/readpassfile.go
Normal file
9
libmxclient/determinant/mxpassfile/readpassfile.go
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package mxpassfile
|
||||
|
||||
// ReadPassfile reads the file at path and parses it into a Passfile.
|
||||
func ReadPassfile(path string) (*Passfile, error) {
|
||||
return readPassfile(path)
|
||||
}
|
||||
22
libmxclient/determinant/mxpassfile/readpassfile_unix.go
Normal file
22
libmxclient/determinant/mxpassfile/readpassfile_unix.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package mxpassfile
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ReadPassfile reads the file at path and parses it into a Passfile.
|
||||
func ReadPassfile(path string) (*Passfile, error) {
|
||||
fileInfo, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
permissions := fileInfo.Mode().Perm()
|
||||
if permissions != 0o600 {
|
||||
return nil, errors.New("To wide permissions, ignore file")
|
||||
}
|
||||
return readPassfile(path)
|
||||
}
|
||||
|
|
@ -2,18 +2,22 @@ module mxclientlib
|
|||
|
||||
go 1.24.0
|
||||
|
||||
require maunium.net/go/mautrix v0.26.2
|
||||
require (
|
||||
github.com/mattn/go-sqlite3 v1.14.33
|
||||
github.com/rs/zerolog v1.34.0
|
||||
go.mau.fi/util v0.9.5
|
||||
maunium.net/go/mautrix v0.26.2
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/rs/zerolog v1.34.0 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.2.0 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
go.mau.fi/util v0.9.5 // indirect
|
||||
golang.org/x/crypto v0.47.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect
|
||||
golang.org/x/net v0.49.0 // indirect
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
|
@ -11,6 +13,10 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
|
|||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0=
|
||||
github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 h1:KPpdlQLZcHfTMQRi6bFQ7ogNO0ltFT4PmtwTLW4W+14=
|
||||
github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
|
|
|||
32
libmxclient/mxapi/discover.go
Normal file
32
libmxclient/mxapi/discover.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package mxapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
func Discover(mxid string) (string, error) {
|
||||
|
||||
localpart, hs, err := id.UserID(mxid).ParseAndValidateRelaxed()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
wk, err := mautrix.DiscoverClientAPI(context.Background(), hs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if wk != nil {
|
||||
hs = wk.Homeserver.BaseURL
|
||||
}
|
||||
|
||||
out, err := json.Marshal(map[string]string{"mxid": mxid, "homeserver": hs, "loginname": localpart})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(out), nil
|
||||
}
|
||||
56
libmxclient/mxapi/login.go
Normal file
56
libmxclient/mxapi/login.go
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
package mxapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
type login_data struct {
|
||||
Homeserver string `json:"homeserver"`
|
||||
Mxid string `json:"mxid"`
|
||||
Loginname string `json:"loginname"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
func Login(data string) (string, error) {
|
||||
var ld login_data
|
||||
err := json.Unmarshal([]byte(data), &ld)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
mauclient, err := mautrix.NewClient(ld.Homeserver, id.UserID(ld.Mxid), "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
deviceName := fmt.Sprintf("smalbot-%d", time.Now().Unix())
|
||||
|
||||
resp, err := mauclient.Login(context.Background(), &mautrix.ReqLogin{
|
||||
Type: "m.login.password",
|
||||
Identifier: mautrix.UserIdentifier{
|
||||
Type: "m.id.user",
|
||||
User: ld.Loginname,
|
||||
},
|
||||
Password: ld.Password,
|
||||
DeviceID: id.DeviceID(deviceName),
|
||||
InitialDeviceDisplayName: deviceName,
|
||||
StoreCredentials: false,
|
||||
StoreHomeserverURL: false,
|
||||
RefreshToken: false,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
tpl := "%s | %s | %s | %s\nrecovery | | | %x\nmaster | | | %x\n"
|
||||
res := fmt.Sprintf(tpl, ld.Homeserver, ld.Loginname, id.UserID(ld.Mxid).Homeserver(), resp.AccessToken, sha256.Sum256([]byte(ld.Password)), sha256.Sum256([]byte(deviceName)))
|
||||
|
||||
return res, nil
|
||||
}
|
||||
193
libmxclient/mxclient/client.go
Normal file
193
libmxclient/mxclient/client.go
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
// Copyright (C) 2026 saces@c-base.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
package mxclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"mxclientlib/determinant/mxpassfile"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"go.mau.fi/util/dbutil"
|
||||
_ "go.mau.fi/util/dbutil/litestream"
|
||||
"maunium.net/go/mautrix"
|
||||
"maunium.net/go/mautrix/crypto"
|
||||
"maunium.net/go/mautrix/crypto/cryptohelper"
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
"maunium.net/go/mautrix/sqlstatestore"
|
||||
)
|
||||
|
||||
type MXClient struct {
|
||||
*mautrix.Client
|
||||
OnEvent func(string)
|
||||
OnMessage func(string)
|
||||
}
|
||||
|
||||
func (mxc *MXClient) _onEvent(ctx context.Context, evt *event.Event) {
|
||||
if evt.GetStateKey() == mxc.UserID.String() && evt.Content.AsMember().Membership == event.MembershipInvite {
|
||||
_, err := mxc.JoinRoomByID(ctx, evt.RoomID)
|
||||
if err == nil {
|
||||
log.Info().
|
||||
Str("room_id", evt.RoomID.String()).
|
||||
Str("inviter", evt.Sender.String()).
|
||||
Msg("Joined room after invite")
|
||||
} else {
|
||||
log.Error().Err(err).
|
||||
Str("room_id", evt.RoomID.String()).
|
||||
Str("inviter", evt.Sender.String()).
|
||||
Msg("Failed to join room after invite")
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Got event: %#v\n", evt)
|
||||
}
|
||||
}
|
||||
|
||||
func (mxc *MXClient) _onMessage(ctx context.Context, evt *event.Event) {
|
||||
out, err := json.Marshal(map[string]interface{}{"sender": evt.Sender.String(),
|
||||
"type": evt.Type.String(),
|
||||
"id": evt.ID.String(),
|
||||
"roomid": evt.RoomID.String(),
|
||||
"content": evt.Content.Raw})
|
||||
|
||||
if err != nil {
|
||||
log.Error().Err(err).
|
||||
Str("id", evt.ID.String()).
|
||||
Str("inviter", evt.Sender.String()).
|
||||
Msg("Marshalling error")
|
||||
return
|
||||
}
|
||||
mxc.OnMessage(string(out))
|
||||
|
||||
/*
|
||||
log.Info().
|
||||
Str("sender", evt.Sender.String()).
|
||||
Str("type", evt.Type.String()).
|
||||
Str("id", evt.ID.String()).
|
||||
Str("roomid", evt.RoomID.String()).
|
||||
Str("body", evt.Content.AsMessage().Body).
|
||||
Msg("Received message")
|
||||
fmt.Printf("Got message: %#v\n", evt)
|
||||
*/
|
||||
}
|
||||
|
||||
type sendmessage_data_content struct {
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
type sendmessage_data struct {
|
||||
RoomId id.RoomID `json:"roomid"`
|
||||
Type event.Type `json:"type"`
|
||||
Content sendmessage_data_content `json:"content"`
|
||||
}
|
||||
|
||||
func (mxc *MXClient) SendRoomMessage(ctx context.Context, data string) (*mautrix.RespSendEvent, error) {
|
||||
var smd sendmessage_data
|
||||
err := json.Unmarshal([]byte(data), &smd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := mxc.SendMessageEvent(ctx, smd.RoomId, event.EventMessage, smd.Content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
|
||||
}
|
||||
|
||||
// NewMXClient creates a new Matrix Client ready for syncing
|
||||
func NewMXClient(homeserverURL string, userID id.UserID, accessToken string) (*MXClient, error) {
|
||||
client, err := mautrix.NewClient(homeserverURL, userID, accessToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// keep this for the import
|
||||
client.Log = zerolog.Nop()
|
||||
// client.Log = zerolog.New(os.Stdout)
|
||||
// client.SyncTraceLog = true
|
||||
|
||||
resp, err := client.Whoami(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.DeviceID = resp.DeviceID
|
||||
|
||||
//fmt.Printf("Device ID: %s\n", client.DeviceID)
|
||||
|
||||
rawdb, err := dbutil.NewWithDialect("smalbot.db", "sqlite3")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//fmt.Println("db is offen.")
|
||||
|
||||
syncer, ok := client.Syncer.(*mautrix.DefaultSyncer)
|
||||
if !ok {
|
||||
return nil, errors.New("panic: syncer implementation error")
|
||||
}
|
||||
|
||||
//mxclient.StateStore = mautrix.NewMemoryStateStore()
|
||||
|
||||
stateStore := sqlstatestore.NewSQLStateStore(rawdb, dbutil.NoopLogger, false)
|
||||
err = stateStore.Upgrade(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to upgrade state db: %w", err)
|
||||
}
|
||||
client.StateStore = stateStore
|
||||
|
||||
pickleKey := []byte("pickle")
|
||||
|
||||
//cryptoStore := crypto.NewMemoryStore(nil)
|
||||
cryptoStore := crypto.NewSQLCryptoStore(rawdb, dbutil.ZeroLogger(log.With().Str("db_section", "crypto").Logger()), "", "", pickleKey)
|
||||
err = cryptoStore.DB.Upgrade(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to upgrade crypto db: %w", err)
|
||||
}
|
||||
|
||||
client.Crypto, err = cryptohelper.NewCryptoHelper(client, pickleKey, cryptoStore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = client.Crypto.Init(context.TODO())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client.Store = cryptoStore
|
||||
|
||||
mxclient := &MXClient{client, nil, nil}
|
||||
|
||||
syncer.ParseEventContent = true
|
||||
syncer.OnEvent(client.StateStoreSyncHandler)
|
||||
|
||||
syncer.OnEventType(event.EventMessage, mxclient._onMessage)
|
||||
syncer.OnEventType(event.StateMember, mxclient._onEvent)
|
||||
|
||||
return mxclient, nil
|
||||
}
|
||||
|
||||
func CreateClient(storage_path string, url string, userID string, accessToken string) (*MXClient, error) {
|
||||
return nil, fmt.Errorf("nope.")
|
||||
}
|
||||
|
||||
func CreateClientPass(mxpassfile_path string, storage_path string, url string, localpart string, domain string) (*MXClient, error) {
|
||||
pf, err := mxpassfile.ReadPassfile(mxpassfile_path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//fmt.Printf("mxpass pf: '%#v'\n", pf)
|
||||
//fmt.Printf("mxpass find: '%s' '%s' '%s'\n", url, localpart, domain)
|
||||
e := pf.FindPasswordFill(url, localpart, domain)
|
||||
if e != nil {
|
||||
//fmt.Printf("mxpass: %#v\n", e)
|
||||
return NewMXClient(e.Matrixhost, id.NewUserID(e.Localpart, e.Domain), e.Token)
|
||||
}
|
||||
return nil, fmt.Errorf("nope.")
|
||||
}
|
||||
|
|
@ -3,15 +3,88 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"mxclientlib/mxapi"
|
||||
"mxclientlib/mxclient"
|
||||
"mxclientlib/mxutils"
|
||||
"unsafe"
|
||||
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
typedef void (*on_event_handler_ptr) (char*);
|
||||
typedef void (*on_message_handler_ptr) (char*);
|
||||
|
||||
static inline void call_c_on_event_handler(on_event_handler_ptr ptr, char* jsonStr) {
|
||||
(ptr)(jsonStr);
|
||||
}
|
||||
|
||||
static inline void call_c_on_message_handler(on_message_handler_ptr ptr, char* jsonStr) {
|
||||
(ptr)(jsonStr);
|
||||
}
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
/*
|
||||
matrix client with c callback
|
||||
*/
|
||||
type CBClient struct {
|
||||
*mxclient.MXClient
|
||||
on_event_handler C.on_event_handler_ptr
|
||||
on_message_handler C.on_message_handler_ptr
|
||||
}
|
||||
|
||||
func (cli *CBClient) OnEvent(s string) {
|
||||
cStr := C.CString(s)
|
||||
defer C.free(unsafe.Pointer(cStr))
|
||||
C.call_c_on_event_handler(cli.on_event_handler, cStr)
|
||||
}
|
||||
|
||||
func (cli *CBClient) OnMessage(s string) {
|
||||
cStr := C.CString(s)
|
||||
defer C.free(unsafe.Pointer(cStr))
|
||||
C.call_c_on_message_handler(cli.on_message_handler, cStr)
|
||||
}
|
||||
|
||||
func (cli *CBClient) Set_on_event_handler(fn C.on_event_handler_ptr) {
|
||||
cli.on_event_handler = fn
|
||||
}
|
||||
|
||||
func (cli *CBClient) Set_on_message_handler(fn C.on_message_handler_ptr) {
|
||||
cli.on_message_handler = fn
|
||||
}
|
||||
|
||||
// NewCClient creates a new Matrix Client ready for syncing
|
||||
func NewCBClient(homeserverURL string, userID id.UserID, accessToken string) (*CBClient, error) {
|
||||
client, err := mxclient.NewMXClient(homeserverURL, userID, accessToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CBClient{client, nil, nil}, nil
|
||||
}
|
||||
|
||||
/*
|
||||
account/client management
|
||||
*/
|
||||
var cclients []*CBClient
|
||||
|
||||
func getClient(id int) (*CBClient, error) {
|
||||
if id < 0 || id >= len(cclients) {
|
||||
return nil, fmt.Errorf("index out of bounds: '%d'", id)
|
||||
}
|
||||
res := cclients[id]
|
||||
return res, nil
|
||||
}
|
||||
|
||||
/*
|
||||
helperse
|
||||
*/
|
||||
|
||||
//export FreeCString
|
||||
func FreeCString(s *C.char) {
|
||||
C.free(unsafe.Pointer(s))
|
||||
|
|
@ -20,6 +93,7 @@ func FreeCString(s *C.char) {
|
|||
/*
|
||||
cli tools
|
||||
*/
|
||||
|
||||
//export cli_discoverhs
|
||||
func cli_discoverhs(id *C.char) *C.char {
|
||||
mxid := C.GoString(id)
|
||||
|
|
@ -67,15 +141,149 @@ func cli_serverinfo(url *C.char) *C.char {
|
|||
}
|
||||
|
||||
/*
|
||||
high api
|
||||
high level api, supports multiple clients
|
||||
the same handler can be attached to multiple clients
|
||||
*/
|
||||
//export createclient
|
||||
func createclient(url *C.char, userID *C.char, accessToken *C.char) C.int {
|
||||
|
||||
//export apiv0_initialize
|
||||
func apiv0_initialize() C.int {
|
||||
return 0
|
||||
}
|
||||
|
||||
//export shutdown
|
||||
func shutdown() C.int {
|
||||
//export apiv0_deinitialize
|
||||
func apiv0_deinitialize() C.int {
|
||||
return 0
|
||||
}
|
||||
|
||||
//export apiv0_discover
|
||||
func apiv0_discover(mxid *C.char) *C.char {
|
||||
result, err := mxapi.Discover(C.GoString(mxid))
|
||||
if err != nil {
|
||||
return C.CString(fmt.Sprintf("ERR: %v", err))
|
||||
}
|
||||
return C.CString(result)
|
||||
}
|
||||
|
||||
//export apiv0_login
|
||||
func apiv0_login(data *C.char) *C.char {
|
||||
result, err := mxapi.Login(C.GoString(data))
|
||||
if err != nil {
|
||||
return C.CString(fmt.Sprintf("ERR: %v", err))
|
||||
}
|
||||
return C.CString(result)
|
||||
}
|
||||
|
||||
//export apiv0_createclient
|
||||
func apiv0_createclient(storage_path *C.char, url *C.char, userID *C.char, accessToken *C.char) *C.char {
|
||||
mxclient, err := mxclient.CreateClient(C.GoString(storage_path), C.GoString(url), C.GoString(userID), C.GoString(accessToken))
|
||||
if err != nil {
|
||||
return C.CString(fmt.Sprintf("ERR: %v", err))
|
||||
}
|
||||
client := &CBClient{mxclient, nil, nil}
|
||||
cclients = append(cclients, client)
|
||||
return C.CString(fmt.Sprintf("{ \"id:\"SUCESS. ID=%d\n", len(cclients)))
|
||||
}
|
||||
|
||||
//export apiv0_createclient_pass
|
||||
func apiv0_createclient_pass(mxpassfile_path *C.char, storage_path *C.char, url *C.char, localpart *C.char, domain *C.char) *C.char {
|
||||
mxclient, err := mxclient.CreateClientPass(C.GoString(mxpassfile_path), C.GoString(storage_path), C.GoString(url), C.GoString(localpart), C.GoString(domain))
|
||||
if err != nil {
|
||||
return C.CString(fmt.Sprintf("ERR: %v", err))
|
||||
}
|
||||
client := &CBClient{mxclient, nil, nil}
|
||||
mxclient.OnEvent = client.OnEvent
|
||||
mxclient.OnMessage = client.OnMessage
|
||||
cclients = append(cclients, client)
|
||||
out, err := json.Marshal(map[string]int{"id": len(cclients) - 1})
|
||||
if err != nil {
|
||||
return C.CString(fmt.Sprintf("ERR: %v", err))
|
||||
}
|
||||
s := string(out)
|
||||
return C.CString(s)
|
||||
}
|
||||
|
||||
//export apiv0_set_on_event_handler
|
||||
func apiv0_set_on_event_handler(cid C.int, fn C.on_event_handler_ptr) *C.char {
|
||||
cli, err := getClient(int(cid))
|
||||
if err != nil {
|
||||
return C.CString(fmt.Sprintf("ERR: %v", err))
|
||||
}
|
||||
|
||||
cli.Set_on_event_handler(fn)
|
||||
return C.CString("SUCCESS.")
|
||||
}
|
||||
|
||||
//export apiv0_set_on_message_handler
|
||||
func apiv0_set_on_message_handler(cid C.int, fn C.on_message_handler_ptr) *C.char {
|
||||
cli, err := getClient(int(cid))
|
||||
if err != nil {
|
||||
return C.CString(fmt.Sprintf("ERR: %v", err))
|
||||
}
|
||||
|
||||
cli.Set_on_message_handler(fn)
|
||||
return C.CString("SUCCESS.")
|
||||
}
|
||||
|
||||
//export apiv0_startclient
|
||||
func apiv0_startclient(cid C.int) *C.char {
|
||||
cli, err := getClient(int(cid))
|
||||
if err != nil {
|
||||
return C.CString(fmt.Sprintf("ERR: %v", err))
|
||||
}
|
||||
|
||||
err = cli.Sync()
|
||||
if err != nil {
|
||||
return C.CString(fmt.Sprintf("ERR: %v", err))
|
||||
}
|
||||
return C.CString("SUCCESS.")
|
||||
}
|
||||
|
||||
//export apiv0_stopclient
|
||||
func apiv0_stopclient(cid C.int) *C.char {
|
||||
cli, err := getClient(int(cid))
|
||||
if err != nil {
|
||||
return C.CString(fmt.Sprintf("ERR: %v", err))
|
||||
}
|
||||
cli.StopSync()
|
||||
// TODO kill current sync request (client.client.http.cancel() or so)
|
||||
return C.CString("SUCCESS.")
|
||||
}
|
||||
|
||||
//export apiv0_sendmessage
|
||||
func apiv0_sendmessage(cid C.int, data *C.char) *C.char {
|
||||
cli, err := getClient(int(cid))
|
||||
if err != nil {
|
||||
return C.CString(fmt.Sprintf("ERR: %v", err))
|
||||
}
|
||||
result, err := cli.SendRoomMessage(context.Background(), C.GoString(data))
|
||||
if err != nil {
|
||||
return C.CString(fmt.Sprintf("ERR: %v", err))
|
||||
}
|
||||
out, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return C.CString(fmt.Sprintf("ERR: %v", err))
|
||||
}
|
||||
s := string(out)
|
||||
return C.CString(s)
|
||||
}
|
||||
|
||||
//export apiv0_removeclient
|
||||
func apiv0_removeclient(cid C.int) C.int {
|
||||
return 0
|
||||
}
|
||||
|
||||
//export apiv0_listclients
|
||||
func apiv0_listclients() *C.char {
|
||||
return C.CString("{}")
|
||||
}
|
||||
|
||||
//export apiv0_getoptions
|
||||
func apiv0_getoptions(cid C.int) *C.char {
|
||||
return C.CString("{}")
|
||||
}
|
||||
|
||||
//export apiv0_setoptions
|
||||
func apiv0_setoptions(cid C.int, opts *C.char) C.int {
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue