walletmanager funcs done, now for wallet funcs

This commit is contained in:
brent s. 2021-12-18 22:33:50 -05:00
parent 8d81c5f0cc
commit d76746d79e
Signed by: bts
GPG Key ID: 8C004C2F93481F6B
13 changed files with 944 additions and 124 deletions

View File

@ -1,3 +1,6 @@
https://api.kde.org/frameworks/kwallet/html/index.html
https://invent.kde.org/frameworks/kwallet/-/blob/master/src/api/KWallet/kwallet.h

https://github.com/99designs/keyring/blob/master/kwallet.go

Dbus methods etc.: https://invent.kde.org/frameworks/kwallet/-/blob/master/src/api/KWallet/org.kde.KWallet.xml

141
consts.go
View File

@ -1,5 +1,20 @@
package gokwallet

// KwalletD Dbus returns.
const (
DbusSuccess int32 = 0
DbusFailure int32 = 1
)

// KwalletD Dbus enums for WalletItem types.
const (
kwalletdEnumTypeUnknown = iota // UnknownItem
kwalletdEnumTypePassword // Password
kwalletdEnumTypeStream // Blob
kwalletdEnumTypeMap // Map
kwalletdEnumTypeUnused = 0xffff // 65535
)

// KWalletD Dbus interfaces.
const (
// DbusService is the Dbus service bus identifier.
@ -14,6 +29,21 @@ const (
DefaultWalletName string = "kdewallet"
// DefaultAppID is the default name for the application (see WalletManager.AppID).
DefaultAppID string = "GoKwallet"
// DefaultWindowID is 0; we aren't guaranteed to have a window, so we pass 0 (per upstream headers' comments).
DefaultWindowID int64 = 0
)

var (
DefaultRecurseOpts *RecurseOpts = &RecurseOpts{
All: false,
Wallets: true,
Folders: true,
AllWalletItems: false,
Passwords: false,
Maps: false,
Blobs: false,
UnknownItems: false,
}
)

// WalletManager interface.
@ -94,10 +124,10 @@ const (
// DbusWMOpenAsync opens (unlocks) a Wallet asynchronously.
DbusWMOpenAsync string = DbusInterfaceWM + ".openAsync"

// DbusWMOpenPath opens (unlocks) a Wallet by its filepath.
// DbusWMOpenPath opens a Wallet by its filepath.
DbusWMOpenPath string = DbusInterfaceWM + ".openPath"

// DbusWMOpenPathAsync opens (unlocks) a Wallet by its filepath asynchronously.
// DbusWMOpenPathAsync opens a Wallet by its filepath asynchronously.
DbusWMOpenPathAsync string = DbusInterfaceWM + ".openPath"

// DbusWMPamOpen opens (unlocks) a Wallet via PAM.
@ -112,19 +142,31 @@ const (
// DbusWMReadEntry fetches a WalletItem by its name from a Folder (as a byteslice).
DbusWMReadEntry string = DbusInterfaceWM + ".readEntry"

// DbusWMReadEntryList returns a map of WalletItem objects in a Folder.
/*
DbusWMReadEntryList returns a map of WalletItem objects in a Folder.

Deprecated: use DbusWMEntriesList instead.
*/
DbusWMReadEntryList string = DbusInterfaceWM + ".readEntryList"

// DbusWMReadMap returns a Map from a Folder (as a byteslice).
DbusWMReadMap string = DbusInterfaceWM + ".readMap"

// DbusWMReadMapList returns a map of Map objects in a Folder.
/*
DbusWMReadMapList returns a map of Map objects in a Folder.

Deprecated: use DbusWMMapList instead.
*/
DbusWMReadMapList string = DbusInterfaceWM + ".readMapList"

// DbusWMReadPassword returns a Password from a Folder (as a byteslice).
DbusWMReadPassword string = DbusInterfaceWM + ".readPassword"

// DbusWMReadPasswordList returns a map of Password objects in a Folder.
/*
DbusWMReadPasswordList returns a map of Password objects in a Folder.

Deprecated: use DbusWMPasswordList instead.
*/
DbusWMReadPasswordList string = DbusInterfaceWM + ".readPasswordList"

// DbusWMReconfigure is [FUNCTION UNKNOWN/UNDOCUMENTED; TODO? NOT IMPLEMENTED.]
@ -163,92 +205,3 @@ const (
// DbusPath is the path for DbusService.
DbusPath string = "/modules/kwalletd5"
)

// Recursion options.

/*
RecurseNone specifies that no recursion should be done.
If present, it takes precedent over all over RecurseOptsFlags present.

Performed in/from:
WalletManager
Wallet
Folder
(WalletItem)
*/
const RecurseNone RecurseOptsFlag = 0
const (
/*
RecurseWallet indicates that Wallet objects should have Wallet.Update called.

Performed in/from: WalletManager
*/
RecurseWallet RecurseOptsFlag = 1 << iota
/*
RecurseFolder indicates that Folder objects should have Folder.Update called.

Performed in/from:
Wallet

May be performed in/from (depending on other flags):
WalletManager
*/
RecurseFolder
/*
RecurseWalletItem indicates that all WalletItem entries should have (WalletItem).Update() called.
If present, it takes precedent over all over relevant RecurseOptsFlags present
(RecursePassword, RecurseMap, RecurseBlob, RecurseUnknown).

Performed in/from:
Folder

May be performed in/from (depending on other flags):
WalletManager
Wallet
*/
RecurseWalletItem
/*
RecursePassword indicates that Password objects should have Password.Update() called.

Performed in/from:
Folder

May be performed in/from (depending on other flags):
WalletManager
Wallet
*/
RecursePassword
/*
RecurseMap indicates that Map objects should have Map.Update() called.

Performed in/from:
Folder

May be performed in/from (depending on other flags):
WalletManager
Wallet
*/
RecurseMap
/*
RecurseBlob indicates that Blob objects should have Blob.Update() called.

Performed in/from:
Folder

May be performed in/from (depending on other flags):
WalletManager
Wallet
*/
RecurseBlob
/*
RecurseUnknown indicates that UnknownItem objects should have UnknownItem.Update() called.

Performed in/from:
Folder

May be performed in/from (depending on other flags):
WalletManager
Wallet
*/
RecurseUnknown
)

5
doc.go
View File

@ -5,7 +5,7 @@ Package gokwallet serves as a Golang interface to KDE's KWallet (https://utils.k

Note that to use this library, the running machine must have both Dbus and kwalletd running.

Note that this library interfaces with kwalletd. KWallet is in the process of moving to libsecret/SecretService
Relatedly, note also that this library interfaces with kwalletd. KWallet is in the process of moving to libsecret/SecretService
(see https://bugs.kde.org/show_bug.cgi?id=313216 and https://invent.kde.org/frameworks/kwallet/-/merge_requests/11),
thus replacing kwalletd.
While there is a pull request in place, it has not yet been merged in (and it may be a while before downstream
@ -72,5 +72,8 @@ Usage

Full documentation can be found via inline documentation.
Additionally, use either https://pkg.go.dev/r00t2.io/gokwallet or https://pkg.go.dev/golang.org/x/tools/cmd/godoc (or `go doc`) in the source root.

You most likely do *not* want to call any New<object> function directly;
NewWalletManager with its RecurseOpts parameter (`recursion`) should get you everything you want/need.
*/
package gokwallet

20
errs.go Normal file
View File

@ -0,0 +1,20 @@
package gokwallet

import (
"errors"
)

var (
/*
ErrNotInitialized will be triggered if attempting to interact with an object that has not been properly initialized.
Notably, in most/all cases this means that it was not created via a New<object> func (for instance,
this would lead to a Wallet missing a handler).
It is intended as a safety check (so that you don't accidentally delete a wallet with e.g. a handler of 0 when
trying to delete a different wallet).
*/
ErrNotInitialized error = errors.New("object not properly initialized")
/*
ErrOperationFailed is a generic failure message that will occur of a Dbus operation returns non-success.
*/
ErrOperationFailed error = errors.New("a Dbus operation has failed to execute successfully")
)

56
folder_funcs.go Normal file
View File

@ -0,0 +1,56 @@
package gokwallet

/*
NewF returns a Wallet. It requires a RecurseOpts
(you can use DefaultRecurseOpts, call NewRecurseOpts, or provide your own RecurseOpts struct).
It also requires a WalletManager and wallet name.
*/
func NewFolder(w *Wallet, name string, recursion *RecurseOpts) (folder *Folder, err error) {

if !w.isInit {
err = ErrNotInitialized
return
}

folder = &Folder{
DbusObject: w.DbusObject,
Name: name,
Passwords: nil,
Maps: nil,
BinaryData: nil,
Unknown: nil,
Recurse: recursion,
wm: w.wm,
wallet: w,
// handle: 0,
isInit: false,
}

if err = folder.folderCheck(); err != nil {
return
}

if folder.Recurse.All || folder.Recurse.Wallets {
if err = folder.Update(); err != nil {
return
}
}

folder.isInit = true

return
}

func (f *Folder) Update() (err error) {

// TODO.

return
}

func (f *Folder) folderCheck() (err error) {

// TODO.

return
}

110
funcs.go Normal file
View File

@ -0,0 +1,110 @@
package gokwallet

import (
"errors"
)

/*
NewRecurseOpts returns a RecurseOpts based on the specified options.
See the documentation for RecurseOpts for descriptions of the behaviour for each recursion option.
warn is a MultiError but should be treated as warnings rather than strictly errors.
*/
func NewRecurseOpts(recurseAll, wallets, folders, recurseAllWalletItems, passwords, maps, blobs, unknownItems bool) (opts *RecurseOpts, warn error) {

var err []error = make([]error, 0)

if recurseAll {
if !wallets {
err = append(err, errors.New("wallets was specified as false but recurseAll is true; recurseAll overrides wallets to true"))
wallets = true
}
if !folders {
err = append(err, errors.New("folders was specified as false but recurseAll is true; recurseAll overrides folders to true"))
folders = true
}
// NOTE: This is commented out because we control these explicitly with recurseAllWalletItems.
/*
if !passwords {
err = append(err, errors.New("passwords was specified as false but recurseAll is true; recurseAll overrides passwords to true"))
passwords = true
}
if !maps {
err = append(err, errors.New("maps was specified as false but recurseAll is true; recurseAll overrides maps to true"))
maps = true
}
if !blobs {
err = append(err, errors.New("blobs was specified as false but recurseAll is true; recurseAll overrides blobs to true"))
blobs = true
}
if !unknownItems {
err = append(err, errors.New("unknownItems was specified as false but recurseAll is true; recurseAll overrides unknownItems to true"))
unknownItems = true
}
if !recurseAllWalletItems {
err = append(
err,
errors.New(
"recurseAllWalletItems was specified as false but recurseAll is true; recurseAll overrides recurseAllWalletItems to true",
),
)
recurseAllWalletItems = true
}
*/
} else {
if recurseAllWalletItems {
if !passwords {
err = append(
err,
errors.New(
"passwords was specified as false but recurseAllWalletItems is true; recurseAllWalletItems overrides passwords to true",
),
)
passwords = true
}
if !maps {
err = append(
err,
errors.New(
"maps was specified as false but recurseAllWalletItems is true; recurseAllWalletItems overrides maps to true",
),
)
maps = true
}
if !blobs {
err = append(
err,
errors.New(
"blobs was specified as false but recurseAllWalletItems is true; recurseAllWalletItems overrides blobs to true",
),
)
blobs = true
}
if !unknownItems {
err = append(
err,
errors.New(
"unknownItems was specified as false but recurseAllWalletItems is true; recurseAllWalletItems overrides unknownItems to true",
),
)
unknownItems = true
}
}
}

opts = &RecurseOpts{
All: recurseAll,
Wallets: wallets,
Folders: folders,
AllWalletItems: recurseAllWalletItems,
Passwords: passwords,
Maps: maps,
Blobs: blobs,
UnknownItems: unknownItems,
}

if err != nil && len(err) > 0 {
warn = NewErrors(err...)
}

return
}

2
go.mod
View File

@ -4,5 +4,5 @@ go 1.17

require (
github.com/godbus/dbus/v5 v5.0.6
r00t2.io/goutils v1.0.1
r00t2.io/goutils v1.1.0
)

4
go.sum
View File

@ -2,6 +2,6 @@ github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7
github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro=
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/jszwec/csvutil v1.5.0/go.mod h1:Rpu7Uu9giO9subDyMCIQfHVDuLrcaC36UA4YcJjGBkg=
r00t2.io/goutils v1.0.1 h1:f1m2QRBF8XsW3peYf1I2q37npgf+5bmJBbrLzCFtm34=
r00t2.io/goutils v1.0.1/go.mod h1:CMK3RGnMSyjDSfYxeFQl/oJTkkUMS1jhSTdGTkAPpQw=
r00t2.io/goutils v1.1.0 h1:vJqEWvdX0TKDyT+hTgws0wA1dc/F8JkQKw5cDHz0wH0=
r00t2.io/goutils v1.1.0/go.mod h1:CMK3RGnMSyjDSfYxeFQl/oJTkkUMS1jhSTdGTkAPpQw=
r00t2.io/sysutils v0.0.0-20210224054841-55ac47c86928/go.mod h1:XzJkBF6SHAODEszJlOcjtGoTHwYnZZNmseA6PyOujes=

57
multierror_funcs.go Normal file
View File

@ -0,0 +1,57 @@
package gokwallet

import (
"fmt"
)

/*
NewErrors returns a new MultiError based on a slice of error.Error (errs).
Any nil errors are trimmed. If there are no actual errors after trimming, err will be nil.
*/
func NewErrors(errs ...error) (err error) {

if errs == nil || len(errs) == 0 {
return
}

var realErrs []error = make([]error, 0)

for _, e := range errs {
if e == nil {
continue
}
realErrs = append(realErrs, e)
}

if len(realErrs) == 0 {
return
}

err = &MultiError{
Errors: realErrs,
ErrorSep: "\n",
}

return
}

func (e *MultiError) Error() (errStr string) {

var numErrs int

if e == nil || len(e.Errors) == 0 {
return
} else {
numErrs = len(e.Errors)
}

for idx, err := range e.Errors {
if (idx + 1) < numErrs {
errStr += fmt.Sprintf(err.Error(), e.ErrorSep)
} else {
errStr += err.Error()
}
}

return
}

182
types.go
View File

@ -2,7 +2,6 @@ package gokwallet

import (
"github.com/godbus/dbus/v5"
"r00t2.io/goutils/types"
)

/*
@ -45,8 +44,22 @@ type WalletManager struct {
/*
Wallets is the collection of Wallets accessible in/to this WalletManager.
Wallet.Name is the map key.
(TODO: When wallet file support is added, the *filename* will be the map key.
This is to mitigate namespace conflicts between Dbus and file wallets.)
*/
Wallets map[string]*Wallet `json:"wallets"`
// Recurse contains the relevant RecurseOpts.
Recurse *RecurseOpts `json:"recurse_opts"`
// Enabled is true if KWalletD is enabled/running.
Enabled bool `json:"enabled"`
// Local is the "local" wallet.
Local *Wallet `json:"local_wallet"`
// Network is the "network" wallet.
Network *Wallet `json:"network_wallet"`
// isInit flags whether this is "properly" set up (i.e. was initialized via NewWalletManager).
isInit bool
// walletFiles are (resolved and vetted) wallet files (kwl, xml).
walletFiles []string
}

// Wallet contains one or more (or none) Folder objects.
@ -59,6 +72,22 @@ type Wallet struct {
Folder.Name is the map key.
*/
Folders map[string]*Folder `json:"folders"`
// Recurse contains the relevant RecurseOpts.
Recurse *RecurseOpts `json:"recurse_opts"`
// IsUnlocked specifies if this Wallet is open ("unlocked") or not.
IsUnlocked bool `json:"open"`
/*
FilePath is:
- empty if this is an internal Wallet, or
- the filepath to the wallet file if this is an on-disk wallet (either .kwl or .xml)
*/
FilePath string `json:"wallet_file"`
// wm is the parent WalletManager this Wallet was fetched from.
wm *WalletManager
// handle is this Wallet's handler number.
handle int32
// isInit flags whether this is "properly" set up (i.e. has a handle).
isInit bool
}

// Folder contains secret object collections of Password, Map, Blob, and UnknownItem objects.
@ -86,6 +115,16 @@ type Folder struct {
Unknown.Name is the map key.
*/
Unknown map[string]*UnknownItem `json:"unknown"`
// Recurse contains the relevant RecurseOpts.
Recurse *RecurseOpts `json:"recurse_opts"`
// wm is the parent WalletManager that Folder.wallet was fetched from.
wm *WalletManager
// wallet is the parent Wallet this Folder was fetched from.
wallet *Wallet
// handle is this Folder's handler number.
handle int32
// isInit flags whether this is "properly" set up (i.e. has a handle).
isInit bool
}

// Password is a straightforward single-value secret of text.
@ -95,6 +134,18 @@ type Password struct {
Name string `json:"name"`
// Value is this Password's value.
Value string `json:"value"`
// Recurse contains the relevant RecurseOpts.
Recurse *RecurseOpts `json:"recurse_opts"`
// wm is the parent WalletManager that Password.folder.wallet was fetched from.
wm *WalletManager
// wallet is the parent Wallet that Password.folder was fetched from.
wallet *Wallet
// folder is the parent Folder this Password was fetched from.
folder *Folder
// handle is this Password's handler number.
handle int32
// isInit flags whether this is "properly" set up (i.e. has a handle).
isInit bool
}

// Map is a dictionary or key/value secret.
@ -104,6 +155,18 @@ type Map struct {
Name string `json:"name"`
// Value is this Map's value.
Value map[string]string `json:"value"`
// Recurse contains the relevant RecurseOpts.
Recurse *RecurseOpts `json:"recurse_opts"`
// wm is the parent WalletManager that Map.folder.wallet was fetched from.
wm *WalletManager
// wallet is the parent Wallet that Map.folder was fetched from.
wallet *Wallet
// folder is the parent Folder this Map was fetched from.
folder *Folder
// handle is this Map's handler number.
handle int32
// isInit flags whether this is "properly" set up (i.e. has a handle).
isInit bool
}

// Blob (binary large object, typographically BLOB) is secret binary data.
@ -113,6 +176,18 @@ type Blob struct {
Name string `json:"name"`
// Value is this Blob's value.
Value []byte `json:"value"`
// Recurse contains the relevant RecurseOpts.
Recurse *RecurseOpts `json:"recurse_opts"`
// wm is the parent WalletManager that Blob.folder.wallet was fetched from.
wm *WalletManager
// wallet is the parent Wallet that Blob.folder was fetched from.
wallet *Wallet
// folder is the parent Folder this Blob was fetched from.
folder *Folder
// handle is this Blob's handler number.
handle int32
// isInit flags whether this is "properly" set up (i.e. has a handle).
isInit bool
}

/*
@ -126,6 +201,18 @@ type UnknownItem struct {
Name string `json:"name"`
// Value is the Dbus path of this UnknownItem.
Value dbus.ObjectPath `json:"value"`
// Recurse contains the relevant RecurseOpts.
Recurse *RecurseOpts `json:"recurse_opts"`
// wm is the parent WalletManager that UnknownItem.folder.wallet was fetched from.
wm *WalletManager
// wallet is the parent Wallet that UnknownItem.folder was fetched from.
wallet *Wallet
// folder is the parent Folder this UnknownItem was fetched from.
folder *Folder
// handle is this UnknownItem's handler number.
handle int32
// isInit flags whether this is "properly" set up (i.e. has a handle).
isInit bool
}

// WalletItem is an interface to manage wallet objects: Password, Map, Blob, or UnknownItem.
@ -134,8 +221,93 @@ type WalletItem interface {
}

/*
RecurseOptsFlag is used to determine whether or not to recurse into items and fully populate them.
One would use a types.BitMask as a parameter type and do <BitMask>.HasFlag(<RecurseOptsFlag>).
See consts.go for the actual flags.
RecurseOpts controls whether recursion should be done on objects when fetching them.
E.g. if fetching a WalletManager (via NewWalletManager) and RecurseOpts.Wallet is true,
then WalletManager.Wallets will be populated with Wallet objects.
*/
type RecurseOptsFlag types.MaskBit
type RecurseOpts struct {
/*
All, if true, specifies that all possible recursions should be done.
If true, it takes precedent over all over RecurseOpts fields (with the exception of RecurseOpts.AllWalletItems).

Performed in/from:
WalletManager
Wallet
Folder
(WalletItem)
*/
All bool `json:"none"`
/*
Wallets, if true, indicates that Wallet objects should have Wallet.Update called.

Performed in/from: WalletManager
*/
Wallets bool `json:"wallet"`
/*
Folders, if true, indicates that Folder objects should have Folder.Update called.

Performed in/from:
Wallet

May be performed in/from (depending on other fields):
WalletManager
*/
Folders bool `json:"folder"`
/*
AllWalletItems, if true, indicates that all WalletItem entries should have (WalletItem).Update() called.
If true, it takes precedent over all over relevant RecurseOpts fields for each WalletItem type
(i.e. RecurseOpts.Passwords, RecurseOpts.Maps, RecurseOpts.Blobs, RecurseOpts.UnknownItems).

Performed in/from:
Folder

May be performed in/from (depending on other fields):
WalletManager
Wallet
*/
AllWalletItems bool `json:"wallet_item"`
/*
Passwords, if true, indicates that Password objects should have Password.Update() called.

Performed in/from:
Folder

May be performed in/from (depending on other fields):
WalletManager
Wallet
*/
Passwords bool `json:"password"`
/*
Maps, if true, indicates that Map objects should have Map.Update() called.

Performed in/from:
Folder

May be performed in/from (depending on other fields):
WalletManager
Wallet
*/
Maps bool `json:"map"`
/*
Blobs, if true, indicates that Blob objects should have Blob.Update() called.

Performed in/from:
Folder

May be performed in/from (depending on other fields):
WalletManager
Wallet
*/
Blobs bool `json:"blob"`
/*
UnknownItems indicates that UnknownItem objects should have UnknownItem.Update() called.

Performed in/from:
Folder

May be performed in/from (depending on other fields):
WalletManager
Wallet
*/
UnknownItems bool `json:"unknown_item"`
}

35
utils.go Normal file
View File

@ -0,0 +1,35 @@
package gokwallet

/*
resultCheck checks the result code from a Dbus call and returns an error if not successful.
See also resultPassed.
*/
func resultCheck(result int32) (err error) {

// This is technically way more complex than it needs to be, but is extendable for future use.
switch i := result; i {
case DbusSuccess:
err = nil
case DbusFailure:
err = ErrOperationFailed
}

return
}

/*
resultPassed checks the result code from a Dbus call and returns a boolean as to whether the result is pass or not.
See also resultCheck.
*/
func resultPassed(result int32) (passed bool) {

// This is technically way more complex than it needs to be, but is extendable for future use.
switch i := result; i {
case DbusSuccess:
passed = true
case DbusFailure:
passed = false
}

return
}

View File

@ -1,14 +1,191 @@
package gokwallet

import (
"github.com/godbus/dbus/v5"
)
/*
NewWallet returns a Wallet. It requires a RecurseOpts
(you can use DefaultRecurseOpts, call NewRecurseOpts, or provide your own RecurseOpts struct).
It also requires a WalletManager and wallet name.
*/
func NewWallet(wm *WalletManager, name string, recursion *RecurseOpts) (wallet *Wallet, err error) {

func NewWallet(wm *WalletManager, path dbus.ObjectPath) (wallet *Wallet, err error) {
if !wm.isInit {
err = ErrNotInitialized
return
}

var open bool
wallet = &Wallet{
DbusObject: wm.DbusObject,
Name: name,
Folders: nil,
Recurse: recursion,
wm: wm,
// handle: 0,
isInit: false,
}

_ = open
// TODO: remove this and leave to caller, since it might use PamOpen instead? Fail back to it?
if err = wallet.walletCheck(); err != nil {
return
}

if wallet.Recurse.All || wallet.Recurse.Wallets {
if err = wallet.Update(); err != nil {
return
}
}

wallet.isInit = true

return
}

// Close closes a Wallet.
func (w *Wallet) Close() (err error) {

var rslt int32

if !w.isInit {
err = ErrNotInitialized
return
}

// Using a handler allows us to close access for this particular parent WalletManager.
if err = w.Dbus.Call(
DbusWMClose, 0, w.handle, false, w.wm.AppID,
).Store(&rslt); err != nil {
return
}

err = resultCheck(rslt)

return
}

/*
ForceClose is like Close but will still close a Wallet even if currently in use by the WalletManager.
(Despite the insinuation by the name, this should be a relatively safe operation).
*/
func (w *Wallet) ForceClose() (err error) {

var rslt int32

if !w.isInit {
err = ErrNotInitialized
return
}

// Using a handler allows us to close access for this particular parent WalletManager.
if err = w.Dbus.Call(
DbusWMClose, 0, w.handle, true, w.wm.AppID,
).Store(&rslt); err != nil {
return
}

err = resultCheck(rslt)

return

return
}

// IsOpen returns whether a Wallet is open ("unlocked") or not (as well as updates Wallet.IsOpen).
func (w *Wallet) IsOpen() (isOpen bool, err error) {

// We can call the same method with w.handle instead of w.Name. We don't have a handler yet though.
if err = w.Dbus.Call(
DbusWMIsOpen, 0, w.Name,
).Store(&w.IsUnlocked); err != nil {
return
}

isOpen = w.IsUnlocked

return
}

// ListFolders lists all Folder names in a Wallet.
func (w *Wallet) ListFolders() (folderList []string, err error) {

if err = w.walletCheck(); err != nil {
return
}

if err = w.Dbus.Call(
DbusWMFolderList, 0, w.handle, w.wm.AppID,
).Store(&folderList); err != nil {
return
}

return
}

/*
Open will open ("unlock") a Wallet.
It will no-op if the Wallet is already open.
*/
func (w *Wallet) Open() (err error) {

if _, err = w.IsOpen(); err != nil {
return
}

if !w.IsUnlocked {
if err = w.Dbus.Call(
DbusWMOpen, 0,
).Store(&w.handle); err != nil {
return
}
}

w.IsUnlocked = true

return
}

// Update fetches/updates all Folder objects in a Wallet.
func (w *Wallet) Update() (err error) {

var folderNames []string
var errs []error = make([]error, 0)

if folderNames, err = w.ListFolders(); err != nil {
return
}

w.Folders = make(map[string]*Folder)

for _, fn := range folderNames {
if w.Folders[fn], err = NewFolder(w, fn, w.Recurse); err != nil {
errs = append(errs, err)
err = nil
continue
}
}

if errs != nil && len(errs) > 0 {
err = NewErrors(errs...)
return
}

return
}

// walletCheck will check if a Wallet is (initialized and) opened and, if not, attempt to open it.
func (w *Wallet) walletCheck() (err error) {

if !w.isInit {
err = ErrNotInitialized
return
}

if _, err = w.IsOpen(); err != nil {
return
}

if !w.IsUnlocked {
if err = w.Open(); err != nil {
return
}
}

return
}

View File

@ -5,11 +5,12 @@ import (
)

/*
NewWalletManager returns a WalletManager.
NewWalletManager returns a WalletManager. It requires a RecurseOpts
(you can use DefaultRecurseOpts, call NewRecurseOpts, or provide your own RecurseOpts struct).
If appId is empty/nil, DefaultAppID will be used as the app ID.
If appId is specified, only the first string is used.
*/
func NewWalletManager(appID ...string) (wm *WalletManager, err error) {
func NewWalletManager(recursion *RecurseOpts, appID ...string) (wm *WalletManager, err error) {

var realAppID string

@ -19,13 +20,245 @@ func NewWalletManager(appID ...string) (wm *WalletManager, err error) {
realAppID = DefaultAppID
}

if wm, err = newWM(realAppID, recursion); err != nil {
return
}

return
}

/*
NewWalletManagerFiles returns a WalletManager from one or more filePaths (*.kwl, *.tar, or *.xml exports).
Note that if the wallet file was created via an "encrypted export", it will be a .kwl file
inside a .tar.
err will contain a MultiError if any filepaths specified do not exist or cannot be opened.
It requires a RecurseOpts (you can use DefaultRecurseOpts, call NewRecurseOpts,
or provide your own RecurseOpts struct).
If appId is empty, DefaultAppID will be used as the app ID.
*/
/* TODO: POC this before exposing. I have NO idea if it'll work.
func NewWalletManagerFiles(recursion *RecurseOpts, appId string, filePaths ...string) (wm *WalletManager, err error) {

var exist bool
var errs []error = make([]error, 0)
var realFilePaths []string = make([]string, 0)

if appId == "" {
appId = DefaultAppID
}

for _, f := range filePaths {
if f == "" {
continue
}
if exist, err = paths.RealPathExists(&f); err != nil {
errs = append(errs, err)
err = nil
continue
}
if !exist {
err = errors.New(fmt.Sprintf("%v does not exist", f))
err = nil
continue
}
realFilePaths = append(realFilePaths, f)
}

// TODO: do the actual newWM here.

if errs != nil && len(errs) > 0 {
err = NewErrors(errs...)
}

return
}
*/

/*
CloseWallet closes a Wallet.
Unlike Wallet.Close, this closes access for ALL applications/WalletManagers
for the specified Wallet - not just this WalletManager.
*/
func (wm *WalletManager) CloseWallet(walletName string) (err error) {
var rslt int32

if !wm.isInit {
err = ErrNotInitialized
return
}

// Using a handler allows us to close access for this particular parent WalletManager.
if err = wm.Dbus.Call(
DbusWMClose, 0, walletName, false,
).Store(&rslt); err != nil {
return
}

err = resultCheck(rslt)

return
}

/*
ForceCloseWallet is like WalletManager.CloseWallet but will still close a Wallet even if currently in use.
Unlike Wallet.ForceClose, this closes access for ALL applications/WalletManagers
for the specified Wallet - not just this WalletManager.
*/
func (wm *WalletManager) FprceCloseWallet(walletName string) (err error) {

var rslt int32

if !wm.isInit {
err = ErrNotInitialized
return
}

// Using a handler allows us to close access for this particular parent WalletManager.
if err = wm.Dbus.Call(
DbusWMClose, 0, walletName, false,
).Store(&rslt); err != nil {
return
}

err = resultCheck(rslt)

return
}

// CloseAllWallets closes all Wallet objects. They do *not* need to be part of WalletManager.Wallets.
func (wm *WalletManager) CloseAllWallets() (err error) {

var call *dbus.Call

if !wm.isInit {
err = ErrNotInitialized
return
}

call = wm.Dbus.Call(
DbusWMCloseAllWallets, 0,
)
err = call.Err

return
}

// IsEnabled returns whether KWallet is enabled or not (and also updates WalletManager.Enabled).
func (wm *WalletManager) IsEnabled() (enabled bool, err error) {

if !wm.isInit {
err = ErrNotInitialized
return
}

if err = wm.Dbus.Call(
DbusWMIsEnabled, 0,
).Store(&wm.Enabled); err != nil {
return
}

enabled = wm.Enabled

return
}

// LocalWallet returns the "local" wallet (and updates WalletManager.Local).
func (wm *WalletManager) LocalWallet() (w *Wallet, err error) {

var wn string

if err = wm.Dbus.Call(
DbusWMLocalWallet, 0,
).Store(&wn); err != nil {
return
}

if w, err = NewWallet(wm, wn, wm.Recurse); err != nil {
return
}

wm.Local = w

return
}

// NetworkWallet returns the "network" wallet (and updates WalletManager.Network).
func (wm *WalletManager) NetworkWallet() (w *Wallet, err error) {

var wn string

if err = wm.Dbus.Call(
DbusWMNetWallet, 0,
).Store(&wn); err != nil {
return
}

if w, err = NewWallet(wm, wn, wm.Recurse); err != nil {
return
}

wm.Network = w

return
}

// WalletNames returns a list of available Wallet names.
func (wm *WalletManager) WalletNames() (wallets []string, err error) {

if err = wm.Dbus.Call(
DbusWMWallets, 0,
).Store(&wallets); err != nil {
return
}

return
}

// Update fetches/updates all Wallet objects in a WalletManager.
func (wm *WalletManager) Update() (err error) {

var walletNames []string
var errs []error = make([]error, 0)

if !wm.isInit {
err = ErrNotInitialized
return
}

if walletNames, err = wm.WalletNames(); err != nil {
return
}

wm.Wallets = make(map[string]*Wallet)

for _, wn := range walletNames {

if wm.Wallets[wn], err = NewWallet(wm, wn, wm.Recurse); err != nil {
errs = append(errs, err)
err = nil
continue
}
}

if errs != nil && len(errs) > 0 {
err = NewErrors(errs...)
return
}

return
}

// newWM is what does the heavy lifting behind NewWalletManager and NewWalletManagerFiles.
func newWM(appId string, recursion *RecurseOpts, filePaths ...string) (wm *WalletManager, err error) {

wm = &WalletManager{
DbusObject: &DbusObject{
Conn: nil,
Dbus: nil,
},
AppID: realAppID,
Wallets: make(map[string]*Wallet),
AppID: appId,
Wallets: nil,
Recurse: recursion,
}

if wm.DbusObject.Conn, err = dbus.SessionBus(); err != nil {
@ -33,18 +266,19 @@ func NewWalletManager(appID ...string) (wm *WalletManager, err error) {
}
wm.DbusObject.Dbus = wm.DbusObject.Conn.Object(DbusService, dbus.ObjectPath(DbusPath))

return
}
if wm.Recurse.All || wm.Recurse.Wallets {
if err = wm.Update(); err != nil {
return
}
if _, err = wm.LocalWallet(); err != nil {
return
}
if _, err = wm.NetworkWallet(); err != nil {
return
}
}

/*
Update fetches/updates all Wallet objects in a WalletManager.
*/
func (wm *WalletManager) Update() (err error) {

var wallets []*Wallet

// TODO.
_ = wallets
wm.isInit = true

return
}