2026-01-13 13:11:02 +01:00
|
|
|
// Copyright (C) 2026 saces@c-base.org
|
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
2026-01-09 18:52:31 +01:00
|
|
|
package main
|
|
|
|
|
|
2026-01-12 17:39:34 +01:00
|
|
|
import (
|
2026-01-31 08:13:53 +01:00
|
|
|
"context"
|
|
|
|
|
"encoding/json"
|
2026-02-06 16:10:58 +01:00
|
|
|
"errors"
|
2026-01-31 08:13:53 +01:00
|
|
|
"fmt"
|
|
|
|
|
"mxclientlib/mxapi"
|
|
|
|
|
"mxclientlib/mxclient"
|
2026-01-20 06:48:27 +01:00
|
|
|
"mxclientlib/mxutils"
|
2026-01-18 23:35:07 +01:00
|
|
|
"unsafe"
|
2026-01-31 08:13:53 +01:00
|
|
|
|
|
|
|
|
"maunium.net/go/mautrix/id"
|
2026-01-12 17:39:34 +01:00
|
|
|
)
|
|
|
|
|
|
2026-01-18 23:35:07 +01:00
|
|
|
/*
|
|
|
|
|
#include <stdlib.h>
|
2026-02-03 21:20:29 +01:00
|
|
|
typedef void (*on_event_handler_ptr) (char*, void*);
|
|
|
|
|
typedef void (*on_message_handler_ptr) (char*, void*);
|
2026-01-31 08:13:53 +01:00
|
|
|
|
2026-02-03 21:20:29 +01:00
|
|
|
static inline void call_c_on_event_handler(on_event_handler_ptr ptr, char* jsonStr, void* pobj) {
|
|
|
|
|
(ptr)(jsonStr, pobj);
|
2026-01-31 08:13:53 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-03 21:20:29 +01:00
|
|
|
static inline void call_c_on_message_handler(on_message_handler_ptr ptr, char* jsonStr, void* pobj) {
|
|
|
|
|
(ptr)(jsonStr, pobj);
|
2026-01-31 08:13:53 +01:00
|
|
|
}
|
|
|
|
|
|
2026-01-18 23:35:07 +01:00
|
|
|
*/
|
2026-01-09 18:52:31 +01:00
|
|
|
import "C"
|
|
|
|
|
|
2026-02-06 16:10:58 +01:00
|
|
|
var apiCanceled = errors.New("canceled by api call")
|
|
|
|
|
|
2026-01-31 08:13:53 +01:00
|
|
|
/*
|
|
|
|
|
matrix client with c callback
|
|
|
|
|
*/
|
|
|
|
|
type CBClient struct {
|
|
|
|
|
*mxclient.MXClient
|
2026-02-03 21:20:29 +01:00
|
|
|
on_event_handler C.on_event_handler_ptr
|
|
|
|
|
on_event_handler_pobj unsafe.Pointer
|
|
|
|
|
on_message_handler C.on_message_handler_ptr
|
|
|
|
|
on_message_handler_pobj unsafe.Pointer
|
2026-02-06 16:10:58 +01:00
|
|
|
syncCancelFunc context.CancelCauseFunc
|
2026-01-31 08:13:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (cli *CBClient) OnEvent(s string) {
|
|
|
|
|
cStr := C.CString(s)
|
|
|
|
|
defer C.free(unsafe.Pointer(cStr))
|
2026-02-03 21:20:29 +01:00
|
|
|
C.call_c_on_event_handler(cli.on_event_handler, cStr, cli.on_event_handler_pobj)
|
2026-01-31 08:13:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (cli *CBClient) OnMessage(s string) {
|
|
|
|
|
cStr := C.CString(s)
|
|
|
|
|
defer C.free(unsafe.Pointer(cStr))
|
2026-02-03 21:20:29 +01:00
|
|
|
C.call_c_on_message_handler(cli.on_message_handler, cStr, cli.on_message_handler_pobj)
|
2026-01-31 08:13:53 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-03 21:20:29 +01:00
|
|
|
func (cli *CBClient) Set_on_event_handler(fn C.on_event_handler_ptr, pobj unsafe.Pointer) {
|
2026-01-31 08:13:53 +01:00
|
|
|
cli.on_event_handler = fn
|
2026-02-03 21:20:29 +01:00
|
|
|
cli.on_event_handler_pobj = pobj
|
2026-01-31 08:13:53 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-03 21:20:29 +01:00
|
|
|
func (cli *CBClient) Set_on_message_handler(fn C.on_message_handler_ptr, pobj unsafe.Pointer) {
|
2026-01-31 08:13:53 +01:00
|
|
|
cli.on_message_handler = fn
|
2026-02-03 21:20:29 +01:00
|
|
|
cli.on_message_handler_pobj = pobj
|
2026-01-31 08:13:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
}
|
2026-02-06 05:41:47 +01:00
|
|
|
return &CBClient{client, nil, nil, nil, nil, nil}, nil
|
2026-01-31 08:13:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
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
|
|
|
|
|
*/
|
|
|
|
|
|
2026-01-18 23:35:07 +01:00
|
|
|
//export FreeCString
|
|
|
|
|
func FreeCString(s *C.char) {
|
|
|
|
|
C.free(unsafe.Pointer(s))
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-13 13:11:02 +01:00
|
|
|
/*
|
|
|
|
|
cli tools
|
|
|
|
|
*/
|
2026-01-31 08:13:53 +01:00
|
|
|
|
2026-01-13 13:11:02 +01:00
|
|
|
//export cli_discoverhs
|
|
|
|
|
func cli_discoverhs(id *C.char) *C.char {
|
2026-01-12 17:39:34 +01:00
|
|
|
mxid := C.GoString(id)
|
2026-01-20 06:48:27 +01:00
|
|
|
result := mxutils.DiscoverHS(mxid)
|
2026-01-12 17:39:34 +01:00
|
|
|
return C.CString(result)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-13 13:11:02 +01:00
|
|
|
//export cli_mkmxtoken
|
|
|
|
|
func cli_mkmxtoken(id *C.char, pw *C.char) *C.char {
|
|
|
|
|
mxid := C.GoString(id)
|
|
|
|
|
mxpw := C.GoString(pw)
|
2026-01-20 06:48:27 +01:00
|
|
|
result := mxutils.MkToken(mxid, mxpw)
|
2026-01-09 18:52:31 +01:00
|
|
|
return C.CString(result)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-13 13:11:02 +01:00
|
|
|
//export cli_whoami
|
|
|
|
|
func cli_whoami(hs *C.char, tk *C.char) *C.char {
|
|
|
|
|
_hs := C.GoString(hs)
|
|
|
|
|
_tk := C.GoString(tk)
|
2026-01-20 06:48:27 +01:00
|
|
|
result := mxutils.Whoami(_hs, _tk)
|
2026-01-13 13:11:02 +01:00
|
|
|
return C.CString(result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//export cli_accountinfo
|
|
|
|
|
func cli_accountinfo(hs *C.char, tk *C.char) *C.char {
|
|
|
|
|
_hs := C.GoString(hs)
|
|
|
|
|
_tk := C.GoString(tk)
|
2026-01-20 06:48:27 +01:00
|
|
|
result := mxutils.AccountInfo(_hs, _tk)
|
2026-01-13 13:11:02 +01:00
|
|
|
return C.CString(result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//export cli_clearaccount
|
|
|
|
|
func cli_clearaccount(hs *C.char, tk *C.char) *C.char {
|
|
|
|
|
_hs := C.GoString(hs)
|
|
|
|
|
_tk := C.GoString(tk)
|
2026-01-20 06:48:27 +01:00
|
|
|
result := mxutils.ClearAccount(_hs, _tk)
|
2026-01-13 13:11:02 +01:00
|
|
|
return C.CString(result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//export cli_serverinfo
|
|
|
|
|
func cli_serverinfo(url *C.char) *C.char {
|
|
|
|
|
_url := C.GoString(url)
|
2026-01-20 06:48:27 +01:00
|
|
|
result := mxutils.ServerInfo(_url)
|
2026-01-13 13:11:02 +01:00
|
|
|
return C.CString(result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2026-01-31 08:13:53 +01:00
|
|
|
high level api, supports multiple clients
|
|
|
|
|
the same handler can be attached to multiple clients
|
2026-01-13 13:11:02 +01:00
|
|
|
*/
|
2026-01-31 08:13:53 +01:00
|
|
|
|
|
|
|
|
//export apiv0_initialize
|
|
|
|
|
func apiv0_initialize() C.int {
|
2026-01-13 13:11:02 +01:00
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-31 08:13:53 +01:00
|
|
|
//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))
|
|
|
|
|
}
|
2026-02-06 05:41:47 +01:00
|
|
|
client := &CBClient{mxclient, nil, nil, nil, nil, nil}
|
2026-01-31 08:13:53 +01:00
|
|
|
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))
|
|
|
|
|
}
|
2026-02-06 05:41:47 +01:00
|
|
|
client := &CBClient{mxclient, nil, nil, nil, nil, nil}
|
2026-01-31 08:13:53 +01:00
|
|
|
mxclient.OnEvent = client.OnEvent
|
|
|
|
|
mxclient.OnMessage = client.OnMessage
|
|
|
|
|
cclients = append(cclients, client)
|
2026-02-11 11:00:21 +01:00
|
|
|
out, err := json.Marshal(map[string]any{"id": len(cclients) - 1, "userid": client.UserID.String(), "deviceid": client.DeviceID.String()})
|
2026-01-31 08:13:53 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return C.CString(fmt.Sprintf("ERR: %v", err))
|
|
|
|
|
}
|
|
|
|
|
s := string(out)
|
|
|
|
|
return C.CString(s)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//export apiv0_set_on_event_handler
|
2026-02-03 21:20:29 +01:00
|
|
|
func apiv0_set_on_event_handler(cid C.int, fn C.on_event_handler_ptr, pobj unsafe.Pointer) *C.char {
|
2026-01-31 08:13:53 +01:00
|
|
|
cli, err := getClient(int(cid))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return C.CString(fmt.Sprintf("ERR: %v", err))
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-03 21:20:29 +01:00
|
|
|
cli.Set_on_event_handler(fn, pobj)
|
2026-01-31 08:13:53 +01:00
|
|
|
return C.CString("SUCCESS.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//export apiv0_set_on_message_handler
|
2026-02-03 21:20:29 +01:00
|
|
|
func apiv0_set_on_message_handler(cid C.int, fn C.on_message_handler_ptr, pobj unsafe.Pointer) *C.char {
|
2026-01-31 08:13:53 +01:00
|
|
|
cli, err := getClient(int(cid))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return C.CString(fmt.Sprintf("ERR: %v", err))
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-03 21:20:29 +01:00
|
|
|
cli.Set_on_message_handler(fn, pobj)
|
2026-01-31 08:13:53 +01:00
|
|
|
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))
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-06 16:10:58 +01:00
|
|
|
ctx, cancel := context.WithCancelCause(context.Background())
|
2026-02-06 05:41:47 +01:00
|
|
|
cli.syncCancelFunc = cancel
|
|
|
|
|
|
|
|
|
|
err = cli.SyncWithContext(ctx)
|
2026-01-31 08:13:53 +01:00
|
|
|
if err != nil {
|
2026-02-06 16:10:58 +01:00
|
|
|
if errors.Is(err, context.Canceled) {
|
|
|
|
|
cause := context.Cause(ctx)
|
|
|
|
|
if errors.Is(cause, apiCanceled) {
|
|
|
|
|
return C.CString("SUCCESS.")
|
|
|
|
|
} else {
|
|
|
|
|
return C.CString(fmt.Sprintf("ERR: %v", cause))
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-31 08:13:53 +01:00
|
|
|
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()
|
2026-02-06 16:10:58 +01:00
|
|
|
cli.syncCancelFunc(apiCanceled)
|
2026-02-06 05:41:47 +01:00
|
|
|
|
2026-01-31 08:13:53 +01:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-07 10:43:11 +01:00
|
|
|
//export apiv0_leaveroom
|
|
|
|
|
func apiv0_leaveroom(cid C.int, roomid *C.char) *C.char {
|
|
|
|
|
cli, err := getClient(int(cid))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return C.CString(fmt.Sprintf("ERR: %v", err))
|
|
|
|
|
}
|
2026-02-07 17:46:17 +01:00
|
|
|
err = cli.LeaveRoomAndForget(context.Background(), id.RoomID(C.GoString(roomid)))
|
2026-02-07 10:43:11 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return C.CString(fmt.Sprintf("ERR: %v", err))
|
|
|
|
|
}
|
|
|
|
|
return C.CString("SUCCESS.")
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-31 08:13:53 +01:00
|
|
|
//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 {
|
2026-01-13 13:11:02 +01:00
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-09 18:52:31 +01:00
|
|
|
func main() {}
|