package ui import ( "crypto/rand" "encoding/base64" "fmt" mrand "math/rand" "os" "path" "path/filepath" "time" "cwtch.im/cwtch/app" "cwtch.im/cwtch/protocol/connections" "cwtch.im/cwtch/settings" "git.openprivacy.ca/openprivacy/connectivity/tor" tea "github.com/charmbracelet/bubbletea" ) const ( offline = iota connecting connected ) func isConnected(m model) (bool, tea.Msg) { switch m.acnState { case offline: return false, acnOfflineMsg{} case connecting: return false, acnConnectingMsg{} default: return true, acnAlreadyOnlineMsg{} } } func turnAcnOn(m *model) tea.Msg { mrand.New(mrand.NewSource(int64(time.Now().Nanosecond()))) port := mrand.Intn(1000) + 9600 controlPort := port + 1 key := make([]byte, 64) _, err := rand.Read(key) if err != nil { return acnErrMsg{err: fmt.Errorf("unable to generate control port password: %s", err)} } if err := os.MkdirAll(path.Join(m.userDir, "/.tor", "tor"), 0700); err != nil { return acnErrMsg{err: fmt.Errorf("unable to create tor directory: %s", err)} } if err := os.MkdirAll(path.Join(m.userDir, "profiles"), 0700); err != nil { return acnErrMsg{err: fmt.Errorf("unable to create profiles directory: %s", err)} } if err := tor.NewTorrc(). WithSocksPort(port). WithOnionTrafficOnly(). WithControlPort(controlPort). WithHashedPassword(base64.StdEncoding.EncodeToString(key)). Build(filepath.Join(m.userDir, ".tor", "tor", "torrc")); err != nil { return acnErrMsg{err: fmt.Errorf("unable to initialise torrc builder: %s", err)} } m.sendStatus("Initialising Tor ACN...") acn, err := tor.NewTorACNWithAuth( path.Join(m.userDir, "/.tor"), "", path.Join(m.userDir, "/.tor", "data"), controlPort, tor.HashedPasswordAuthenticator{ Password: base64.StdEncoding.EncodeToString(key), }, ) if err != nil { return acnErrMsg{err: fmt.Errorf("unable to bootstrap tor: %s", err)} } m.sendStatus("Waiting for ACN to bootstrap...") if err := acn.WaitTillBootstrapped(); err != nil { return acnErrMsg{err: fmt.Errorf("unable to initialise tor: %s", err)} } settingsFile, err := settings.InitGlobalSettingsFile(m.userDir, "") if err != nil { return acnErrMsg{err: fmt.Errorf("unable to initialise settings: %s", err)} } gSettings := settingsFile.ReadGlobalSettings() gSettings.ExperimentsEnabled = false gSettings.DownloadPath = "./" settingsFile.WriteGlobalSettings(gSettings) app := app.NewApp(acn, m.userDir, settingsFile) app.InstallEngineHooks(connections.DefaultEngineHooks{}) return AcnOnMsg{ app: app, acn: acn, } } func turnAcnOff(m *model, quitProgram bool) tea.Msg { if m.acnState != offline { logDebug(*m, "Shutting down application and ACN now") m.app.Shutdown() m.acn.Close() } return acnOffMsg{quitProgram: quitProgram} }