api: rework discover & login
This commit is contained in:
parent
5d02e28a51
commit
5c29242109
6 changed files with 82 additions and 92 deletions
|
|
@ -4,31 +4,33 @@ package mxapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"maunium.net/go/mautrix"
|
"maunium.net/go/mautrix"
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Discover(mxid string) (string, error) {
|
type DiscoverInfo struct {
|
||||||
|
Homeserver string `json:"homeserver"`
|
||||||
|
UserID id.UserID `json:"user_id"`
|
||||||
|
LoginName string `json:"login_name"`
|
||||||
|
}
|
||||||
|
|
||||||
localpart, hs, err := id.UserID(mxid).ParseAndValidateRelaxed()
|
// Discover tries to guess the homeserver from the given matrix id
|
||||||
|
func Discover(userID id.UserID) (di *DiscoverInfo, err error) {
|
||||||
|
var tmp_di DiscoverInfo
|
||||||
|
tmp_di.LoginName, tmp_di.Homeserver, err = userID.ParseAndValidateRelaxed()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
wk, err := mautrix.DiscoverClientAPI(context.Background(), hs)
|
wk, err := mautrix.DiscoverClientAPI(context.Background(), tmp_di.Homeserver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return
|
||||||
}
|
}
|
||||||
if wk != nil {
|
if wk != nil {
|
||||||
hs = wk.Homeserver.BaseURL
|
tmp_di.Homeserver = wk.Homeserver.BaseURL
|
||||||
}
|
}
|
||||||
|
tmp_di.UserID = userID
|
||||||
out, err := json.Marshal(map[string]string{"mxid": mxid, "homeserver": hs, "loginname": localpart})
|
di = &tmp_di
|
||||||
if err != nil {
|
return
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(out), nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@ package mxapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -15,56 +13,47 @@ import (
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
type login_data struct {
|
type LoginInfo struct {
|
||||||
Homeserver string `json:"homeserver"`
|
DiscoverInfo DiscoverInfo `json:"discover_info"`
|
||||||
Mxid string `json:"mxid"`
|
Password string `json:"password"`
|
||||||
Loginname string `json:"loginname"`
|
DeviceID id.DeviceID `json:"deviceid"`
|
||||||
Password string `json:"password"`
|
DeviceName string `json:"devicename"`
|
||||||
DeviceID string `json:"deviceid"`
|
MXPassFile string `json:"mxpassfile"`
|
||||||
DeviceName string `json:"devicename"`
|
|
||||||
MXPassFile string `json:"mxpassfile"`
|
|
||||||
MakeMasterKey bool `json:"make_master_key"`
|
|
||||||
MakeRecoveryKey bool `json:"make_recovery_key"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Login(data string) (string, error) {
|
func Login(li *LoginInfo) (string, error) {
|
||||||
var ld login_data
|
|
||||||
err := json.Unmarshal([]byte(data), &ld)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ld.MXPassFile != "" {
|
if li.MXPassFile != "" {
|
||||||
if _, err := os.Stat(ld.MXPassFile); err == nil {
|
if _, err := os.Stat(li.MXPassFile); err == nil {
|
||||||
return "", fmt.Errorf("mxpassfile '%s' already exists", ld.MXPassFile)
|
return "", fmt.Errorf("mxpassfile '%s' already exists", li.MXPassFile)
|
||||||
} else if !errors.Is(err, os.ErrNotExist) {
|
} else if !errors.Is(err, os.ErrNotExist) {
|
||||||
return "", fmt.Errorf("error while checking mxpassfile: %v", err)
|
return "", fmt.Errorf("error while checking mxpassfile: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mauclient, err := mautrix.NewClient(ld.Homeserver, id.UserID(ld.Mxid), "")
|
mauclient, err := mautrix.NewClient(li.DiscoverInfo.Homeserver, li.DiscoverInfo.UserID, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
if ld.DeviceID == "" {
|
if li.DeviceID == "" {
|
||||||
ld.DeviceID = fmt.Sprintf("libmxclient-%d", now.Unix())
|
li.DeviceID = id.DeviceID(fmt.Sprintf("libmxclient-%d", now.Unix()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if ld.DeviceName == "" {
|
if li.DeviceName == "" {
|
||||||
ld.DeviceName = fmt.Sprintf("libmxclient-%s", now.Format(time.RFC3339))
|
li.DeviceName = fmt.Sprintf("libmxclient-%s", now.Format(time.RFC3339))
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := mauclient.Login(context.Background(), &mautrix.ReqLogin{
|
resp, err := mauclient.Login(context.Background(), &mautrix.ReqLogin{
|
||||||
Type: "m.login.password",
|
Type: "m.login.password",
|
||||||
Identifier: mautrix.UserIdentifier{
|
Identifier: mautrix.UserIdentifier{
|
||||||
Type: "m.id.user",
|
Type: "m.id.user",
|
||||||
User: ld.Loginname,
|
User: li.DiscoverInfo.LoginName,
|
||||||
},
|
},
|
||||||
Password: ld.Password,
|
Password: li.Password,
|
||||||
DeviceID: id.DeviceID(ld.DeviceID),
|
DeviceID: li.DeviceID,
|
||||||
InitialDeviceDisplayName: ld.DeviceName,
|
InitialDeviceDisplayName: li.DeviceName,
|
||||||
StoreCredentials: false,
|
StoreCredentials: false,
|
||||||
StoreHomeserverURL: false,
|
StoreHomeserverURL: false,
|
||||||
RefreshToken: false,
|
RefreshToken: false,
|
||||||
|
|
@ -73,26 +62,14 @@ func Login(data string) (string, error) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
res := fmt.Sprintf("%s | %s | %s | %s\n", ld.Homeserver, ld.Loginname, id.UserID(ld.Mxid).Homeserver(), resp.AccessToken)
|
res := fmt.Sprintf("%s | %s | %s | %s\n", li.DiscoverInfo.Homeserver, li.DiscoverInfo.LoginName, li.DiscoverInfo.UserID.Homeserver(), resp.AccessToken)
|
||||||
|
|
||||||
if ld.MakeMasterKey {
|
if li.MXPassFile != "" {
|
||||||
masterkey := make([]byte, 32)
|
err := os.WriteFile(li.MXPassFile, []byte(res), 0600)
|
||||||
rand.Read(masterkey)
|
|
||||||
res = fmt.Sprintf("%smaster | | | %x\n", res, masterkey)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ld.MakeRecoveryKey {
|
|
||||||
recoverykey := make([]byte, 32)
|
|
||||||
rand.Read(recoverykey)
|
|
||||||
res = fmt.Sprintf("%srecovery | | | %x\n", res, recoverykey)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ld.MXPassFile != "" {
|
|
||||||
err := os.WriteFile(ld.MXPassFile, []byte(res), 0600)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unable to write file: %w", err)
|
return "", fmt.Errorf("unable to write file: %w", err)
|
||||||
}
|
}
|
||||||
return "SUCCESS.", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,12 @@ var apiCanceled = errors.New("canceled by api call")
|
||||||
type conversion helpers with some basic validation
|
type conversion helpers with some basic validation
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
func c2UserID(userid *C.char) (id.UserID, error) {
|
||||||
|
userID := id.UserID(C.GoString(userid))
|
||||||
|
_, _, err := userID.ParseAndValidateRelaxed()
|
||||||
|
return userID, err
|
||||||
|
}
|
||||||
|
|
||||||
func c2RoomID(roomid *C.char) (id.RoomID, error) {
|
func c2RoomID(roomid *C.char) (id.RoomID, error) {
|
||||||
rid := C.GoString(roomid)
|
rid := C.GoString(roomid)
|
||||||
if rid == "" || rid[0] != '!' {
|
if rid == "" || rid[0] != '!' {
|
||||||
|
|
@ -283,19 +289,31 @@ func apiv0_deinitialize() C.int {
|
||||||
}
|
}
|
||||||
|
|
||||||
//export apiv0_discover
|
//export apiv0_discover
|
||||||
func apiv0_discover(mxid *C.char) *C.char {
|
func apiv0_discover(userid *C.char) *C.char {
|
||||||
result, err := mxapi.Discover(C.GoString(mxid))
|
userID, err := c2UserID(userid)
|
||||||
|
if err != nil {
|
||||||
|
returnErr(err)
|
||||||
|
}
|
||||||
|
result, err := mxapi.Discover(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return C.CString(fmt.Sprintf("ERR: %v", err))
|
return C.CString(fmt.Sprintf("ERR: %v", err))
|
||||||
}
|
}
|
||||||
return C.CString(result)
|
return returnJSON(result, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export apiv0_login
|
//export apiv0_login
|
||||||
func apiv0_login(data *C.char) *C.char {
|
func apiv0_login(login_info *C.char) *C.char {
|
||||||
result, err := mxapi.Login(C.GoString(data))
|
var loginInfo mxapi.LoginInfo
|
||||||
|
err := json.Unmarshal([]byte(C.GoString(login_info)), &loginInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return C.CString(fmt.Sprintf("ERR: %v", err))
|
return returnErr(err)
|
||||||
|
}
|
||||||
|
result, err := mxapi.Login(&loginInfo)
|
||||||
|
if err != nil {
|
||||||
|
return returnErr(err)
|
||||||
|
}
|
||||||
|
if result == "" {
|
||||||
|
returnErr(nil)
|
||||||
}
|
}
|
||||||
return C.CString(result)
|
return C.CString(result)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Copyright (C) 2026 saces@c-base.org
|
# Copyright (C) 2026 saces@c-base.org
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import datetime
|
from datetime import datetime
|
||||||
import getpass
|
import getpass
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
|
@ -39,22 +39,22 @@ def catch_exception(func=None, *, handle):
|
||||||
def smalsetup(mxid, mxpassfile):
|
def smalsetup(mxid, mxpassfile):
|
||||||
"""Utility for creating smalbot mxpass files"""
|
"""Utility for creating smalbot mxpass files"""
|
||||||
|
|
||||||
create_mxpass = len(mxpassfile.strip()) > 0
|
now = int(time.time())
|
||||||
|
|
||||||
if create_mxpass:
|
if len(mxpassfile.strip()) > 0:
|
||||||
if os.path.exists(mxpassfile):
|
if os.path.exists(mxpassfile):
|
||||||
raise click.ClickException(f"file {mxpassfile} exists.")
|
raise click.ClickException(f"file {mxpassfile} exists.")
|
||||||
|
|
||||||
result_dict = ApiV0.Discover(mxid)
|
discover_info = ApiV0.Discover(mxid)
|
||||||
|
|
||||||
result_dict["password"] = getpass.getpass(prompt="Password: ")
|
login_info = {
|
||||||
result_dict["make_master_key"] = True
|
"discover_info": discover_info,
|
||||||
result_dict["make_recovery_key"] = True
|
"deviceid": f"smalbot-{now}",
|
||||||
|
"devicename": f"smalbot-{datetime.fromtimestamp(now)}",
|
||||||
|
"mxpassfile": mxpassfile,
|
||||||
|
"password": getpass.getpass(prompt="Password: "),
|
||||||
|
}
|
||||||
|
|
||||||
now = int(time.time())
|
ApiV0.Login(login_info)
|
||||||
result_dict["deviceid"] = f"smalbot-{now}"
|
|
||||||
result_dict["devicename"] = f"smalbot-{datetime.datetime.fromtimestamp(now)}"
|
|
||||||
|
|
||||||
ApiV0.Login(result_dict, ".mxpass")
|
click.echo("login created. start your bot now or run e2eesetup.")
|
||||||
|
|
||||||
click.echo("login created. start your bot now.")
|
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,8 @@ ffibuilder.cdef(
|
||||||
extern char* cliv0_generic_request(char* hs, char* access_token, char* method, char* path, char* data);
|
extern char* cliv0_generic_request(char* hs, char* access_token, char* method, char* path, char* data);
|
||||||
extern int apiv0_initialize();
|
extern int apiv0_initialize();
|
||||||
extern int apiv0_deinitialize();
|
extern int apiv0_deinitialize();
|
||||||
extern char* apiv0_discover(char* mxid);
|
extern char* apiv0_discover(char* user_id);
|
||||||
extern char* apiv0_login(char* data);
|
extern char* apiv0_login(char* login_info);
|
||||||
extern char* apiv0_createclient(char* storage_path, char* hs, char* mxid, char* accessToken);
|
extern char* apiv0_createclient(char* storage_path, char* hs, char* mxid, char* accessToken);
|
||||||
extern char* apiv0_createclient_pass(char* mxpassfile, char* storage_path, char* hs, char* localpart, char* domain);
|
extern char* apiv0_createclient_pass(char* mxpassfile, char* storage_path, char* hs, char* localpart, char* domain);
|
||||||
extern char* apiv0_set_on_event_handler(int cid, on_event_handler_ptr ptr, void* pobj);
|
extern char* apiv0_set_on_event_handler(int cid, on_event_handler_ptr ptr, void* pobj);
|
||||||
|
|
|
||||||
|
|
@ -155,14 +155,7 @@ class ApiV0:
|
||||||
return CheckApiResult(res)
|
return CheckApiResult(res)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def Login(data: dict, mxpassfile: str):
|
def Login(login_info: dict):
|
||||||
withpass = False
|
res = ApiV0Api.login(login_info)
|
||||||
if mxpassfile is not None and len(mxpassfile.strip()) > 0:
|
CheckApiErrorOnly(res)
|
||||||
data["mxpassfile"] = mxpassfile
|
return res
|
||||||
withpass = True
|
|
||||||
res = ApiV0Api.login(data)
|
|
||||||
if withpass:
|
|
||||||
CheckApiError(res)
|
|
||||||
else:
|
|
||||||
CheckApiErrorOnly(res)
|
|
||||||
return res
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue