<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package libkb

import (
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"

	logger "github.com/keybase/client/go/logger"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/systemd"
	"github.com/syndtr/goleveldb/leveldb/opt"
)

type NullConfiguration struct{}

func (n NullConfiguration) GetHome() string                                                { return "" }
func (n NullConfiguration) GetMobileSharedHome() string                                    { return "" }
func (n NullConfiguration) GetServerURI() (string, error)                                  { return "", nil }
func (n NullConfiguration) GetConfigFilename() string                                      { return "" }
func (n NullConfiguration) GetUpdaterConfigFilename() string                               { return "" }
func (n NullConfiguration) GetGUIConfigFilename() string                                   { return "" }
func (n NullConfiguration) GetDeviceCloneStateFilename() string                            { return "" }
func (n NullConfiguration) GetSessionFilename() string                                     { return "" }
func (n NullConfiguration) GetDbFilename() string                                          { return "" }
func (n NullConfiguration) GetChatDbFilename() string                                      { return "" }
func (n NullConfiguration) GetPvlKitFilename() string                                      { return "" }
func (n NullConfiguration) GetParamProofKitFilename() string                               { return "" }
func (n NullConfiguration) GetExternalURLKitFilename() string                              { return "" }
func (n NullConfiguration) GetProveBypass() (bool, bool)                                   { return false, false }
func (n NullConfiguration) GetUsername() NormalizedUsername                                { return NormalizedUsername("") }
func (n NullConfiguration) GetEmail() string                                               { return "" }
func (n NullConfiguration) GetUpgradePerUserKey() (bool, bool)                             { return false, false }
func (n NullConfiguration) GetProxy() string                                               { return "" }
func (n NullConfiguration) GetProxyType() string                                           { return "" }
func (n NullConfiguration) IsCertPinningEnabled() bool                                     { return true }
func (n NullConfiguration) GetGpgHome() string                                             { return "" }
func (n NullConfiguration) GetBundledCA(h string) string                                   { return "" }
func (n NullConfiguration) GetUserCacheMaxAge() (time.Duration, bool)                      { return 0, false }
func (n NullConfiguration) GetProofCacheSize() (int, bool)                                 { return 0, false }
func (n NullConfiguration) GetProofCacheLongDur() (time.Duration, bool)                    { return 0, false }
func (n NullConfiguration) GetProofCacheMediumDur() (time.Duration, bool)                  { return 0, false }
func (n NullConfiguration) GetProofCacheShortDur() (time.Duration, bool)                   { return 0, false }
func (n NullConfiguration) GetLinkCacheSize() (int, bool)                                  { return 0, false }
func (n NullConfiguration) GetLinkCacheCleanDur() (time.Duration, bool)                    { return 0, false }
func (n NullConfiguration) GetUPAKCacheSize() (int, bool)                                  { return 0, false }
func (n NullConfiguration) GetUIDMapFullNameCacheSize() (int, bool)                        { return 0, false }
func (n NullConfiguration) GetPayloadCacheSize() (int, bool)                               { return 0, false }
func (n NullConfiguration) GetMerkleKIDs() []string                                        { return nil }
func (n NullConfiguration) GetCodeSigningKIDs() []string                                   { return nil }
func (n NullConfiguration) GetPinentry() string                                            { return "" }
func (n NullConfiguration) GetUID() (ret keybase1.UID)                                     { return }
func (n NullConfiguration) GetGpg() string                                                 { return "" }
func (n NullConfiguration) GetGpgOptions() []string                                        { return nil }
func (n NullConfiguration) GetPGPFingerprint() *PGPFingerprint                             { return nil }
func (n NullConfiguration) GetSecretKeyringTemplate() string                               { return "" }
func (n NullConfiguration) GetSalt() []byte                                                { return nil }
func (n NullConfiguration) GetSocketFile() string                                          { return "" }
func (n NullConfiguration) GetPidFile() string                                             { return "" }
func (n NullConfiguration) GetStandalone() (bool, bool)                                    { return false, false }
func (n NullConfiguration) GetLocalRPCDebug() string                                       { return "" }
func (n NullConfiguration) GetTimers() string                                              { return "" }
func (n NullConfiguration) GetDeviceID() keybase1.DeviceID                                 { return "" }
func (n NullConfiguration) GetDeviceIDForUsername(un NormalizedUsername) keybase1.DeviceID { return "" }
func (n NullConfiguration) GetDeviceIDForUID(u keybase1.UID) keybase1.DeviceID             { return "" }
func (n NullConfiguration) GetProxyCACerts() ([]string, error)                             { return nil, nil }
func (n NullConfiguration) GetUsernameForUID(u keybase1.UID) NormalizedUsername {
	return NormalizedUsername("")
}
func (n NullConfiguration) GetUIDForUsername(u NormalizedUsername) keybase1.UID {
	return keybase1.UID("")
}
func (n NullConfiguration) GetAutoFork() (bool, bool)                       { return false, false }
func (n NullConfiguration) GetRunMode() (RunMode, error)                    { return NoRunMode, nil }
func (n NullConfiguration) GetNoAutoFork() (bool, bool)                     { return false, false }
func (n NullConfiguration) GetLogFile() string                              { return "" }
func (n NullConfiguration) GetEKLogFile() string                            { return "" }
func (n NullConfiguration) GetUseDefaultLogFile() (bool, bool)              { return false, false }
func (n NullConfiguration) GetUseRootConfigFile() (bool, bool)              { return false, false }
func (n NullConfiguration) GetLogPrefix() string                            { return "" }
func (n NullConfiguration) GetScraperTimeout() (time.Duration, bool)        { return 0, false }
func (n NullConfiguration) GetAPITimeout() (time.Duration, bool)            { return 0, false }
func (n NullConfiguration) GetTorMode() (TorMode, error)                    { return TorNone, nil }
func (n NullConfiguration) GetTorHiddenAddress() string                     { return "" }
func (n NullConfiguration) GetTorProxy() string                             { return "" }
func (n NullConfiguration) GetUpdatePreferenceAuto() (bool, bool)           { return false, false }
func (n NullConfiguration) GetUpdatePreferenceSnoozeUntil() keybase1.Time   { return keybase1.Time(0) }
func (n NullConfiguration) GetUpdateLastChecked() keybase1.Time             { return keybase1.Time(0) }
func (n NullConfiguration) GetUpdatePreferenceSkip() string                 { return "" }
func (n NullConfiguration) GetUpdateURL() string                            { return "" }
func (n NullConfiguration) GetUpdateDisabled() (bool, bool)                 { return false, false }
func (n NullConfiguration) GetVDebugSetting() string                        { return "" }
func (n NullConfiguration) GetLocalTrackMaxAge() (time.Duration, bool)      { return 0, false }
func (n NullConfiguration) GetGregorURI() string                            { return "" }
func (n NullConfiguration) GetGregorSaveInterval() (time.Duration, bool)    { return 0, false }
func (n NullConfiguration) GetGregorPingInterval() (time.Duration, bool)    { return 0, false }
func (n NullConfiguration) GetGregorPingTimeout() (time.Duration, bool)     { return 0, false }
func (n NullConfiguration) GetChatDelivererInterval() (time.Duration, bool) { return 0, false }
func (n NullConfiguration) GetGregorDisabled() (bool, bool)                 { return false, false }
func (n NullConfiguration) GetSecretStorePrimingDisabled() (bool, bool)     { return false, false }
func (n NullConfiguration) GetMountDir() string                             { return "" }
func (n NullConfiguration) GetMountDirDefault() string                      { return "" }
func (n NullConfiguration) GetBGIdentifierDisabled() (bool, bool)           { return false, false }
func (n NullConfiguration) GetFeatureFlags() (FeatureFlags, error)          { return FeatureFlags{}, nil }
func (n NullConfiguration) GetAppType() AppType                             { return NoAppType }
func (n NullConfiguration) IsMobileExtension() (bool, bool)                 { return false, false }
func (n NullConfiguration) GetSlowGregorConn() (bool, bool)                 { return false, false }
func (n NullConfiguration) GetReadDeletedSigChain() (bool, bool)            { return false, false }
func (n NullConfiguration) GetRememberPassphrase() (bool, bool)             { return false, false }
func (n NullConfiguration) GetLevelDBNumFiles() (int, bool)                 { return 0, false }
func (n NullConfiguration) GetChatInboxSourceLocalizeThreads() (int, bool)  { return 1, false }
func (n NullConfiguration) GetAttachmentHTTPStartPort() (int, bool)         { return 0, false }
func (n NullConfiguration) GetAttachmentDisableMulti() (bool, bool)         { return false, false }
func (n NullConfiguration) GetDisableTeamAuditor() (bool, bool)             { return false, false }
func (n NullConfiguration) GetDisableMerkleAuditor() (bool, bool)           { return false, false }
func (n NullConfiguration) GetDisableSearchIndexer() (bool, bool)           { return false, false }
func (n NullConfiguration) GetDisableBgConvLoader() (bool, bool)            { return false, false }
func (n NullConfiguration) GetDisableTeamBoxAuditor() (bool, bool)          { return false, false }
func (n NullConfiguration) GetEnableBotLiteMode() (bool, bool)              { return false, false }
func (n NullConfiguration) GetExtraNetLogging() (bool, bool)                { return false, false }
func (n NullConfiguration) GetForceLinuxKeyring() (bool, bool)              { return false, false }
func (n NullConfiguration) GetForceSecretStoreFile() (bool, bool)           { return false, false }
func (n NullConfiguration) GetChatOutboxStorageEngine() string              { return "" }
func (n NullConfiguration) GetRuntimeStatsEnabled() (bool, bool)            { return false, false }
func (n NullConfiguration) GetBug3964RepairTime(NormalizedUsername) (time.Time, error) {
	return time.Time{}, nil
}
func (n NullConfiguration) GetUserConfig() (*UserConfig, error) { return nil, nil }
func (n NullConfiguration) GetUserConfigForUsername(s NormalizedUsername) (*UserConfig, error) {
	return nil, nil
}
func (n NullConfiguration) GetGString(string) string          { return "" }
func (n NullConfiguration) GetString(string) string           { return "" }
func (n NullConfiguration) GetBool(string, bool) (bool, bool) { return false, false }

