From 0ace27ce9a9e366dfcc1338f28c00f77513d97b1 Mon Sep 17 00:00:00 2001 From: Roger Date: Sat, 4 Apr 2026 14:11:08 +0800 Subject: [PATCH] feat: wire V2 architecture into CLI entry point (#540) * feat: wire V2 architecture into CLI entry point * fix: warn and exit early when no browsers found --- browser/browser.go | 152 +++++++++++------------- browser/browser_darwin.go | 179 ++++++++++++---------------- browser/browser_linux.go | 127 ++++++++------------ browser/browser_test.go | 216 ++++++++++++++++++++++++++++++++++ browser/browser_windows.go | 174 +++++++++++++-------------- cmd/hack-browser-data/main.go | 32 ++++- 6 files changed, 515 insertions(+), 365 deletions(-) create mode 100644 browser/browser_test.go diff --git a/browser/browser.go b/browser/browser.go index ff1149e..472ad46 100644 --- a/browser/browser.go +++ b/browser/browser.go @@ -1,122 +1,104 @@ package browser import ( + "fmt" "path/filepath" "sort" "strings" "github.com/moond4rk/hackbrowserdata/browser/chromium" "github.com/moond4rk/hackbrowserdata/browser/firefox" - "github.com/moond4rk/hackbrowserdata/browserdata" "github.com/moond4rk/hackbrowserdata/log" - "github.com/moond4rk/hackbrowserdata/utils/fileutil" - "github.com/moond4rk/hackbrowserdata/utils/typeutil" + "github.com/moond4rk/hackbrowserdata/types" ) +// Browser is the interface that both chromium.Browser and firefox.Browser implement. type Browser interface { - // Name is browser's name - Name() string - // BrowsingData returns all browsing data in the browser. - BrowsingData(isFullExport bool) (*browserdata.BrowserData, error) + BrowserName() string + ProfileName() string + Extract(categories []types.Category) (*types.BrowserData, error) } -// PickBrowsers returns a list of browsers that match the name and profile. -func PickBrowsers(name, profile string) ([]Browser, error) { +// PickBrowsers returns browsers matching the given name. +// When name is "all", all known browsers are tried. +// profilePath overrides the default user data directory (only when targeting a specific browser). +func PickBrowsers(name, profilePath string) ([]Browser, error) { + return pickFromConfigs(platformBrowsers(), name, profilePath) +} + +// pickFromConfigs is the testable core of PickBrowsers. +func pickFromConfigs(configs []types.BrowserConfig, name, profilePath string) ([]Browser, error) { + name = strings.ToLower(name) + var browsers []Browser - clist := pickChromium(name, profile) - for _, b := range clist { - if b != nil { - browsers = append(browsers, b) + for _, cfg := range configs { + if name != "all" && cfg.Key != name { + continue } - } - flist := pickFirefox(name, profile) - for _, b := range flist { - if b != nil { - browsers = append(browsers, b) + + if profilePath != "" && name != "all" { + if cfg.Kind == types.KindFirefox { + cfg.UserDataDir = filepath.Dir(filepath.Clean(profilePath)) + } else { + cfg.UserDataDir = profilePath + } } + + bs, err := newBrowsers(cfg) + if err != nil { + log.Errorf("browser %s: %v", cfg.Name, err) + continue + } + if len(bs) == 0 { + log.Debugf("browser %s not found at %s", cfg.Name, cfg.UserDataDir) + continue + } + browsers = append(browsers, bs...) } return browsers, nil } -func pickChromium(name, profile string) []Browser { - var browsers []Browser - name = strings.ToLower(name) - if name == "all" { - for _, v := range chromiumList { - if !fileutil.IsDirExists(filepath.Clean(v.profilePath)) { - log.Warnf("find browser failed, profile folder does not exist, browser %s", v.name) - continue - } - multiChromium, err := chromium.New(v.name, v.storage, v.profilePath, v.dataTypes) - if err != nil { - log.Errorf("new chromium error %v", err) - continue - } - for _, b := range multiChromium { - log.Warnf("find browser success, browser %s", b.Name()) - browsers = append(browsers, b) - } - } - } - if c, ok := chromiumList[name]; ok { - if profile == "" { - profile = c.profilePath - } - if !fileutil.IsDirExists(filepath.Clean(profile)) { - log.Errorf("find browser failed, profile folder does not exist, browser %s", c.name) - } - chromes, err := chromium.New(c.name, c.storage, profile, c.dataTypes) +// newBrowsers dispatches to the correct engine based on BrowserKind. +func newBrowsers(cfg types.BrowserConfig) ([]Browser, error) { + switch cfg.Kind { + case types.KindChromium, types.KindChromiumYandex, types.KindChromiumOpera: + bs, err := chromium.NewBrowsers(cfg) if err != nil { - log.Errorf("new chromium error %v", err) + return nil, err } - for _, chrome := range chromes { - log.Warnf("find browser success, browser %s", chrome.Name()) - browsers = append(browsers, chrome) + browsers := make([]Browser, len(bs)) + for i, b := range bs { + browsers[i] = b } + return browsers, nil + + case types.KindFirefox: + bs, err := firefox.NewBrowsers(cfg) + if err != nil { + return nil, err + } + browsers := make([]Browser, len(bs)) + for i, b := range bs { + browsers[i] = b + } + return browsers, nil + + default: + return nil, fmt.Errorf("unknown browser kind: %d", cfg.Kind) } - return browsers -} - -func pickFirefox(name, profile string) []Browser { - var browsers []Browser - name = strings.ToLower(name) - if name == "all" || name == "firefox" { - for _, v := range firefoxList { - if profile == "" { - profile = v.profilePath - } else { - profile = fileutil.ParentDir(profile) - } - - if !fileutil.IsDirExists(filepath.Clean(profile)) { - log.Warnf("find browser failed, profile folder does not exist, browser %s", v.name) - continue - } - - if multiFirefox, err := firefox.New(profile, v.dataTypes); err == nil { - for _, b := range multiFirefox { - log.Warnf("find browser success, browser %s", b.Name()) - browsers = append(browsers, b) - } - } else { - log.Errorf("new firefox error %v", err) - } - } - - return browsers - } - - return nil } +// ListBrowsers returns sorted keys of all platform browsers. func ListBrowsers() []string { var l []string - l = append(l, typeutil.Keys(chromiumList)...) - l = append(l, typeutil.Keys(firefoxList)...) + for _, cfg := range platformBrowsers() { + l = append(l, cfg.Key) + } sort.Strings(l) return l } +// Names returns a pipe-separated list of browser keys for CLI help text. func Names() string { return strings.Join(ListBrowsers(), "|") } diff --git a/browser/browser_darwin.go b/browser/browser_darwin.go index ac2adf2..fc8ca17 100644 --- a/browser/browser_darwin.go +++ b/browser/browser_darwin.go @@ -6,119 +6,90 @@ import ( "github.com/moond4rk/hackbrowserdata/types" ) -var ( - chromiumList = map[string]struct { - name string - storage string - profilePath string - dataTypes []types.DataType - }{ - "chrome": { - name: chromeName, - storage: chromeStorageName, - profilePath: chromeProfilePath, - dataTypes: types.DefaultChromiumTypes, +func platformBrowsers() []types.BrowserConfig { + return []types.BrowserConfig{ + { + Key: "chrome", + Name: chromeName, + Kind: types.KindChromium, + Storage: "Chrome", + UserDataDir: homeDir + "/Library/Application Support/Google/Chrome", }, - "edge": { - name: edgeName, - storage: edgeStorageName, - profilePath: edgeProfilePath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "edge", + Name: edgeName, + Kind: types.KindChromium, + Storage: "Microsoft Edge", + UserDataDir: homeDir + "/Library/Application Support/Microsoft Edge", }, - "chromium": { - name: chromiumName, - storage: chromiumStorageName, - profilePath: chromiumProfilePath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "chromium", + Name: chromiumName, + Kind: types.KindChromium, + Storage: "Chromium", + UserDataDir: homeDir + "/Library/Application Support/Chromium", }, - "chrome-beta": { - name: chromeBetaName, - storage: chromeBetaStorageName, - profilePath: chromeBetaProfilePath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "chrome-beta", + Name: chromeBetaName, + Kind: types.KindChromium, + Storage: "Chrome", + UserDataDir: homeDir + "/Library/Application Support/Google/Chrome Beta", }, - "opera": { - name: operaName, - profilePath: operaProfilePath, - storage: operaStorageName, - dataTypes: types.DefaultChromiumTypes, + { + Key: "opera", + Name: operaName, + Kind: types.KindChromiumOpera, + Storage: "Opera", + UserDataDir: homeDir + "/Library/Application Support/com.operasoftware.Opera", }, - "opera-gx": { - name: operaGXName, - profilePath: operaGXProfilePath, - storage: operaStorageName, - dataTypes: types.DefaultChromiumTypes, + { + Key: "opera-gx", + Name: operaGXName, + Kind: types.KindChromiumOpera, + Storage: "Opera", + UserDataDir: homeDir + "/Library/Application Support/com.operasoftware.OperaGX", }, - "vivaldi": { - name: vivaldiName, - storage: vivaldiStorageName, - profilePath: vivaldiProfilePath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "vivaldi", + Name: vivaldiName, + Kind: types.KindChromium, + Storage: "Vivaldi", + UserDataDir: homeDir + "/Library/Application Support/Vivaldi", }, - "coccoc": { - name: coccocName, - storage: coccocStorageName, - profilePath: coccocProfilePath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "coccoc", + Name: coccocName, + Kind: types.KindChromium, + Storage: "CocCoc", + UserDataDir: homeDir + "/Library/Application Support/Coccoc", }, - "brave": { - name: braveName, - profilePath: braveProfilePath, - storage: braveStorageName, - dataTypes: types.DefaultChromiumTypes, + { + Key: "brave", + Name: braveName, + Kind: types.KindChromium, + Storage: "Brave", + UserDataDir: homeDir + "/Library/Application Support/BraveSoftware/Brave-Browser", }, - "yandex": { - name: yandexName, - storage: yandexStorageName, - profilePath: yandexProfilePath, - dataTypes: types.DefaultYandexTypes, + { + Key: "yandex", + Name: yandexName, + Kind: types.KindChromiumYandex, + Storage: "Yandex", + UserDataDir: homeDir + "/Library/Application Support/Yandex/YandexBrowser", }, - "arc": { - name: arcName, - profilePath: arcProfilePath, - storage: arcStorageName, - dataTypes: types.DefaultChromiumTypes, + { + Key: "arc", + Name: arcName, + Kind: types.KindChromium, + Storage: "Arc", + UserDataDir: homeDir + "/Library/Application Support/Arc/User Data", + }, + { + Key: "firefox", + Name: firefoxName, + Kind: types.KindFirefox, + UserDataDir: homeDir + "/Library/Application Support/Firefox/Profiles", }, } - firefoxList = map[string]struct { - name string - storage string - profilePath string - dataTypes []types.DataType - }{ - "firefox": { - name: firefoxName, - profilePath: firefoxProfilePath, - dataTypes: types.DefaultFirefoxTypes, - }, - } -) - -var ( - chromeProfilePath = homeDir + "/Library/Application Support/Google/Chrome/Default/" - chromeBetaProfilePath = homeDir + "/Library/Application Support/Google/Chrome Beta/Default/" - chromiumProfilePath = homeDir + "/Library/Application Support/Chromium/Default/" - edgeProfilePath = homeDir + "/Library/Application Support/Microsoft Edge/Default/" - braveProfilePath = homeDir + "/Library/Application Support/BraveSoftware/Brave-Browser/Default/" - operaProfilePath = homeDir + "/Library/Application Support/com.operasoftware.Opera/Default/" - operaGXProfilePath = homeDir + "/Library/Application Support/com.operasoftware.OperaGX/Default/" - vivaldiProfilePath = homeDir + "/Library/Application Support/Vivaldi/Default/" - coccocProfilePath = homeDir + "/Library/Application Support/Coccoc/Default/" - yandexProfilePath = homeDir + "/Library/Application Support/Yandex/YandexBrowser/Default/" - arcProfilePath = homeDir + "/Library/Application Support/Arc/User Data/Default" - - firefoxProfilePath = homeDir + "/Library/Application Support/Firefox/Profiles/" -) - -const ( - chromeStorageName = "Chrome" - chromeBetaStorageName = "Chrome" - chromiumStorageName = "Chromium" - edgeStorageName = "Microsoft Edge" - braveStorageName = "Brave" - operaStorageName = "Opera" - vivaldiStorageName = "Vivaldi" - coccocStorageName = "CocCoc" - yandexStorageName = "Yandex" - arcStorageName = "Arc" -) +} diff --git a/browser/browser_linux.go b/browser/browser_linux.go index 0b8df05..ffb25df 100644 --- a/browser/browser_linux.go +++ b/browser/browser_linux.go @@ -6,87 +6,62 @@ import ( "github.com/moond4rk/hackbrowserdata/types" ) -var ( - chromiumList = map[string]struct { - name string - storage string - profilePath string - dataTypes []types.DataType - }{ - "chrome": { - name: chromeName, - storage: chromeStorageName, - profilePath: chromeProfilePath, - dataTypes: types.DefaultChromiumTypes, +func platformBrowsers() []types.BrowserConfig { + return []types.BrowserConfig{ + { + Key: "chrome", + Name: chromeName, + Kind: types.KindChromium, + Storage: "Chrome Safe Storage", + UserDataDir: homeDir + "/.config/google-chrome", }, - "edge": { - name: edgeName, - storage: edgeStorageName, - profilePath: edgeProfilePath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "edge", + Name: edgeName, + Kind: types.KindChromium, + Storage: "Chromium Safe Storage", + UserDataDir: homeDir + "/.config/microsoft-edge", }, - "chromium": { - name: chromiumName, - storage: chromiumStorageName, - profilePath: chromiumProfilePath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "chromium", + Name: chromiumName, + Kind: types.KindChromium, + Storage: "Chromium Safe Storage", + UserDataDir: homeDir + "/.config/chromium", }, - "chrome-beta": { - name: chromeBetaName, - storage: chromeBetaStorageName, - profilePath: chromeBetaProfilePath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "chrome-beta", + Name: chromeBetaName, + Kind: types.KindChromium, + Storage: "Chrome Safe Storage", + UserDataDir: homeDir + "/.config/google-chrome-beta", }, - "opera": { - name: operaName, - profilePath: operaProfilePath, - storage: operaStorageName, - dataTypes: types.DefaultChromiumTypes, + { + Key: "opera", + Name: operaName, + Kind: types.KindChromiumOpera, + Storage: "Chromium Safe Storage", + UserDataDir: homeDir + "/.config/opera", }, - "vivaldi": { - name: vivaldiName, - storage: vivaldiStorageName, - profilePath: vivaldiProfilePath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "vivaldi", + Name: vivaldiName, + Kind: types.KindChromium, + Storage: "Chrome Safe Storage", + UserDataDir: homeDir + "/.config/vivaldi", }, - "brave": { - name: braveName, - profilePath: braveProfilePath, - storage: braveStorageName, - dataTypes: types.DefaultChromiumTypes, + { + Key: "brave", + Name: braveName, + Kind: types.KindChromium, + Storage: "Brave Safe Storage", + UserDataDir: homeDir + "/.config/BraveSoftware/Brave-Browser", + }, + { + Key: "firefox", + Name: firefoxName, + Kind: types.KindFirefox, + UserDataDir: homeDir + "/.mozilla/firefox", }, } - firefoxList = map[string]struct { - name string - storage string - profilePath string - dataTypes []types.DataType - }{ - "firefox": { - name: firefoxName, - profilePath: firefoxProfilePath, - dataTypes: types.DefaultFirefoxTypes, - }, - } -) - -var ( - firefoxProfilePath = homeDir + "/.mozilla/firefox/" - chromeProfilePath = homeDir + "/.config/google-chrome/Default/" - chromiumProfilePath = homeDir + "/.config/chromium/Default/" - edgeProfilePath = homeDir + "/.config/microsoft-edge/Default/" - braveProfilePath = homeDir + "/.config/BraveSoftware/Brave-Browser/Default/" - chromeBetaProfilePath = homeDir + "/.config/google-chrome-beta/Default/" - operaProfilePath = homeDir + "/.config/opera/Default/" - vivaldiProfilePath = homeDir + "/.config/vivaldi/Default/" -) - -const ( - chromeStorageName = "Chrome Safe Storage" - chromiumStorageName = "Chromium Safe Storage" - edgeStorageName = "Chromium Safe Storage" - braveStorageName = "Brave Safe Storage" - chromeBetaStorageName = "Chrome Safe Storage" - operaStorageName = "Chromium Safe Storage" - vivaldiStorageName = "Chrome Safe Storage" -) +} diff --git a/browser/browser_test.go b/browser/browser_test.go new file mode 100644 index 0000000..27500cd --- /dev/null +++ b/browser/browser_test.go @@ -0,0 +1,216 @@ +package browser + +import ( + "os" + "path/filepath" + "sort" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/moond4rk/hackbrowserdata/types" +) + +func mkFile(t *testing.T, parts ...string) { + t.Helper() + path := filepath.Join(parts...) + require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755)) + require.NoError(t, os.WriteFile(path, []byte("test"), 0o644)) +} + +func TestListBrowsers(t *testing.T) { + list := ListBrowsers() + assert.True(t, len(list) > 0) + assert.True(t, sort.StringsAreSorted(list)) +} + +func TestPickFromConfigs_NameFilter(t *testing.T) { + dir := t.TempDir() + mkFile(t, dir, "Default", "Login Data") + mkFile(t, dir, "Default", "History") + + configs := []types.BrowserConfig{ + {Key: "chrome", Name: "Chrome", Kind: types.KindChromium, UserDataDir: dir}, + {Key: "edge", Name: "Edge", Kind: types.KindChromium, UserDataDir: dir}, + } + + tests := []struct { + name string + pickName string + wantNames []string + wantProfiles []string + }{ + { + name: "exact match", + pickName: "chrome", + wantNames: []string{"Chrome"}, + wantProfiles: []string{"Default"}, + }, + { + name: "case insensitive", + pickName: "Chrome", + wantNames: []string{"Chrome"}, + wantProfiles: []string{"Default"}, + }, + { + name: "all returns both", + pickName: "all", + wantNames: []string{"Chrome", "Edge"}, + wantProfiles: []string{"Default", "Default"}, + }, + { + name: "unknown returns empty", + pickName: "safari", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + browsers, err := pickFromConfigs(configs, tt.pickName, "") + require.NoError(t, err) + assertBrowsers(t, browsers, tt.wantNames, tt.wantProfiles) + }) + } +} + +func TestPickFromConfigs_BrowserKind(t *testing.T) { + chromeDir := t.TempDir() + mkFile(t, chromeDir, "Default", "Login Data") + mkFile(t, chromeDir, "Default", "History") + mkFile(t, chromeDir, "Profile 1", "Login Data") + mkFile(t, chromeDir, "Profile 1", "History") + + firefoxDir := t.TempDir() + mkFile(t, firefoxDir, "abc123.default-release", "logins.json") + mkFile(t, firefoxDir, "abc123.default-release", "places.sqlite") + + yandexDir := t.TempDir() + mkFile(t, yandexDir, "Default", "Ya Passman Data") + mkFile(t, yandexDir, "Default", "History") + + tests := []struct { + name string + configs []types.BrowserConfig + wantNames []string + wantProfiles []string + }{ + { + name: "chromium multi-profile", + configs: []types.BrowserConfig{ + {Key: "chrome", Name: "Chrome", Kind: types.KindChromium, UserDataDir: chromeDir}, + }, + wantNames: []string{"Chrome", "Chrome"}, + wantProfiles: []string{"Default", "Profile 1"}, + }, + { + name: "firefox random dir", + configs: []types.BrowserConfig{ + {Key: "firefox", Name: "Firefox", Kind: types.KindFirefox, UserDataDir: firefoxDir}, + }, + wantNames: []string{"Firefox"}, + wantProfiles: []string{"abc123.default-release"}, + }, + { + name: "yandex variant", + configs: []types.BrowserConfig{ + {Key: "yandex", Name: "Yandex", Kind: types.KindChromiumYandex, UserDataDir: yandexDir}, + }, + wantNames: []string{"Yandex"}, + wantProfiles: []string{"Default"}, + }, + { + name: "nonexistent dir", + configs: []types.BrowserConfig{ + {Key: "chrome", Name: "Chrome", Kind: types.KindChromium, UserDataDir: "/nonexistent"}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + browsers, err := pickFromConfigs(tt.configs, "all", "") + require.NoError(t, err) + assertBrowsers(t, browsers, tt.wantNames, tt.wantProfiles) + }) + } +} + +func TestPickFromConfigs_ProfilePath(t *testing.T) { + chromeDir := t.TempDir() + mkFile(t, chromeDir, "Default", "Login Data") + mkFile(t, chromeDir, "Default", "History") + mkFile(t, chromeDir, "Profile 1", "Login Data") + mkFile(t, chromeDir, "Profile 1", "History") + + firefoxDir := t.TempDir() + mkFile(t, firefoxDir, "abc123.default-release", "logins.json") + mkFile(t, firefoxDir, "abc123.default-release", "places.sqlite") + + tests := []struct { + name string + configs []types.BrowserConfig + pickName string + profilePath string + wantNames []string + wantProfiles []string + }{ + { + name: "chromium uses path directly", + configs: []types.BrowserConfig{ + {Key: "chrome", Name: "Chrome", Kind: types.KindChromium, UserDataDir: "/wrong"}, + }, + pickName: "chrome", + profilePath: filepath.Join(chromeDir, "Default"), + wantNames: []string{"Chrome"}, + wantProfiles: []string{"Default"}, + }, + { + name: "firefox uses parent dir", + configs: []types.BrowserConfig{ + {Key: "firefox", Name: "Firefox", Kind: types.KindFirefox, UserDataDir: "/wrong"}, + }, + pickName: "firefox", + profilePath: filepath.Join(firefoxDir, "abc123.default-release"), + wantNames: []string{"Firefox"}, + wantProfiles: []string{"abc123.default-release"}, + }, + { + name: "ignored when name is all", + configs: []types.BrowserConfig{ + {Key: "chrome", Name: "Chrome", Kind: types.KindChromium, UserDataDir: chromeDir}, + }, + pickName: "all", + profilePath: "/some/override", + wantNames: []string{"Chrome", "Chrome"}, + wantProfiles: []string{"Default", "Profile 1"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + browsers, err := pickFromConfigs(tt.configs, tt.pickName, tt.profilePath) + require.NoError(t, err) + assertBrowsers(t, browsers, tt.wantNames, tt.wantProfiles) + }) + } +} + +// assertBrowsers verifies browser names and profiles match expectations (order-independent). +func assertBrowsers(t *testing.T, browsers []Browser, wantNames, wantProfiles []string) { + t.Helper() + assert.Len(t, browsers, len(wantNames)) + + var gotNames, gotProfiles []string + for _, b := range browsers { + gotNames = append(gotNames, b.BrowserName()) + gotProfiles = append(gotProfiles, b.ProfileName()) + } + sort.Strings(gotNames) + sort.Strings(gotProfiles) + sort.Strings(wantNames) + sort.Strings(wantProfiles) + + assert.Equal(t, wantNames, gotNames) + assert.Equal(t, wantProfiles, gotProfiles) +} diff --git a/browser/browser_windows.go b/browser/browser_windows.go index 6c0d0a4..d468bde 100644 --- a/browser/browser_windows.go +++ b/browser/browser_windows.go @@ -6,113 +6,97 @@ import ( "github.com/moond4rk/hackbrowserdata/types" ) -var ( - chromiumList = map[string]struct { - name string - profilePath string - storage string - dataTypes []types.DataType - }{ - "chrome": { - name: chromeName, - profilePath: chromeUserDataPath, - dataTypes: types.DefaultChromiumTypes, +func platformBrowsers() []types.BrowserConfig { + return []types.BrowserConfig{ + { + Key: "chrome", + Name: chromeName, + Kind: types.KindChromium, + UserDataDir: homeDir + "/AppData/Local/Google/Chrome/User Data", }, - "edge": { - name: edgeName, - profilePath: edgeProfilePath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "edge", + Name: edgeName, + Kind: types.KindChromium, + UserDataDir: homeDir + "/AppData/Local/Microsoft/Edge/User Data", }, - "chromium": { - name: chromiumName, - profilePath: chromiumUserDataPath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "chromium", + Name: chromiumName, + Kind: types.KindChromium, + UserDataDir: homeDir + "/AppData/Local/Chromium/User Data", }, - "chrome-beta": { - name: chromeBetaName, - profilePath: chromeBetaUserDataPath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "chrome-beta", + Name: chromeBetaName, + Kind: types.KindChromium, + UserDataDir: homeDir + "/AppData/Local/Google/Chrome Beta/User Data", }, - "opera": { - name: operaName, - profilePath: operaProfilePath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "opera", + Name: operaName, + Kind: types.KindChromiumOpera, + UserDataDir: homeDir + "/AppData/Roaming/Opera Software/Opera Stable", }, - "opera-gx": { - name: operaGXName, - profilePath: operaGXProfilePath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "opera-gx", + Name: operaGXName, + Kind: types.KindChromiumOpera, + UserDataDir: homeDir + "/AppData/Roaming/Opera Software/Opera GX Stable", }, - "vivaldi": { - name: vivaldiName, - profilePath: vivaldiProfilePath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "vivaldi", + Name: vivaldiName, + Kind: types.KindChromium, + UserDataDir: homeDir + "/AppData/Local/Vivaldi/User Data", }, - "coccoc": { - name: coccocName, - profilePath: coccocProfilePath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "coccoc", + Name: coccocName, + Kind: types.KindChromium, + UserDataDir: homeDir + "/AppData/Local/CocCoc/Browser/User Data", }, - "brave": { - name: braveName, - profilePath: braveProfilePath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "brave", + Name: braveName, + Kind: types.KindChromium, + UserDataDir: homeDir + "/AppData/Local/BraveSoftware/Brave-Browser/User Data", }, - "yandex": { - name: yandexName, - profilePath: yandexProfilePath, - dataTypes: types.DefaultYandexTypes, + { + Key: "yandex", + Name: yandexName, + Kind: types.KindChromiumYandex, + UserDataDir: homeDir + "/AppData/Local/Yandex/YandexBrowser/User Data", }, - "360": { - name: speed360Name, - profilePath: speed360ProfilePath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "360", + Name: speed360Name, + Kind: types.KindChromium, + UserDataDir: homeDir + "/AppData/Local/360chrome/Chrome/User Data", }, - "qq": { - name: qqBrowserName, - profilePath: qqBrowserProfilePath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "qq", + Name: qqBrowserName, + Kind: types.KindChromium, + UserDataDir: homeDir + "/AppData/Local/Tencent/QQBrowser/User Data", }, - "dc": { - name: dcBrowserName, - profilePath: dcBrowserProfilePath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "dc", + Name: dcBrowserName, + Kind: types.KindChromium, + UserDataDir: homeDir + "/AppData/Local/DCBrowser/User Data", }, - "sogou": { - name: sogouName, - profilePath: sogouProfilePath, - dataTypes: types.DefaultChromiumTypes, + { + Key: "sogou", + Name: sogouName, + Kind: types.KindChromium, + UserDataDir: homeDir + "/AppData/Roaming/SogouExplorer/Webkit", + }, + { + Key: "firefox", + Name: firefoxName, + Kind: types.KindFirefox, + UserDataDir: homeDir + "/AppData/Roaming/Mozilla/Firefox/Profiles", }, } - firefoxList = map[string]struct { - name string - storage string - profilePath string - dataTypes []types.DataType - }{ - "firefox": { - name: firefoxName, - profilePath: firefoxProfilePath, - dataTypes: types.DefaultFirefoxTypes, - }, - } -) - -var ( - chromeUserDataPath = homeDir + "/AppData/Local/Google/Chrome/User Data/Default/" - chromeBetaUserDataPath = homeDir + "/AppData/Local/Google/Chrome Beta/User Data/Default/" - chromiumUserDataPath = homeDir + "/AppData/Local/Chromium/User Data/Default/" - edgeProfilePath = homeDir + "/AppData/Local/Microsoft/Edge/User Data/Default/" - braveProfilePath = homeDir + "/AppData/Local/BraveSoftware/Brave-Browser/User Data/Default/" - speed360ProfilePath = homeDir + "/AppData/Local/360chrome/Chrome/User Data/Default/" - qqBrowserProfilePath = homeDir + "/AppData/Local/Tencent/QQBrowser/User Data/Default/" - operaProfilePath = homeDir + "/AppData/Roaming/Opera Software/Opera Stable/" - operaGXProfilePath = homeDir + "/AppData/Roaming/Opera Software/Opera GX Stable/" - vivaldiProfilePath = homeDir + "/AppData/Local/Vivaldi/User Data/Default/" - coccocProfilePath = homeDir + "/AppData/Local/CocCoc/Browser/User Data/Default/" - yandexProfilePath = homeDir + "/AppData/Local/Yandex/YandexBrowser/User Data/Default/" - dcBrowserProfilePath = homeDir + "/AppData/Local/DCBrowser/User Data/Default/" - sogouProfilePath = homeDir + "/AppData/Roaming/SogouExplorer/Webkit/Default/" - - firefoxProfilePath = homeDir + "/AppData/Roaming/Mozilla/Firefox/Profiles/" -) +} diff --git a/cmd/hack-browser-data/main.go b/cmd/hack-browser-data/main.go index 633e164..e9121b6 100644 --- a/cmd/hack-browser-data/main.go +++ b/cmd/hack-browser-data/main.go @@ -7,6 +7,8 @@ import ( "github.com/moond4rk/hackbrowserdata/browser" "github.com/moond4rk/hackbrowserdata/log" + "github.com/moond4rk/hackbrowserdata/output" + "github.com/moond4rk/hackbrowserdata/types" "github.com/moond4rk/hackbrowserdata/utils/fileutil" ) @@ -44,19 +46,39 @@ func Execute() { if verbose { log.SetVerbose() } + browsers, err := browser.PickBrowsers(browserName, profilePath) if err != nil { - log.Errorf("pick browsers %v", err) + log.Errorf("pick browsers: %v", err) + return err + } + if len(browsers) == 0 { + log.Warnf("no browsers found") + return nil + } + + categories := types.AllCategories + if !isFullExport { + categories = types.NonSensitiveCategories() + } + + w, err := output.NewWriter(outputDir, outputFormat) + if err != nil { + log.Errorf("create output writer: %v", err) return err } for _, b := range browsers { - data, err := b.BrowsingData(isFullExport) + data, err := b.Extract(categories) if err != nil { - log.Errorf("get browsing data error %v", err) - continue + log.Errorf("extract %s/%s: %v", b.BrowserName(), b.ProfileName(), err) } - data.Output(outputDir, b.Name(), outputFormat) + w.Add(b.BrowserName(), b.ProfileName(), data) + } + + if err := w.Write(); err != nil { + log.Errorf("write output: %v", err) + return err } if compress {