From d76746d79e8d14090d33ae8146b5e25a34b079cc Mon Sep 17 00:00:00 2001 From: brent s Date: Sat, 18 Dec 2021 22:33:50 -0500 Subject: [PATCH] walletmanager funcs done, now for wallet funcs --- .ref/URLs | 3 + consts.go | 141 ++++++++-------------- doc.go | 5 +- errs.go | 20 ++++ folder_funcs.go | 56 +++++++++ funcs.go | 110 +++++++++++++++++ go.mod | 2 +- go.sum | 4 +- multierror_funcs.go | 57 +++++++++ types.go | 182 +++++++++++++++++++++++++++- utils.go | 35 ++++++ wallet_funcs.go | 189 ++++++++++++++++++++++++++++- walletmanager_funcs.go | 264 ++++++++++++++++++++++++++++++++++++++--- 13 files changed, 944 insertions(+), 124 deletions(-) create mode 100644 errs.go create mode 100644 folder_funcs.go create mode 100644 funcs.go create mode 100644 multierror_funcs.go create mode 100644 utils.go diff --git a/.ref/URLs b/.ref/URLs index 40a17dc..8108923 100644 --- a/.ref/URLs +++ b/.ref/URLs @@ -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 diff --git a/consts.go b/consts.go index 1023cd7..a455ab1 100644 --- a/consts.go +++ b/consts.go @@ -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 -) diff --git a/doc.go b/doc.go index 97ce7df..a6fa227 100644 --- a/doc.go +++ b/doc.go @@ -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 function directly; +NewWalletManager with its RecurseOpts parameter (`recursion`) should get you everything you want/need. */ package gokwallet diff --git a/errs.go b/errs.go new file mode 100644 index 0000000..fddc0e8 --- /dev/null +++ b/errs.go @@ -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 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") +) diff --git a/folder_funcs.go b/folder_funcs.go new file mode 100644 index 0000000..e6df1a6 --- /dev/null +++ b/folder_funcs.go @@ -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 +} diff --git a/funcs.go b/funcs.go new file mode 100644 index 0000000..99ade25 --- /dev/null +++ b/funcs.go @@ -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 +} diff --git a/go.mod b/go.mod index 9a39c8d..bcf7110 100644 --- a/go.mod +++ b/go.mod @@ -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 ) diff --git a/go.sum b/go.sum index acb71a9..166ba77 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/multierror_funcs.go b/multierror_funcs.go new file mode 100644 index 0000000..1f3bfa6 --- /dev/null +++ b/multierror_funcs.go @@ -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 +} diff --git a/types.go b/types.go index b696108..9862155 100644 --- a/types.go +++ b/types.go @@ -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 .HasFlag(). - 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"` +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..7e0dd67 --- /dev/null +++ b/utils.go @@ -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 +} diff --git a/wallet_funcs.go b/wallet_funcs.go index 046a12b..7644df0 100644 --- a/wallet_funcs.go +++ b/wallet_funcs.go @@ -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 } diff --git a/walletmanager_funcs.go b/walletmanager_funcs.go index 7099868..91da59c 100644 --- a/walletmanager_funcs.go +++ b/walletmanager_funcs.go @@ -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 }