diff --git a/chipper/cmd/windows/main.go b/chipper/cmd/windows/main.go index 17d82a8d..87c24374 100755 --- a/chipper/cmd/windows/main.go +++ b/chipper/cmd/windows/main.go @@ -4,16 +4,17 @@ import ( "fmt" "os" "os/exec" + "path/filepath" "runtime" "strconv" "github.com/getlantern/systray" "github.com/kercre123/chipper/pkg/logger" "github.com/kercre123/chipper/pkg/vars" - "github.com/kercre123/chipper/pkg/wirepod/sdkapp" botsetup "github.com/kercre123/chipper/pkg/wirepod/setup" stt "github.com/kercre123/chipper/pkg/wirepod/stt/vosk" "github.com/ncruces/zenity" + "golang.org/x/sys/windows/registry" ) // this directory contains code which compiled a single program for end users. gui elements are implemented. @@ -25,7 +26,7 @@ var mBoxSuccess = `Wire-pod has started successfully! It is now running in the b var mBoxIcon = "./icons/start-up-full.png" func getNeedsSetupMsg() string { - return `Wire-pod is now running in the background. You must set it up by heading to http://` + botsetup.GetOutboundIP().String() + `:` + sdkapp.WebPort + ` in a browser.` + return `Wire-pod is now running in the background. You must set it up by heading to http://` + botsetup.GetOutboundIP().String() + `:` + vars.WebPort + ` in a browser.` } func main() { @@ -47,7 +48,23 @@ func main() { os.Exit(1) } } - os.WriteFile(conf+"/runningPID", []byte(strconv.Itoa(os.Getpid())), 0777) + os.WriteFile(conf+"\\runningPID", []byte(strconv.Itoa(os.Getpid())), 0777) + os.WriteFile(filepath.Join(os.TempDir(), "/wirepodrunningPID"), []byte(strconv.Itoa(os.Getpid())), 0777) + + keyPath := `Software\Microsoft\Windows\CurrentVersion\Uninstall\wire-pod` + k, err := registry.OpenKey(registry.LOCAL_MACHINE, keyPath, registry.QUERY_VALUE) + if err != nil { + ErrMsg(fmt.Errorf("error opening key from the registry: " + err.Error())) + } + val, _, err := k.GetStringValue("InstallPath") + if err != nil { + ErrMsg(fmt.Errorf("error getting value from the registry: " + err.Error())) + } + err = os.Chdir(filepath.Join(val, "chipper")) + fmt.Println("Working directory: " + val) + if err != nil { + ErrMsg(fmt.Errorf("error setting directory to " + val)) + } systray.Run(onReady, onExit) } @@ -94,7 +111,7 @@ func onReady() { ) ExitProgram(0) case <-mBrowse.ClickedCh: - go openBrowser("http://" + botsetup.GetOutboundIP().String() + ":" + sdkapp.WebPort) + go openBrowser("http://" + botsetup.GetOutboundIP().String() + ":" + vars.WebPort) } } }() diff --git a/chipper/cmd/windows/win-initwirepod.go b/chipper/cmd/windows/win-initwirepod.go index 5e8b4211..5fc13896 100644 --- a/chipper/cmd/windows/win-initwirepod.go +++ b/chipper/cmd/windows/win-initwirepod.go @@ -22,7 +22,6 @@ import ( "github.com/kercre123/chipper/pkg/vars" wpweb "github.com/kercre123/chipper/pkg/wirepod/config-ws" wp "github.com/kercre123/chipper/pkg/wirepod/preqs" - "github.com/kercre123/chipper/pkg/wirepod/sdkapp" sdkWeb "github.com/kercre123/chipper/pkg/wirepod/sdkapp" botsetup "github.com/kercre123/chipper/pkg/wirepod/setup" "github.com/ncruces/zenity" @@ -41,7 +40,7 @@ var voiceProcessor *wp.Server var epodIsPosting bool -var NotSetUp string = "Wire-pod is not setup. Use the webserver at port " + sdkapp.WebPort + " to set up wire-pod." +var NotSetUp string = "Wire-pod is not setup. Use the webserver at port " + vars.WebPort + " to set up wire-pod." func NeedsSetupMsg() { go func() { @@ -53,7 +52,7 @@ func NeedsSetupMsg() { ) if err != nil { if err == zenity.ErrExtraButton { - openBrowser("http://" + botsetup.GetOutboundIP().String() + ":" + sdkapp.WebPort) + openBrowser("http://" + botsetup.GetOutboundIP().String() + ":" + vars.WebPort) } } }() @@ -129,20 +128,20 @@ func BeginWirepodSpecific(sttInitFunc func() error, sttHandlerFunc interface{}, func StartFromProgramInit(sttInitFunc func() error, sttHandlerFunc interface{}, voiceProcessorName string) { err := BeginWirepodSpecific(sttInitFunc, sttHandlerFunc, voiceProcessorName) if err != nil { - logger.Println("Wire-pod is not setup. Use the webserver at port " + sdkapp.WebPort + " to set up wire-pod.") + logger.Println("Wire-pod is not setup. Use the webserver at port " + vars.WebPort + " to set up wire-pod.") vars.APIConfig.PastInitialSetup = false vars.WriteConfigToDisk() NeedsSetupMsg() - systray.SetTooltip("wire-pod must be set up at http://" + botsetup.GetOutboundIP().String() + ":" + sdkapp.WebPort) + systray.SetTooltip("wire-pod must be set up at http://" + botsetup.GetOutboundIP().String() + ":" + vars.WebPort) } else if !vars.APIConfig.PastInitialSetup { - logger.Println("Wire-pod is not setup. Use the webserver at port " + sdkapp.WebPort + " to set up wire-pod.") + logger.Println("Wire-pod is not setup. Use the webserver at port " + vars.WebPort + " to set up wire-pod.") NeedsSetupMsg() - systray.SetTooltip("wire-pod must be set up at http://" + botsetup.GetOutboundIP().String() + ":" + sdkapp.WebPort) + systray.SetTooltip("wire-pod must be set up at http://" + botsetup.GetOutboundIP().String() + ":" + vars.WebPort) } else if vars.APIConfig.STT.Service == "vosk" && vars.APIConfig.STT.Language == "" { logger.Println("\033[33m\033[1mLanguage value is blank, but STT service is Vosk. Reinitiating setup process.\033[0m") - logger.Println("Wire-pod is not setup. Use the webserver at port " + sdkapp.WebPort + " to set up wire-pod.") + logger.Println("Wire-pod is not setup. Use the webserver at port " + vars.WebPort + " to set up wire-pod.") NeedsSetupMsg() - systray.SetTooltip("wire-pod must be set up at http://" + botsetup.GetOutboundIP().String() + ":" + sdkapp.WebPort) + systray.SetTooltip("wire-pod must be set up at http://" + botsetup.GetOutboundIP().String() + ":" + vars.WebPort) vars.APIConfig.PastInitialSetup = false } else { go StartChipper(true) diff --git a/chipper/cmd/wire-pod-installer/install.go b/chipper/cmd/wire-pod-installer/install.go index b7afc8da..039aa3e1 100644 --- a/chipper/cmd/wire-pod-installer/install.go +++ b/chipper/cmd/wire-pod-installer/install.go @@ -123,11 +123,18 @@ func InstallWirePod(is InstallSettings) error { UpdateInstallStatus("Updating registry...") UpdateRegistry(is) + if is.RunAtStartup { + RunPodAtStartup(is) + } + UpdateInstallBar(90) UpdateInstallStatus("Creating shortcut...") CreateShortcut(is) + UpdateInstallStatus("Creating firewall rules...") + AllowThroughFirewall(is) + UpdateInstallStatus("Done!") UpdateInstallBar(100) diff --git a/chipper/cmd/wire-pod-installer/main.go b/chipper/cmd/wire-pod-installer/main.go index ce667832..c36a5ba6 100644 --- a/chipper/cmd/wire-pod-installer/main.go +++ b/chipper/cmd/wire-pod-installer/main.go @@ -4,8 +4,11 @@ import ( "embed" "fmt" "os" + "os/exec" + "path/filepath" "strconv" "strings" + "syscall" "fyne.io/fyne/v2" "fyne.io/fyne/v2/app" @@ -20,6 +23,8 @@ var iconData embed.FS var amd64podURL string = "https://github.com/kercre123/wire-pod/releases/latest/download/wire-pod-win-amd64.zip" +//var amd64podURL string = "http://192.168.1.2:82/wire-pod-win-amd64.zip" + var DefaultInstallationDirectory string = "C:\\Program Files\\wire-pod" var icon *fyne.StaticResource @@ -55,7 +60,14 @@ func GetStatusChan() chan string { return installerStatusUpdate } -func PostInstall(myApp fyne.App) { +func ExecuteDetached(program string) error { + cmd := exec.Command(program) + // Start the program in a new process group to make it detached + cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP} + return cmd.Start() +} + +func PostInstall(myApp fyne.App, is InstallSettings) { var shouldStartPod bool = true window := myApp.NewWindow("wire-pod installer") window.Resize(fyne.Size{Width: 600, Height: 100}) @@ -74,7 +86,8 @@ func PostInstall(myApp fyne.App) { exitButton := widget.NewButton("Exit", func() { if shouldStartPod { - fmt.Println("Would start wire-pod here") + window.Hide() + ExecuteDetached(filepath.Join(is.Where, "chipper/chipper.exe")) } os.Exit(0) }) @@ -120,7 +133,7 @@ func DoInstall(myApp fyne.App, is InstallSettings) { }() InstallWirePod(is) window.Hide() - PostInstall(myApp) + PostInstall(myApp, is) } func GetPreferences(myApp fyne.App) { @@ -129,12 +142,9 @@ func GetPreferences(myApp fyne.App) { window.SetIcon(icon) window.Resize(fyne.Size{Width: 600, Height: 200}) window.CenterOnScreen() - launchOnStartup := widget.NewCheck("Launch on startup?", func(checked bool) { + launchOnStartup := widget.NewCheck("Automatically launch wire-pod after login?", func(checked bool) { is.RunAtStartup = checked }) - autoUpdate := widget.NewCheck("Auto-update?", func(checked bool) { - is.AutoUpdate = checked - }) installDir := widget.NewEntry() installDir.SetText(DefaultInstallationDirectory) @@ -168,7 +178,6 @@ func GetPreferences(myApp fyne.App) { Text: "This program will install wire-pod with the following settings.", }), launchOnStartup, - autoUpdate, widget.NewSeparator(), widget.NewRichText(&widget.TextSegment{ Text: "Installation Directory", @@ -199,6 +208,9 @@ func StopWirePodIfRunning() { } func ValidateInstallDirectory(dir string) bool { + if dir == "C:\\Program Files" || dir == "C:\\Program Files\\" { + return false + } var dirWithoutLast string splitDir := strings.Split(dir, "\\") dirWithoutLast = splitDir[0] diff --git a/chipper/cmd/wire-pod-installer/registry.go b/chipper/cmd/wire-pod-installer/registry.go index ec7ac477..3c3c68c3 100644 --- a/chipper/cmd/wire-pod-installer/registry.go +++ b/chipper/cmd/wire-pod-installer/registry.go @@ -2,6 +2,8 @@ package main import ( "fmt" + "log" + "os/exec" "path/filepath" "golang.org/x/sys/windows/registry" @@ -35,5 +37,43 @@ func UpdateRegistry(is InstallSettings) { k.SetStringValue("Publisher", publisher) k.SetStringValue("UninstallString", uninstallString) k.SetStringValue("InstallLocation", installLocation) + k.SetStringValue("InstallPath", is.Where) fmt.Println("Registry entries successfully created") } + +func RunPodAtStartup(is InstallSettings) { + key, _ := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Run`, registry.SET_VALUE) + key.SetStringValue("wire-pod", filepath.Join(is.Where, "\\chipper\\chipper.exe")) +} + +func AllowThroughFirewall(is InstallSettings) { + cmdStr := fmt.Sprintf("netsh advfirewall firewall add rule name=\"wire-pod\" dir=in action=allow program=\"%s\\chipper\\chipper.exe\" enable=yes", is.Where) + fmt.Println("Executing command:", cmdStr) + cmd := exec.Command("netsh", "advfirewall", "firewall", "add", "rule", + "name=wire-pod", + "dir=in", + "action=allow", + "profile=any", + "program="+is.Where+"\\chipper\\chipper.exe", + "enable=yes") + + out, err := cmd.Output() + if err != nil { + fmt.Println(string(out)) + log.Fatalf("Failed to execute command in: %s", err) + } + cmd = exec.Command("netsh", "advfirewall", "firewall", "add", "rule", + "name=wire-pod", + "dir=out", + "action=allow", + "profile=any", + "program="+is.Where+"\\chipper\\chipper.exe", + "enable=yes") + + err = cmd.Run() + if err != nil { + log.Fatalf("Failed to execute command out: %s", err) + } + + log.Println("Firewall rule added successfully.") +} diff --git a/chipper/cmd/wire-pod-installer/uninstall/main.go b/chipper/cmd/wire-pod-installer/uninstall/main.go index 6686eb09..3361f182 100644 --- a/chipper/cmd/wire-pod-installer/uninstall/main.go +++ b/chipper/cmd/wire-pod-installer/uninstall/main.go @@ -11,9 +11,7 @@ import ( ) func StopWirePodIfRunning() { - confDir, _ := os.UserConfigDir() - podDir := confDir + "/wire-pod" - podPid, err := os.ReadFile(podDir + "/runningPID") + podPid, err := os.ReadFile(filepath.Join(os.TempDir(), "/wirepodrunningPID")) if err == nil { pid, _ := strconv.Atoi(string(podPid)) // doesn't work on unix, but should on Windows @@ -29,15 +27,46 @@ func StopWirePodIfRunning() { func main() { StopWirePodIfRunning() - wd, _ := os.Getwd() - os.RemoveAll(filepath.Join(wd, "chipper")) - os.RemoveAll(filepath.Join(wd, "vector-cloud")) + err := zenity.Question( + "Would you like to remove application data, like saved bot settings or API preferences?", + zenity.ExtraButton("No"), + ) + if err == nil { + conf, _ := os.UserConfigDir() + os.RemoveAll(filepath.Join(conf, "wire-pod")) + } keyPath := `Software\Microsoft\Windows\CurrentVersion\Uninstall\wire-pod` + k, err := registry.OpenKey(registry.LOCAL_MACHINE, keyPath, registry.QUERY_VALUE) + if err != nil { + fmt.Println(err) + return + } + val, _, err := k.GetStringValue("InstallPath") + if err != nil { + fmt.Println(err) + return + } + + k.Close() registry.DeleteKey(registry.LOCAL_MACHINE, keyPath) + + DontRunPodAtStartup() + + fmt.Println(val) + + os.RemoveAll(filepath.Join(val, "chipper")) + os.RemoveAll(filepath.Join(val, "vector-cloud")) + os.Remove("C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\wire-pod.lnk") zenity.Info( - "wire-pod has been successfully uninstalled.", + "wire-pod has successfully been uninstalled.", zenity.InfoIcon, zenity.Title("wire-pod uninstaller"), ) + os.RemoveAll(val) os.Exit(0) } + +func DontRunPodAtStartup() { + key, _ := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Run`, registry.SET_VALUE) + key.DeleteValue("wire-pod") +} diff --git a/chipper/pkg/vars/vars.go b/chipper/pkg/vars/vars.go index 779ab60c..f4338af0 100644 --- a/chipper/pkg/vars/vars.go +++ b/chipper/pkg/vars/vars.go @@ -39,6 +39,8 @@ var ( Certs = "../certs" ) +var WebPort string = "8080" + // /home/name/.anki_vector/ var SDKIniPath string var BotJdocs []botjdoc @@ -136,6 +138,17 @@ func Init() { os.Mkdir(Certs, 0777) } + if os.Getenv("WEBSERVER_PORT") != "" { + if _, err := strconv.Atoi(os.Getenv("WEBSERVER_PORT")); err == nil { + WebPort = os.Getenv("WEBSERVER_PORT") + } else { + logger.Println("WEBSERVER_PORT contains letters, using default of 8080") + WebPort = "8080" + } + } else { + WebPort = "8080" + } + // figure out user SDK path, containing sdk_config.ini // has to be done like this because wire-pod is running as root // path should be /home/name/wire-pod/chipper diff --git a/chipper/pkg/wirepod/config-ws/webserver.go b/chipper/pkg/wirepod/config-ws/webserver.go index dbe97d9b..d80af6f2 100755 --- a/chipper/pkg/wirepod/config-ws/webserver.go +++ b/chipper/pkg/wirepod/config-ws/webserver.go @@ -12,7 +12,6 @@ import ( "github.com/kercre123/chipper/pkg/vars" "github.com/kercre123/chipper/pkg/wirepod/localization" processreqs "github.com/kercre123/chipper/pkg/wirepod/preqs" - "github.com/kercre123/chipper/pkg/wirepod/sdkapp" botsetup "github.com/kercre123/chipper/pkg/wirepod/setup" "github.com/ncruces/zenity" ) @@ -333,12 +332,12 @@ func StartWebServer() { http.HandleFunc("/session-certs/", certHandler) webRoot := http.FileServer(http.Dir("./webroot")) http.Handle("/", webRoot) - fmt.Printf("Starting webserver at port " + sdkapp.WebPort + " (http://localhost:" + sdkapp.WebPort + ")\n") - if err := http.ListenAndServe(":"+sdkapp.WebPort, nil); err != nil { - logger.Println("Error binding to " + sdkapp.WebPort + ": " + err.Error()) + fmt.Printf("Starting webserver at port " + vars.WebPort + " (http://localhost:" + vars.WebPort + ")\n") + if err := http.ListenAndServe(":"+vars.WebPort, nil); err != nil { + logger.Println("Error binding to " + vars.WebPort + ": " + err.Error()) if vars.Packaged { zenity.Error( - "FATAL: Wire-pod was unable to bind to port "+sdkapp.WebPort+". Another process is likely using it. Exiting.", + "FATAL: Wire-pod was unable to bind to port "+vars.WebPort+". Another process is likely using it. Exiting.", zenity.ErrorIcon, zenity.Title("wire-pod"), ) diff --git a/chipper/pkg/wirepod/sdkapp/server.go b/chipper/pkg/wirepod/sdkapp/server.go index 53f2df19..04382c1a 100755 --- a/chipper/pkg/wirepod/sdkapp/server.go +++ b/chipper/pkg/wirepod/sdkapp/server.go @@ -20,8 +20,6 @@ import ( "github.com/ncruces/zenity" ) -var WebPort string - const serverFiles string = "./webroot/sdkapp" func SdkapiHandler(w http.ResponseWriter, r *http.Request) { @@ -496,17 +494,7 @@ func BeginServer() { logger.Println("Starting SDK app") fmt.Printf("Starting server at port 80 for connCheck\n") ipAddr := botsetup.GetOutboundIP().String() - if os.Getenv("WEBSERVER_PORT") != "" { - if _, err := strconv.Atoi(os.Getenv("WEBSERVER_PORT")); err == nil { - WebPort = os.Getenv("WEBSERVER_PORT") - } else { - logger.Println("WEBSERVER_PORT contains letters, using default of 8080") - WebPort = "8080" - } - } else { - WebPort = "8080" - } - logger.Println("\033[1;36mConfiguration page: http://" + ipAddr + ":" + WebPort + "\033[0m") + logger.Println("\033[1;36mConfiguration page: http://" + ipAddr + ":" + vars.WebPort + "\033[0m") if err := http.ListenAndServe(":80", nil); err != nil { if vars.Packaged { zenity.Warning(