package main import ( "flag" "io" "fmt" "log" "math/rand" "net" "net/http" "os" "strings" "sync" "time" "embed" "github.com/gorilla/mux" "riedstra.dev/mitch/steam-export/steam" ) type steamLib struct { steam.Library sync.Mutex } var ( Version = "Development" Logger = log.New(os.Stderr, "", log.LstdFlags) Listen = ":8899" //go:embed static/* embeddedStatic embed.FS Lib steamLib ) func reloadLib() { Logger.Println("Starting library reload") Lib.Lock() defer Lib.Unlock() var err error l2, err := steam.NewLibrary(DefaultLib) if err != nil { Logger.Printf("Error reopening library: %s", err) return } Lib.Library = *l2 Logger.Println("Done reloading library") } func setLibHandler(w http.ResponseWriter, r *http.Request) { if unauthorizedIfNotLocal(w, r) { return } err := r.ParseForm() if err != nil { Logger.Printf("Setlib: While parsing form: %s", err) http.Error(w, fmt.Sprintf("Invalid form: %s", err), 400) return } DefaultLib = r.Form.Get("path") reloadLib() http.Redirect(w, r, "/", 302) } func staticHandler(w http.ResponseWriter, r *http.Request){ /* vars := mux.Vars(r) fn, ok := vars["fn"] if !ok { http.Error(w, "Not found", http.StatusNotFound) return } */ fn := r.URL.Path fh, err := embeddedStatic.Open(fn) if err != nil { Logger.Printf("While reading embedded file: %s", err) http.Error(w, "Not found", http.StatusNotFound) return } defer fh.Close() _, err = io.Copy(w, fh) if err != nil { Logger.Printf("While sending static file: %s", err) } } func quitHandler(w http.ResponseWriter, r *http.Request) { if unauthorizedIfNotLocal(w, r) { return } Logger.Println("Quit was called, exiting") w.Header().Add("Content-type", "text/plain") w.Write([]byte("Shutting down...")) go func() { time.Sleep(time.Second * 2) os.Exit(0) }() return } func unauthorizedIfNotLocal(w http.ResponseWriter, r *http.Request) bool { if !isLocal(r.RemoteAddr) { http.Error(w, "Unauthorized", http.StatusUnauthorized) Logger.Printf("Unauthorized request from: %s for %s", r.RemoteAddr, r.RequestURI) return true } return false } func isLocal(addr string) bool { _, localNet, _ := net.ParseCIDR("127.0.0.1/8") return localNet.Contains(net.ParseIP(strings.Split(addr, ":")[0])) } // getHostIP attempts to guess the IP address of the current machine and // returns that. Simply bails at the first non sane looking IP and returns it. // Not ideal but it should work well enough most of the time func getHostIP() string { iFaces, err := net.Interfaces() if err != nil { return "127.0.0.1" } // RFC 3927 _, ipv4LinkLocal, _ := net.ParseCIDR("169.254.0.0/16") for _, iFace := range iFaces { addrs, err := iFace.Addrs() if err != nil { return "127.0.0.1" } for _, a := range addrs { n, ok := a.(*net.IPNet) if !ok { continue } if n.IP.To4() != nil && !n.IP.IsLoopback() && !ipv4LinkLocal.Contains(n.IP.To4()) { return n.IP.String() } } } return "127.0.0.1" } func getPort() string { s := strings.Split(Listen, ":") if len(s) != 2 { return Listen } return s[1] } func main() { fl := flag.NewFlagSet("steam-export", flag.ExitOnError) debug := fl.Bool("d", false, "Print line numbers in log") fl.StringVar(&Listen, "l", Listen, "What address do we listen on?") fl.StringVar(&DefaultLib, "L", DefaultLib, "Full path to default library") fl.Parse(os.Args[1:]) if *debug { Logger.SetFlags(log.LstdFlags | log.Llongfile) } var err error var l *steam.Library l, err = steam.NewLibrary(DefaultLib) if err != nil { Logger.Fatalf("While opening library path: %s", err) } Lib.Library = *l go installer(getPath) r := mux.NewRouter() r.HandleFunc("/quit", quitHandler) r.HandleFunc("/setLib", setLibHandler) r.HandleFunc("/delete", gameDelete) r.HandleFunc("/install", gameInstaller) r.HandleFunc("/steam-export-web.exe", serveSelf) r.HandleFunc("/download/{game}", gameDownloader) r.HandleFunc("/status", statsHandler) r.PathPrefix("/static").Handler( http.FileServer(http.FS(embeddedStatic))) r.HandleFunc("/", index) s := http.Server{ Handler: r, Addr: Listen, } go startBrowser() for i := 0; i < 5; i++ { err = s.ListenAndServe() if err != nil { Logger.Printf("Encountered: %s", err) rand.Seed(time.Now().UnixNano()) Listen = fmt.Sprintf(":%d", rand.Intn(63000)+1024) Logger.Printf("Trying: %s", Listen) s.Addr = Listen } } }