http.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. package megauploader
  2. //go:generate rice embed-go
  3. // this file contains all the web-related code for MegaUploader
  4. import (
  5. "encoding/json"
  6. "fmt"
  7. "net/http"
  8. "os"
  9. "path"
  10. "path/filepath"
  11. "strings"
  12. "sync"
  13. rice "github.com/GeertJohan/go.rice"
  14. "github.com/c2h5oh/datasize"
  15. "github.com/gorilla/handlers"
  16. "github.com/gorilla/mux"
  17. "github.com/unrolled/secure"
  18. )
  19. // MegaUploader acts as controller for all the application. Since this is inherently a web-focused
  20. // application, it will include http utils
  21. type MegaUploader struct {
  22. Conf Config
  23. configLock *sync.RWMutex
  24. secureMW *secure.Secure
  25. }
  26. // NewMegaUploader create a new mega uploader
  27. func NewMegaUploader(c Config) MegaUploader {
  28. return MegaUploader{
  29. Conf: c,
  30. configLock: new(sync.RWMutex),
  31. secureMW: secure.New(secure.Options{
  32. FrameDeny: true,
  33. ContentTypeNosniff: true,
  34. BrowserXssFilter: true,
  35. ContentSecurityPolicy: "default-src 'self'; img-src 'self' data: ",
  36. }),
  37. }
  38. }
  39. func (mu *MegaUploader) ChangeConf(newconf Config) {
  40. mu.configLock.Lock()
  41. mu.Conf = newconf
  42. mu.configLock.Unlock()
  43. }
  44. // confAcquire is a middleware to read-lock configuration
  45. func (mu *MegaUploader) confAcquire(inner http.Handler) http.Handler {
  46. f := func(w http.ResponseWriter, r *http.Request) {
  47. mu.configLock.RLock()
  48. inner.ServeHTTP(w, r)
  49. mu.configLock.RUnlock()
  50. }
  51. return http.HandlerFunc(f)
  52. }
  53. func (mu *MegaUploader) privateMiddleware(inner func(w http.ResponseWriter, r *http.Request)) http.Handler {
  54. return mu.secureMW.Handler(mu.confAcquire(requireUserMiddleware(inner)))
  55. }
  56. func (mu *MegaUploader) publicMiddleware(inner func(w http.ResponseWriter, r *http.Request)) http.Handler {
  57. return mu.secureMW.Handler(requireUserMiddleware(inner))
  58. }
  59. // SetupRoutes adds API routes
  60. func (mu *MegaUploader) SetupRoutes() http.Handler {
  61. prefix := strings.TrimRight(mu.Conf.Global.RoutePrefix, "/")
  62. root := mux.NewRouter()
  63. r := root.PathPrefix(prefix).Subrouter()
  64. r.Handle("/", mu.privateMiddleware(mu.home))
  65. r.Handle("/upload/{share}", mu.privateMiddleware(mu.uploadUI))
  66. static := rice.MustFindBox("res/static")
  67. r.PathPrefix("/static/").Handler(mu.publicMiddleware(
  68. http.StripPrefix(prefix+"/static/", http.FileServer(static.HTTPBox())).ServeHTTP,
  69. ))
  70. api := r.PathPrefix("/api/").Subrouter()
  71. api.Handle("/share", mu.privateMiddleware(mu.listShares))
  72. api.Handle("/share/{share}", mu.privateMiddleware(mu.getShare))
  73. api.Handle("/upload/{share}", mu.privateMiddleware(mu.upload)).Methods(http.MethodPost)
  74. return handlers.CombinedLoggingHandler(os.Stdout, r)
  75. }
  76. func getUser(r *http.Request) (string, error) {
  77. user := r.Header.Get("X-Forwarded-User")
  78. if user == "" {
  79. return "", fmt.Errorf("User not set")
  80. }
  81. return user, nil
  82. }
  83. func requireUserMiddleware(inner func(w http.ResponseWriter, r *http.Request)) http.Handler {
  84. f := func(w http.ResponseWriter, r *http.Request) {
  85. _, err := getUser(r)
  86. if err != nil {
  87. http.Error(w, "Only logged-in users are authorized", http.StatusUnauthorized)
  88. return
  89. }
  90. inner(w, r)
  91. }
  92. return http.HandlerFunc(f)
  93. }
  94. func (mu *MegaUploader) listShares(w http.ResponseWriter, r *http.Request) {
  95. user, _ := getUser(r) // user is set: checked by middleware
  96. shares := mu.Conf.GetAuthShares(user)
  97. serialized, err := json.Marshal(shares)
  98. if err != nil {
  99. http.Error(w, err.Error(), 500)
  100. return
  101. }
  102. w.Header().Set("Content-type", "application/json")
  103. w.Write(serialized)
  104. }
  105. func (mu *MegaUploader) getShare(w http.ResponseWriter, r *http.Request) {
  106. sharename := mux.Vars(r)["share"]
  107. share, err := mu.Conf.GetShare(sharename)
  108. if err != nil {
  109. http.Error(w, fmt.Sprintf("Share '%s' not found: %s", sharename, err), http.StatusNotFound)
  110. return
  111. }
  112. user, _ := getUser(r) // user is set: checked by middleware
  113. if !share.IsAuthorized(user) {
  114. http.Error(w, "You are not authorized to this share", http.StatusForbidden)
  115. return
  116. }
  117. serialized, err := json.Marshal(share)
  118. if err != nil {
  119. http.Error(w, err.Error(), 500)
  120. return
  121. }
  122. w.Header().Set("Content-type", "application/json")
  123. w.Write(serialized)
  124. }
  125. // on success, redirect to view URL
  126. func (mu *MegaUploader) upload(w http.ResponseWriter, r *http.Request) {
  127. user, _ := getUser(r) // user is set: checked by middleware
  128. sharename := mux.Vars(r)["share"]
  129. share, err := mu.Conf.GetAuthShare(sharename, user)
  130. if err != nil {
  131. http.Error(w, fmt.Sprintf("Share '%s' not found: %s", sharename, err), http.StatusNotFound)
  132. return
  133. }
  134. sizelimit := uint64(20 * datasize.MB)
  135. if share.SizeLimit.Bytes() > 0 {
  136. sizelimit = share.SizeLimit.Bytes()
  137. }
  138. mpreader, err := r.MultipartReader()
  139. if err != nil {
  140. fmt.Fprintln(os.Stderr, err)
  141. http.Error(w, "Bad request: error parsing form", http.StatusBadRequest)
  142. return
  143. }
  144. part, err := mpreader.NextPart()
  145. if err != nil {
  146. fmt.Fprintln(os.Stderr, err)
  147. http.Error(w, "Bad request: error reading part from multipart form", http.StatusBadRequest)
  148. }
  149. fname, err := share.Upload(part, filepath.Base(part.FileName()))
  150. if err != nil {
  151. fmt.Fprintln(os.Stderr, err)
  152. http.Error(w, "Error uploading", http.StatusInternalServerError)
  153. return
  154. }
  155. finfo, err := os.Stat(fname)
  156. if err != nil {
  157. fmt.Fprintln(os.Stderr, err)
  158. http.Error(w, "Error uploading", http.StatusInternalServerError)
  159. return
  160. }
  161. if uint64(finfo.Size()) > sizelimit {
  162. err = os.Remove(fname)
  163. if err != nil {
  164. fmt.Fprintln(os.Stderr, "could not delete file exceeding size limit", err)
  165. }
  166. http.Error(w, "File size limit exceeded", http.StatusBadRequest)
  167. return
  168. }
  169. fname = filepath.Base(fname)
  170. u, err := mu.Conf.GetShareURL(sharename)
  171. if err != nil {
  172. w.Write([]byte(fname))
  173. return
  174. }
  175. u.Path = path.Join(u.Path, fname)
  176. w.Write([]byte(u.String()))
  177. }