gosecret/funcs.go

209 lines
4.7 KiB
Go

package gosecret
import (
`strings`
`github.com/godbus/dbus/v5`
`r00t2.io/goutils/multierr`
)
// isPrompt returns a boolean that is true if path is/requires a prompt(ed path) and false if it is/does not.
func isPrompt(path dbus.ObjectPath) (prompt bool) {
prompt = strings.HasPrefix(string(path), DbusPromptPrefix)
return
}
// connIsValid returns a boolean if the dbus.conn named conn is active.
func connIsValid(conn *dbus.Conn) (ok bool, err error) {
// dbus.Conn.Names() will ALWAYS return a []string with at least ONE element.
if conn == nil || (conn.Names() == nil || len(conn.Names()) < 1) {
err = ErrNoDbusConn
return
}
ok = true
return
}
/*
pathIsValid implements path checking for valid Dbus paths. Currently it only checks to make sure path is not a blank string.
The path argument can be either a string or dbus.ObjectPath.
*/
func pathIsValid(path interface{}) (ok bool, err error) {
var realPath string
switch p := path.(type) {
case dbus.ObjectPath:
if !p.IsValid() {
err = ErrBadDbusPath
return
}
realPath = string(p)
case string:
realPath = p
default:
err = ErrBadDbusPath
return
}
if strings.TrimSpace(realPath) == "" {
err = ErrBadDbusPath
return
}
ok = true
return
}
/*
validConnPath condenses the checks for connIsValid and pathIsValid into one func due to how frequently this check is done.
If err is not nil, it IS a *multierr.MultiError.
*/
func validConnPath(conn *dbus.Conn, path interface{}) (cr *ConnPathCheckResult, err error) {
var errs *multierr.MultiError = multierr.NewMultiError()
cr = new(ConnPathCheckResult)
if cr.ConnOK, err = connIsValid(conn); err != nil {
errs.AddError(err)
err = nil
}
if cr.PathOK, err = pathIsValid(path); err != nil {
errs.AddError(err)
err = nil
}
if !errs.IsEmpty() {
err = errs
}
return
}
/*
pathsFromProp returns a slice of dbus.ObjectPath (paths) from a dbus.Variant (prop).
If prop cannot typeswitch to paths, an ErrInvalidProperty will be raised.
*/
func pathsFromProp(prop dbus.Variant) (paths []dbus.ObjectPath, err error) {
switch v := prop.Value().(type) {
case []dbus.ObjectPath:
paths = v
default:
err = ErrInvalidProperty
return
}
return
}
/*
pathsFromPath returns a slice of dbus.ObjectPath based on an object given by path using the dbus.Conn specified by conn.
Internally it uses pathsFromProp.
*/
func pathsFromPath(bus dbus.BusObject, path string) (paths []dbus.ObjectPath, err error) {
var v dbus.Variant
if v, err = bus.GetProperty(path); err != nil {
return
}
if paths, err = pathsFromProp(v); err != nil {
return
}
return
}
/*
NameFromPath returns an actual name (as it appears in Dbus) from a dbus.ObjectPath.
Note that you can get any object's dbus.ObjectPath via <object>.Dbus.Path().
path is validated to ensure it is not an empty string.
*/
func NameFromPath(path dbus.ObjectPath) (name string, err error) {
var strSplit []string
var ok bool
if ok, err = pathIsValid(path); err != nil {
return
} else if !ok {
err = ErrBadDbusPath
return
}
strSplit = strings.Split(string(path), "/")
if len(strSplit) < 1 {
err = ErrBadDbusPath
return
}
name = strSplit[len(strSplit)-1]
return
}
/*
CheckErrIsFromLegacy takes an error.Error from e.g.:
Service.SearchItems
Collection.CreateItem
NewItem
Item.ChangeItemType
Item.Type
and (in order) attempt to typeswitch to a *multierr.MultiError, then iterate through
the *multierr.MultiError.Errors, attempt to typeswitch each of them to a Dbus.Error, and then finally
check if it is regarding a missing Type property.
This is *very explicitly* only useful for the above functions/methods. If used anywhere else,
it's liable to return an incorrect isLegacy even if parsed == true.
It is admittedly convoluted and obtuse, but this saves a lot of boilerplate for users.
It wouldn't be necessary if projects didn't insist on using the legacy draft SecretService specification.
But here we are.
isLegacy is true if this Service's API destination is legacy spec. Note that this is checking for
very explicit conditions; isLegacy may return false but it is in fact running on a legacy API.
Don't rely on this too much.
parsed is true if we found an error type we were able to perform logic of determination on.
*/
func CheckErrIsFromLegacy(err error) (isLegacy, parsed bool) {
switch e := err.(type) {
case *multierr.MultiError:
parsed = true
for _, i := range e.Errors {
switch e2 := i.(type) {
case dbus.Error:
if e2.Name == "org.freedesktop.DBus.Error.UnknownProperty" {
isLegacy = true
return
}
default:
continue
}
}
case dbus.Error:
parsed = true
if e.Name == "org.freedesktop.DBus.Error.UnknownProperty" {
isLegacy = true
return
}
}
return
}