diff --git a/browsers/firefox/cli_commands.go b/browsers/firefox/cli_commands.go index c0950d6..335a0c6 100644 --- a/browsers/firefox/cli_commands.go +++ b/browsers/firefox/cli_commands.go @@ -29,48 +29,49 @@ import ( "git.blob42.xyz/gosuki/gosuki/cmd" "git.blob42.xyz/gosuki/gosuki/internal/logging" "git.blob42.xyz/gosuki/gosuki/pkg/browsers/mozilla" - "git.blob42.xyz/gosuki/gosuki/internal/utils" "github.com/urfave/cli/v2" ) var fflog = logging.GetLogger("FF") -var ffUnlockVFSCmd = cli.Command{ - Name: "unlock", - Aliases: []string{"u"}, - Action: ffUnlockVFS, -} +var ( + ffUnlockVFSCmd = cli.Command{ + Name: "unlock", + Aliases: []string{"u"}, + Action: ffUnlockVFS, + } -var ffCheckVFSCmd = cli.Command{ - Name: "check", - Aliases: []string{"c"}, - Action: ffCheckVFS, -} + ffCheckVFSCmd = cli.Command{ + Name: "check", + Aliases: []string{"c"}, + Action: ffCheckVFS, + } -var ffVFSCommands = cli.Command{ - Name: "vfs", - Usage: "VFS locking commands", - Subcommands: []*cli.Command{ - &ffUnlockVFSCmd, + ffVFSCommands = cli.Command{ + Name: "vfs", + Usage: "VFS locking commands", + Subcommands: []*cli.Command{ + &ffUnlockVFSCmd, &ffCheckVFSCmd, }, } -var ffListProfilesCmd = cli.Command{ - Name: "list", - Aliases: []string{"l"}, - Action: ffListProfiles, -} + ffListProfilesCmd = cli.Command{ + Name: "list", + Aliases: []string{"l"}, + Action: ffListProfiles, + } -var ffProfilesCmds = cli.Command{ - Name: "profiles", - Aliases: []string{"p"}, - Usage: "Profiles commands", - Subcommands: []*cli.Command{ - &ffListProfilesCmd, - }, -} + ffProfilesCmds = cli.Command{ + Name: "profiles", + Aliases: []string{"p"}, + Usage: "Profiles commands", + Subcommands: []*cli.Command{ + &ffListProfilesCmd, + }, + } +) var FirefoxCmds = &cli.Command{ Name: "firefox", @@ -90,14 +91,21 @@ func init() { //TODO: #54 define interface for modules to handle and list profiles //FIX: Remove since profile listing is implemented at the main module level func ffListProfiles(_ *cli.Context) error { - profs, err := FirefoxProfileManager.GetProfiles() - if err != nil { - return err + flavours := FirefoxProfileManager.ListFlavours() + for _, f := range flavours { + profs, err := FirefoxProfileManager.GetProfiles(f.Name) + if err != nil { + return err + } + for _, p := range profs { + if fullPath, err := p.AbsolutePath(); err != nil { + return err + } else { + fmt.Printf("%-10s \t %s\n", p.Name, fullPath) + } + } } - for _, p := range profs { - fmt.Printf("%-10s \t %s\n", p.Name, utils.ExpandPath(FirefoxProfileManager.ConfigDir, p.Path)) - } return nil } diff --git a/browsers/firefox/config.go b/browsers/firefox/config.go index 37c6db9..19e1cb8 100644 --- a/browsers/firefox/config.go +++ b/browsers/firefox/config.go @@ -24,27 +24,26 @@ package firefox import ( "fmt" - "os" - "path/filepath" "git.blob42.xyz/gosuki/gosuki/internal/config" "git.blob42.xyz/gosuki/gosuki/internal/database" - "git.blob42.xyz/gosuki/gosuki/internal/utils" "git.blob42.xyz/gosuki/gosuki/pkg/browsers/mozilla" "git.blob42.xyz/gosuki/gosuki/pkg/modules" "git.blob42.xyz/gosuki/gosuki/pkg/parsing" + "git.blob42.xyz/gosuki/gosuki/pkg/profiles" "git.blob42.xyz/gosuki/gosuki/pkg/tree" "github.com/fatih/structs" "github.com/mitchellh/mapstructure" - "github.com/urfave/cli/v2" ) const ( - BrowserName = "firefox" //TODO: auto detect firefox base dir based on OS and installed flavors - FirefoxBaseDir = "$HOME/.mozilla/firefox" + // FirefoxBaseDir = "$HOME/.mozilla/firefox" DefaultProfile = "default" + + // Default flavour to use + BrowserName = mozilla.FirefoxFlavour ) var ( @@ -52,15 +51,14 @@ var ( // firefox global config state. FFConfig = NewFirefoxConfig() - ffProfileLoader = &mozilla.INIProfileLoader{ + ffProfileLoader = &profiles.INIProfileLoader{ //BasePath to be set at runtime in init ProfilesFile: mozilla.ProfilesFile, } - FirefoxProfileManager = &mozilla.MozProfileManager{ - BrowserName: BrowserName, - PathGetter: ffProfileLoader, - } + FirefoxProfileManager = mozilla.NewMozProfileManager( + ffProfileLoader, + ) ) // FirefoxConfig implements the Configurator interface @@ -86,27 +84,34 @@ type FirefoxConfig struct { } func setBookmarkDir(fc *FirefoxConfig) { - pm := FirefoxProfileManager + var err error + // pm := FirefoxProfileManager + // expand environment variables in path - pm.ConfigDir = filepath.Join(os.ExpandEnv(FirefoxBaseDir)) + // pm.ConfigDir = filepath.Join(os.ExpandEnv(FirefoxBaseDir)) // Check if base folder exists - exists, err := utils.CheckDirExists(pm.ConfigDir) - if !exists { - log.Criticalf("the base firefox folder <%s> does not exist", pm.ConfigDir) - } + // handled by profiles.Detect() + // exists, err := utils.CheckDirExists(pm.ConfigDir) + // if !exists { + // log.Criticalf("the base firefox folder <%s> does not exist", pm.ConfigDir) + // } - if err != nil { + // if err != nil { + // log.Fatal(err) + // return + // } + + // Set in NewMozProfileManager + // ffProfileLoader.BasePath = pm.ConfigDir + + // load the default profile from the one defined in the config + var profile *profiles.Profile + if profile, err = FirefoxProfileManager.GetProfileByName(BrowserName, fc.Profile); err != nil { log.Fatal(err) - return } - // The next part prepares the default profile using the profile manager - ffProfileLoader.BasePath = pm.ConfigDir - - // use default profile - // WIP: calling multiple profiles uses the following logic - bookmarkDir, err := FirefoxProfileManager.GetProfilePath(fc.Profile) + bookmarkDir, err := profile.AbsolutePath() if err != nil { log.Fatal(err) } @@ -185,51 +190,6 @@ func (fc *FirefoxConfig) MapFrom(src interface{}) error { return mapstructure.Decode(src, fc) } -//REFACT: -// Hook called when the config is ready -func initFirefoxConfig(c *cli.Context) error { - log.Debugf(" initializing config") - - // The following code is executed before the cli context is ready - // so we cannot use cli flags here - - pm := FirefoxProfileManager - - // expand environment variables in path - pm.ConfigDir = filepath.Join(os.ExpandEnv(FirefoxBaseDir)) - - // Check if base folder exists - exists, err := utils.CheckDirExists(pm.ConfigDir) - if !exists { - log.Criticalf("the base firefox folder <%s> does not exist", pm.ConfigDir) - } - - if err != nil { - log.Fatal(err) - return err - } - - // The next part prepares the default profile using the profile manager - ffProfileLoader.BasePath = pm.ConfigDir - - - - // use default profile - // WIP: calling multiple profiles uses the following logic - bookmarkDir, err := FirefoxProfileManager.GetProfilePath(FFConfig.Profile) - if err != nil { - log.Fatal(err) - } - -// update bookmark dir in firefox config - //TEST: verify that bookmark dir is set before browser is started - FFConfig.BkDir = bookmarkDir - log.Debugf("Using profile %s", bookmarkDir) - return nil -} - func init() { config.RegisterConfigurator(BrowserName, FFConfig) - - // config.RegisterConfReadyHooks(initFirefoxConfig) } diff --git a/browsers/firefox/firefox.go b/browsers/firefox/firefox.go index 7f96efd..2ca9c58 100644 --- a/browsers/firefox/firefox.go +++ b/browsers/firefox/firefox.go @@ -90,6 +90,11 @@ type Firefox struct { } +func (firefox *Firefox) ListFlavours() []profiles.BrowserFlavour { + return FirefoxProfileManager.ListFlavours() +} + + // func (ff *Firefox) updateModifiedFolders(since timestamp) ([]*MozFolder, error) { // // Get list of modified folders @@ -265,16 +270,8 @@ func (f Firefox) fullId() string { } // Implements the profiles.ProfileManager interface -func (f *Firefox) GetProfiles() ([]*profiles.Profile, error) { - return FirefoxProfileManager.GetProfiles() -} - -func (f *Firefox) GetDefaultProfile() (*profiles.Profile, error) { - return FirefoxProfileManager.GetDefaultProfile() -} - -func (f *Firefox) GetProfilePath(p profiles.Profile) string { - return filepath.Join(FirefoxProfileManager.ConfigDir, p.Path) +func (f *Firefox) GetProfiles(flavour string) ([]*profiles.Profile, error) { + return FirefoxProfileManager.GetProfiles(flavour) } // If should watch all profiles @@ -288,13 +285,12 @@ func (f *Firefox) UseProfile(p profiles.Profile) error { f.Profile = p.Name // setup the bookmark dir - bookmarkDir, err := FirefoxProfileManager.GetProfilePath(p.Name) - if err != nil { + if bookmarkDir, err := p.AbsolutePath(); err != nil { return err + } else { + f.BkDir = bookmarkDir + return nil } - - f.BkDir = bookmarkDir - return nil } func (f *Firefox) cloneConfig() { @@ -312,19 +308,16 @@ func (f *Firefox) Init(ctx *modules.Context, p *profiles.Profile) error { f.Profile = p.Name - bookmarkDir, err := FirefoxProfileManager.GetProfilePath(p.Name) - if err != nil { + if bookmarkDir, err := p.AbsolutePath(); err != nil { return err + } else { + f.BkDir = bookmarkDir } - f.BkDir = bookmarkDir return f.init(ctx) } // TEST: -// TODO: implement watching of multiple profiles. -// NOTE: should be done at core gosuki level where multiple instances are spawned for each profile -// // Implements browser.Initializer interface func (f *Firefox) init(ctx *modules.Context) error { log.Infof("initializing <%s>", f.fullId()) @@ -356,7 +349,6 @@ func (f *Firefox) init(ctx *modules.Context) error { /* *Run reducer to avoid duplicate jobs when a batch of events is received */ - // TODO!: make a new copy of places for every new event change // Add a reducer to the watcher log.Debugf("Running reducer on path <%s>", watchedPath) @@ -748,6 +740,7 @@ func init() { var _ modules.BrowserModule = (*Firefox)(nil) var _ modules.ProfileInitializer = (*Firefox)(nil) +var _ profiles.ProfileManager = (*Firefox)(nil) var _ modules.Loader = (*Firefox)(nil) var _ modules.Shutdowner = (*Firefox)(nil) var _ watch.WatchRunner = (*Firefox)(nil) diff --git a/cmd/gosuki/daemon.go b/cmd/gosuki/daemon.go index 201bda6..23decab 100644 --- a/cmd/gosuki/daemon.go +++ b/cmd/gosuki/daemon.go @@ -156,18 +156,21 @@ func startDaemon(c *cli.Context) error { bpm, ok := browser.(profiles.ProfileManager) if ok { if bpm.WatchAllProfiles() { - profs, err := bpm.GetProfiles() - if err != nil { - log.Critical("could not get profiles") - continue - } - for _, p := range profs { - log.Debugf("profile: <%s>", p.Name) - err = runModule(manager, c, browserMod, p) + falvours := bpm.ListFlavours() + for _, f := range falvours { + profs, err := bpm.GetProfiles(f.Name) if err != nil { - log.Critical(err) + log.Critical("could not get profiles") continue } + for _, p := range profs { + log.Debugf("profile: <%s>", p.Name) + err = runModule(manager, c, browserMod, p) + if err != nil { + log.Critical(err) + continue + } + } } } else { log.Debugf("profile manager <%s> not watching all profiles", diff --git a/cmd/gosuki/main.go b/cmd/gosuki/main.go index 868666f..91668fa 100644 --- a/cmd/gosuki/main.go +++ b/cmd/gosuki/main.go @@ -38,7 +38,7 @@ import ( _ "git.blob42.xyz/gosuki/gosuki/browsers/firefox" // Load chrome browser module - _ "git.blob42.xyz/gosuki/gosuki/browsers/chrome" + // _ "git.blob42.xyz/gosuki/gosuki/browsers/chrome" ) var log = logging.GetLogger("") @@ -95,6 +95,9 @@ func main() { // Execute config hooks //TODO: better doc for what are Conf hooks ??? + // modules can run custom code before the CLI is ready. + // For example read the environment and set configuration options to be + // used by the module instances. config.RunConfHooks(c) initConfig() diff --git a/cmd/mod_commands.go b/cmd/mod_commands.go index 707f93a..08ea244 100644 --- a/cmd/mod_commands.go +++ b/cmd/mod_commands.go @@ -82,4 +82,3 @@ var listModulesCmd = &cli.Command{ return nil }, } - diff --git a/cmd/profile_cmds.go b/cmd/profile_cmds.go index e140c84..525f314 100644 --- a/cmd/profile_cmds.go +++ b/cmd/profile_cmds.go @@ -22,11 +22,14 @@ package cmd import ( + "errors" "fmt" + "github.com/urfave/cli/v2" + + "git.blob42.xyz/gosuki/gosuki/internal/utils" "git.blob42.xyz/gosuki/gosuki/pkg/modules" "git.blob42.xyz/gosuki/gosuki/pkg/profiles" - "github.com/urfave/cli/v2" ) @@ -37,15 +40,15 @@ var ProfileCmds = &cli.Command{ Usage: "profile commands", Subcommands: []*cli.Command{ listProfilesCmd, + detectInstalledCmd, }, } - //TODO: only enable commands when modules which implement profiles interfaces // are available var listProfilesCmd = &cli.Command{ Name: "list", - Usage: "list available profiles", + Usage: "list all available profiles", Action: func(c *cli.Context) error { browsers := modules.GetBrowserModules() @@ -59,24 +62,68 @@ var listProfilesCmd = &cli.Command{ pm, isProfileManager := brmod.(profiles.ProfileManager) if !isProfileManager{ - log.Critical("not profile manager") + return errors.New("not profile manager") } - if isProfileManager { - // handle default profile commands - profs, err := pm.GetProfiles() - if err != nil { + flavours := pm.ListFlavours() + for _, f := range flavours { + fmt.Printf("Profiles for <%s> flavour <%s>:\n\n", br.ModInfo().ID, f.Name) + if profs, err := pm.GetProfiles(f.Name); err != nil { return err + } else { + for _, p := range profs { + pPath, err := p.AbsolutePath() + if err != nil { + return err + } + fmt.Printf("%-10s \t %s\n", p.Name, pPath) + } } - - for _, p := range profs { - fmt.Printf("%-10s \t %s\n", p.Name, pm.GetProfilePath(*p)) - } - - + fmt.Println() } + } + return nil + }, +} + + +var detectInstalledCmd = &cli.Command{ + Name: "detect", + Aliases: []string{"d"}, + Usage: "detect installed browsers", + Action: func(_ *cli.Context) error { + mods := modules.GetModules() + for _, mod := range mods { + browser, isBrowser := mod.ModInfo().New().(modules.BrowserModule) + if !isBrowser { + log.Debugf("module <%s> is not a browser", mod.ModInfo().ID) + continue + } + + pm, isProf := browser.(profiles.ProfileManager) + if !isProf { + log.Debugf("module <%s> is not a profile manager", mod.ModInfo().ID) + continue + } + + flavours := pm.ListFlavours() + if len(flavours) > 0 { + fmt.Printf("Installed browsers:\n\n") + } + for _, f := range flavours { + log.Debugf("found flavour <%s> for <%s>", f.Name, mod.ModInfo().ID) + if dir, err := utils.ExpandPath(f.BaseDir); err != nil { + log.Errorf("could not expand path <%s> for flavour <%s>", f.BaseDir, f.Name) + continue + } else { + f.BaseDir = dir + } + fmt.Printf("-%-10s \t %s\n", f.Name, f.BaseDir) + } + } + return nil }, } diff --git a/internal/utils/paths.go b/internal/utils/paths.go index 2a93cc7..e6198d6 100644 --- a/internal/utils/paths.go +++ b/internal/utils/paths.go @@ -94,6 +94,18 @@ func GetHomeDir() string { return user.HomeDir } -func ExpandPath(paths ...string) string { - return os.ExpandEnv(filepath.Join(paths...)) +// ExpandPath expands a path with environment variables and tilde +// Symlinks are followed by default +func ExpandPath(paths ...string) (string, error) { + var homedir string + var err error + if homedir, err = os.UserHomeDir(); err != nil { + return "", err + } + path := os.ExpandEnv(filepath.Join(paths...)) + + if path[0] == '~' { + path = filepath.Join(homedir, path[1:]) + } + return filepath.EvalSymlinks(path) } diff --git a/pkg/browsers/mozilla/profiles.go b/pkg/browsers/mozilla/profiles.go index e011e86..2c4d71e 100644 --- a/pkg/browsers/mozilla/profiles.go +++ b/pkg/browsers/mozilla/profiles.go @@ -25,24 +25,25 @@ package mozilla import ( "errors" "fmt" - "path/filepath" "regexp" "git.blob42.xyz/gosuki/gosuki/internal/logging" - _debug "git.blob42.xyz/gosuki/gosuki/pkg/profiles" + "git.blob42.xyz/gosuki/gosuki/internal/utils" + "git.blob42.xyz/gosuki/gosuki/pkg/profiles" "github.com/go-ini/ini" ) -// ProfileManager interface -type ProfileManager = _debug.ProfileManager -type INIProfileLoader = _debug.INIProfileLoader -type PathGetter = _debug.PathGetter - const ( ProfilesFile = "profiles.ini" ) +// Browser flavour names +const ( + FirefoxFlavour = "firefox" + LibreWolfFlavour = "librewolf" +) + var ( log = logging.GetLogger("mozilla") ReIniProfiles = regexp.MustCompile(`(?i)profile`) @@ -50,47 +51,62 @@ var ( ErrProfilesIni = errors.New("could not parse profiles.ini file") ErrNoDefaultProfile = errors.New("no default profile found") - // Common default profiles for mozilla/firefox based browsers - DefaultProfileNames = map[string]string{ - "firefox-esr": "default-esr", + //TODO: multi platform + // linux mozilla browsers + MozBrowsers = map[string]profiles.BrowserFlavour{ + FirefoxFlavour: { FirefoxFlavour , "~/.mozilla/firefox"} , + LibreWolfFlavour: { LibreWolfFlavour , "~/.librewolf"} , } ) type MozProfileManager struct { - BrowserName string - ConfigDir string - ProfilesFile *ini.File - PathGetter PathGetter - ProfileManager + PathResolver profiles.PathResolver } -func (pm *MozProfileManager) loadProfile() error { +func NewMozProfileManager(resolver profiles.PathResolver) *MozProfileManager { - log.Debugf("loading profile from <%s>", pm.PathGetter.GetPath()) - pFile, err := ini.Load(pm.PathGetter.GetPath()) + return &MozProfileManager{ + PathResolver: resolver, + } +} + +func (pm *MozProfileManager) loadINIProfile(r profiles.PathResolver) (*ini.File, error) { + log.Debugf("loading profile from <%s>", r.GetPath()) + profilePath, err := utils.ExpandPath(r.GetPath()) if err != nil { - return err + return nil, err + } + + pFile, err := ini.Load(profilePath) + if err != nil { + return nil, err } - pm.ProfilesFile = pFile - return nil + return pFile, nil } -func (pm *MozProfileManager) GetProfiles() ([]*_debug.Profile, error) { - err := pm.loadProfile() - if err != nil { - return nil, err - } +//TODO: should also handle flavours +func (pm *MozProfileManager) GetProfiles(flavour string) ([]*profiles.Profile, error) { + var pFile *ini.File + var err error + f, ok := MozBrowsers[flavour] - sections := pm.ProfilesFile.Sections() - var filtered []*ini.Section - var result []*_debug.Profile + if !ok { + return nil, fmt.Errorf("unknown flavour <%s>", flavour) + } + + pm.PathResolver.SetBaseDir(f.BaseDir) + if pFile, err = pm.loadINIProfile(pm.PathResolver); err != nil { + return nil, err + } + + sections := pFile.Sections() + var result []*profiles.Profile for _, section := range sections { if ReIniProfiles.MatchString(section.Name()) { - filtered = append(filtered, section) - - p := &_debug.Profile{ + p := &profiles.Profile{ Id: section.Name(), + BaseDir: f.BaseDir, } err := section.MapTo(p) @@ -98,32 +114,28 @@ func (pm *MozProfileManager) GetProfiles() ([]*_debug.Profile, error) { return nil, err } - result = append(result, p) - } } + if len(result) == 0 { + return nil, ErrProfilesIni + } + return result, nil } +// TODO!: ConfigDir is stored in the profile, stop using ConfigDir in the base +// profile manager // GetProfilePath returns the absolute directory path to a mozilla profile. -func (pm *MozProfileManager) GetProfilePath(name string) (string, error) { - log.Debugf("using config dir %s", pm.ConfigDir) - p, err := pm.GetProfileByName(name) - if err != nil { - return "", err - } - rawPath := filepath.Join(pm.ConfigDir, p.Path) - fullPath , err := filepath.EvalSymlinks(rawPath) +//TODO!: fix the mess of GetProfilePath and GetProfielPathByName +// one method has to be moved as a function +// func (pm *MozProfileManager) GetProfilePath(prof profiles.Profile) (string, error) { +// return utils.ExpandPath(p.BaseDir, p.Path) +// } - return fullPath, err - - // Eval symlinks -} - -func (pm *MozProfileManager) GetProfileByName(name string) (*_debug.Profile, error) { - profs, err := pm.GetProfiles() +func (pm *MozProfileManager) GetProfileByName(flavour string, name string) (*profiles.Profile, error) { + profs, err := pm.GetProfiles(flavour) if err != nil { return nil, err } @@ -137,41 +149,15 @@ func (pm *MozProfileManager) GetProfileByName(name string) (*_debug.Profile, err return nil, fmt.Errorf("profile %s not found", name) } -// TEST: -func (pm *MozProfileManager) GetDefaultProfile() (*_debug.Profile, error) { - profs, err := pm.GetProfiles() - if err != nil { - return nil, err - } +func (pm *MozProfileManager) ListFlavours() []profiles.BrowserFlavour { + var result []profiles.BrowserFlavour - defaultProfileName, ok := DefaultProfileNames[pm.BrowserName] - if !ok { - defaultProfileName = "default" - } - - log.Debugf("looking for profile %s", defaultProfileName) - for _, p := range profs { - if p.Name == defaultProfileName { - return p, nil + // detect local flavours + for _, v := range MozBrowsers { + if v.Detect() { + result = append(result, v) } } - return nil, ErrNoDefaultProfile -} - -func (pm *MozProfileManager) ListProfiles() ([]string, error) { - pm.loadProfile() - sections := pm.ProfilesFile.SectionStrings() - var result []string - for _, s := range sections { - if ReIniProfiles.MatchString(s) { - result = append(result, s) - } - } - - if len(result) == 0 { - return nil, ErrProfilesIni - } - - return result, nil + return result } diff --git a/pkg/browsers/mozilla/profiles_test.go b/pkg/browsers/mozilla/profiles_test.go index 818bd9a..701f081 100644 --- a/pkg/browsers/mozilla/profiles_test.go +++ b/pkg/browsers/mozilla/profiles_test.go @@ -1,12 +1,12 @@ package mozilla import ( - "testing" - + "git.blob42.xyz/gosuki/gosuki/pkg/profiles" "github.com/stretchr/testify/assert" + "testing" ) -var OkProfile = &INIProfileLoader{ +var OkProfile = &profiles.INIProfileLoader{ BasePath: "testdata", ProfilesFile: "profiles_ok.ini", } @@ -21,70 +21,46 @@ var okNames = []string{ "profile1", } -var BadProfile = &INIProfileLoader{ +var BadProfile = &profiles.INIProfileLoader{ BasePath: "testdata", ProfilesFile: "profiles_bad.ini", } -func TestListProfiles(t *testing.T) { +func TestGetProfiles(t *testing.T) { t.Run("OK", func(t *testing.T) { pm := &MozProfileManager{ - PathGetter: OkProfile, + PathResolver: OkProfile, } - t.Log("Listing profiles") - profiles, err := pm.ListProfiles() + profs, err := pm.GetProfiles() if err != nil { t.Error(err) } - for _, p := range profiles { - t.Logf("found profiles: %s", p) + var pPaths []string + var pNames []string + for _, p := range profs { + pPaths = append(pPaths, p.Path) + pNames = append(pNames, p.Name) + + //TEST: Test the absolute path + } - if profiles[0] != "Profile0" { - t.Error("Expected Profile0") + assert.ElementsMatch(t, okPaths, pPaths) + assert.ElementsMatch(t, okNames, pNames) + + if profs[0].Name != "default" { + t.Error("Expected default profile in profiles.ini") } }) - t.Run("Bad", func(t *testing.T) { pm := &MozProfileManager{ - PathGetter: BadProfile, + PathResolver: BadProfile, } - _, err := pm.ListProfiles() - + _, err := pm.GetProfiles() if err != ErrProfilesIni || err == nil { t.Error("Expected error parsing bad profiles file") } }) - -} - -func TestGetProfiles(t *testing.T) { - pm := &MozProfileManager{ - PathGetter: OkProfile, - - } - - profs, err := pm.GetProfiles() - if err != nil { - t.Error(err) - } - - - var pPaths []string - var pNames []string - for _, p := range profs { - pPaths = append(pPaths, p.Path) - pNames = append(pNames, p.Name) - - //TEST: Test the absolute path - - } - assert.ElementsMatch(t, okPaths, pPaths) - assert.ElementsMatch(t, okNames, pNames) - - if profs[0].Name != "default" { - t.Error("Expected default profile in profiles.ini") - } } diff --git a/pkg/browsers/mozilla/testdata/profiles_bad.ini b/pkg/browsers/mozilla/testdata/profiles_bad.ini index 1de177b..84dc7a1 100644 --- a/pkg/browsers/mozilla/testdata/profiles_bad.ini +++ b/pkg/browsers/mozilla/testdata/profiles_bad.ini @@ -1,16 +1,2 @@ -# -# Copyright ⓒ 2023 Chakib Ben Ziane and [`GoSuki` contributors](https://github.com/blob42/gosuki/graphs/contributors). -# -# All rights reserved. -# -# SPDX-License-Identifier: AGPL-3.0-or-later -# -# This file is part of GoSuki. -# -# GoSuki is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. -# -# GoSuki is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License along with gosuki. If not, see . [Test] Name=Does not contain a firefox profile diff --git a/pkg/modules/browser.go b/pkg/modules/browser.go index bc5e61c..a3dfcf0 100644 --- a/pkg/modules/browser.go +++ b/pkg/modules/browser.go @@ -23,8 +23,6 @@ package modules import ( "fmt" - "path" - "path/filepath" "time" "git.blob42.xyz/gosuki/gosuki/hooks" @@ -116,7 +114,7 @@ func (b *BrowserConfig) GetWatcher() *watch.WatchDescriptor { func (b BrowserConfig) BookmarkDir() (string, error) { var err error - bDir, err := filepath.EvalSymlinks(utils.ExpandPath(b.BkDir)) + bDir, err := utils.ExpandPath(b.BkDir) if err != nil { return "", err } @@ -171,8 +169,7 @@ func (b BrowserConfig) HasHook(hook hooks.Hook) bool { // It expands the path by concatenating the base directory and bookmarks file, // then checks if it exists. func (b BrowserConfig) BookmarkPath() (string, error) { - bPath, err := filepath.EvalSymlinks(path.Join(utils.ExpandPath(b.BkDir), - b.BkFile)) + bPath, err := utils.ExpandPath(b.BkDir, b.BkFile) if err != nil { log.Error(err) } diff --git a/pkg/profiles/ini-profile.go b/pkg/profiles/ini.go similarity index 94% rename from pkg/profiles/ini-profile.go rename to pkg/profiles/ini.go index 9e1c047..7d0cf58 100644 --- a/pkg/profiles/ini-profile.go +++ b/pkg/profiles/ini.go @@ -32,3 +32,7 @@ type INIProfileLoader struct { func (pg *INIProfileLoader) GetPath() string { return filepath.Join(pg.BasePath, pg.ProfilesFile) } + +func (pg *INIProfileLoader) SetBaseDir(dir string) { + pg.BasePath = dir +} diff --git a/pkg/profiles/profiles.go b/pkg/profiles/profiles.go index 63316a0..dfa9111 100644 --- a/pkg/profiles/profiles.go +++ b/pkg/profiles/profiles.go @@ -22,33 +22,40 @@ // Package profiles ... package profiles - -// go:build linux - - -const ( - XDGHome = "XDG_CONFIG_HOME" +import ( + "git.blob42.xyz/gosuki/gosuki/internal/logging" + "git.blob42.xyz/gosuki/gosuki/internal/utils" ) -// ProfileManager is any module that can detect or list profiles, usually a browser module. +var log = logging.GetLogger("profiles") + +// ProfileManager is any module that can detect or list profiles, usually a browser module. +// One profile manager should be created for each browser flavour. type ProfileManager interface { - // Get all profile details - GetProfiles() ([]*Profile, error) + // Returns all profiles for a given flavour + GetProfiles(flavour string) ([]*Profile, error) + //TODO!: remove // Returns the default profile if no profile is selected - GetDefaultProfile() (*Profile, error) + // GetDefaultProfile() (*Profile, error) + //TODO!: remove + //TODO!: fix input to method, should take string ? // Return that absolute path to a profile and follow symlinks - GetProfilePath(Profile) (string) + // GetProfilePath(Profile) (string) // If should watch all profiles WatchAllProfiles() bool // Notifies the module to use a custom profile UseProfile(p Profile) error + + // Returns all flavours supported by this module + ListFlavours() []BrowserFlavour } + type Profile struct { // Unique identifier for the profile Id string @@ -56,10 +63,44 @@ type Profile struct { // Name of the profile Name string - // relative path to profile + // relative path to profile from base dir Path string + + // Base dir of the profile + BaseDir string } -type PathGetter interface { - GetPath() string +func (f Profile) AbsolutePath() (string, error) { + return utils.ExpandPath(f.BaseDir, f.Path) } + +// PathResolver allows custom path resolution for profiles +// See the INIProfileLoader for an example +type PathResolver interface { + GetPath() string + SetBaseDir(string) +} + +// The BrowserFlavour struct stores the name of the browser and the base +// directory where the profiles are stored. +// Example flavours: chrome-stable, chrome-unstable, firefox, firefox-esr, librewolf, etc. +type BrowserFlavour struct { + Name string + BaseDir string +} + +// Detect if the browser is installed. Returns true if the path exists +func (b BrowserFlavour) Detect() bool { + var dir string + var err error + if dir, err = utils.ExpandPath(b.BaseDir); err != nil { + log.Warningf("could not expand path <%s>: %s", b.BaseDir, err) + return false + } else if _, err = utils.CheckDirExists(dir); err != nil { + log.Warningf("could not find browser <%s> at <%s>: %s", b.Name, dir, err) + return false + } + + return true +} +