func (n NullConfiguration) GetAllUsernames() (NormalizedUsername, []NormalizedUsername, error) {
	return NormalizedUsername(""), nil, nil
}
func (n NullConfiguration) GetAllUserConfigs() (*UserConfig, []UserConfig, error) {
	return nil, nil, nil
}

func (n NullConfiguration) GetDebug() (bool, bool) {
	return false, false
}
func (n NullConfiguration) GetDisplayRawUntrustedOutput() (bool, bool) {
	return false, false
}
func (n NullConfiguration) GetLogFormat() string {
	return ""
}
func (n NullConfiguration) GetAPIDump() (bool, bool) {
	return false, false
}
func (n NullConfiguration) GetNoPinentry() (bool, bool) {
	return false, false
}

func (n NullConfiguration) GetStringAtPath(string) (string, bool) {
	return "", false
}
func (n NullConfiguration) GetInterfaceAtPath(string) (interface{}, error) {
	return nil, nil
}

func (n NullConfiguration) GetBoolAtPath(string) (bool, bool) {
	return false, false
}

func (n NullConfiguration) GetIntAtPath(string) (int, bool) {
	return 0, false
}

func (n NullConfiguration) GetNullAtPath(string) bool {
	return false
}

func (n NullConfiguration) GetSecurityAccessGroupOverride() (bool, bool) {
	return false, false
}

