From 5c292421090b1093f4aff44bdf7a63f6755b1491 Mon Sep 17 00:00:00 2001 From: saces Date: Wed, 29 Apr 2026 00:35:12 +0200 Subject: [PATCH] api: rework discover & login --- libmxclient/mxapi/discover.go | 30 ++++++----- libmxclient/mxapi/login.go | 69 ++++++++---------------- libmxclient/mxclientlib.go | 30 ++++++++--- mxsmal/src/mxsmal/smalsetup/smalsetup.py | 26 ++++----- pygomx/build_ffi.py | 4 +- pygomx/src/pygomx/apiv0.py | 15 ++---- 6 files changed, 82 insertions(+), 92 deletions(-) diff --git a/libmxclient/mxapi/discover.go b/libmxclient/mxapi/discover.go index d5096d2..2952415 100644 --- a/libmxclient/mxapi/discover.go +++ b/libmxclient/mxapi/discover.go @@ -4,31 +4,33 @@ package mxapi import ( "context" - "encoding/json" "maunium.net/go/mautrix" "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 { - return "", err + return } - wk, err := mautrix.DiscoverClientAPI(context.Background(), hs) + wk, err := mautrix.DiscoverClientAPI(context.Background(), tmp_di.Homeserver) if err != nil { - return "", err + return } if wk != nil { - hs = wk.Homeserver.BaseURL + tmp_di.Homeserver = 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 + tmp_di.UserID = userID + di = &tmp_di + return } diff --git a/libmxclient/mxapi/login.go b/libmxclient/mxapi/login.go index 5c1dda9..0345e59 100644 --- a/libmxclient/mxapi/login.go +++ b/libmxclient/mxapi/login.go @@ -4,8 +4,6 @@ package mxapi import ( "context" - "crypto/rand" - "encoding/json" "errors" "fmt" "os" @@ -15,56 +13,47 @@ import ( "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"` - DeviceID string `json:"deviceid"` - DeviceName string `json:"devicename"` - MXPassFile string `json:"mxpassfile"` - MakeMasterKey bool `json:"make_master_key"` - MakeRecoveryKey bool `json:"make_recovery_key"` +type LoginInfo struct { + DiscoverInfo DiscoverInfo `json:"discover_info"` + Password string `json:"password"` + DeviceID id.DeviceID `json:"deviceid"` + DeviceName string `json:"devicename"` + MXPassFile string `json:"mxpassfile"` } -func Login(data string) (string, error) { - var ld login_data - err := json.Unmarshal([]byte(data), &ld) - if err != nil { - return "", err - } +func Login(li *LoginInfo) (string, error) { - if ld.MXPassFile != "" { - if _, err := os.Stat(ld.MXPassFile); err == nil { - return "", fmt.Errorf("mxpassfile '%s' already exists", ld.MXPassFile) + if li.MXPassFile != "" { + if _, err := os.Stat(li.MXPassFile); err == nil { + return "", fmt.Errorf("mxpassfile '%s' already exists", li.MXPassFile) } else if !errors.Is(err, os.ErrNotExist) { 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 { return "", err } now := time.Now() - if ld.DeviceID == "" { - ld.DeviceID = fmt.Sprintf("libmxclient-%d", now.Unix()) + if li.DeviceID == "" { + li.DeviceID = id.DeviceID(fmt.Sprintf("libmxclient-%d", now.Unix())) } - if ld.DeviceName == "" { - ld.DeviceName = fmt.Sprintf("libmxclient-%s", now.Format(time.RFC3339)) + if li.DeviceName == "" { + li.DeviceName = fmt.Sprintf("libmxclient-%s", now.Format(time.RFC3339)) } resp, err := mauclient.Login(context.Background(), &mautrix.ReqLogin{ Type: "m.login.password", Identifier: mautrix.UserIdentifier{ Type: "m.id.user", - User: ld.Loginname, + User: li.DiscoverInfo.LoginName, }, - Password: ld.Password, - DeviceID: id.DeviceID(ld.DeviceID), - InitialDeviceDisplayName: ld.DeviceName, + Password: li.Password, + DeviceID: li.DeviceID, + InitialDeviceDisplayName: li.DeviceName, StoreCredentials: false, StoreHomeserverURL: false, RefreshToken: false, @@ -73,26 +62,14 @@ func Login(data string) (string, error) { 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 { - masterkey := make([]byte, 32) - 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 li.MXPassFile != "" { + err := os.WriteFile(li.MXPassFile, []byte(res), 0600) if err != nil { return "", fmt.Errorf("unable to write file: %w", err) } - return "SUCCESS.", nil + return "", nil } return res, nil diff --git a/libmxclient/mxclientlib.go b/libmxclient/mxclientlib.go index aa98c7d..ad74efd 100644 --- a/libmxclient/mxclientlib.go +++ b/libmxclient/mxclientlib.go @@ -48,6 +48,12 @@ var apiCanceled = errors.New("canceled by api call") 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) { rid := C.GoString(roomid) if rid == "" || rid[0] != '!' { @@ -283,19 +289,31 @@ func apiv0_deinitialize() C.int { } //export apiv0_discover -func apiv0_discover(mxid *C.char) *C.char { - result, err := mxapi.Discover(C.GoString(mxid)) +func apiv0_discover(userid *C.char) *C.char { + userID, err := c2UserID(userid) + if err != nil { + returnErr(err) + } + result, err := mxapi.Discover(userID) if err != nil { return C.CString(fmt.Sprintf("ERR: %v", err)) } - return C.CString(result) + return returnJSON(result, err) } //export apiv0_login -func apiv0_login(data *C.char) *C.char { - result, err := mxapi.Login(C.GoString(data)) +func apiv0_login(login_info *C.char) *C.char { + var loginInfo mxapi.LoginInfo + err := json.Unmarshal([]byte(C.GoString(login_info)), &loginInfo) 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) } diff --git a/mxsmal/src/mxsmal/smalsetup/smalsetup.py b/mxsmal/src/mxsmal/smalsetup/smalsetup.py index 8f93e0c..6899973 100644 --- a/mxsmal/src/mxsmal/smalsetup/smalsetup.py +++ b/mxsmal/src/mxsmal/smalsetup/smalsetup.py @@ -1,6 +1,6 @@ # Copyright (C) 2026 saces@c-base.org # SPDX-License-Identifier: AGPL-3.0-only -import datetime +from datetime import datetime import getpass import os import time @@ -39,22 +39,22 @@ def catch_exception(func=None, *, handle): def smalsetup(mxid, mxpassfile): """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): raise click.ClickException(f"file {mxpassfile} exists.") - result_dict = ApiV0.Discover(mxid) + discover_info = ApiV0.Discover(mxid) - result_dict["password"] = getpass.getpass(prompt="Password: ") - result_dict["make_master_key"] = True - result_dict["make_recovery_key"] = True + login_info = { + "discover_info": discover_info, + "deviceid": f"smalbot-{now}", + "devicename": f"smalbot-{datetime.fromtimestamp(now)}", + "mxpassfile": mxpassfile, + "password": getpass.getpass(prompt="Password: "), + } - now = int(time.time()) - result_dict["deviceid"] = f"smalbot-{now}" - result_dict["devicename"] = f"smalbot-{datetime.datetime.fromtimestamp(now)}" + ApiV0.Login(login_info) - ApiV0.Login(result_dict, ".mxpass") - - click.echo("login created. start your bot now.") + click.echo("login created. start your bot now or run e2eesetup.") diff --git a/pygomx/build_ffi.py b/pygomx/build_ffi.py index 4e5a6d2..13476e6 100644 --- a/pygomx/build_ffi.py +++ b/pygomx/build_ffi.py @@ -50,8 +50,8 @@ ffibuilder.cdef( extern char* cliv0_generic_request(char* hs, char* access_token, char* method, char* path, char* data); extern int apiv0_initialize(); extern int apiv0_deinitialize(); - extern char* apiv0_discover(char* mxid); - extern char* apiv0_login(char* data); + extern char* apiv0_discover(char* user_id); + extern char* apiv0_login(char* login_info); 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_set_on_event_handler(int cid, on_event_handler_ptr ptr, void* pobj); diff --git a/pygomx/src/pygomx/apiv0.py b/pygomx/src/pygomx/apiv0.py index de2707b..edf0114 100644 --- a/pygomx/src/pygomx/apiv0.py +++ b/pygomx/src/pygomx/apiv0.py @@ -155,14 +155,7 @@ class ApiV0: return CheckApiResult(res) @staticmethod - def Login(data: dict, mxpassfile: str): - withpass = False - if mxpassfile is not None and len(mxpassfile.strip()) > 0: - data["mxpassfile"] = mxpassfile - withpass = True - res = ApiV0Api.login(data) - if withpass: - CheckApiError(res) - else: - CheckApiErrorOnly(res) - return res + def Login(login_info: dict): + res = ApiV0Api.login(login_info) + CheckApiErrorOnly(res) + return res