From 8df08611a9bee8b1ee33ac08d31d6ed7371988b7 Mon Sep 17 00:00:00 2001 From: saces Date: Fri, 13 Mar 2026 13:02:30 +0100 Subject: [PATCH] add api and tool for query mxpassfiles, fixes --- .../determinant/mxpassfile/mxpassfile.go | 20 +++----- libmxclient/mxclientlib.go | 14 ++++++ libmxclient/mxutils/mxpass.go | 20 ++++++++ pygomx/build_ffi.py | 1 + smal/pyproject.toml | 1 + smal/src/pymxutils/mxutils/__init__.py | 13 +++--- smal/src/pymxutils/mxutils/passitem.py | 46 +++++++++++++++++++ 7 files changed, 95 insertions(+), 20 deletions(-) create mode 100644 libmxclient/mxutils/mxpass.go create mode 100644 smal/src/pymxutils/mxutils/passitem.py diff --git a/libmxclient/determinant/mxpassfile/mxpassfile.go b/libmxclient/determinant/mxpassfile/mxpassfile.go index 6ea2652..7226fc3 100644 --- a/libmxclient/determinant/mxpassfile/mxpassfile.go +++ b/libmxclient/determinant/mxpassfile/mxpassfile.go @@ -87,28 +87,20 @@ func parseLine(line string) *Entry { } } -func isWild(s string) bool { - if s == "" || s == "*" { +func superCMP(fileitem, filteritem string) bool { + if filteritem == "*" { 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 + return fileitem == filteritem } // 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) { + if superCMP(e.Matrixhost, matrixhost) && + superCMP(e.Localpart, localpart) && + superCMP(e.Domain, domain) { return e.Token } } diff --git a/libmxclient/mxclientlib.go b/libmxclient/mxclientlib.go index 9dbc72e..365dc1b 100644 --- a/libmxclient/mxclientlib.go +++ b/libmxclient/mxclientlib.go @@ -170,6 +170,20 @@ func cliv0_serverinfo(url *C.char) *C.char { return C.CString(result) } +//export cliv0_mxpassitem +func cliv0_mxpassitem(mxpassfile_path *C.char, url *C.char, localpart *C.char, domain *C.char) *C.char { + item, err := mxutils.GetMXPassItem(C.GoString(mxpassfile_path), C.GoString(url), C.GoString(localpart), C.GoString(domain)) + if err != nil { + return C.CString(fmt.Sprintf("ERR: %v", err)) + } + out, err := json.Marshal(item) + if err != nil { + return C.CString(fmt.Sprintf("ERR: %v", err)) + } + s := string(out) + return C.CString(s) +} + /* high level api, supports multiple clients the same handler can be attached to multiple clients diff --git a/libmxclient/mxutils/mxpass.go b/libmxclient/mxutils/mxpass.go new file mode 100644 index 0000000..46e1be5 --- /dev/null +++ b/libmxclient/mxutils/mxpass.go @@ -0,0 +1,20 @@ +// Copyright (C) 2026 saces@c-base.org +// SPDX-License-Identifier: AGPL-3.0-only +package mxutils + +import ( + "errors" + "mxclientlib/determinant/mxpassfile" +) + +func GetMXPassItem(mxpassfile_path string, url string, localpart string, domain string) (*mxpassfile.Entry, error) { + pf, err := mxpassfile.ReadPassfile(mxpassfile_path) + if err != nil { + return nil, err + } + e := pf.FindPasswordFill(url, localpart, domain) + if e == nil { + return nil, errors.New("no item found.") + } + return e, nil +} diff --git a/pygomx/build_ffi.py b/pygomx/build_ffi.py index bc1cd7a..be96195 100644 --- a/pygomx/build_ffi.py +++ b/pygomx/build_ffi.py @@ -41,6 +41,7 @@ ffibuilder.cdef( extern char* cliv0_accountinfo(char* hs, char* accessToken); extern char* cliv0_clearaccount(char* hs, char* accessToken); extern char* cliv0_serverinfo(char* url); + extern char* cliv0_mxpassitem(char* mxpassfile, char* hs, char* localpart, char* domain); extern int apiv0_initialize(); extern int apiv0_deinitialize(); extern char* apiv0_discover(char* mxid); diff --git a/smal/pyproject.toml b/smal/pyproject.toml index d41747e..4f36bbb 100644 --- a/smal/pyproject.toml +++ b/smal/pyproject.toml @@ -38,6 +38,7 @@ mxtoken = "pymxutils.mxutils:mktoken" mxaccountinfo = "pymxutils.mxutils:accountinfo" mxclearaccount = "pymxutils.mxutils:clearaccount" mxserverinfo = "pymxutils.mxutils:serverinfo" +mxpassitem = "pymxutils.mxutils:passitem" smalsetup = "smal.smalsetup:smalsetup" demobot = "demobot:main" simplebot = "demobot.simple:main" diff --git a/smal/src/pymxutils/mxutils/__init__.py b/smal/src/pymxutils/mxutils/__init__.py index 25409a5..5d9412e 100644 --- a/smal/src/pymxutils/mxutils/__init__.py +++ b/smal/src/pymxutils/mxutils/__init__.py @@ -1,6 +1,7 @@ -from .discoverhs import discoverhs -from .mktoken import mktoken -from .whoami import whoami -from .accountinfo import accountinfo -from .clearaccount import clearaccount -from .serverinfo import serverinfo +from .discoverhs import discoverhs as discoverhs +from .mktoken import mktoken as mktoken +from .whoami import whoami as whoami +from .accountinfo import accountinfo as accountinfo +from .clearaccount import clearaccount as clearaccount +from .serverinfo import serverinfo as serverinfo +from .passitem import passitem as passitem diff --git a/smal/src/pymxutils/mxutils/passitem.py b/smal/src/pymxutils/mxutils/passitem.py new file mode 100644 index 0000000..91a2c1f --- /dev/null +++ b/smal/src/pymxutils/mxutils/passitem.py @@ -0,0 +1,46 @@ +# Copyright (C) 2026 saces@c-base.org +# SPDX-License-Identifier: AGPL-3.0-only +from _pygomx import lib, ffi +import click +import json + + +@click.command() +@click.option( + "-s", "--secret", "show_secret", is_flag=True, help="print only the secret" +) +@click.option("-u", "--url", "hs_url", metavar="url", help="url selector") +@click.option( + "-l", "--localpart", "localpart", metavar="localpart", help="localpart selector" +) +@click.option("-d", "--domain", "domain", metavar="domain", help="domain selector") +@click.argument("mxpassfile", metavar="mxpassfilepath", required=False) +def passitem(mxpassfile, show_secret, hs_url, localpart, domain): + """utility to get items from mxpasss files""" + + # defaults + if mxpassfile is None: + mxpassfile = ".mxpass" + if hs_url is None: + hs_url = "*" + if localpart is None: + localpart = "*" + if domain is None: + domain = "*" + + r = lib.cliv0_mxpassitem( + mxpassfile.encode(encoding="utf-8"), + hs_url.encode(encoding="utf-8"), + localpart.encode(encoding="utf-8"), + domain.encode(encoding="utf-8"), + ) + result = ffi.string(r).decode("utf-8") + lib.FreeCString(r) + + result_dict = json.loads(result) + + if show_secret: + print(result_dict["Token"]) + else: + result_dict["Token"] = "***" + print(json.dumps(result_dict))