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.

253 lines
7.7 KiB

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