diff --git a/assets.go b/assets.go index 8e27f5a3..77d7f3a3 100644 --- a/assets.go +++ b/assets.go @@ -12,9 +12,9 @@ import ( const assetsURL = "/_internal" // Serve provides the needed assets for the front-end -func serveAssets(w http.ResponseWriter, r *http.Request, m *FileManager) (int, error) { +func serveAssets(ctx *requestContext, w http.ResponseWriter, r *http.Request) (int, error) { // gets the filename to be used with Assets function - filename := strings.Replace(r.URL.Path, m.BaseURL+assetsURL, "", 1) + filename := strings.Replace(r.URL.Path, ctx.FileManager.BaseURL+assetsURL, "", 1) var file []byte var err error @@ -22,10 +22,10 @@ func serveAssets(w http.ResponseWriter, r *http.Request, m *FileManager) (int, e switch { case strings.HasPrefix(filename, "/css"): filename = strings.Replace(filename, "/css/", "", 1) - file, err = m.assets.css.Bytes(filename) + file, err = ctx.FileManager.assets.css.Bytes(filename) case strings.HasPrefix(filename, "/js"): filename = strings.Replace(filename, "/js/", "", 1) - file, err = m.assets.js.Bytes(filename) + file, err = ctx.FileManager.assets.js.Bytes(filename) default: err = errors.New("not found") } diff --git a/checksum.go b/checksum.go index ee418a17..db4bfb79 100644 --- a/checksum.go +++ b/checksum.go @@ -14,10 +14,10 @@ import ( ) // checksum calculates the hash of a file. Supports MD5, SHA1, SHA256 and SHA512. -func checksum(w http.ResponseWriter, r *http.Request, i *fileInfo) (int, error) { +func checksum(ctx *requestContext, w http.ResponseWriter, r *http.Request) (int, error) { query := r.URL.Query().Get("checksum") - file, err := os.Open(i.Path) + file, err := os.Open(ctx.Info.Path) if err != nil { return errorToHTTP(err, true), err } diff --git a/command.go b/command.go index e95071b7..e415f583 100644 --- a/command.go +++ b/command.go @@ -22,7 +22,7 @@ var ( ) // command handles the requests for VCS related commands: git, svn and mercurial -func command(w http.ResponseWriter, r *http.Request, c *FileManager, u *User) (int, error) { +func command(ctx *requestContext, w http.ResponseWriter, r *http.Request) (int, error) { // Upgrades the connection to a websocket and checks for errors. conn, err := upgrader.Upgrade(w, r, nil) if err != nil { @@ -51,7 +51,7 @@ func command(w http.ResponseWriter, r *http.Request, c *FileManager, u *User) (i // Check if the command is allowed allowed := false - for _, cmd := range u.Commands { + for _, cmd := range ctx.User.Commands { if cmd == command[0] { allowed = true } @@ -77,7 +77,7 @@ func command(w http.ResponseWriter, r *http.Request, c *FileManager, u *User) (i } // Gets the path and initializes a buffer. - path := strings.Replace(r.URL.Path, c.BaseURL, c.scope, 1) + path := strings.Replace(r.URL.Path, ctx.FileManager.BaseURL, ctx.User.scope, 1) path = filepath.Clean(path) buff := new(bytes.Buffer) diff --git a/download.go b/download.go index 8f5ddaa7..1010553c 100644 --- a/download.go +++ b/download.go @@ -14,12 +14,12 @@ import ( // download creates an archive in one of the supported formats (zip, tar, // tar.gz or tar.bz2) and sends it to be downloaded. -func download(w http.ResponseWriter, r *http.Request, i *fileInfo) (int, error) { +func download(ctx *requestContext, w http.ResponseWriter, r *http.Request) (int, error) { query := r.URL.Query().Get("download") - if !i.IsDir { - w.Header().Set("Content-Disposition", "attachment; filename="+i.Name) - http.ServeFile(w, r, i.Path) + if !ctx.Info.IsDir { + w.Header().Set("Content-Disposition", "attachment; filename="+ctx.Info.Name) + http.ServeFile(w, r, ctx.Info.Path) return 0, nil } @@ -34,11 +34,11 @@ func download(w http.ResponseWriter, r *http.Request, i *fileInfo) (int, error) return http.StatusInternalServerError, err } - files = append(files, filepath.Join(i.Path, name)) + files = append(files, filepath.Join(ctx.Info.Path, name)) } } else { - files = append(files, i.Path) + files = append(files, ctx.Info.Path) } if query == "true" { @@ -84,7 +84,7 @@ func download(w http.ResponseWriter, r *http.Request, i *fileInfo) (int, error) return http.StatusInternalServerError, err } - name := i.Name + name := ctx.Info.Name if name == "." || name == "" { name = "download" } diff --git a/editor.go b/editor.go index cf9cd39d..a06e92a6 100644 --- a/editor.go +++ b/editor.go @@ -89,22 +89,22 @@ Error: // serveSingle serves a single file in an editor (if it is editable), shows the // plain file, or downloads it if it can't be shown. -func serveSingle(w http.ResponseWriter, r *http.Request, c *FileManager, u *User, i *fileInfo) (int, error) { +func serveSingle(ctx *requestContext, w http.ResponseWriter, r *http.Request) (int, error) { var err error - if err = i.RetrieveFileType(); err != nil { + if err = ctx.Info.RetrieveFileType(); err != nil { return errorToHTTP(err, true), err } p := &page{ - Name: i.Name, - Path: i.VirtualPath, + Name: ctx.Info.Name, + Path: ctx.Info.VirtualPath, IsDir: false, - Data: i, - User: u, - PrefixURL: c.PrefixURL, - BaseURL: c.AbsoluteURL(), - WebDavURL: c.AbsoluteWebDavURL(), + Data: ctx.Info, + User: ctx.User, + PrefixURL: ctx.FileManager.PrefixURL, + BaseURL: ctx.FileManager.AbsoluteURL(), + WebDavURL: ctx.FileManager.AbsoluteWebDavURL(), } // If the request accepts JSON, we send the file information. @@ -112,23 +112,23 @@ func serveSingle(w http.ResponseWriter, r *http.Request, c *FileManager, u *User return p.PrintAsJSON(w) } - if i.Type == "text" { - if err = i.Read(); err != nil { + if ctx.Info.Type == "text" { + if err = ctx.Info.Read(); err != nil { return errorToHTTP(err, true), err } } - if i.CanBeEdited() && u.AllowEdit { - p.Data, err = getEditor(r, i) + if ctx.Info.CanBeEdited() && ctx.User.AllowEdit { + p.Data, err = getEditor(r, ctx.Info) p.Editor = true if err != nil { return http.StatusInternalServerError, err } - return p.PrintAsHTML(w, c.assets.templates, "frontmatter", "editor") + return p.PrintAsHTML(w, ctx.FileManager.assets.templates, "frontmatter", "editor") } - return p.PrintAsHTML(w, c.assets.templates, "single") + return p.PrintAsHTML(w, ctx.FileManager.assets.templates, "single") } func editorClass(mode string) string { diff --git a/filemanager.go b/filemanager.go index 9ec2bcc6..7ceb7d5e 100644 --- a/filemanager.go +++ b/filemanager.go @@ -225,7 +225,11 @@ func (m *FileManager) NewUser(username string) error { // ServeHTTP determines if the request is for this plugin, and if all prerequisites are met. func (m *FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { var ( - u *User + ctx = &requestContext{ + FileManager: m, + User: nil, + Info: nil, + } code int err error ) @@ -234,7 +238,7 @@ func (m *FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, er // method is GET and Status Forbidden otherwise. if matchURL(r.URL.Path, m.BaseURL+assetsURL) { if r.Method == http.MethodGet { - return serveAssets(w, r, m) + return serveAssets(ctx, w, r) } return http.StatusForbidden, nil @@ -242,14 +246,14 @@ func (m *FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, er username, _, _ := r.BasicAuth() if _, ok := m.Users[username]; ok { - u = m.Users[username] + ctx.User = m.Users[username] } else { - u = m.User + ctx.User = m.User } // Checks if the request URL is for the WebDav server if matchURL(r.URL.Path, m.WebDavURL) { - return serveWebDAV(w, r, m, u) + return serveWebDAV(ctx, w, r) } w.Header().Set("x-frame-options", "SAMEORIGIN") @@ -257,7 +261,7 @@ func (m *FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, er w.Header().Set("x-xss-protection", "1; mode=block") // Checks if the User is allowed to access this file - if !u.Allowed(strings.TrimPrefix(r.URL.Path, m.BaseURL)) { + if !ctx.User.Allowed(strings.TrimPrefix(r.URL.Path, m.BaseURL)) { if r.Method == http.MethodGet { return htmlError( w, http.StatusForbidden, @@ -269,18 +273,18 @@ func (m *FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, er } if r.URL.Query().Get("search") != "" { - return search(w, r, m, u) + return search(ctx, w, r) } if r.URL.Query().Get("command") != "" { - return command(w, r, m, u) + return command(ctx, w, r) } if r.Method == http.MethodGet { var f *fileInfo // Obtains the information of the directory/file. - f, err = getInfo(r.URL, m, u) + f, err = getInfo(r.URL, m, ctx.User) if err != nil { if r.Method == http.MethodGet { return htmlError(w, code, err) @@ -299,16 +303,16 @@ func (m *FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, er switch { case r.URL.Query().Get("download") != "": - code, err = download(w, r, f) + code, err = download(ctx, w, r) case !f.IsDir && r.URL.Query().Get("checksum") != "": - code, err = checksum(w, r, f) + code, err = checksum(ctx, w, r) case r.URL.Query().Get("raw") == "true" && !f.IsDir: http.ServeFile(w, r, f.Path) code, err = 0, nil case f.IsDir: - code, err = serveListing(w, r, m, u, f) + code, err = serveListing(ctx, w, r) default: - code, err = serveSingle(w, r, m, u, f) + code, err = serveSingle(ctx, w, r) } if err != nil { diff --git a/http.go b/http.go index 6eca02ee..3078d692 100644 --- a/http.go +++ b/http.go @@ -6,6 +6,12 @@ import ( "strings" ) +type requestContext struct { + User *User + FileManager *FileManager + Info *fileInfo +} + // responseWriterNoBody is a wrapper used to suprress the body of the response // to a request. Mainly used for HEAD requests. type responseWriterNoBody struct { diff --git a/http_listing.go b/http_listing.go index 69192caf..c6e2074c 100644 --- a/http_listing.go +++ b/http_listing.go @@ -10,22 +10,22 @@ import ( ) // serveListing presents the user with a listage of a directory folder. -func serveListing(w http.ResponseWriter, r *http.Request, c *FileManager, u *User, i *fileInfo) (int, error) { +func serveListing(ctx *requestContext, w http.ResponseWriter, r *http.Request) (int, error) { var err error // Loads the content of the directory - listing, err := getListing(u, i.VirtualPath, c.PrefixURL+r.URL.Path) + listing, err := getListing(ctx.User, ctx.Info.VirtualPath, ctx.FileManager.PrefixURL+r.URL.Path) if err != nil { return errorToHTTP(err, true), err } listing.Context = httpserver.Context{ - Root: http.Dir(u.scope), + Root: http.Dir(ctx.User.scope), Req: r, URL: r.URL, } - cookieScope := c.BaseURL + cookieScope := ctx.FileManager.BaseURL if cookieScope == "" { cookieScope = "/" } @@ -80,17 +80,17 @@ func serveListing(w http.ResponseWriter, r *http.Request, c *FileManager, u *Use p := &page{ minimal: r.Header.Get("Minimal") == "true", Name: listing.Name, - Path: i.VirtualPath, + Path: ctx.Info.VirtualPath, IsDir: true, - User: u, - PrefixURL: c.PrefixURL, - BaseURL: c.AbsoluteURL(), - WebDavURL: c.AbsoluteWebDavURL(), + User: ctx.User, + PrefixURL: ctx.FileManager.PrefixURL, + BaseURL: ctx.FileManager.AbsoluteURL(), + WebDavURL: ctx.FileManager.AbsoluteWebDavURL(), Display: displayMode, Data: listing, } - return p.PrintAsHTML(w, c.assets.templates, "listing") + return p.PrintAsHTML(w, ctx.FileManager.assets.templates, "listing") } // handleSortOrder gets and stores for a Listing the 'sort' and 'order', diff --git a/http_put.go b/http_put.go index 67164119..484700e0 100644 --- a/http_put.go +++ b/http_put.go @@ -14,7 +14,7 @@ import ( ) // put is used to update a file that was edited -func put(w http.ResponseWriter, r *http.Request, c *FileManager, u *User) (err error) { +func put(ctx *requestContext, w http.ResponseWriter, r *http.Request) (err error) { var ( data = map[string]interface{}{} file []byte diff --git a/http_single.go b/http_single.go deleted file mode 100644 index 2e5b8ff3..00000000 --- a/http_single.go +++ /dev/null @@ -1 +0,0 @@ -package filemanager diff --git a/search.go b/search.go index 9876c2b2..a67481cc 100644 --- a/search.go +++ b/search.go @@ -43,7 +43,7 @@ func parseSearch(value string) *searchOptions { } // search searches for a file or directory. -func search(w http.ResponseWriter, r *http.Request, m *FileManager, u *User) (int, error) { +func search(ctx *requestContext, w http.ResponseWriter, r *http.Request) (int, error) { // Upgrades the connection to a websocket and checks for errors. conn, err := upgrader.Upgrade(w, r, nil) if err != nil { @@ -71,10 +71,10 @@ func search(w http.ResponseWriter, r *http.Request, m *FileManager, u *User) (in } search = parseSearch(value) - scope := strings.Replace(r.URL.Path, m.BaseURL, "", 1) + scope := strings.Replace(r.URL.Path, ctx.FileManager.BaseURL, "", 1) scope = strings.TrimPrefix(scope, "/") scope = "/" + scope - scope = u.scope + scope + scope = ctx.User.scope + scope scope = strings.Replace(scope, "\\", "/", -1) scope = filepath.Clean(scope) @@ -92,7 +92,7 @@ func search(w http.ResponseWriter, r *http.Request, m *FileManager, u *User) (in } if strings.Contains(path, term) { - if !u.Allowed(path) { + if !ctx.User.Allowed(path) { return nil } diff --git a/webdav.go b/webdav.go index 9a041ee5..f15ebe90 100644 --- a/webdav.go +++ b/webdav.go @@ -8,11 +8,11 @@ import ( ) // serveWebDAV handles the webDAV route of the File Manager. -func serveWebDAV(w http.ResponseWriter, r *http.Request, m *FileManager, u *User) (int, error) { +func serveWebDAV(ctx *requestContext, w http.ResponseWriter, r *http.Request) (int, error) { var err error // Checks for user permissions relatively to this path. - if !u.Allowed(strings.TrimPrefix(r.URL.Path, m.WebDavURL)) { + if !ctx.User.Allowed(strings.TrimPrefix(r.URL.Path, ctx.FileManager.WebDavURL)) { return http.StatusForbidden, nil } @@ -26,8 +26,8 @@ func serveWebDAV(w http.ResponseWriter, r *http.Request, m *FileManager, u *User // // It was decided on https://github.com/hacdias/caddy-filemanager/issues/85 // that GET, for collections, will return the same as PROPFIND method. - path := strings.Replace(r.URL.Path, m.WebDavURL, "", 1) - path = u.scope + "/" + path + path := strings.Replace(r.URL.Path, ctx.FileManager.WebDavURL, "", 1) + path = ctx.User.scope + "/" + path path = filepath.Clean(path) var i os.FileInfo @@ -45,28 +45,28 @@ func serveWebDAV(w http.ResponseWriter, r *http.Request, m *FileManager, u *User } } case "PROPPATCH", "MOVE", "PATCH", "PUT", "DELETE": - if !u.AllowEdit { + if !ctx.User.AllowEdit { return http.StatusForbidden, nil } case "MKCOL", "COPY": - if !u.AllowNew { + if !ctx.User.AllowNew { return http.StatusForbidden, nil } } // Preprocess the PUT request if it's the case if r.Method == http.MethodPut { - if err = m.BeforeSave(r, m, u); err != nil { + if err = ctx.FileManager.BeforeSave(r, ctx.FileManager, ctx.User); err != nil { return http.StatusInternalServerError, err } - if put(w, r, m, u) != nil { + if put(ctx, w, r) != nil { return http.StatusInternalServerError, err } } - m.handler.ServeHTTP(w, r) - if err = m.AfterSave(r, m, u); err != nil { + ctx.FileManager.handler.ServeHTTP(w, r) + if err = ctx.FileManager.AfterSave(r, ctx.FileManager, ctx.User); err != nil { return http.StatusInternalServerError, err }