|
@@ -0,0 +1,120 @@
|
|
|
+/*
|
|
|
+Copyright (C) 2015-2017 Antoine Tenart
|
|
|
+
|
|
|
+Antoine Tenart <antoine.tenart@ack.tf>
|
|
|
+
|
|
|
+This file is licensed under the terms of the GNU General Public License version
|
|
|
+2. This program is licensed "as is" without any warranty of any kind, whether
|
|
|
+express or implied.
|
|
|
+*/
|
|
|
+
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "log"
|
|
|
+ "net"
|
|
|
+ "net/http"
|
|
|
+ "os"
|
|
|
+ "path"
|
|
|
+ "strconv"
|
|
|
+
|
|
|
+ "github.com/atotto/clipboard"
|
|
|
+ docopt "github.com/docopt/docopt-go"
|
|
|
+)
|
|
|
+
|
|
|
+var (
|
|
|
+ version = "serve 0.2"
|
|
|
+ usage = `Temporary HTTP server to share local files
|
|
|
+
|
|
|
+Usage:
|
|
|
+ serve [options] [FILE]
|
|
|
+ serve --help
|
|
|
+
|
|
|
+Options:
|
|
|
+ FILE File or directory to serve [default: .]
|
|
|
+ -p <port>, --port <port> Port number to listen on [default: 8080]
|
|
|
+ -c <count>, --count <count> Limit the number of allowed GET to <count>
|
|
|
+ -h --help Print this help
|
|
|
+`
|
|
|
+)
|
|
|
+
|
|
|
+func outboundIP() net.IP {
|
|
|
+ c, err := net.Dial("udp", "8.8.8.8:80")
|
|
|
+ if err != nil { log.Fatal(err) }
|
|
|
+ defer c.Close()
|
|
|
+
|
|
|
+ return c.LocalAddr().(*net.UDPAddr).IP
|
|
|
+}
|
|
|
+
|
|
|
+func main() {
|
|
|
+ var handler http.Handler = nil
|
|
|
+ var count int = -1
|
|
|
+
|
|
|
+ args, _ := docopt.Parse(usage, os.Args[1:], true, version, true)
|
|
|
+
|
|
|
+ f, _ := args["FILE"].(string)
|
|
|
+ f = path.Clean(f)
|
|
|
+ if f == "" { f = "." }
|
|
|
+
|
|
|
+ resource := path.Base(f)
|
|
|
+
|
|
|
+ fi, err := os.Stat(f)
|
|
|
+ if err != nil { log.Fatal(err) }
|
|
|
+ switch mode := fi.Mode(); {
|
|
|
+ case mode.IsRegular():
|
|
|
+ if limit, ok := args["--count"].(string); ok {
|
|
|
+ var err error
|
|
|
+ count, err = strconv.Atoi(limit)
|
|
|
+ if err != nil { log.Fatal(err) }
|
|
|
+ }
|
|
|
+
|
|
|
+ if resource == "." { resource = "" }
|
|
|
+ handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ var extra string = ""
|
|
|
+ if count > 1 {
|
|
|
+ extra = fmt.Sprintf(", %d remaining GET",
|
|
|
+ count - 1)
|
|
|
+ }
|
|
|
+
|
|
|
+ client, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
|
+ if err != nil { client = "unknown client" }
|
|
|
+ fmt.Printf("Connexion from %s requesting %s%s\n", client,
|
|
|
+ r.URL.Path, extra)
|
|
|
+
|
|
|
+ if r.URL.Path != fmt.Sprintf("/%s", resource) {
|
|
|
+ http.Error(w, "File not found", 404)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if count > 0 { count-- }
|
|
|
+
|
|
|
+ w.Header().Set("Content-Type", "application/octet-stream")
|
|
|
+ w.Header().Set("Content-Transfer-Encoding", "binary")
|
|
|
+ w.Header().Set("Content-Disposition",
|
|
|
+ fmt.Sprintf("attachment; filename=%s", resource))
|
|
|
+ w.Header().Set("Content-Length",
|
|
|
+ strconv.FormatInt(fi.Size(), 10))
|
|
|
+ w.Header().Set("Cache-Control", "private")
|
|
|
+ w.Header().Set("Pragma", "private")
|
|
|
+ w.Header().Set("Expires", "0")
|
|
|
+
|
|
|
+ http.ServeFile(w, r, f)
|
|
|
+ if count == 0 { os.Exit(0) }
|
|
|
+ })
|
|
|
+ case mode.IsDir():
|
|
|
+ resource = ""
|
|
|
+ handler = http.FileServer(http.Dir(f))
|
|
|
+ default:
|
|
|
+ log.Fatal("Error: unsupported file type.")
|
|
|
+ }
|
|
|
+
|
|
|
+ uri := fmt.Sprintf("http://%s:%s/%s", outboundIP(),
|
|
|
+ args["--port"].(string), resource)
|
|
|
+ fmt.Printf("Serving %s at %s\n", f, uri)
|
|
|
+
|
|
|
+ err = clipboard.WriteAll(uri)
|
|
|
+ if err != nil { log.Print(err) }
|
|
|
+
|
|
|
+ err = http.ListenAndServe(":" + args["--port"].(string), handler)
|
|
|
+ if err != nil { log.Fatal(err) }
|
|
|
+}
|