main.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. /*
  2. Copyright (C) 2015-2017 Antoine Tenart
  3. Antoine Tenart <antoine.tenart@ack.tf>
  4. This file is licensed under the terms of the GNU General Public License version
  5. 2. This program is licensed "as is" without any warranty of any kind, whether
  6. express or implied.
  7. */
  8. package main
  9. import (
  10. "fmt"
  11. "log"
  12. "net"
  13. "net/http"
  14. "os"
  15. "path"
  16. "strconv"
  17. "github.com/atotto/clipboard"
  18. docopt "github.com/docopt/docopt-go"
  19. )
  20. var (
  21. version = "serve 0.2"
  22. usage = `Temporary HTTP server to share local files
  23. Usage:
  24. serve [options] [FILE]
  25. serve --help
  26. Options:
  27. FILE File or directory to serve [default: .]
  28. -p <port>, --port <port> Port number to listen on [default: 8080]
  29. -c <count>, --count <count> Limit the number of allowed GET to <count>
  30. -h --help Print this help
  31. `
  32. )
  33. func outboundIP() net.IP {
  34. c, err := net.Dial("udp", "8.8.8.8:80")
  35. if err != nil { log.Fatal(err) }
  36. defer c.Close()
  37. return c.LocalAddr().(*net.UDPAddr).IP
  38. }
  39. func main() {
  40. var handler http.Handler = nil
  41. var count int = -1
  42. args, _ := docopt.Parse(usage, os.Args[1:], true, version, true)
  43. f, _ := args["FILE"].(string)
  44. f = path.Clean(f)
  45. if f == "" { f = "." }
  46. resource := path.Base(f)
  47. fi, err := os.Stat(f)
  48. if err != nil { log.Fatal(err) }
  49. switch mode := fi.Mode(); {
  50. case mode.IsRegular():
  51. if limit, ok := args["--count"].(string); ok {
  52. var err error
  53. count, err = strconv.Atoi(limit)
  54. if err != nil { log.Fatal(err) }
  55. }
  56. if resource == "." { resource = "" }
  57. handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  58. var extra string = ""
  59. if count > 1 {
  60. extra = fmt.Sprintf(", %d remaining GET",
  61. count - 1)
  62. }
  63. client, _, err := net.SplitHostPort(r.RemoteAddr)
  64. if err != nil { client = "unknown client" }
  65. fmt.Printf("Connexion from %s requesting %s%s\n", client,
  66. r.URL.Path, extra)
  67. if r.URL.Path != fmt.Sprintf("/%s", resource) {
  68. http.Error(w, "File not found", 404)
  69. return
  70. }
  71. if count > 0 { count-- }
  72. w.Header().Set("Content-Type", "application/octet-stream")
  73. w.Header().Set("Content-Transfer-Encoding", "binary")
  74. w.Header().Set("Content-Disposition",
  75. fmt.Sprintf("attachment; filename=%s", resource))
  76. w.Header().Set("Content-Length",
  77. strconv.FormatInt(fi.Size(), 10))
  78. w.Header().Set("Cache-Control", "private")
  79. w.Header().Set("Pragma", "private")
  80. w.Header().Set("Expires", "0")
  81. http.ServeFile(w, r, f)
  82. if count == 0 { os.Exit(0) }
  83. })
  84. case mode.IsDir():
  85. resource = ""
  86. handler = http.FileServer(http.Dir(f))
  87. default:
  88. log.Fatal("Error: unsupported file type.")
  89. }
  90. uri := fmt.Sprintf("http://%s:%s/%s", outboundIP(),
  91. args["--port"].(string), resource)
  92. fmt.Printf("Serving %s at %s\n", f, uri)
  93. err = clipboard.WriteAll(uri)
  94. if err != nil { log.Print(err) }
  95. err = http.ListenAndServe(":" + args["--port"].(string), handler)
  96. if err != nil { log.Fatal(err) }
  97. }