type TestParameters struct {
	ConfigFilename   string
	Home             string
	MobileSharedHome string
	GPG              string
	GPGHome          string
	GPGOptions       []string
	Debug            bool
	// Whether we are in Devel Mode
	Devel bool
	// If we're in dev mode, the name for this test, with a random
	// suffix.
	DevelName                string
	RuntimeDir               string
	DisableUpgradePerUserKey bool
	EnvironmentFeatureFlags  FeatureFlags

	// set to true to use production run mode in tests
	UseProductionRunMode bool

	// whether LogoutIfRevoked check should be skipped to avoid races
	// during resets.
	SkipLogoutIfRevokedCheck bool

	// On if, in test, we want to skip sending system chat messages
	SkipSendingSystemChatMessages bool

	// If we need to use the real clock for NIST generation (as in really
	// whacky tests liks TestRekey).
	UseTimeClockForNISTs bool

	// TeamNoHeadMerkleStore is used for testing to emulate older clients
	// that didn't store the head merkle sequence to team chain state. We
	// have an upgrade path in the code that we'd like to test.
	TeamNoHeadMerkleStore bool

	// TeamSkipAudit is on because some team chains are "canned" and therefore
	// might point off of the merkle sequence in the database. So it's just
	// easiest to skip the audit in those cases.
	TeamSkipAudit bool

	// NoGregor is on if we want to test the service without any gregor conection
	NoGregor bool

	// TeamAuditParams can be customized if we want to control the behavior
	// of audits deep in a test
	TeamAuditParams *TeamAuditParams

	// Toggle if we want to try to 'prime' the secret store before using it.
	SecretStorePrimingDisabled bool
}

func (tp TestParameters) GetDebug() (bool, bool) {
	if tp.Debug {
		return true, true
	}
	return false, false
}

func (tp TestParameters) GetNoGregor() (bool, bool) {
	if tp.NoGregor {
		return true, true
	}
	return false, false
}

func (tp TestParameters) GetSecretStorePrimingDisabled() (bool, bool) {
	if tp.SecretStorePrimingDisabled {
		return true, true
	}
	return false, false
}

