cairde/ui/acn.go

112 lines
2.7 KiB
Go

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}
}