diff --git a/popup.js b/popup.js
index 9d8b28b..9f0bd95 100644
--- a/popup.js
+++ b/popup.js
@@ -1,11 +1,3 @@
-var lastStatus;
-
-function browseToURL() {
- if (lastStatus && lastStatus.browseToURL) {
- chrome.tabs.create({ url: lastStatus.browseToURL });
- }
-}
-
document.addEventListener("DOMContentLoaded", () => {
const toggleSlider = document.getElementById("toggleSlider");
const slider = document.querySelector(".slider");
@@ -45,9 +37,15 @@ document.addEventListener("DOMContentLoaded", () => {
return;
}
if (status.needsLogin) {
- stateDisplay.innerHTML = status.browseToURL
- ? `Log in`
- : "Login required; no URL";
+ if (status.browseToURL) {
+ stateDisplay.innerHTML = `Log in`;
+ stateDisplay.querySelector("a").addEventListener("click", (e) => {
+ e.preventDefault();
+ chrome.tabs.create({ url: status.browseToURL });
+ });
+ } else {
+ stateDisplay.innerHTML = "Login required; no URL";
+ }
return;
}
if (typeof status === "string" && status === "Disconnected") {
diff --git a/registry_other.go b/registry_other.go
new file mode 100644
index 0000000..f633555
--- /dev/null
+++ b/registry_other.go
@@ -0,0 +1,6 @@
+//go:build !windows
+
+package main
+
+func installRegistry(browserByte, jsonPath string) error { return nil }
+func uninstallRegistry(browserByte string) error { return nil }
diff --git a/registry_windows.go b/registry_windows.go
new file mode 100644
index 0000000..3ab1d7d
--- /dev/null
+++ b/registry_windows.go
@@ -0,0 +1,29 @@
+//go:build windows
+
+package main
+
+import "golang.org/x/sys/windows/registry"
+
+func registryKeyPath(browserByte string) string {
+ if browserByte == "F" {
+ return `Software\Mozilla\NativeMessagingHosts\com.tailscale.browserext.firefox`
+ }
+ return `Software\Google\Chrome\NativeMessagingHosts\com.tailscale.browserext.chrome`
+}
+
+func installRegistry(browserByte, jsonPath string) error {
+ key, _, err := registry.CreateKey(registry.CURRENT_USER, registryKeyPath(browserByte), registry.SET_VALUE)
+ if err != nil {
+ return err
+ }
+ defer key.Close()
+ return key.SetStringValue("", jsonPath)
+}
+
+func uninstallRegistry(browserByte string) error {
+ err := registry.DeleteKey(registry.CURRENT_USER, registryKeyPath(browserByte))
+ if err == registry.ErrNotExist {
+ return nil
+ }
+ return err
+}
diff --git a/syslog_other.go b/syslog_other.go
new file mode 100644
index 0000000..dc098ef
--- /dev/null
+++ b/syslog_other.go
@@ -0,0 +1,24 @@
+//go:build !windows
+
+package main
+
+import (
+ "fmt"
+ "log"
+ "log/syslog"
+
+ "tailscale.com/types/logger"
+)
+
+func trySetSyslog(logf *logger.Logf) {
+ w, err := syslog.Dial("tcp", "localhost:5555", syslog.LOG_INFO, "browser")
+ if err != nil {
+ log.Printf("syslog: %v", err)
+ return
+ }
+ log.Printf("syslog dialed")
+ *logf = func(f string, a ...any) {
+ fmt.Fprintf(w, f, a...)
+ }
+ log.SetOutput(w)
+}
diff --git a/syslog_windows.go b/syslog_windows.go
new file mode 100644
index 0000000..075bd6c
--- /dev/null
+++ b/syslog_windows.go
@@ -0,0 +1,7 @@
+package main
+
+import "tailscale.com/types/logger"
+
+func trySetSyslog(logf *logger.Logf) {
+ // syslog is not available on Windows; use default logging.
+}
diff --git a/ts-browser-ext.go b/ts-browser-ext.go
index fb8cc54..4b790ea 100644
--- a/ts-browser-ext.go
+++ b/ts-browser-ext.go
@@ -10,7 +10,6 @@ import (
"fmt"
"io"
"log"
- "log/syslog"
"net"
"net/http"
"net/http/httputil"
@@ -70,15 +69,7 @@ To register it once, run:
h := newHost(os.Stdin, os.Stdout)
- if w, err := syslog.Dial("tcp", "localhost:5555", syslog.LOG_INFO, "browser"); err == nil {
- log.Printf("syslog dialed")
- h.logf = func(f string, a ...any) {
- fmt.Fprintf(w, f, a...)
- }
- log.SetOutput(w)
- } else {
- log.Printf("syslog: %v", err)
- }
+ trySetSyslog(&h.logf)
ln := h.getProxyListener()
port := ln.Addr().(*net.TCPAddr).Port
@@ -112,8 +103,10 @@ func getTargetDir(browserByte string) (string, error) {
} else if browserByte == "F" {
dir = filepath.Join(home, "Library", "Application Support", "Mozilla", "NativeMessagingHosts")
}
+ case "windows":
+ dir = filepath.Join(home, "AppData", "Local", "TailscaleBrowserExt")
default:
- return "", fmt.Errorf("TODO: implement support for installing on %q", runtime.GOOS)
+ return "", fmt.Errorf("unsupported OS %q", runtime.GOOS)
}
if err := os.MkdirAll(dir, 0755); err != nil {
return "", err
@@ -121,13 +114,20 @@ func getTargetDir(browserByte string) (string, error) {
return dir, nil
}
+func binaryName() string {
+ if runtime.GOOS == "windows" {
+ return "ts-browser-ext.exe"
+ }
+ return "ts-browser-ext"
+}
+
func uninstall() error {
for _, browserByte := range []string{"C", "F"} {
targetDir, err := getTargetDir(browserByte)
if err != nil {
return err
}
- targetBin := filepath.Join(targetDir, "ts-browser-ext")
+ targetBin := filepath.Join(targetDir, binaryName())
targetJSON := filepath.Join(targetDir, "com.tailscale.browserext.chrome.json")
if browserByte == "F" {
targetJSON = filepath.Join(targetDir, "com.tailscale.browserext.firefox.json")
@@ -138,6 +138,9 @@ func uninstall() error {
if err := os.Remove(targetJSON); err != nil && !os.IsNotExist(err) {
return err
}
+ if err := uninstallRegistry(browserByte); err != nil {
+ return fmt.Errorf("removing registry key: %w", err)
+ }
}
return nil
}
@@ -167,13 +170,17 @@ func install(installArg string) error {
if err != nil {
return err
}
- targetBin := filepath.Join(targetDir, "ts-browser-ext")
+ targetBin := filepath.Join(targetDir, binaryName())
if err := os.WriteFile(targetBin, binary, 0755); err != nil {
return err
}
log.SetFlags(0)
log.Printf("copied binary to %v", targetBin)
+ // Use forward slashes in the JSON path so it works on all platforms
+ // (Chrome/Firefox on Windows accept forward slashes).
+ jsonBinPath := filepath.ToSlash(targetBin)
+
var targetJSON string
var jsonConf []byte
@@ -188,7 +195,7 @@ func install(installArg string) error {
"allowed_origins": [
"chrome-extension://%s/"
]
- }`, targetBin, extension)
+ }`, jsonBinPath, extension)
case "F":
targetJSON = filepath.Join(targetDir, "com.tailscale.browserext.firefox.json")
jsonConf = fmt.Appendf(nil, `{
@@ -199,7 +206,7 @@ func install(installArg string) error {
"allowed_extensions": [
"browser-ext@tailscale.com"
]
- }`, targetBin)
+ }`, jsonBinPath)
default:
return fmt.Errorf("unknown browser prefix byte %q", browserByte)
}
@@ -207,6 +214,10 @@ func install(installArg string) error {
return err
}
log.Printf("wrote registration to %v", targetJSON)
+
+ if err := installRegistry(browserByte, targetJSON); err != nil {
+ return fmt.Errorf("writing registry: %w", err)
+ }
return nil
}