diff --git a/blob_funcs.go b/blob_funcs.go new file mode 100644 index 0000000..63ab00b --- /dev/null +++ b/blob_funcs.go @@ -0,0 +1,51 @@ +package gokwallet + +/* + NewBlob returns a Blob. It requires a RecurseOpts + (you can use DefaultRecurseOpts, call NewRecurseOpts, or provide your own RecurseOpts struct). + It also requires a Folder. +*/ +func NewBlob(f *Folder, keyName string, recursion *RecurseOpts) (blob *Blob, err error) { + + if !f.isInit { + err = ErrNotInitialized + return + } + + blob = &Blob{ + DbusObject: f.DbusObject, + Name: keyName, + // Value: "", + Recurse: recursion, + wm: f.wallet.wm, + wallet: f.wallet, + folder: f, + isInit: false, + } + + if blob.Recurse.AllWalletItems || blob.Recurse.Blobs { + if err = blob.Update(); err != nil { + return + } + } + + blob.isInit = true + + return +} + +// Update fetches a Blob's Blob.Value. +func (b *Blob) Update() (err error) { + + // TODO. + + return +} + +// isWalletItem is needed for interface membership. +func (b *Blob) isWalletItem() (isWalletItem bool) { + + isWalletItem = true + + return +} diff --git a/consts.go b/consts.go index a455ab1..7a3bdf2 100644 --- a/consts.go +++ b/consts.go @@ -8,11 +8,11 @@ const ( // KwalletD Dbus enums for WalletItem types. const ( - kwalletdEnumTypeUnknown = iota // UnknownItem - kwalletdEnumTypePassword // Password - kwalletdEnumTypeStream // Blob - kwalletdEnumTypeMap // Map - kwalletdEnumTypeUnused = 0xffff // 65535 + kwalletdEnumTypeUnknown int32 = iota // UnknownItem (0) + kwalletdEnumTypePassword // Password (1) + kwalletdEnumTypeStream // Blob (2) + kwalletdEnumTypeMap // Map (3) + kwalletdEnumTypeUnused = 0xffff // 65535 ) // KWalletD Dbus interfaces. diff --git a/errs.go b/errs.go index fddc0e8..da36db7 100644 --- a/errs.go +++ b/errs.go @@ -17,4 +17,10 @@ var ( 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") + /* + ErrNoCreate is triggered if attempting to create an item (Folder, Password, etc.) but it fails. + */ + ErrNoCreate error = errors.New("failed to create an object") + // ErrNoDisconnect can occur if trying to disconnect a Wallet from a WalletManager/application and a failure occurs. + ErrNoDisconnect error = errors.New("failed to disconnect wallet from application") ) diff --git a/folder_funcs.go b/folder_funcs.go index e6df1a6..77b9cdc 100644 --- a/folder_funcs.go +++ b/folder_funcs.go @@ -1,9 +1,13 @@ package gokwallet +import ( + "github.com/godbus/dbus/v5" +) + /* - NewF returns a Wallet. It requires a RecurseOpts + NewFolder returns a Folder. It requires a RecurseOpts (you can use DefaultRecurseOpts, call NewRecurseOpts, or provide your own RecurseOpts struct). - It also requires a WalletManager and wallet name. + It also requires a Wallet. */ func NewFolder(w *Wallet, name string, recursion *RecurseOpts) (folder *Folder, err error) { @@ -22,15 +26,14 @@ func NewFolder(w *Wallet, name string, recursion *RecurseOpts) (folder *Folder, Recurse: recursion, wm: w.wm, wallet: w, - // handle: 0, - isInit: false, + isInit: false, } - if err = folder.folderCheck(); err != nil { - return - } - - if folder.Recurse.All || folder.Recurse.Wallets { + if folder.Recurse.AllWalletItems || + folder.Recurse.Passwords || + folder.Recurse.Maps || + folder.Recurse.Blobs || + folder.Recurse.UnknownItems { if err = folder.Update(); err != nil { return } @@ -41,16 +44,212 @@ func NewFolder(w *Wallet, name string, recursion *RecurseOpts) (folder *Folder, return } +// Update runs all of the configured Update[type] methods for a Folder, depending on Folder.Recurse configuration. func (f *Folder) Update() (err error) { - // TODO. + var errs []error = make([]error, 0) + + if f.Recurse.AllWalletItems || f.Recurse.Passwords { + if err = f.UpdatePasswords(); err != nil { + errs = append(errs, err) + err = nil + } + } + if f.Recurse.AllWalletItems || f.Recurse.Maps { + if err = f.UpdateMaps(); err != nil { + errs = append(errs, err) + err = nil + } + } + if f.Recurse.AllWalletItems || f.Recurse.Blobs { + if err = f.UpdateBlobs(); err != nil { + errs = append(errs, err) + err = nil + } + } + if f.Recurse.AllWalletItems || f.Recurse.UnknownItems { + if err = f.UpdateUnknowns(); err != nil { + errs = append(errs, err) + err = nil + } + } + + if errs != nil && len(errs) > 0 { + err = NewErrors(errs...) + } return } -func (f *Folder) folderCheck() (err error) { +// UpdatePasswords updates (populates) a Folder's Folder.Passwords. +func (f *Folder) UpdatePasswords() (err error) { - // TODO. + var mapKeys []string + var variant dbus.Variant + var errs []error = make([]error, 0) + + if !f.isInit { + err = ErrNotInitialized + return + } + + if err = f.Dbus.Call( + DbusWMPasswordList, 0, f.wallet.handle, f.Name, f.wallet.wm.AppID, + ).Store(&variant); err != nil { + return + } + + mapKeys = bytemapKeys(variant) + + f.Passwords = make(map[string]*Password, len(mapKeys)) + + for _, k := range mapKeys { + if f.Passwords[k], err = NewPassword(f, k, f.Recurse); err != nil { + errs = append(errs, err) + err = nil + } + } + + if errs != nil && len(errs) > 0 { + err = NewErrors(errs...) + } + + return +} + +// UpdateMaps updates (populates) a Folder's Folder.Maps. +func (f *Folder) UpdateMaps() (err error) { + + var mapKeys []string + var variant dbus.Variant + var errs []error = make([]error, 0) + + if err = f.Dbus.Call( + DbusWMMapList, 0, f.wallet.handle, f.Name, f.wallet.wm.AppID, + ).Store(&variant); err != nil { + return + } + + mapKeys = bytemapKeys(variant) + + f.Maps = make(map[string]*Map, len(mapKeys)) + + for _, k := range mapKeys { + if f.Maps[k], err = NewMap(f, k, f.Recurse); err != nil { + errs = append(errs, err) + err = nil + } + } + + if errs != nil && len(errs) > 0 { + err = NewErrors(errs...) + } + + return +} + +// UpdateBlobs updates (populates) a Folder's Folder.BinaryData. +func (f *Folder) UpdateBlobs() (err error) { + + var mapKeys []string + var isBlob bool + var variant dbus.Variant + var errs []error = make([]error, 0) + + if !f.isInit { + err = ErrNotInitialized + return + } + + if err = f.Dbus.Call( + DbusWMEntriesList, 0, f.wallet.handle, f.Name, f.wallet.wm.AppID, + ).Store(&variant); err != nil { + return + } + + mapKeys = bytemapKeys(variant) + + f.BinaryData = make(map[string]*Blob, len(mapKeys)) + + for _, k := range mapKeys { + if isBlob, err = f.isType(k, kwalletdEnumTypeStream); err != nil { + errs = append(errs, err) + err = nil + continue + } + if !isBlob { + continue + } + + if f.BinaryData[k], err = NewBlob(f, k, f.Recurse); err != nil { + errs = append(errs, err) + err = nil + } + } + + if errs != nil && len(errs) > 0 { + err = NewErrors(errs...) + } + + return +} + +// UpdateUnknowns updates (populates) a Folder's Folder.Unknown. +func (f *Folder) UpdateUnknowns() (err error) { + + var mapKeys []string + var isUnknown bool + var variant dbus.Variant + var errs []error = make([]error, 0) + + if !f.isInit { + err = ErrNotInitialized + return + } + + if err = f.Dbus.Call( + DbusWMEntriesList, 0, f.wallet.handle, f.Name, f.wallet.wm.AppID, + ).Store(&variant); err != nil { + return + } + + mapKeys = bytemapKeys(variant) + + f.Unknown = make(map[string]*UnknownItem, len(mapKeys)) + + for _, k := range mapKeys { + if isUnknown, err = f.isType(k, kwalletdEnumTypeUnknown); err != nil { + errs = append(errs, err) + err = nil + continue + } + if !isUnknown { + continue + } + + if f.Unknown[k], err = NewUnknownItem(f, k, f.Recurse); err != nil { + errs = append(errs, err) + err = nil + } + } + + if errs != nil && len(errs) > 0 { + err = NewErrors(errs...) + } + + return +} + +// isType checks if a certain key keyName is of type typeCheck (via kwalletdEnumType*). +func (f *Folder) isType(keyName string, typeCheck int32) (isOfType bool, err error) { + + var entryType int32 + + if err = f.Dbus.Call( + DbusWMEntryType, 0, f.wallet.handle, f.Name, keyName, f.wallet.wm.AppID, + ).Store(&entryType); err != nil { + return + } return } diff --git a/map_funcs.go b/map_funcs.go new file mode 100644 index 0000000..864b0e4 --- /dev/null +++ b/map_funcs.go @@ -0,0 +1,51 @@ +package gokwallet + +/* + NewMap returns a Map. It requires a RecurseOpts + (you can use DefaultRecurseOpts, call NewRecurseOpts, or provide your own RecurseOpts struct). + It also requires a Folder. +*/ +func NewMap(f *Folder, keyName string, recursion *RecurseOpts) (m *Map, err error) { + + if !f.isInit { + err = ErrNotInitialized + return + } + + m = &Map{ + DbusObject: f.DbusObject, + Name: keyName, + // Value: "", + Recurse: recursion, + wm: f.wallet.wm, + wallet: f.wallet, + folder: f, + isInit: false, + } + + if m.Recurse.AllWalletItems || m.Recurse.Maps { + if err = m.Update(); err != nil { + return + } + } + + m.isInit = true + + return +} + +// Update fetches a Map's Map.Value. +func (m *Map) Update() (err error) { + + // TODO. + + return +} + +// isWalletItem is needed for interface membership. +func (m *Map) isWalletItem() (isWalletItem bool) { + + isWalletItem = true + + return +} diff --git a/password_funcs.go b/password_funcs.go new file mode 100644 index 0000000..2400894 --- /dev/null +++ b/password_funcs.go @@ -0,0 +1,51 @@ +package gokwallet + +/* + NewPassword returns a Password. It requires a RecurseOpts + (you can use DefaultRecurseOpts, call NewRecurseOpts, or provide your own RecurseOpts struct). + It also requires a Folder. +*/ +func NewPassword(f *Folder, keyName string, recursion *RecurseOpts) (password *Password, err error) { + + if !f.isInit { + err = ErrNotInitialized + return + } + + password = &Password{ + DbusObject: f.DbusObject, + Name: keyName, + // Value: "", + Recurse: recursion, + wm: f.wallet.wm, + wallet: f.wallet, + folder: f, + isInit: false, + } + + if password.Recurse.AllWalletItems || password.Recurse.Passwords { + if err = password.Update(); err != nil { + return + } + } + + password.isInit = true + + return +} + +// Update fetches a Password's Password.Value. +func (p *Password) Update() (err error) { + + // TODO. + + return +} + +// isWalletItem is needed for interface membership. +func (p *Password) isWalletItem() (isWalletItem bool) { + + isWalletItem = true + + return +} diff --git a/types.go b/types.go index 9862155..54a3bbf 100644 --- a/types.go +++ b/types.go @@ -121,8 +121,6 @@ type Folder struct { 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 } @@ -142,8 +140,6 @@ type Password struct { 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 } @@ -163,8 +159,6 @@ type Map struct { 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 } @@ -184,8 +178,6 @@ type Blob struct { 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 } @@ -209,8 +201,6 @@ type UnknownItem struct { 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 } diff --git a/unknownitem_funcs.go b/unknownitem_funcs.go new file mode 100644 index 0000000..be1af3e --- /dev/null +++ b/unknownitem_funcs.go @@ -0,0 +1,51 @@ +package gokwallet + +/* + NewUnknownItem returns an UnknownItem. It requires a RecurseOpts + (you can use DefaultRecurseOpts, call NewRecurseOpts, or provide your own RecurseOpts struct). + It also requires a Folder. +*/ +func NewUnknownItem(f *Folder, keyName string, recursion *RecurseOpts) (unknown *UnknownItem, err error) { + + if !f.isInit { + err = ErrNotInitialized + return + } + + unknown = &UnknownItem{ + DbusObject: f.DbusObject, + Name: keyName, + // Value: "", + Recurse: recursion, + wm: f.wallet.wm, + wallet: f.wallet, + folder: f, + isInit: false, + } + + if unknown.Recurse.AllWalletItems || unknown.Recurse.UnknownItems { + if err = unknown.Update(); err != nil { + return + } + } + + unknown.isInit = true + + return +} + +// Update fetches an UnknownItem's UnknownItem.Value. +func (u *UnknownItem) Update() (err error) { + + // TODO. + + return +} + +// isWalletItem is needed for interface membership. +func (u *UnknownItem) isWalletItem() (isWalletItem bool) { + + isWalletItem = true + + return +} diff --git a/utils.go b/utils.go index 7e0dd67..b4fa7b9 100644 --- a/utils.go +++ b/utils.go @@ -1,5 +1,9 @@ package gokwallet +import ( + "github.com/godbus/dbus/v5" +) + /* resultCheck checks the result code from a Dbus call and returns an error if not successful. See also resultPassed. @@ -12,6 +16,8 @@ func resultCheck(result int32) (err error) { err = nil case DbusFailure: err = ErrOperationFailed + default: + err = ErrOperationFailed } return @@ -29,6 +35,26 @@ func resultPassed(result int32) (passed bool) { passed = true case DbusFailure: passed = false + default: + passed = false + } + + return +} + +// bytemapKeys is used to parse out Map names when fetching from Dbus. +func bytemapKeys(variant dbus.Variant) (keyNames []string) { + + var d map[string]dbus.Variant + + d = variant.Value().(map[string]dbus.Variant) + + keyNames = make([]string, len(d)) + + idx := 0 + for k, _ := range d { + keyNames[idx] = k + idx++ } return diff --git a/wallet_funcs.go b/wallet_funcs.go index 7644df0..8e1d0d8 100644 --- a/wallet_funcs.go +++ b/wallet_funcs.go @@ -1,5 +1,9 @@ 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). @@ -27,7 +31,7 @@ func NewWallet(wm *WalletManager, name string, recursion *RecurseOpts) (wallet * return } - if wallet.Recurse.All || wallet.Recurse.Wallets { + if wallet.Recurse.All || wallet.Recurse.Folders { if err = wallet.Update(); err != nil { return } @@ -38,13 +42,73 @@ func NewWallet(wm *WalletManager, name string, recursion *RecurseOpts) (wallet * return } +// Disconnect disconnects this Wallet from its parent WalletManager. +func (w *Wallet) Disconnect() (err error) { + + var ok bool + + if err = w.walletCheck(); err != nil { + return + } + + if err = w.Dbus.Call( + DbusWMDisconnectApp, 0, w.Name, w.wm.AppID, + ).Store(&ok); err != nil { + return + } + + if !ok { + err = ErrNoDisconnect + } + + return +} + +// DisconnectApplication disconnects this Wallet from a specified WalletManager/application (see Wallet.Connections). +func (w *Wallet) DisconnectApplication(appName string) (err error) { + + var ok bool + + if err = w.walletCheck(); err != nil { + return + } + + if err = w.Dbus.Call( + DbusWMDisconnectApp, 0, appName, w.wm.AppID, + ).Store(&ok); err != nil { + return + } + + if !ok { + err = ErrNoDisconnect + } + + return +} + +/* + ChangePassword will change (or set) the password for a Wallet. + Note that this *must* be done via the windowing layer. +*/ +func (w *Wallet) ChangePassword() (err error) { + + var call *dbus.Call + + call = w.Dbus.Call( + DbusWMChangePassword, 0, w.Name, DefaultWindowID, w.wm.AppID, + ) + + err = call.Err + + return +} + // Close closes a Wallet. func (w *Wallet) Close() (err error) { var rslt int32 - if !w.isInit { - err = ErrNotInitialized + if err = w.walletCheck(); err != nil { return } @@ -60,6 +124,80 @@ func (w *Wallet) Close() (err error) { return } +// Connections lists the application names for connections to ("users of") this Wallet. +func (w *Wallet) Connections() (connList []string, err error) { + + if err = w.walletCheck(); err != nil { + return + } + + if err = w.Dbus.Call( + DbusWMUsers, 0, w.Name, + ).Store(&connList); err != nil { + return + } + + return +} + +// CreateFolder creates a new Folder in a Wallet. +func (w *Wallet) CreateFolder(name string) (err error) { + + var ok bool + + if err = w.walletCheck(); err != nil { + return + } + + if err = w.Dbus.Call( + DbusWMCreateFolder, 0, w.handle, name, w.wm.AppID, + ).Store(&ok); err != nil { + return + } + + if !ok { + err = ErrNoCreate + } + + return +} + +// Delete deletes a Wallet. +func (w *Wallet) Delete() (err error) { + + var rslt int32 + + if err = w.walletCheck(); err != nil { + return + } + + if err = w.Dbus.Call( + DbusWMDeleteWallet, 0, w.Name, + ).Store(&rslt); err != nil { + return + } + + err = resultCheck(rslt) + + return +} + +// FolderExists indicates if a Folder exists in a Wallet or not. +func (w *Wallet) FolderExists(folderName string) (exists bool, err error) { + + var notExists bool + + if err = w.Dbus.Call( + DbusWMFolderNotExist, 0, w.Name, folderName, + ).Store(¬Exists); err != nil { + return + } + + exists = !notExists + + 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). @@ -83,8 +221,6 @@ func (w *Wallet) ForceClose() (err error) { err = resultCheck(rslt) return - - return } // IsOpen returns whether a Wallet is open ("unlocked") or not (as well as updates Wallet.IsOpen). @@ -124,6 +260,8 @@ func (w *Wallet) ListFolders() (folderList []string, err error) { */ func (w *Wallet) Open() (err error) { + var handler *int32 + if _, err = w.IsOpen(); err != nil { return } @@ -131,11 +269,18 @@ func (w *Wallet) Open() (err error) { if !w.IsUnlocked { if err = w.Dbus.Call( DbusWMOpen, 0, - ).Store(&w.handle); err != nil { + ).Store(handler); err != nil { return } } + if handler == nil { + err = ErrOperationFailed + return + } else { + w.handle = *handler + } + w.IsUnlocked = true return diff --git a/walletmanager_funcs.go b/walletmanager_funcs.go index 91da59c..1572fff 100644 --- a/walletmanager_funcs.go +++ b/walletmanager_funcs.go @@ -104,7 +104,7 @@ func (wm *WalletManager) CloseWallet(walletName string) (err error) { 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) { +func (wm *WalletManager) ForceCloseWallet(walletName string) (err error) { var rslt int32 @@ -202,7 +202,7 @@ func (wm *WalletManager) NetworkWallet() (w *Wallet, err error) { return } -// WalletNames returns a list of available Wallet names. +// WalletNames returns a list of existing Wallet names. func (wm *WalletManager) WalletNames() (wallets []string, err error) { if err = wm.Dbus.Call(