type Env struct {
	sync.RWMutex
	cmd           CommandLine
	config        ConfigReader
	HomeFinder    HomeFinder
	writer        ConfigWriter
	Test          *TestParameters
	updaterConfig UpdaterConfigReader
	guiConfig     *JSONFile
}

func (e *Env) GetConfig() ConfigReader {
	e.RLock()
	defer e.RUnlock()
	return e.config
}

func (e *Env) GetConfigWriter() ConfigWriter {
	e.RLock()
	defer e.RUnlock()
	return e.writer
}

func (e *Env) SetCommandLine(cmd CommandLine) {
	e.Lock()
	defer e.Unlock()
	e.cmd = cmd
}

func (e *Env) GetCommandLine() CommandLine {
	e.RLock()
	defer e.RUnlock()
	return e.cmd
}

func (e *Env) SetConfig(r ConfigReader, w ConfigWriter) {
	e.Lock()
	defer e.Unlock()
	e.config = r
	e.writer = w
}

func (e *Env) SetGUIConfig(j *JSONFile) {
	e.Lock()
	defer e.Unlock()
	e.guiConfig = j
}

func (e *Env) GetGUIConfig() *JSONFile {
	e.RLock()
	defer e.RUnlock()
	return e.guiConfig
}

func (e *Env) SetUpdaterConfig(r UpdaterConfigReader) {
	e.Lock()
	defer e.Unlock()
	e.updaterConfig = r
}

func (e *Env) GetUpdaterConfig() UpdaterConfigReader {
	e.RLock()
	defer e.RUnlock()
	return e.updaterConfig
}

func (e *Env) GetOldMountDirDefault() string {
	switch RuntimeGroup() {
	case keybase1.RuntimeGroup_LINUXLIKE:
		return filepath.Join(e.GetDataDir(), "fs")
	default:
		return e.GetMountDirDefault()
	}
}

func (e *Env) GetMountDirDefault() string {
	switch RuntimeGroup() {
	case keybase1.RuntimeGroup_DARWINLIKE:
		volumes := "/Volumes"
		user, err := user.Current()
		if err != nil {
			panic(fmt.Sprintf("Couldn't get current user: %+v", err))
		}
		var runmodeName string
		switch e.GetRunMode() {
		case DevelRunMode:
			runmodeName = "KeybaseDevel"
		case StagingRunMode:
			runmodeName = "KeybaseStaging"
		case ProductionRunMode:
			runmodeName = "Keybase"
		default:
			panic("Invalid run mode")
		}
		return filepath.Join(volumes, fmt.Sprintf(
			"%s (%s)", runmodeName, user.Username))
	case keybase1.RuntimeGroup_LINUXLIKE:
		return filepath.Join(e.GetRuntimeDir(), "kbfs")
	// kbfsdokan depends on an empty default
	case keybase1.RuntimeGroup_WINDOWSLIKE:
		return ""
	default:
		return filepath.Join(e.GetRuntimeDir(), "kbfs")
	}
}

func (e *Env) GetMountDir() (string, error) {
	return e.GetString(
		func() string { return e.cmd.GetMountDir() },
		func() string { return os.Getenv("KEYBASE_MOUNTDIR") },
		func() string { return e.GetConfig().GetMountDir() },
		func() string { return e.GetMountDirDefault() },
	), nil
}

func NewEnv(cmd CommandLine, config ConfigReader, getLog LogGetter) *Env {
	return newEnv(cmd, config, runtime.GOOS, getLog)
}

func newEnv(cmd CommandLine, config ConfigReader, osname string, getLog LogGetter) *Env {
	if cmd == nil {
		cmd = NullConfiguration{}
	}
	if config == nil {
		config = NullConfiguration{}
	}
	e := Env{cmd: cmd, config: config, Test: &TestParameters{}}

	e.HomeFinder = NewHomeFinder("keybase",
		func() string { return e.getHomeFromTestOrCmd() },
		func() string { return e.GetConfig().GetHome() },
		func() string { return e.getMobileSharedHomeFromCmdOrConfig() },
		osname,
		func() RunMode { return e.GetRunMode() },
		getLog,
		os.Getenv)
	return &e
}

