diff --git a/http/editor.go b/editor.go similarity index 89% rename from http/editor.go rename to editor.go index 724a6884..4e785c88 100644 --- a/http/editor.go +++ b/editor.go @@ -1,4 +1,4 @@ -package http +package filemanager import ( "bytes" @@ -7,13 +7,12 @@ import ( "path/filepath" "strings" - fm "github.com/hacdias/filemanager" "github.com/hacdias/filemanager/frontmatter" "github.com/spf13/hugo/parser" ) -// Editor contains the information for the editor page -type Editor struct { +// editor contains the information for the editor page +type editor struct { Class string Mode string Visual bool @@ -24,12 +23,12 @@ type Editor struct { } } -// getEditor gets the editor based on a FileInfo struct -func getEditor(r *http.Request, i *fm.FileInfo) (*Editor, error) { +// newEditor gets the editor based on a FileInfo struct +func newEditor(r *http.Request, i *file) (*editor, error) { var err error // Create a new editor variable and set the mode - e := new(Editor) + e := new(editor) e.Mode = editorMode(i.Name) e.Class = editorClass(e.Mode) diff --git a/file.go b/file.go index 009b8449..70961ce2 100644 --- a/file.go +++ b/file.go @@ -13,8 +13,8 @@ import ( humanize "github.com/dustin/go-humanize" ) -// FileInfo contains the information about a particular file or directory. -type FileInfo struct { +// file contains the information about a particular file or directory. +type file struct { Name string Size int64 URL string @@ -30,11 +30,11 @@ type FileInfo struct { UserAllowed bool // Indicates if the user has enough permissions } -// GetInfo retrieves the file information and the error, if there is any. -func GetInfo(url *url.URL, c *Config, u *User) (*FileInfo, error) { +// getFile retrieves the file information and the error, if there is any. +func getFile(url *url.URL, c *Config, u *User) (*file, error) { var err error - i := &FileInfo{URL: c.PrefixURL + url.Path} + i := &file{URL: c.PrefixURL + url.Path} i.VirtualPath = strings.Replace(url.Path, c.BaseURL, "", 1) i.VirtualPath = strings.TrimPrefix(i.VirtualPath, "/") i.VirtualPath = "/" + i.VirtualPath @@ -75,7 +75,7 @@ var textExtensions = [...]string{ // RetrieveFileType obtains the mimetype and a simplified internal Type // using the first 512 bytes from the file. -func (i *FileInfo) RetrieveFileType() error { +func (i *file) RetrieveFileType() error { i.Mimetype = mime.TypeByExtension(i.Extension) if i.Mimetype == "" { @@ -126,7 +126,7 @@ func (i *FileInfo) RetrieveFileType() error { } // Reads the file. -func (i *FileInfo) Read() error { +func (i *file) Read() error { if len(i.Content) != 0 { return nil } @@ -140,22 +140,22 @@ func (i *FileInfo) Read() error { } // StringifyContent returns the string version of Raw -func (i FileInfo) StringifyContent() string { +func (i file) StringifyContent() string { return string(i.Content) } // HumanSize returns the size of the file as a human-readable string // in IEC format (i.e. power of 2 or base 1024). -func (i FileInfo) HumanSize() string { +func (i file) HumanSize() string { return humanize.IBytes(uint64(i.Size)) } // HumanModTime returns the modified time of the file as a human-readable string. -func (i FileInfo) HumanModTime(format string) string { +func (i file) HumanModTime(format string) string { return i.ModTime.Format(format) } // CanBeEdited checks if the extension of a file is supported by the editor -func (i FileInfo) CanBeEdited() bool { +func (i file) CanBeEdited() bool { return i.Type == "text" } diff --git a/page/functions.go b/functions.go similarity index 98% rename from page/functions.go rename to functions.go index 2eb088f9..a85d3991 100644 --- a/page/functions.go +++ b/functions.go @@ -1,4 +1,4 @@ -package page +package filemanager import ( "encoding/base64" diff --git a/page/functions_test.go b/functions_test.go similarity index 97% rename from page/functions_test.go rename to functions_test.go index 58b83a49..7c5b5c8a 100644 --- a/page/functions_test.go +++ b/functions_test.go @@ -1,4 +1,4 @@ -package page +package filemanager import "testing" diff --git a/http/http.go b/http.go similarity index 77% rename from http/http.go rename to http.go index 26bb1a4f..6e49a3f1 100644 --- a/http/http.go +++ b/http.go @@ -1,4 +1,4 @@ -package http +package filemanager import ( "errors" @@ -7,21 +7,18 @@ import ( "path/filepath" "strings" - fm "github.com/hacdias/filemanager" - "github.com/hacdias/filemanager/assets" - "github.com/hacdias/filemanager/page" "github.com/mholt/caddy/caddyhttp/httpserver" ) // ServeHTTP starts FileManager. -func ServeHTTP(w http.ResponseWriter, r *http.Request, c *fm.Config) (int, error) { +func (c *Config) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { var ( - fi *fm.FileInfo - user *fm.User + fi *file + user *User code int err error ) - + /* TODO: readd this // Checks if the URL matches the Assets URL. Returns the asset if the // method is GET and Status Forbidden otherwise. if strings.HasPrefix(r.URL.Path, c.BaseURL+assets.BaseURL) { @@ -30,7 +27,7 @@ func ServeHTTP(w http.ResponseWriter, r *http.Request, c *fm.Config) (int, error } return http.StatusForbidden, nil - } + } */ // Obtains the user. username, _, _ := r.BasicAuth() @@ -91,7 +88,7 @@ func ServeHTTP(w http.ResponseWriter, r *http.Request, c *fm.Config) (int, error return http.StatusInternalServerError, err } - if preProccessPUT(w, r, c, user) != nil { + if c.preProccessPUT(w, r, user) != nil { return http.StatusInternalServerError, err } } @@ -111,7 +108,7 @@ func ServeHTTP(w http.ResponseWriter, r *http.Request, c *fm.Config) (int, error // Checks if the User is allowed to access this file if !user.Allowed(strings.TrimPrefix(r.URL.Path, c.BaseURL)) { if r.Method == http.MethodGet { - return page.PrintErrorHTML( + return printError( w, http.StatusForbidden, errors.New("You don't have permission to access this page"), ) @@ -121,20 +118,20 @@ func ServeHTTP(w http.ResponseWriter, r *http.Request, c *fm.Config) (int, error } if r.URL.Query().Get("search") != "" { - return search(w, r, c, user) + return c.search(w, r, user) } if r.URL.Query().Get("command") != "" { - return command(w, r, c, user) + return c.command(w, r, user) } if r.Method == http.MethodGet { // Gets the information of the directory/file - fi, err = fm.GetInfo(r.URL, c, user) + fi, err = getFile(r.URL, c, user) code = errorToHTTPCode(err, false) if err != nil { if r.Method == http.MethodGet { - return page.PrintErrorHTML(w, code, err) + return printError(w, code, err) } return code, err } @@ -148,20 +145,20 @@ func ServeHTTP(w http.ResponseWriter, r *http.Request, c *fm.Config) (int, error switch { case r.URL.Query().Get("download") != "": - code, err = download(w, r, c, fi) + code, err = c.download(w, r, fi) case r.URL.Query().Get("raw") == "true" && !fi.IsDir: http.ServeFile(w, r, fi.Path) code, err = 0, nil case !fi.IsDir && r.URL.Query().Get("checksum") != "": - code, err = checksum(w, r, c, fi) + code, err = c.checksum(w, r, fi) case fi.IsDir: - code, err = serveListing(w, r, c, user, fi) + code, err = c.serveListing(w, r, user, fi) default: - code, err = serveSingle(w, r, c, user, fi) + code, err = c.serveSingle(w, r, user, fi) } if err != nil { - code, err = page.PrintErrorHTML(w, code, err) + code, err = printError(w, code, err) } return code, err @@ -169,21 +166,3 @@ func ServeHTTP(w http.ResponseWriter, r *http.Request, c *fm.Config) (int, error return http.StatusNotImplemented, nil } - -// errorToHTTPCode converts errors to HTTP Status Code. -func errorToHTTPCode(err error, gone bool) int { - switch { - case os.IsPermission(err): - return http.StatusForbidden - case os.IsNotExist(err): - if !gone { - return http.StatusNotFound - } - - return http.StatusGone - case os.IsExist(err): - return http.StatusGone - default: - return http.StatusInternalServerError - } -} diff --git a/http/checksum.go b/http_checksum.go similarity index 76% rename from http/checksum.go rename to http_checksum.go index a6d535bd..12039a6a 100644 --- a/http/checksum.go +++ b/http_checksum.go @@ -1,4 +1,4 @@ -package http +package filemanager import ( "crypto/md5" @@ -6,17 +6,15 @@ import ( "crypto/sha256" "crypto/sha512" "encoding/hex" - e "errors" + "errors" "hash" "io" "net/http" "os" - - fm "github.com/hacdias/filemanager" ) // checksum calculates the hash of a filemanager. Supports MD5, SHA1, SHA256 and SHA512. -func checksum(w http.ResponseWriter, r *http.Request, c *fm.Config, i *fm.FileInfo) (int, error) { +func (c *Config) checksum(w http.ResponseWriter, r *http.Request, i *file) (int, error) { query := r.URL.Query().Get("checksum") file, err := os.Open(i.Path) @@ -38,7 +36,7 @@ func checksum(w http.ResponseWriter, r *http.Request, c *fm.Config, i *fm.FileIn case "sha512": h = sha512.New() default: - return http.StatusBadRequest, e.New("Unknown HASH type") + return http.StatusBadRequest, errors.New("Unknown HASH type") } _, err = io.Copy(h, file) diff --git a/http/command.go b/http_command.go similarity index 95% rename from http/command.go rename to http_command.go index 1c1a8ce9..2529b152 100644 --- a/http/command.go +++ b/http_command.go @@ -1,4 +1,4 @@ -package http +package filemanager import ( "bytes" @@ -9,7 +9,6 @@ import ( "time" "github.com/gorilla/websocket" - fm "github.com/hacdias/filemanager" ) var upgrader = websocket.Upgrader{ @@ -23,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 *fm.Config, u *fm.User) (int, error) { +func (c *Config) command(w http.ResponseWriter, r *http.Request, u *User) (int, error) { // Upgrades the connection to a websocket and checks for errors. conn, err := upgrader.Upgrade(w, r, nil) if err != nil { diff --git a/http/download.go b/http_download.go similarity index 92% rename from http/download.go rename to http_download.go index b1848e47..6d987d58 100644 --- a/http/download.go +++ b/http_download.go @@ -1,4 +1,4 @@ -package http +package filemanager import ( "io" @@ -9,13 +9,12 @@ import ( "path/filepath" "strings" - fm "github.com/hacdias/filemanager" "github.com/mholt/archiver" ) // 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, c *fm.Config, i *fm.FileInfo) (int, error) { +func (c *Config) download(w http.ResponseWriter, r *http.Request, i *file) (int, error) { query := r.URL.Query().Get("download") if !i.IsDir { diff --git a/http/listing.go b/http_listing.go similarity index 89% rename from http/listing.go rename to http_listing.go index 807bbe18..3f82bc7e 100644 --- a/http/listing.go +++ b/http_listing.go @@ -1,4 +1,4 @@ -package http +package filemanager import ( "encoding/json" @@ -6,17 +6,15 @@ import ( "strconv" "strings" - fm "github.com/hacdias/filemanager" - "github.com/hacdias/filemanager/page" "github.com/mholt/caddy/caddyhttp/httpserver" ) // serveListing presents the user with a listage of a directory folder. -func serveListing(w http.ResponseWriter, r *http.Request, c *fm.Config, u *fm.User, i *fm.FileInfo) (int, error) { +func (c *Config) serveListing(w http.ResponseWriter, r *http.Request, u *User, i *file) (int, error) { var err error // Loads the content of the directory - listing, err := fm.GetListing(u, i.VirtualPath, c.PrefixURL+r.URL.Path) + listing, err := GetListing(u, i.VirtualPath, c.PrefixURL+r.URL.Path) if err != nil { return errorToHTTPCode(err, true), err } @@ -79,9 +77,9 @@ func serveListing(w http.ResponseWriter, r *http.Request, c *fm.Config, u *fm.Us Secure: r.TLS != nil, }) - page := &page.Page{ + page := &page{ Minimal: r.Header.Get("Minimal") == "true", - Info: &page.Info{ + Info: &pageInfo{ Name: listing.Name, Path: i.VirtualPath, IsDir: true, @@ -92,7 +90,7 @@ func serveListing(w http.ResponseWriter, r *http.Request, c *fm.Config, u *fm.Us }, } - return page.PrintAsHTML(w, "listing") + return page.PrintHTML(w, "listing") } // handleSortOrder gets and stores for a Listing the 'sort' and 'order', diff --git a/http/put.go b/http_put.go similarity index 94% rename from http/put.go rename to http_put.go index 23d0418b..679b9802 100644 --- a/http/put.go +++ b/http_put.go @@ -1,4 +1,4 @@ -package http +package filemanager import ( "bytes" @@ -10,17 +10,11 @@ import ( "strconv" "strings" - "github.com/hacdias/filemanager" "github.com/hacdias/filemanager/frontmatter" ) // preProccessPUT is used to update a file that was edited -func preProccessPUT( - w http.ResponseWriter, - r *http.Request, - c *filemanager.Config, - u *filemanager.User, -) (err error) { +func (c *Config) preProccessPUT(w http.ResponseWriter, r *http.Request, u *User) (err error) { var ( data = map[string]interface{}{} file []byte diff --git a/http/search.go b/http_search.go similarity index 94% rename from http/search.go rename to http_search.go index 3604404f..8604fcc5 100644 --- a/http/search.go +++ b/http_search.go @@ -1,4 +1,4 @@ -package http +package filemanager import ( "net/http" @@ -7,7 +7,6 @@ import ( "strings" "github.com/gorilla/websocket" - fm "github.com/hacdias/filemanager" ) type searchOptions struct { @@ -44,7 +43,7 @@ func parseSearch(value string) *searchOptions { } // search ... -func search(w http.ResponseWriter, r *http.Request, c *fm.Config, u *fm.User) (int, error) { +func (c *Config) search(w http.ResponseWriter, r *http.Request, u *User) (int, error) { // Upgrades the connection to a websocket and checks for errors. conn, err := upgrader.Upgrade(w, r, nil) if err != nil { diff --git a/http/single.go b/http_single.go similarity index 64% rename from http/single.go rename to http_single.go index 88709af6..fbf0f47d 100644 --- a/http/single.go +++ b/http_single.go @@ -1,24 +1,21 @@ -package http +package filemanager import ( "net/http" "strings" - - fm "github.com/hacdias/filemanager" - "github.com/hacdias/filemanager/page" ) // 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 *fm.Config, u *fm.User, i *fm.FileInfo) (int, error) { +func (c *Config) serveSingle(w http.ResponseWriter, r *http.Request, u *User, i *file) (int, error) { var err error if err = i.RetrieveFileType(); err != nil { return errorToHTTPCode(err, true), err } - p := &page.Page{ - Info: &page.Info{ + p := &page{ + Info: &pageInfo{ Name: i.Name, Path: i.VirtualPath, IsDir: false, @@ -30,7 +27,7 @@ func serveSingle(w http.ResponseWriter, r *http.Request, c *fm.Config, u *fm.Use // If the request accepts JSON, we send the file information. if strings.Contains(r.Header.Get("Accept"), "application/json") { - return p.PrintAsJSON(w) + return p.PrintJSON(w) } if i.Type == "text" { @@ -40,14 +37,14 @@ func serveSingle(w http.ResponseWriter, r *http.Request, c *fm.Config, u *fm.Use } if i.CanBeEdited() && u.AllowEdit { - p.Data, err = getEditor(r, i) - p.Editor = true + p.Info.Data, err = newEditor(r, i) + p.Info.Editor = true if err != nil { return http.StatusInternalServerError, err } - return p.PrintAsHTML(w, "frontmatter", "editor") + return p.PrintHTML(w, "frontmatter", "editor") } - return p.PrintAsHTML(w, "single") + return p.PrintHTML(w, "single") } diff --git a/http_utils.go b/http_utils.go new file mode 100644 index 00000000..6ce6f8ea --- /dev/null +++ b/http_utils.go @@ -0,0 +1,24 @@ +package filemanager + +import ( + "net/http" + "os" +) + +// errorToHTTPCode converts errors to HTTP Status Code. +func errorToHTTPCode(err error, gone bool) int { + switch { + case os.IsPermission(err): + return http.StatusForbidden + case os.IsNotExist(err): + if !gone { + return http.StatusNotFound + } + + return http.StatusGone + case os.IsExist(err): + return http.StatusGone + default: + return http.StatusInternalServerError + } +} diff --git a/listing.go b/listing.go index b272c2af..79c53c11 100644 --- a/listing.go +++ b/listing.go @@ -18,7 +18,7 @@ type Listing struct { // The full path of the request relatively to a File System Path string // The items (files and folders) in the path - Items []FileInfo + Items []file // The number of directories in the listing NumDirs int // The number of files (items that aren't directories) in the listing @@ -49,7 +49,7 @@ func GetListing(u *User, filePath string, baseURL string) (*Listing, error) { } var ( - fileinfos []FileInfo + fileinfos []*file dirCount, fileCount int ) @@ -71,7 +71,7 @@ func GetListing(u *User, filePath string, baseURL string) (*Listing, error) { // Absolute URL url := url.URL{Path: baseURL + name} - i := FileInfo{ + i := &file{ Name: f.Name(), Size: f.Size(), ModTime: f.ModTime(), diff --git a/page/page.go b/page.go similarity index 53% rename from page/page.go rename to page.go index 8b8c3a65..ba46b4f8 100644 --- a/page/page.go +++ b/page.go @@ -1,31 +1,29 @@ -// Package page is used to render the HTML to the end user -package page +package filemanager import ( "bytes" "encoding/json" + "errors" "html/template" "log" "net/http" + "strconv" "strings" - - "github.com/hacdias/filemanager" - "github.com/hacdias/filemanager/assets" ) -// Page contains the informations and functions needed to show the Page -type Page struct { - *Info +// page contains the informations and functions needed to show the Page +type page struct { + Info *pageInfo Minimal bool } -// Info contains the information of a Page -type Info struct { +// pageInfo contains the information of a Page +type pageInfo struct { Name string Path string IsDir bool - User *filemanager.User - Config *filemanager.Config + User *User + Config *Config Data interface{} Editor bool Display string @@ -39,7 +37,7 @@ type BreadcrumbMapItem struct { // BreadcrumbMap returns p.Path where every element is a map // of URLs and path segment names. -func (i Info) BreadcrumbMap() []BreadcrumbMapItem { +func (i pageInfo) BreadcrumbMap() []BreadcrumbMapItem { result := []BreadcrumbMapItem{} if len(i.Path) == 0 { @@ -76,7 +74,7 @@ func (i Info) BreadcrumbMap() []BreadcrumbMapItem { } // PreviousLink returns the path of the previous folder -func (i Info) PreviousLink() string { +func (i pageInfo) PreviousLink() string { path := strings.TrimSuffix(i.Path, "/") path = strings.TrimPrefix(path, "/") path = i.Config.AbsoluteURL() + "/" + path @@ -89,8 +87,8 @@ func (i Info) PreviousLink() string { return path } -// PrintAsHTML formats the page in HTML and executes the template -func (p Page) PrintAsHTML(w http.ResponseWriter, templates ...string) (int, error) { +// PrintHTML formats the page in HTML and executes the template +func (p page) PrintHTML(w http.ResponseWriter, templates ...string) (int, error) { if p.Minimal { templates = append(templates, "minimal") @@ -103,7 +101,8 @@ func (p Page) PrintAsHTML(w http.ResponseWriter, templates ...string) (int, erro // For each template, add it to the the tpl variable for i, t := range templates { // Get the template from the assets - Page, err := assets.Asset("templates/" + t + ".tmpl") + //Page, err := assets.Asset("templates/" + t + ".tmpl") + Page, err := []byte("Shit"), errors.New("Hello") // Check if there is some error. If so, the template doesn't exist if err != nil { @@ -137,8 +136,8 @@ func (p Page) PrintAsHTML(w http.ResponseWriter, templates ...string) (int, erro return http.StatusOK, err } -// PrintAsJSON prints the current Page information in JSON -func (p Page) PrintAsJSON(w http.ResponseWriter) (int, error) { +// PrintJSON prints the current Page information in JSON +func (p page) PrintJSON(w http.ResponseWriter) (int, error) { marsh, err := json.MarshalIndent(p.Info.Data, "", " ") if err != nil { return http.StatusInternalServerError, err @@ -151,3 +150,61 @@ func (p Page) PrintAsJSON(w http.ResponseWriter) (int, error) { return http.StatusOK, nil } + +// printError prints the error page +func printError(w http.ResponseWriter, code int, err error) (int, error) { + tpl := errorTemplate + tpl = strings.Replace(tpl, "TITLE", strconv.Itoa(code)+" "+http.StatusText(code), -1) + tpl = strings.Replace(tpl, "CODE", err.Error(), -1) + + _, err = w.Write([]byte(tpl)) + + if err != nil { + return http.StatusInternalServerError, err + } + return http.StatusOK, nil +} + +const errorTemplate = ` + +
+Try reloading the page or hitting the back button. If this error persists, it seems that you may have found a bug! Please create an issue at hacdias/caddy-filemanager repository on GitHub with the code below.
+ +CODE
+ Try reloading the page or hitting the back button. If this error persists, it seems that you may have found a bug! Please create an issue at hacdias/caddy-filemanager repository on GitHub with the code below.
- -CODE
-