/* Copyright (C) 2015-2017 Antoine Tenart Antoine Tenart 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" "strings" "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 number to listen on [default: 8080] -c , --count Limit the number of allowed GET to -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 logConnexion(r *http.Request, extra string) { 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) } 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) } logConnexion(r, 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.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { logConnexion(r, "") upath := r.URL.Path if !strings.HasPrefix(upath, "/") { upath = fmt.Sprintf("/%s", upath) } http.ServeFile(w, r, fmt.Sprintf("%s%s", f, upath)) }) 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(fmt.Sprintf(":%s", args["--port"].(string)), handler) if err != nil { log.Fatal(err) } }