// run_windows.go //go:build windows // +build windows package spm import ( "fmt" "golang.org/x/sys/windows/registry" ) // RegisterApp writes the necessary registry keys, making it appear as offically installed app func RegisterApp() error { exePath, err := GetInstallDir() if err != nil { return fmt.Errorf("failed to get install directory: %w", err) } // 1. Create Uninstall/Modify entry uninstallKeyPath := `Software\Microsoft\Windows\CurrentVersion\Uninstall\SpitfireBrowser` uk, _, err := registry.CreateKey(registry.LOCAL_MACHINE, uninstallKeyPath, registry.ALL_ACCESS) if err != nil { return fmt.Errorf("failed to create uninstall key: %w", err) } defer uk.Close() if err := uk.SetStringValue("DisplayName", "Spitfire"); err != nil { return err } if err := uk.SetStringValue("UninstallString", exePath+" --uninstall"); err != nil { return err } if err := uk.SetStringValue("ModifyPath", exePath+" --modify"); err != nil { return err } if err := uk.SetStringValue("DisplayIcon", exePath); err != nil { return err } // 2. Register as a browser for default apps clientKeyPath := `Software\Clients\StartMenuInternet\SpitfireBrowser` ck, _, err := registry.CreateKey(registry.LOCAL_MACHINE, clientKeyPath, registry.ALL_ACCESS) if err != nil { return fmt.Errorf("failed to create client key: %w", err) } defer ck.Close() if err := ck.SetStringValue("", "Spitfire"); err != nil { return err } // Create Capabilities subkey capabilitiesKeyPath := clientKeyPath + `\Capabilities` capk, _, err := registry.CreateKey(registry.LOCAL_MACHINE, capabilitiesKeyPath, registry.ALL_ACCESS) if err != nil { return fmt.Errorf("failed to create capabilities key: %w", err) } defer capk.Close() if err := capk.SetStringValue("ApplicationName", "Spitfire"); err != nil { return err } if err := capk.SetStringValue("ApplicationDescription", "A custom browser"); err != nil { return err } // Set file associations assocKeyPath := capabilitiesKeyPath + `\FileAssociations` ak, _, err := registry.CreateKey(registry.LOCAL_MACHINE, assocKeyPath, registry.ALL_ACCESS) if err != nil { return fmt.Errorf("failed to create file associations key: %w", err) } defer ak.Close() associations := map[string]string{ ".html": "SpitfireBrowserHTML", "HTTP": "SpitfireBrowserHTML", "HTTPS": "SpitfireBrowserHTML", } for ext, progID := range associations { if err := ak.SetStringValue(ext, progID); err != nil { return err } } return nil } // UnregisterApp removes the registry entries created by registerApp. func UnregisterApp() error { // Remove the Uninstall/Modify entry. uninstallKeyPath := `Software\Microsoft\Windows\CurrentVersion\Uninstall\SpitfireBrowser` if err := deleteRegistryTree(registry.LOCAL_MACHINE, uninstallKeyPath); err != nil { return fmt.Errorf("failed to delete uninstall key: %w", err) } // Remove the browser registration entry. clientKeyPath := `Software\Clients\StartMenuInternet\SpitfireBrowser` if err := deleteRegistryTree(registry.LOCAL_MACHINE, clientKeyPath); err != nil { return fmt.Errorf("failed to delete client key: %w", err) } return nil } // deleteRegistryTree recursively deletes a registry key and all its subkeys. func deleteRegistryTree(root registry.Key, path string) error { // Open the key with ALL_ACCESS permissions. key, err := registry.OpenKey(root, path, registry.ALL_ACCESS) if err != nil { // If the key does not exist, there's nothing to do. if err == registry.ErrNotExist { return nil } return err } // Read the names of all subkeys. subKeys, err := key.ReadSubKeyNames(-1) key.Close() // Close the key so it can be deleted later. if err != nil { return err } // Recursively delete each subkey. for _, subKey := range subKeys { subKeyPath := path + `\` + subKey if err := deleteRegistryTree(root, subKeyPath); err != nil { return err } } // Finally, delete the (now empty) key. return registry.DeleteKey(root, path) }