2017-06-24 19:12:15 +08:00
|
|
|
package filemanager
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io/ioutil"
|
|
|
|
"mime"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
humanize "github.com/dustin/go-humanize"
|
|
|
|
)
|
|
|
|
|
2017-06-25 20:03:59 +08:00
|
|
|
// fileInfo contains the information about a particular file or directory.
|
|
|
|
type fileInfo struct {
|
2017-06-24 19:12:15 +08:00
|
|
|
Name string
|
|
|
|
Size int64
|
|
|
|
URL string
|
|
|
|
Extension string
|
|
|
|
ModTime time.Time
|
|
|
|
Mode os.FileMode
|
|
|
|
IsDir bool
|
|
|
|
Path string // Relative path to Current Working Directory
|
|
|
|
VirtualPath string // Relative path to user's virtual File System
|
|
|
|
Mimetype string
|
|
|
|
Type string
|
|
|
|
UserAllowed bool // Indicates if the user has enough permissions
|
|
|
|
|
|
|
|
content []byte
|
|
|
|
}
|
|
|
|
|
2017-06-25 20:03:59 +08:00
|
|
|
// getInfo gets the file information and, in case of error, returns the
|
2017-06-24 19:12:15 +08:00
|
|
|
// respective HTTP error code
|
2017-06-25 20:03:59 +08:00
|
|
|
func getInfo(url *url.URL, c *FileManager, u *user) (*fileInfo, error) {
|
2017-06-24 19:12:15 +08:00
|
|
|
var err error
|
|
|
|
|
2017-06-25 20:03:59 +08:00
|
|
|
i := &fileInfo{URL: c.PrefixURL + url.Path}
|
2017-06-25 20:09:49 +08:00
|
|
|
i.VirtualPath = strings.Replace(url.Path, c.baseURL, "", 1)
|
2017-06-24 19:12:15 +08:00
|
|
|
i.VirtualPath = strings.TrimPrefix(i.VirtualPath, "/")
|
|
|
|
i.VirtualPath = "/" + i.VirtualPath
|
|
|
|
|
2017-06-25 17:52:34 +08:00
|
|
|
i.Path = u.scope + i.VirtualPath
|
2017-06-24 19:12:15 +08:00
|
|
|
i.Path = filepath.Clean(i.Path)
|
|
|
|
|
|
|
|
info, err := os.Stat(i.Path)
|
|
|
|
if err != nil {
|
|
|
|
return i, err
|
|
|
|
}
|
|
|
|
|
|
|
|
i.Name = info.Name()
|
|
|
|
i.ModTime = info.ModTime()
|
|
|
|
i.Mode = info.Mode()
|
|
|
|
i.IsDir = info.IsDir()
|
|
|
|
i.Size = info.Size()
|
|
|
|
i.Extension = filepath.Ext(i.Name)
|
|
|
|
return i, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var textExtensions = [...]string{
|
|
|
|
".md", ".markdown", ".mdown", ".mmark",
|
|
|
|
".asciidoc", ".adoc", ".ad",
|
|
|
|
".rst",
|
|
|
|
".json", ".toml", ".yaml", ".csv", ".xml", ".rss", ".conf", ".ini",
|
|
|
|
".tex", ".sty",
|
|
|
|
".css", ".sass", ".scss",
|
|
|
|
".js",
|
|
|
|
".html",
|
|
|
|
".txt", ".rtf",
|
|
|
|
".sh", ".bash", ".ps1", ".bat", ".cmd",
|
|
|
|
".php", ".pl", ".py",
|
|
|
|
"Caddyfile",
|
|
|
|
".c", ".cc", ".h", ".hh", ".cpp", ".hpp", ".f90",
|
|
|
|
".f", ".bas", ".d", ".ada", ".nim", ".cr", ".java", ".cs", ".vala", ".vapi",
|
|
|
|
}
|
|
|
|
|
|
|
|
// RetrieveFileType obtains the mimetype and a simplified internal Type
|
|
|
|
// using the first 512 bytes from the file.
|
2017-06-25 20:03:59 +08:00
|
|
|
func (i *fileInfo) RetrieveFileType() error {
|
2017-06-24 19:12:15 +08:00
|
|
|
i.Mimetype = mime.TypeByExtension(i.Extension)
|
|
|
|
|
|
|
|
if i.Mimetype == "" {
|
|
|
|
err := i.Read()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
i.Mimetype = http.DetectContentType(i.content)
|
|
|
|
}
|
|
|
|
|
|
|
|
if strings.HasPrefix(i.Mimetype, "video") {
|
|
|
|
i.Type = "video"
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if strings.HasPrefix(i.Mimetype, "audio") {
|
|
|
|
i.Type = "audio"
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if strings.HasPrefix(i.Mimetype, "image") {
|
|
|
|
i.Type = "image"
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if strings.HasPrefix(i.Mimetype, "text") {
|
|
|
|
i.Type = "text"
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if strings.HasPrefix(i.Mimetype, "application/javascript") {
|
|
|
|
i.Type = "text"
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the type isn't text (and is blob for example), it will check some
|
|
|
|
// common types that are mistaken not to be text.
|
|
|
|
for _, extension := range textExtensions {
|
|
|
|
if strings.HasSuffix(i.Name, extension) {
|
|
|
|
i.Type = "text"
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
i.Type = "blob"
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reads the file.
|
2017-06-25 20:03:59 +08:00
|
|
|
func (i *fileInfo) Read() error {
|
2017-06-24 19:12:15 +08:00
|
|
|
if len(i.content) != 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var err error
|
|
|
|
i.content, err = ioutil.ReadFile(i.Path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// StringifyContent returns the string version of Raw
|
2017-06-25 20:03:59 +08:00
|
|
|
func (i fileInfo) StringifyContent() string {
|
2017-06-24 19:12:15 +08:00
|
|
|
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).
|
2017-06-25 20:03:59 +08:00
|
|
|
func (i fileInfo) HumanSize() string {
|
2017-06-24 19:12:15 +08:00
|
|
|
return humanize.IBytes(uint64(i.Size))
|
|
|
|
}
|
|
|
|
|
|
|
|
// HumanModTime returns the modified time of the file as a human-readable string.
|
2017-06-25 20:03:59 +08:00
|
|
|
func (i fileInfo) HumanModTime(format string) string {
|
2017-06-24 19:12:15 +08:00
|
|
|
return i.ModTime.Format(format)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CanBeEdited checks if the extension of a file is supported by the editor
|
2017-06-25 20:03:59 +08:00
|
|
|
func (i fileInfo) CanBeEdited() bool {
|
2017-06-24 19:12:15 +08:00
|
|
|
return i.Type == "text"
|
|
|
|
}
|