Contains the Concourse pipeline definition for building a line-server container
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

234 lines
7.4 KiB

9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. package main
  2. import (
  3. "flag"
  4. "log"
  5. "net"
  6. "net/http"
  7. "net/http/fcgi"
  8. "net/url"
  9. "os"
  10. "regexp"
  11. "strconv"
  12. "time"
  13. "github.com/GeertJohan/go.rice"
  14. "github.com/flosch/pongo2"
  15. "github.com/vharitonsky/iniflags"
  16. "github.com/zenazn/goji/graceful"
  17. "github.com/zenazn/goji/web"
  18. "github.com/zenazn/goji/web/middleware"
  19. )
  20. var Config struct {
  21. bind string
  22. filesDir string
  23. metaDir string
  24. siteName string
  25. siteURL string
  26. sitePath string
  27. certFile string
  28. keyFile string
  29. contentSecurityPolicy string
  30. fileContentSecurityPolicy string
  31. xFrameOptions string
  32. maxSize int64
  33. realIp bool
  34. noLogs bool
  35. allowHotlink bool
  36. fastcgi bool
  37. remoteUploads bool
  38. authFile string
  39. remoteAuthFile string
  40. }
  41. var Templates = make(map[string]*pongo2.Template)
  42. var TemplateSet *pongo2.TemplateSet
  43. var staticBox *rice.Box
  44. var timeStarted time.Time
  45. var timeStartedStr string
  46. var remoteAuthKeys []string
  47. func setup() *web.Mux {
  48. mux := web.New()
  49. // middleware
  50. mux.Use(middleware.RequestID)
  51. if Config.realIp {
  52. mux.Use(middleware.RealIP)
  53. }
  54. if !Config.noLogs {
  55. mux.Use(middleware.Logger)
  56. }
  57. mux.Use(middleware.Recoverer)
  58. mux.Use(middleware.AutomaticOptions)
  59. mux.Use(ContentSecurityPolicy(CSPOptions{
  60. policy: Config.contentSecurityPolicy,
  61. frame: Config.xFrameOptions,
  62. }))
  63. if Config.authFile != "" {
  64. mux.Use(UploadAuth(AuthOptions{
  65. AuthFile: Config.authFile,
  66. UnauthMethods: []string{"GET", "HEAD", "OPTIONS", "TRACE"},
  67. }))
  68. }
  69. // make directories if needed
  70. err := os.MkdirAll(Config.filesDir, 0755)
  71. if err != nil {
  72. log.Fatal("Could not create files directory:", err)
  73. }
  74. err = os.MkdirAll(Config.metaDir, 0700)
  75. if err != nil {
  76. log.Fatal("Could not create metadata directory:", err)
  77. }
  78. // ensure siteURL ends wth '/'
  79. if lastChar := Config.siteURL[len(Config.siteURL)-1:]; lastChar != "/" {
  80. Config.siteURL = Config.siteURL + "/"
  81. }
  82. parsedUrl, err := url.Parse(Config.siteURL)
  83. if err != nil {
  84. log.Fatal("Could not parse siteurl:", err)
  85. }
  86. Config.sitePath = parsedUrl.Path
  87. // Template setup
  88. p2l, err := NewPongo2TemplatesLoader()
  89. if err != nil {
  90. log.Fatal("Error: could not load templates", err)
  91. }
  92. TemplateSet := pongo2.NewSet("templates", p2l)
  93. TemplateSet.Globals["sitename"] = Config.siteName
  94. TemplateSet.Globals["siteurl"] = Config.siteURL
  95. TemplateSet.Globals["sitepath"] = Config.sitePath
  96. TemplateSet.Globals["using_auth"] = Config.authFile != ""
  97. err = populateTemplatesMap(TemplateSet, Templates)
  98. if err != nil {
  99. log.Fatal("Error: could not load templates", err)
  100. }
  101. staticBox = rice.MustFindBox("static")
  102. timeStarted = time.Now()
  103. timeStartedStr = strconv.FormatInt(timeStarted.Unix(), 10)
  104. // Routing setup
  105. nameRe := regexp.MustCompile("^" + Config.sitePath + `(?P<name>[a-z0-9-\.]+)$`)
  106. selifRe := regexp.MustCompile("^" + Config.sitePath + `selif/(?P<name>[a-z0-9-\.]+)$`)
  107. selifIndexRe := regexp.MustCompile("^" + Config.sitePath + `selif/$`)
  108. torrentRe := regexp.MustCompile("^" + Config.sitePath + `(?P<name>[a-z0-9-\.]+)/torrent$`)
  109. if Config.authFile == "" {
  110. mux.Get(Config.sitePath, indexHandler)
  111. mux.Get(Config.sitePath+"paste/", pasteHandler)
  112. } else {
  113. mux.Get(Config.sitePath, http.RedirectHandler(Config.sitePath+"API", 303))
  114. mux.Get(Config.sitePath+"paste/", http.RedirectHandler(Config.sitePath+"API/", 303))
  115. }
  116. mux.Get(Config.sitePath+"paste", http.RedirectHandler(Config.sitePath+"paste/", 301))
  117. mux.Get(Config.sitePath+"API/", apiDocHandler)
  118. mux.Get(Config.sitePath+"API", http.RedirectHandler(Config.sitePath+"API/", 301))
  119. if Config.remoteUploads {
  120. mux.Get(Config.sitePath+"upload", uploadRemote)
  121. mux.Get(Config.sitePath+"upload/", uploadRemote)
  122. if Config.remoteAuthFile != "" {
  123. remoteAuthKeys = readAuthKeys(Config.remoteAuthFile)
  124. }
  125. }
  126. mux.Post(Config.sitePath+"upload", uploadPostHandler)
  127. mux.Post(Config.sitePath+"upload/", uploadPostHandler)
  128. mux.Put(Config.sitePath+"upload", uploadPutHandler)
  129. mux.Put(Config.sitePath+"upload/", uploadPutHandler)
  130. mux.Put(Config.sitePath+"upload/:name", uploadPutHandler)
  131. mux.Delete(Config.sitePath+":name", deleteHandler)
  132. mux.Get(Config.sitePath+"static/*", staticHandler)
  133. mux.Get(Config.sitePath+"favicon.ico", staticHandler)
  134. mux.Get(Config.sitePath+"robots.txt", staticHandler)
  135. mux.Get(nameRe, fileDisplayHandler)
  136. mux.Get(selifRe, fileServeHandler)
  137. mux.Get(selifIndexRe, unauthorizedHandler)
  138. mux.Get(torrentRe, fileTorrentHandler)
  139. mux.NotFound(notFoundHandler)
  140. return mux
  141. }
  142. func main() {
  143. flag.StringVar(&Config.bind, "bind", "127.0.0.1:8080",
  144. "host to bind to (default: 127.0.0.1:8080)")
  145. flag.StringVar(&Config.filesDir, "filespath", "files/",
  146. "path to files directory")
  147. flag.StringVar(&Config.metaDir, "metapath", "meta/",
  148. "path to metadata directory")
  149. flag.BoolVar(&Config.noLogs, "nologs", false,
  150. "remove stdout output for each request")
  151. flag.BoolVar(&Config.allowHotlink, "allowhotlink", false,
  152. "Allow hotlinking of files")
  153. flag.StringVar(&Config.siteName, "sitename", "linx",
  154. "name of the site")
  155. flag.StringVar(&Config.siteURL, "siteurl", "http://"+Config.bind+"/",
  156. "site base url (including trailing slash)")
  157. flag.Int64Var(&Config.maxSize, "maxsize", 4*1024*1024*1024,
  158. "maximum upload file size in bytes (default 4GB)")
  159. flag.StringVar(&Config.certFile, "certfile", "",
  160. "path to ssl certificate (for https)")
  161. flag.StringVar(&Config.keyFile, "keyfile", "",
  162. "path to ssl key (for https)")
  163. flag.BoolVar(&Config.realIp, "realip", false,
  164. "use X-Real-IP/X-Forwarded-For headers as original host")
  165. flag.BoolVar(&Config.fastcgi, "fastcgi", false,
  166. "serve through fastcgi")
  167. flag.BoolVar(&Config.remoteUploads, "remoteuploads", false,
  168. "enable remote uploads")
  169. flag.StringVar(&Config.authFile, "authfile", "",
  170. "path to a file containing newline-separated scrypted auth keys")
  171. flag.StringVar(&Config.remoteAuthFile, "remoteauthfile", "",
  172. "path to a file containing newline-separated scrypted auth keys for remote uploads")
  173. flag.StringVar(&Config.contentSecurityPolicy, "contentsecuritypolicy",
  174. "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; referrer origin;",
  175. "value of default Content-Security-Policy header")
  176. flag.StringVar(&Config.fileContentSecurityPolicy, "filecontentsecuritypolicy",
  177. "default-src 'none'; img-src 'self'; object-src 'self'; media-src 'self'; style-src 'self' 'unsafe-inline'; referrer origin;",
  178. "value of Content-Security-Policy header for file access")
  179. flag.StringVar(&Config.xFrameOptions, "xframeoptions", "SAMEORIGIN",
  180. "value of X-Frame-Options header")
  181. iniflags.Parse()
  182. mux := setup()
  183. if Config.fastcgi {
  184. listener, err := net.Listen("tcp", Config.bind)
  185. if err != nil {
  186. log.Fatal("Could not bind: ", err)
  187. }
  188. log.Printf("Serving over fastcgi, bound on %s, using siteurl %s", Config.bind, Config.siteURL)
  189. fcgi.Serve(listener, mux)
  190. } else if Config.certFile != "" {
  191. log.Printf("Serving over https, bound on %s, using siteurl %s", Config.bind, Config.siteURL)
  192. err := graceful.ListenAndServeTLS(Config.bind, Config.certFile, Config.keyFile, mux)
  193. if err != nil {
  194. log.Fatal(err)
  195. }
  196. } else {
  197. log.Printf("Serving over http, bound on %s, using siteurl %s", Config.bind, Config.siteURL)
  198. err := graceful.ListenAndServe(Config.bind, mux)
  199. if err != nil {
  200. log.Fatal(err)
  201. }
  202. }
  203. }