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.

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