func (e *Env) getHomeFromTestOrCmd() string {
	return e.GetString(
		func() string { return e.Test.Home },
		func() string { return e.cmd.GetHome() },
	)
}<commit_msg>normalize home dirs passed in through commandline (#18546)<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package libkb

import (
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"

	logger "github.com/keybase/client/go/logger"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/systemd"
	"github.com/syndtr/goleveldb/leveldb/opt"
)

type NullConfiguration struct{}

func (n NullConfiguration) GetHome() string                                                { return "" }
func (n NullConfiguration) GetMobileSharedHome() string                                    { return "" }
func (n NullConfiguration) GetServerURI() (string, error)                                  { return "", nil }
func (n NullConfiguration) GetConfigFilename() string                                      { return "" }
func (n NullConfiguration) GetUpdaterConfigFilename() string                               { return "" }
func (n NullConfiguration) GetGUIConfigFilename() string                                   { return "" }
func (n NullConfiguration) GetDeviceCloneStateFilename() string                            { return "" }
func (n NullConfiguration) GetSessionFilename() string                                     { return "" }
func (n NullConfiguration) GetDbFilename() string                                          { return "" }
func (n NullConfiguration) GetChatDbFilename() string                                      { return "" }
func (n NullConfiguration) GetPvlKitFilename() string                                      { return "" }
func (n NullConfiguration) GetParamProofKitFilename() string                               { return "" }
func (n NullConfiguration) GetExternalURLKitFilename() string                              { return "" }
func (n NullConfiguration) GetProveBypass() (bool, bool)                                   { return false, false }
func (n NullConfiguration) GetUsername() NormalizedUsername                                { return NormalizedUsername("") }
func (n NullConfiguration) GetEmail() string                                               { return "" }
func (n NullConfiguration) GetUpgradePerUserKey() (bool, bool)                             { return false, false }
func (n NullConfiguration) GetProxy() string                                               { return "" }
func (n NullConfiguration) GetProxyType() string                                           { return "" }
func (n NullConfiguration) IsCertPinningEnabled() bool                                     { return true }
func (n NullConfiguration) GetGpgHome() string                                             { return "" }
func (n NullConfiguration) GetBundledCA(h string) string                                   { return "" }
func (n NullConfiguration) GetUserCacheMaxAge() (time.Duration, bool)                      { return 0, false }
func (n NullConfiguration) GetProofCacheSize() (int, bool)                                 { return 0, false }
func (n NullConfiguration) GetProofCacheLongDur() (time.Duration, bool)                    { return 0, false }
func (n NullConfiguration) GetProofCacheMediumDur() (time.Duration, bool)                  { return 0, false }
func (n NullConfiguration) GetProofCacheShortDur() (time.Duration, bool)                   { return 0, false }
func (n NullConfiguration) GetLinkCacheSize() (int, bool)                                  { return 0, false }
func (n NullConfiguration) GetLinkCacheCleanDur() (time.Duration, bool)                    { return 0, false }
func (n NullConfiguration) GetUPAKCacheSize() (int, bool)                                  { return 0, false }
func (n NullConfiguration) GetUIDMapFullNameCacheSize() (int, bool)                        { return 0, false }
func (n NullConfiguration) GetPayloadCacheSize() (int, bool)                               { return 0, false }
func (n NullConfiguration) GetMerkleKIDs() []string                                        { return nil }
func (n NullConfiguration) GetCodeSigningKIDs() []string                                   { return nil }
func (n NullConfiguration) GetPinentry() string                                            { return "" }
func (n NullConfiguration) GetUID() (ret keybase1.UID)                                     { return }
func (n NullConfiguration) GetGpg() string                                                 { return "" }
func (n NullConfiguration) GetGpgOptions() []string                                        { return nil }
func (n NullConfiguration) GetPGPFingerprint() *PGPFingerprint                             { return nil }
func (n NullConfiguration) GetSecretKeyringTemplate() string                               { return "" }
func (n NullConfiguration) GetSalt() []byte                                                { return nil }
func (n NullConfiguration) GetSocketFile() string                                          { return "" }
func (n NullConfiguration) GetPidFile() string                                             { return "" }
func (n NullConfiguration) GetStandalone() (bool, bool)                                    { return false, false }
func (n NullConfiguration) GetLocalRPCDebug() string                                       { return "" }
func (n NullConfiguration) GetTimers() string                                              { return "" }
func (n NullConfiguration) GetDeviceID() keybase1.DeviceID                                 { return "" }
func (n NullConfiguration) GetDeviceIDForUsername(un NormalizedUsername) keybase1.DeviceID { return "" }
func (n NullConfiguration) GetDeviceIDForUID(u keybase1.UID) keybase1.DeviceID             { return "" }
func (n NullConfiguration) GetProxyCACerts() ([]string, error)                             { return nil, nil }
func (n NullConfiguration) GetUsernameForUID(u keybase1.UID) NormalizedUsername {
	return NormalizedUsername("")
}
func (n NullConfiguration) GetUIDForUsername(u NormalizedUsername) keybase1.UID {
	return keybase1.UID("")
}
func (n NullConfiguration) GetAutoFork() (bool, bool)                       { return false, false }
func (n NullConfiguration) GetRunMode() (RunMode, error)                    { return NoRunMode, nil }
func (n NullConfiguration) GetNoAutoFork() (bool, bool)                     { return false, false }
func (n NullConfiguration) GetLogFile() string                              { return "" }
func (n NullConfiguration) GetEKLogFile() string                            { return "" }
func (n NullConfiguration) GetUseDefaultLogFile() (bool, bool)              { return false, false }
func (n NullConfiguration) GetUseRootConfigFile() (bool, bool)              { return false, false }
func (n NullConfiguration) GetLogPrefix() string                            { return "" }
func (n NullConfiguration) GetScraperTimeout() (time.Duration, bool)        { return 0, false }
func (n NullConfiguration) GetAPITimeout() (time.Duration, bool)            { return 0, false }
func (n NullConfiguration) GetTorMode() (TorMode, error)                    { return TorNone, nil }
func (n NullConfiguration) GetTorHiddenAddress() string                     { return "" }
func (n NullConfiguration) GetTorProxy() string                             { return "" }
func (n NullConfiguration) GetUpdatePreferenceAuto() (bool, bool)           { return false, false }
func (n NullConfiguration) GetUpdatePreferenceSnoozeUntil() keybase1.Time   { return keybase1.Time(0) }
func (n NullConfiguration) GetUpdateLastChecked() keybase1.Time             { return keybase1.Time(0) }
func (n NullConfiguration) GetUpdatePreferenceSkip() string                 { return "" }
func (n NullConfiguration) GetUpdateURL() string                            { return "" }
func (n NullConfiguration) GetUpdateDisabled() (bool, bool)                 { return false, false }
func (n NullConfiguration) GetVDebugSetting() string                        { return "" }
func (n NullConfiguration) GetLocalTrackMaxAge() (time.Duration, bool)      { return 0, false }
func (n NullConfiguration) GetGregorURI() string                            { return "" }
func (n NullConfiguration) GetGregorSaveInterval() (time.Duration, bool)    { return 0, false }
func (n NullConfiguration) GetGregorPingInterval() (time.Duration, bool)    { return 0, false }
func (n NullConfiguration) GetGregorPingTimeout() (time.Duration, bool)     { return 0, false }
func (n NullConfiguration) GetChatDelivererInterval() (time.Duration, bool) { return 0, false }
func (n NullConfiguration) GetGregorDisabled() (bool, bool)                 { return false, false }
func (n NullConfiguration) GetSecretStorePrimingDisabled() (bool, bool)     { return false, false }
func (n NullConfiguration) GetMountDir() string                             { return "" }
func (n NullConfiguration) GetMountDirDefault() string                      { return "" }
func (n NullConfiguration) GetBGIdentifierDisabled() (bool, bool)           { return false, false }
func (n NullConfiguration) GetFeatureFlags() (FeatureFlags, error)          { return FeatureFlags{}, nil }
func (n NullConfiguration) GetAppType() AppType                             { return NoAppType }
func (n NullConfiguration) IsMobileExtension() (bool, bool)                 { return false, false }
func (n NullConfiguration) GetSlowGregorConn() (bool, bool)                 { return false, false }
func (n NullConfiguration) GetReadDeletedSigChain() (bool, bool)            { return false, false }
func (n NullConfiguration) GetRememberPassphrase() (bool, bool)             { return false, false }
func (n NullConfiguration) GetLevelDBNumFiles() (int, bool)                 { return 0, false }
func (n NullConfiguration) GetChatInboxSourceLocalizeThreads() (int, bool)  { return 1, false }
func (n NullConfiguration) GetAttachmentHTTPStartPort() (int, bool)         { return 0, false }
func (n NullConfiguration) GetAttachmentDisableMulti() (bool, bool)         { return false, false }
func (n NullConfiguration) GetDisableTeamAuditor() (bool, bool)             { return false, false }
func (n NullConfiguration) GetDisableMerkleAuditor() (bool, bool)           { return false, false }
func (n NullConfiguration) GetDisableSearchIndexer() (bool, bool)           { return false, false }
func (n NullConfiguration) GetDisableBgConvLoader() (bool, bool)            { return false, false }
func (n NullConfiguration) GetDisableTeamBoxAuditor() (bool, bool)          { return false, false }
func (n NullConfiguration) GetEnableBotLiteMode() (bool, bool)              { return false, false }
func (n NullConfiguration) GetExtraNetLogging() (bool, bool)                { return false, false }
func (n NullConfiguration) GetForceLinuxKeyring() (bool, bool)              { return false, false }
func (n NullConfiguration) GetForceSecretStoreFile() (bool, bool)           { return false, false }
func (n NullConfiguration) GetChatOutboxStorageEngine() string              { return "" }
func (n NullConfiguration) GetRuntimeStatsEnabled() (bool, bool)            { return false, false }
func (n NullConfiguration) GetBug3964RepairTime(NormalizedUsername) (time.Time, error) {
	return time.Time{}, nil
}
func (n NullConfiguration) GetUserConfig() (*UserConfig, error) { return nil, nil }
func (n NullConfiguration) GetUserConfigForUsername(s NormalizedUsername) (*UserConfig, error) {
	return nil, nil
}
func (n NullConfiguration) GetGString(string) string          { return "" }
func (n NullConfiguration) GetString(string) string           { return "" }
func (n NullConfiguration) GetBool(string, bool) (bool, bool) { return false, false }

func (n NullConfiguration) GetAllUsernames() (NormalizedUsername, []NormalizedUsername, error) {
	return NormalizedUsername(""), nil, nil
}
func (n NullConfiguration) GetAllUserConfigs() (*UserConfig, []UserConfig, error) {
	return nil, nil, nil
}

func (n NullConfiguration) GetDebug() (bool, bool) {
	return false, false
}
func (n NullConfiguration) GetDisplayRawUntrustedOutput() (bool, bool) {
	return false, false
}
func (n NullConfiguration) GetLogFormat() string {
	return ""
}
func (n NullConfiguration) GetAPIDump() (bool, bool) {
	return false, false
}
func (n NullConfiguration) GetNoPinentry() (bool, bool) {
	return false, false
}

func (n NullConfiguration) GetStringAtPath(string) (string, bool) {
	return "", false
}
func (n NullConfiguration) GetInterfaceAtPath(string) (interface{}, error) {
	return nil, nil
}

func (n NullConfiguration) GetBoolAtPath(string) (bool, bool) {
	return false, false
}

func (n NullConfiguration) GetIntAtPath(string) (int, bool) {
	return 0, false
}

func (n NullConfiguration) GetNullAtPath(string) bool {
	return false
}

func (n NullConfiguration) GetSecurityAccessGroupOverride() (bool, bool) {
	return false, false
}

type TestParameters struct {
	ConfigFilename   string
	Home             string
	MobileSharedHome string
	GPG              string
	GPGHome          string
	GPGOptions       []string
	Debug            bool
	// Whether we are in Devel Mode
	Devel bool
	// If we're in dev mode, the name for this test, with a random
	// suffix.
	DevelName                string
	RuntimeDir               string
	DisableUpgradePerUserKey bool
	EnvironmentFeatureFlags  FeatureFlags

	// set to true to use production run mode in tests
	UseProductionRunMode bool

	// whether LogoutIfRevoked check should be skipped to avoid races
	// during resets.
	SkipLogoutIfRevokedCheck bool

	// On if, in test, we want to skip sending system chat messages
	SkipSendingSystemChatMessages bool

	// If we need to use the real clock for NIST generation (as in really
	// whacky tests liks TestRekey).
	UseTimeClockForNISTs bool

	// TeamNoHeadMerkleStore is used for testing to emulate older clients
	// that didn't store the head merkle sequence to team chain state. We
	// have an upgrade path in the code that we'd like to test.
	TeamNoHeadMerkleStore bool

	// TeamSkipAudit is on because some team chains are "canned" and therefore
	// might point off of the merkle sequence in the database. So it's just
	// easiest to skip the audit in those cases.
	TeamSkipAudit bool

	// NoGregor is on if we want to test the service without any gregor conection
	NoGregor bool

	// TeamAuditParams can be customized if we want to control the behavior
	// of audits deep in a test
	TeamAuditParams *TeamAuditParams

	// Toggle if we want to try to 'prime' the secret store before using it.
	SecretStorePrimingDisabled bool
}

func (tp TestParameters) GetDebug() (bool, bool) {
	if tp.Debug {
		return true, true
	}
	return false, false
}

func (tp TestParameters) GetNoGregor() (bool, bool) {
	if tp.NoGregor {
		return true, true
	}
	return false, false
}

func (tp TestParameters) GetSecretStorePrimingDisabled() (bool, bool) {
	if tp.SecretStorePrimingDisabled {
		return true, true
	}
	return false, false
}

type Env struct {
	sync.RWMutex
	cmd           CommandLine
	config        ConfigReader
	HomeFinder    HomeFinder
	writer        ConfigWriter
	Test          *TestParameters
	updaterConfig UpdaterConfigReader
	guiConfig     *JSONFile
}

func (e *Env) GetConfig() ConfigReader {
	e.RLock()
	defer e.RUnlock()
	return e.config
}

func (e *Env) GetConfigWriter() ConfigWriter {
	e.RLock()
	defer e.RUnlock()
	return e.writer
}

func (e *Env) SetCommandLine(cmd CommandLine) {
	e.Lock()
	defer e.Unlock()
	e.cmd = cmd
}

func (e *Env) GetCommandLine() CommandLine {
	e.RLock()
	defer e.RUnlock()
	return e.cmd
}

func (e *Env) SetConfig(r ConfigReader, w ConfigWriter) {
	e.Lock()
	defer e.Unlock()
	e.config = r
	e.writer = w
}

func (e *Env) SetGUIConfig(j *JSONFile) {
	e.Lock()
	defer e.Unlock()
	e.guiConfig = j
}

func (e *Env) GetGUIConfig() *JSONFile {
	e.RLock()
	defer e.RUnlock()
	return e.guiConfig
}

func (e *Env) SetUpdaterConfig(r UpdaterConfigReader) {
	e.Lock()
	defer e.Unlock()
	e.updaterConfig = r
}

func (e *Env) GetUpdaterConfig() UpdaterConfigReader {
	e.RLock()
	defer e.RUnlock()
	return e.updaterConfig
}

func (e *Env) GetOldMountDirDefault() string {
	switch RuntimeGroup() {
	case keybase1.RuntimeGroup_LINUXLIKE:
		return filepath.Join(e.GetDataDir(), "fs")
	default:
		return e.GetMountDirDefault()
	}
}

func (e *Env) GetMountDirDefault() string {
	switch RuntimeGroup() {
	case keybase1.RuntimeGroup_DARWINLIKE:
		volumes := "/Volumes"
		user, err := user.Current()
		if err != nil {
			panic(fmt.Sprintf("Couldn't get current user: %+v", err))
		}
		var runmodeName string
		switch e.GetRunMode() {
		case DevelRunMode:
			runmodeName = "KeybaseDevel"
		case StagingRunMode:
			runmodeName = "KeybaseStaging"
		case ProductionRunMode:
			runmodeName = "Keybase"
		default:
			panic("Invalid run mode")
		}
		return filepath.Join(volumes, fmt.Sprintf(
			"%s (%s)", runmodeName, user.Username))
	case keybase1.RuntimeGroup_LINUXLIKE:
		return filepath.Join(e.GetRuntimeDir(), "kbfs")
	// kbfsdokan depends on an empty default
	case keybase1.RuntimeGroup_WINDOWSLIKE:
		return ""
	default:
		return filepath.Join(e.GetRuntimeDir(), "kbfs")
	}
}

func (e *Env) GetMountDir() (string, error) {
	return e.GetString(
		func() string { return e.cmd.GetMountDir() },
		func() string { return os.Getenv("KEYBASE_MOUNTDIR") },
		func() string { return e.GetConfig().GetMountDir() },
		func() string { return e.GetMountDirDefault() },
	), nil
}

func NewEnv(cmd CommandLine, config ConfigReader, getLog LogGetter) *Env {
	return newEnv(cmd, config, runtime.GOOS, getLog)
}

func newEnv(cmd CommandLine, config ConfigReader, osname string, getLog LogGetter) *Env {
	if cmd == nil {
		cmd = NullConfiguration{}
	}
	if config == nil {
		config = NullConfiguration{}
	}
	e := Env{cmd: cmd, config: config, Test: &TestParameters{}}

	e.HomeFinder = NewHomeFinder("keybase",
		func() string { return e.getHomeFromTestOrCmd() },
		func() string { return e.GetConfig().GetHome() },
		func() string { return e.getMobileSharedHomeFromCmdOrConfig() },
		osname,
		func() RunMode { return e.GetRunMode() },
		getLog,
		os.Getenv)
	return &e
}

func (e *Env) getHomeFromTestOrCmd() string {
	return e.GetString(
		func() string { return e.Test.Home },
		func() string {
			home := e.cmd.GetHome()
			if home == "" {
				return ""
			}
			absHome, err := filepath.Abs(home)
			if err != nil {
				return home
			}
			return absHome
		},
	)
}
{'fraction_non_alphanumeric': 0.09790877116449001, 'fraction_numerical': 0.0028082994792376693, 'mean_word_length': 2.6769924812030075, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 106, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6109429', 'n_tokens_mistral': 10121, 'n_tokens_neox': 9875, 'n_words': 3160}