diff --git a/browser/chromium/chromium.go b/browser/chromium/chromium.go index 5a20023..f2459e0 100644 --- a/browser/chromium/chromium.go +++ b/browser/chromium/chromium.go @@ -8,23 +8,66 @@ import ( "github.com/browserutils/kooky/internal/cookies" ) -func ReadCookies(ctx context.Context, filename string, filters ...kooky.Filter) ([]*kooky.Cookie, error) { - return cookies.SingleRead(cookieStore, filename, filters...).ReadAllCookies(ctx) +// KeyringConfig configures how the cookie store retrieves +// the decryption key from the system keyring. +// +// All fields are optional. When empty, defaults are derived +// from the browser name (e.g. "Chromium" → "Chromium Safe Storage"). +// +// See the exported Keyring* variables for known browser configurations. +type KeyringConfig struct { + Browser string // browser name for display/filtering, e.g. "vivaldi" (defaults to "chromium") + Account string // keychain account, e.g. "Chrome", "Microsoft Edge" + Storage string // keychain entry, e.g. "Chrome Safe Storage" (derived from Account if empty) + Application string // secret service / kwallet app attr, e.g. "chrome" (derived from Account if empty) + PortalAppID string // xdg-desktop-portal app ID, e.g. "org.chromium.Chromium" } -func TraverseCookies(filename string, filters ...kooky.Filter) kooky.CookieSeq { - return cookies.SingleRead(cookieStore, filename, filters...) +// Known keyring configurations for Chromium-based browsers +// that do not have their own package. +var ( + // linux: $XDG_CONFIG_HOME/vivaldi/Default/Cookies + KeyringConfigVivaldiLinux = &KeyringConfig{Browser: `vivaldi`, Account: `Chrome`, PortalAppID: `com.vivaldi.Vivaldi`} + // macOS: ~/Library/Application Support/Vivaldi/Default/Cookies + KeyringConfigVivaldiDarwin = &KeyringConfig{Browser: `vivaldi`, Account: `Vivaldi`} + + // Application may be "arc" or "chrome" depending on version + KeyringConfigArc = &KeyringConfig{Browser: `arc`, Account: `Arc`} +) + +func ReadCookies(ctx context.Context, filename string, keyring *KeyringConfig, filters ...kooky.Filter) ([]*kooky.Cookie, error) { + return cookies.SingleRead(cookieStoreFunc(keyring), filename, filters...).ReadAllCookies(ctx) +} + +func TraverseCookies(filename string, keyring *KeyringConfig, filters ...kooky.Filter) kooky.CookieSeq { + return cookies.SingleRead(cookieStoreFunc(keyring), filename, filters...) } // CookieStore has to be closed with CookieStore.Close() after use. -func CookieStore(filename string, filters ...kooky.Filter) (kooky.CookieStore, error) { - return cookieStore(filename, filters...) +func CookieStore(filename string, keyring *KeyringConfig, filters ...kooky.Filter) (kooky.CookieStore, error) { + return cookieStore(filename, keyring, filters...) +} + +func cookieStoreFunc(keyring *KeyringConfig) func(filename string, filters ...kooky.Filter) (*cookies.CookieJar, error) { + return func(filename string, filters ...kooky.Filter) (*cookies.CookieJar, error) { + return cookieStore(filename, keyring, filters...) + } } -func cookieStore(filename string, filters ...kooky.Filter) (*cookies.CookieJar, error) { +func cookieStore(filename string, keyring *KeyringConfig, filters ...kooky.Filter) (*cookies.CookieJar, error) { s := &chrome.CookieStore{} s.FileNameStr = filename s.BrowserStr = `chromium` + if keyring != nil && len(keyring.Browser) > 0 { + s.BrowserStr = keyring.Browser + } + + if keyring != nil { + s.SetSafeStorage(keyring.Account, keyring.Storage, keyring.Application) + if len(keyring.PortalAppID) > 0 { + s.SetPortalAppID(keyring.PortalAppID) + } + } return cookies.NewCookieJar(s, filters...), nil } diff --git a/internal/chrome/cookiestore.go b/internal/chrome/cookiestore.go index 8c06679..93ad66c 100644 --- a/internal/chrome/cookiestore.go +++ b/internal/chrome/cookiestore.go @@ -16,7 +16,7 @@ type CookieStore struct { KeyringPasswordBytes []byte PasswordBytes []byte DecryptionMethod func(data, password []byte, dbVersion int64) ([]byte, error) - storage safeStorage + keyringConfig safeStorage dbVersion int64 dbFile *os.File } diff --git a/internal/chrome/derivatives.go b/internal/chrome/derivatives.go index 4df601a..e459c3c 100644 --- a/internal/chrome/derivatives.go +++ b/internal/chrome/derivatives.go @@ -9,69 +9,69 @@ import ( type safeStorage struct { account string // e.g. "Chromium", "Chrome", "Microsoft Edge" - name string // e.g. "Chromium Safe Storage" + storage string // e.g. "Chromium Safe Storage" application string // Secret Service application attr, e.g. "chromium", "chrome" portalAppID string // xdg-desktop-portal app ID, e.g. "com.vivaldi.Vivaldi" } -func (s *CookieStore) SetSafeStorage(account, name, application string) { +func (s *CookieStore) SetSafeStorage(account, storage, application string) { if s == nil { return } if len(account) == 0 && len(s.BrowserStr) > 0 { account = cases.Title(language.English, cases.Compact).String(s.BrowserStr) } - if len(name) == 0 { - name = account + ` Safe Storage` + if len(storage) == 0 { + storage = account + ` Safe Storage` } if len(application) == 0 { application = strings.ToLower(account) } - s.storage.account = account - s.storage.name = name - s.storage.application = application + s.keyringConfig.account = account + s.keyringConfig.storage = storage + s.keyringConfig.application = application } -func (s *CookieStore) safeStorageName() string { +func (s *CookieStore) SetPortalAppID(id string) { if s == nil { - return `` + return } - if len(s.storage.name) == 0 { + s.keyringConfig.portalAppID = id +} + +func (s *CookieStore) ensureKeyring() { + if s != nil && len(s.keyringConfig.storage) == 0 { s.SetSafeStorage(``, ``, ``) } - return s.storage.name } func (s *CookieStore) safeStorageAccount() string { if s == nil { return `` } - if len(s.storage.name) == 0 { - s.SetSafeStorage(``, ``, ``) - } - return s.storage.account + s.ensureKeyring() + return s.keyringConfig.account } -func (s *CookieStore) safeStorageApplication() string { +func (s *CookieStore) safeStorageName() string { if s == nil { return `` } - if len(s.storage.name) == 0 { - s.SetSafeStorage(``, ``, ``) - } - return s.storage.application + s.ensureKeyring() + return s.keyringConfig.storage } -func (s *CookieStore) SetPortalAppID(id string) { +func (s *CookieStore) safeStorageApplication() string { if s == nil { - return + return `` } - s.storage.portalAppID = id + s.ensureKeyring() + return s.keyringConfig.application } func (s *CookieStore) portalAppIDValue() string { if s == nil { return `` } - return s.storage.portalAppID + return s.keyringConfig.portalAppID }