| 
									
										
										
										
											2018-02-01 13:38:43 +00:00
										 |  |  | package filebrowser | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2017-07-02 16:53:47 +00:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2017-06-27 14:44:20 +00:00
										 |  |  | 	"crypto/md5" | 
					
						
							|  |  |  | 	"crypto/sha1" | 
					
						
							|  |  |  | 	"crypto/sha256" | 
					
						
							|  |  |  | 	"crypto/sha512" | 
					
						
							|  |  |  | 	"encoding/hex" | 
					
						
							|  |  |  | 	"hash" | 
					
						
							|  |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 	"io/ioutil" | 
					
						
							|  |  |  | 	"mime" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"net/url" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 	"strings" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-11 16:45:30 +00:00
										 |  |  | 	"github.com/gohugoio/hugo/parser" | 
					
						
							| 
									
										
										
										
											2018-07-29 11:22:11 +00:00
										 |  |  | 	"github.com/maruel/natural" | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-18 08:00:32 +00:00
										 |  |  | // File contains the information about a particular file or directory.
 | 
					
						
							|  |  |  | type File struct { | 
					
						
							| 
									
										
										
										
											2017-08-19 11:35:44 +00:00
										 |  |  | 	// Indicates the Kind of view on the front-end (Listing, editor or preview).
 | 
					
						
							| 
									
										
										
										
											2017-07-02 16:40:52 +00:00
										 |  |  | 	Kind string `json:"kind"` | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 	// The name of the file.
 | 
					
						
							| 
									
										
										
										
											2017-06-28 15:05:30 +00:00
										 |  |  | 	Name string `json:"name"` | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 	// The Size of the file.
 | 
					
						
							| 
									
										
										
										
											2017-06-28 15:05:30 +00:00
										 |  |  | 	Size int64 `json:"size"` | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 	// The absolute URL.
 | 
					
						
							| 
									
										
										
										
											2017-06-28 15:05:30 +00:00
										 |  |  | 	URL string `json:"url"` | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 	// The extension of the file.
 | 
					
						
							| 
									
										
										
										
											2017-06-28 15:05:30 +00:00
										 |  |  | 	Extension string `json:"extension"` | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 	// The last modified time.
 | 
					
						
							| 
									
										
										
										
											2017-06-28 15:05:30 +00:00
										 |  |  | 	ModTime time.Time `json:"modified"` | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 	// The File Mode.
 | 
					
						
							| 
									
										
										
										
											2017-06-28 15:05:30 +00:00
										 |  |  | 	Mode os.FileMode `json:"mode"` | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 	// Indicates if this file is a directory.
 | 
					
						
							| 
									
										
										
										
											2017-06-28 15:05:30 +00:00
										 |  |  | 	IsDir bool `json:"isDir"` | 
					
						
							| 
									
										
										
										
											2017-06-25 13:24:26 +00:00
										 |  |  | 	// Absolute path.
 | 
					
						
							| 
									
										
										
										
											2017-06-28 15:05:30 +00:00
										 |  |  | 	Path string `json:"path"` | 
					
						
							| 
									
										
										
										
											2017-06-25 13:24:26 +00:00
										 |  |  | 	// Relative path to user's virtual File System.
 | 
					
						
							| 
									
										
										
										
											2017-06-28 15:05:30 +00:00
										 |  |  | 	VirtualPath string `json:"virtualPath"` | 
					
						
							| 
									
										
										
										
											2017-06-25 13:24:26 +00:00
										 |  |  | 	// Indicates the file content type: video, text, image, music or blob.
 | 
					
						
							| 
									
										
										
										
											2017-06-28 15:05:30 +00:00
										 |  |  | 	Type string `json:"type"` | 
					
						
							| 
									
										
										
										
											2017-06-28 10:45:41 +00:00
										 |  |  | 	// Stores the content of a text file.
 | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 	Content string `json:"content,omitempty"` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-19 11:35:44 +00:00
										 |  |  | 	*Listing `json:",omitempty"` | 
					
						
							| 
									
										
										
										
											2017-07-01 07:50:42 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	Metadata string `json:"metadata,omitempty"` | 
					
						
							|  |  |  | 	Language string `json:"language,omitempty"` | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-19 11:35:44 +00:00
										 |  |  | // A Listing is the context used to fill out a template.
 | 
					
						
							|  |  |  | type Listing struct { | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 	// The items (files and folders) in the path.
 | 
					
						
							| 
									
										
										
										
											2017-08-18 08:00:32 +00:00
										 |  |  | 	Items []*File `json:"items"` | 
					
						
							| 
									
										
										
										
											2017-08-19 11:35:44 +00:00
										 |  |  | 	// The number of directories in the Listing.
 | 
					
						
							| 
									
										
										
										
											2017-06-28 15:05:30 +00:00
										 |  |  | 	NumDirs int `json:"numDirs"` | 
					
						
							| 
									
										
										
										
											2017-08-19 11:35:44 +00:00
										 |  |  | 	// The number of files (items that aren't directories) in the Listing.
 | 
					
						
							| 
									
										
										
										
											2017-06-28 15:05:30 +00:00
										 |  |  | 	NumFiles int `json:"numFiles"` | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 	// Which sorting order is used.
 | 
					
						
							| 
									
										
										
										
											2017-06-28 15:05:30 +00:00
										 |  |  | 	Sort string `json:"sort"` | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 	// And which order.
 | 
					
						
							| 
									
										
										
										
											2017-06-28 15:05:30 +00:00
										 |  |  | 	Order string `json:"order"` | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-18 08:00:32 +00:00
										 |  |  | // GetInfo gets the file information and, in case of error, returns the
 | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | // respective HTTP error code
 | 
					
						
							| 
									
										
										
										
											2018-02-01 13:38:43 +00:00
										 |  |  | func GetInfo(url *url.URL, c *FileBrowser, u *User) (*File, error) { | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 	var err error | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-18 08:00:32 +00:00
										 |  |  | 	i := &File{ | 
					
						
							| 
									
										
										
										
											2017-07-26 14:55:39 +00:00
										 |  |  | 		URL:         "/files" + url.String(), | 
					
						
							| 
									
										
										
										
											2017-07-02 16:40:52 +00:00
										 |  |  | 		VirtualPath: url.Path, | 
					
						
							| 
									
										
										
										
											2017-08-20 08:21:36 +00:00
										 |  |  | 		Path:        filepath.Join(u.Scope, url.Path), | 
					
						
							| 
									
										
										
										
											2017-07-02 16:40:52 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-26 10:14:05 +00:00
										 |  |  | 	info, err := u.FileSystem.Stat(url.Path) | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 	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) | 
					
						
							| 
									
										
										
										
											2017-07-03 14:19:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if i.IsDir && !strings.HasSuffix(i.URL, "/") { | 
					
						
							|  |  |  | 		i.URL += "/" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 	return i, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-18 08:00:32 +00:00
										 |  |  | // GetListing gets the information about a specific directory and its files.
 | 
					
						
							|  |  |  | func (i *File) GetListing(u *User, r *http.Request) error { | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 	// Gets the directory information using the Virtual File System of
 | 
					
						
							|  |  |  | 	// the user configuration.
 | 
					
						
							| 
									
										
										
										
											2017-08-18 08:00:32 +00:00
										 |  |  | 	f, err := u.FileSystem.OpenFile(i.VirtualPath, os.O_RDONLY, 0) | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 	defer f.Close() | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Reads the directory and gets the information about the files.
 | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 	files, err := f.Readdir(-1) | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var ( | 
					
						
							| 
									
										
										
										
											2017-08-18 08:00:32 +00:00
										 |  |  | 		fileinfos           []*File | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 		dirCount, fileCount int | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-26 14:55:39 +00:00
										 |  |  | 	baseurl, err := url.PathUnescape(i.URL) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 	for _, f := range files { | 
					
						
							|  |  |  | 		name := f.Name() | 
					
						
							| 
									
										
										
										
											2017-08-18 08:00:32 +00:00
										 |  |  | 		allowed := u.Allowed("/" + name) | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if !allowed { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-14 07:21:26 +00:00
										 |  |  | 		if strings.HasPrefix(f.Mode().String(), "L") { | 
					
						
							| 
									
										
										
										
											2018-02-18 09:49:40 +00:00
										 |  |  | 			// It's a symbolic link. We try to follow it. If it doesn't work,
 | 
					
						
							|  |  |  | 			// we stay with the link information instead if the target's.
 | 
					
						
							| 
									
										
										
										
											2017-11-14 07:21:26 +00:00
										 |  |  | 			info, err := os.Stat(f.Name()) | 
					
						
							| 
									
										
										
										
											2018-02-18 09:49:40 +00:00
										 |  |  | 			if err == nil { | 
					
						
							|  |  |  | 				f = info | 
					
						
							| 
									
										
										
										
											2017-11-14 07:21:26 +00:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 		if f.IsDir() { | 
					
						
							|  |  |  | 			name += "/" | 
					
						
							|  |  |  | 			dirCount++ | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			fileCount++ | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Absolute URL
 | 
					
						
							| 
									
										
										
										
											2017-07-26 14:55:39 +00:00
										 |  |  | 		url := url.URL{Path: baseurl + name} | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-18 08:00:32 +00:00
										 |  |  | 		i := &File{ | 
					
						
							| 
									
										
										
										
											2017-07-08 11:46:19 +00:00
										 |  |  | 			Name:        f.Name(), | 
					
						
							|  |  |  | 			Size:        f.Size(), | 
					
						
							|  |  |  | 			ModTime:     f.ModTime(), | 
					
						
							|  |  |  | 			Mode:        f.Mode(), | 
					
						
							|  |  |  | 			IsDir:       f.IsDir(), | 
					
						
							|  |  |  | 			URL:         url.String(), | 
					
						
							|  |  |  | 			Extension:   filepath.Ext(name), | 
					
						
							|  |  |  | 			VirtualPath: filepath.Join(i.VirtualPath, name), | 
					
						
							|  |  |  | 			Path:        filepath.Join(i.Path, name), | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-26 08:44:09 +00:00
										 |  |  | 		i.GetFileType(false) | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 		fileinfos = append(fileinfos, i) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-19 11:35:44 +00:00
										 |  |  | 	i.Listing = &Listing{ | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 		Items:    fileinfos, | 
					
						
							|  |  |  | 		NumDirs:  dirCount, | 
					
						
							|  |  |  | 		NumFiles: fileCount, | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-18 08:00:32 +00:00
										 |  |  | // GetEditor gets the editor based on a Info struct
 | 
					
						
							|  |  |  | func (i *File) GetEditor() error { | 
					
						
							| 
									
										
										
										
											2017-07-01 07:50:42 +00:00
										 |  |  | 	i.Language = editorLanguage(i.Extension) | 
					
						
							|  |  |  | 	// If the editor will hold only content, leave now.
 | 
					
						
							|  |  |  | 	if editorMode(i.Language) == "content" { | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-01 07:50:42 +00:00
										 |  |  | 	// If the file doesn't have any kind of metadata, leave now.
 | 
					
						
							| 
									
										
										
										
											2017-07-02 16:53:47 +00:00
										 |  |  | 	if !hasRune(i.Content) { | 
					
						
							| 
									
										
										
										
											2017-07-01 07:50:42 +00:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-02 16:53:47 +00:00
										 |  |  | 	buffer := bytes.NewBuffer([]byte(i.Content)) | 
					
						
							|  |  |  | 	page, err := parser.ReadFrom(buffer) | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-02 16:53:47 +00:00
										 |  |  | 	// If there is an error, just ignore it and return nil.
 | 
					
						
							|  |  |  | 	// This way, the file can be served for editing.
 | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-02 16:53:47 +00:00
										 |  |  | 	i.Content = strings.TrimSpace(string(page.Content())) | 
					
						
							|  |  |  | 	i.Metadata = strings.TrimSpace(string(page.FrontMatter())) | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-26 08:44:09 +00:00
										 |  |  | // GetFileType obtains the mimetype and converts it to a simple
 | 
					
						
							| 
									
										
										
										
											2017-06-25 13:24:26 +00:00
										 |  |  | // type nomenclature.
 | 
					
						
							| 
									
										
										
										
											2017-08-18 08:00:32 +00:00
										 |  |  | func (i *File) GetFileType(checkContent bool) error { | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 	var content []byte | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-25 13:24:26 +00:00
										 |  |  | 	// Tries to get the file mimetype using its extension.
 | 
					
						
							|  |  |  | 	mimetype := mime.TypeByExtension(i.Extension) | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-08 11:46:19 +00:00
										 |  |  | 	if mimetype == "" && checkContent { | 
					
						
							| 
									
										
										
										
											2017-07-10 07:43:46 +00:00
										 |  |  | 		file, err := os.Open(i.Path) | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-07-10 07:43:46 +00:00
										 |  |  | 		defer file.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Only the first 512 bytes are used to sniff the content type.
 | 
					
						
							|  |  |  | 		buffer := make([]byte, 512) | 
					
						
							|  |  |  | 		_, err = file.Read(buffer) | 
					
						
							|  |  |  | 		if err != nil && err != io.EOF { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-25 13:24:26 +00:00
										 |  |  | 		// Tries to get the file mimetype using its first
 | 
					
						
							|  |  |  | 		// 512 bytes.
 | 
					
						
							| 
									
										
										
										
											2017-07-10 07:43:46 +00:00
										 |  |  | 		mimetype = http.DetectContentType(buffer) | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-25 13:24:26 +00:00
										 |  |  | 	if strings.HasPrefix(mimetype, "video") { | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 		i.Type = "video" | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-25 13:24:26 +00:00
										 |  |  | 	if strings.HasPrefix(mimetype, "audio") { | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 		i.Type = "audio" | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-25 13:24:26 +00:00
										 |  |  | 	if strings.HasPrefix(mimetype, "image") { | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 		i.Type = "image" | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-25 13:24:26 +00:00
										 |  |  | 	if strings.HasPrefix(mimetype, "text") { | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 		i.Type = "text" | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 		goto End | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-25 13:24:26 +00:00
										 |  |  | 	if strings.HasPrefix(mimetype, "application/javascript") { | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 		i.Type = "text" | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 		goto End | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// If the type isn't text (and is blob for example), it will check some
 | 
					
						
							|  |  |  | 	// common types that are mistaken not to be text.
 | 
					
						
							| 
									
										
										
										
											2017-11-15 21:03:50 +00:00
										 |  |  | 	if isInTextExtensions(i.Name) { | 
					
						
							|  |  |  | 		i.Type = "text" | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		i.Type = "blob" | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | End: | 
					
						
							|  |  |  | 	// If the file type is text, save its content.
 | 
					
						
							|  |  |  | 	if i.Type == "text" { | 
					
						
							|  |  |  | 		if len(content) == 0 { | 
					
						
							|  |  |  | 			content, err = ioutil.ReadFile(i.Path) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 		i.Content = string(content) | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-18 08:00:32 +00:00
										 |  |  | // Checksum retrieves the checksum of a file.
 | 
					
						
							|  |  |  | func (i File) Checksum(algo string) (string, error) { | 
					
						
							| 
									
										
										
										
											2017-06-27 14:44:20 +00:00
										 |  |  | 	file, err := os.Open(i.Path) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	defer file.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var h hash.Hash | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-18 08:00:32 +00:00
										 |  |  | 	switch algo { | 
					
						
							| 
									
										
										
										
											2017-06-27 14:44:20 +00:00
										 |  |  | 	case "md5": | 
					
						
							|  |  |  | 		h = md5.New() | 
					
						
							|  |  |  | 	case "sha1": | 
					
						
							|  |  |  | 		h = sha1.New() | 
					
						
							|  |  |  | 	case "sha256": | 
					
						
							|  |  |  | 		h = sha256.New() | 
					
						
							|  |  |  | 	case "sha512": | 
					
						
							|  |  |  | 		h = sha512.New() | 
					
						
							|  |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2017-08-19 11:35:44 +00:00
										 |  |  | 		return "", ErrInvalidOption | 
					
						
							| 
									
										
										
										
											2017-06-27 14:44:20 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_, err = io.Copy(h, file) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return hex.EncodeToString(h.Sum(nil)), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | // CanBeEdited checks if the extension of a file is supported by the editor
 | 
					
						
							| 
									
										
										
										
											2017-08-18 08:00:32 +00:00
										 |  |  | func (i File) CanBeEdited() bool { | 
					
						
							| 
									
										
										
										
											2017-06-24 11:12:15 +00:00
										 |  |  | 	return i.Type == "text" | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | // ApplySort applies the sort order using .Order and .Sort
 | 
					
						
							| 
									
										
										
										
											2017-08-19 11:35:44 +00:00
										 |  |  | func (l Listing) ApplySort() { | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 	// Check '.Order' to know how to sort
 | 
					
						
							|  |  |  | 	if l.Order == "desc" { | 
					
						
							|  |  |  | 		switch l.Sort { | 
					
						
							|  |  |  | 		case "name": | 
					
						
							|  |  |  | 			sort.Sort(sort.Reverse(byName(l))) | 
					
						
							|  |  |  | 		case "size": | 
					
						
							|  |  |  | 			sort.Sort(sort.Reverse(bySize(l))) | 
					
						
							| 
									
										
										
										
											2017-07-29 06:57:44 +00:00
										 |  |  | 		case "modified": | 
					
						
							|  |  |  | 			sort.Sort(sort.Reverse(byModified(l))) | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			// If not one of the above, do nothing
 | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { // If we had more Orderings we could add them here
 | 
					
						
							|  |  |  | 		switch l.Sort { | 
					
						
							|  |  |  | 		case "name": | 
					
						
							|  |  |  | 			sort.Sort(byName(l)) | 
					
						
							|  |  |  | 		case "size": | 
					
						
							|  |  |  | 			sort.Sort(bySize(l)) | 
					
						
							| 
									
										
										
										
											2017-07-29 06:57:44 +00:00
										 |  |  | 		case "modified": | 
					
						
							|  |  |  | 			sort.Sort(byModified(l)) | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			sort.Sort(byName(l)) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-19 11:35:44 +00:00
										 |  |  | // Implement sorting for Listing
 | 
					
						
							|  |  |  | type byName Listing | 
					
						
							|  |  |  | type bySize Listing | 
					
						
							|  |  |  | type byModified Listing | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | // By Name
 | 
					
						
							|  |  |  | func (l byName) Len() int { | 
					
						
							|  |  |  | 	return len(l.Items) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l byName) Swap(i, j int) { | 
					
						
							|  |  |  | 	l.Items[i], l.Items[j] = l.Items[j], l.Items[i] | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Treat upper and lower case equally
 | 
					
						
							|  |  |  | func (l byName) Less(i, j int) bool { | 
					
						
							|  |  |  | 	if l.Items[i].IsDir && !l.Items[j].IsDir { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if !l.Items[i].IsDir && l.Items[j].IsDir { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-29 11:22:11 +00:00
										 |  |  | 	return natural.Less(l.Items[i].Name, l.Items[j].Name) | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // By Size
 | 
					
						
							|  |  |  | func (l bySize) Len() int { | 
					
						
							|  |  |  | 	return len(l.Items) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l bySize) Swap(i, j int) { | 
					
						
							|  |  |  | 	l.Items[i], l.Items[j] = l.Items[j], l.Items[i] | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const directoryOffset = -1 << 31 // = math.MinInt32
 | 
					
						
							|  |  |  | func (l bySize) Less(i, j int) bool { | 
					
						
							|  |  |  | 	iSize, jSize := l.Items[i].Size, l.Items[j].Size | 
					
						
							|  |  |  | 	if l.Items[i].IsDir { | 
					
						
							|  |  |  | 		iSize = directoryOffset + iSize | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if l.Items[j].IsDir { | 
					
						
							|  |  |  | 		jSize = directoryOffset + jSize | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return iSize < jSize | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-29 06:57:44 +00:00
										 |  |  | // By Modified
 | 
					
						
							|  |  |  | func (l byModified) Len() int { | 
					
						
							|  |  |  | 	return len(l.Items) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l byModified) Swap(i, j int) { | 
					
						
							|  |  |  | 	l.Items[i], l.Items[j] = l.Items[j], l.Items[i] | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l byModified) Less(i, j int) bool { | 
					
						
							|  |  |  | 	iModified, jModified := l.Items[i].ModTime, l.Items[j].ModTime | 
					
						
							|  |  |  | 	return iModified.Sub(jModified) < 0 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-15 21:03:50 +00:00
										 |  |  | // textExtensions is the sorted list of text extensions which
 | 
					
						
							|  |  |  | // can be edited.
 | 
					
						
							|  |  |  | var textExtensions = []string{ | 
					
						
							|  |  |  | 	".ad", ".ada", ".adoc", ".asciidoc", | 
					
						
							|  |  |  | 	".bas", ".bash", ".bat", | 
					
						
							|  |  |  | 	".c", ".cc", ".cmd", ".conf", ".cpp", ".cr", ".cs", ".css", ".csv", | 
					
						
							|  |  |  | 	".d", | 
					
						
							|  |  |  | 	".f", ".f90", | 
					
						
							|  |  |  | 	".h", ".hh", ".hpp", ".htaccess", ".html", | 
					
						
							|  |  |  | 	".ini", | 
					
						
							|  |  |  | 	".java", ".js", ".json", | 
					
						
							|  |  |  | 	".markdown", ".md", ".mdown", ".mmark", | 
					
						
							|  |  |  | 	".nim", | 
					
						
							|  |  |  | 	".php", ".pl", ".ps1", ".py", | 
					
						
							|  |  |  | 	".rss", ".rst", ".rtf", | 
					
						
							|  |  |  | 	".sass", ".scss", ".sh", ".sty", | 
					
						
							|  |  |  | 	".tex", ".tml", ".toml", ".txt", | 
					
						
							|  |  |  | 	".vala", ".vapi", | 
					
						
							|  |  |  | 	".xml", | 
					
						
							|  |  |  | 	".yaml", ".yml", | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | 	"Caddyfile", | 
					
						
							| 
									
										
										
										
											2017-11-15 21:03:50 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // isInTextExtensions checks if a file can be edited by its extensions.
 | 
					
						
							|  |  |  | func isInTextExtensions(name string) bool { | 
					
						
							|  |  |  | 	search := filepath.Ext(name) | 
					
						
							|  |  |  | 	if search == "" { | 
					
						
							|  |  |  | 		search = name | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	i := sort.SearchStrings(textExtensions, search) | 
					
						
							|  |  |  | 	return i < len(textExtensions) && textExtensions[i] == search | 
					
						
							| 
									
										
										
										
											2017-06-27 18:00:58 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-02 16:53:47 +00:00
										 |  |  | // hasRune checks if the file has the frontmatter rune
 | 
					
						
							|  |  |  | func hasRune(file string) bool { | 
					
						
							|  |  |  | 	return strings.HasPrefix(file, "---") || | 
					
						
							|  |  |  | 		strings.HasPrefix(file, "+++") || | 
					
						
							|  |  |  | 		strings.HasPrefix(file, "{") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | func editorMode(language string) string { | 
					
						
							|  |  |  | 	switch language { | 
					
						
							|  |  |  | 	case "markdown", "asciidoc", "rst": | 
					
						
							| 
									
										
										
										
											2017-07-01 07:50:42 +00:00
										 |  |  | 		return "content+metadata" | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-01 07:50:42 +00:00
										 |  |  | 	return "content" | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func editorLanguage(mode string) string { | 
					
						
							| 
									
										
										
										
											2017-06-30 17:03:08 +00:00
										 |  |  | 	mode = strings.TrimPrefix(mode, ".") | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	switch mode { | 
					
						
							|  |  |  | 	case "md", "markdown", "mdown", "mmark": | 
					
						
							|  |  |  | 		mode = "markdown" | 
					
						
							| 
									
										
										
										
											2017-06-30 17:03:08 +00:00
										 |  |  | 	case "yml": | 
					
						
							|  |  |  | 		mode = "yaml" | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 	case "asciidoc", "adoc", "ad": | 
					
						
							|  |  |  | 		mode = "asciidoc" | 
					
						
							|  |  |  | 	case "rst": | 
					
						
							|  |  |  | 		mode = "rst" | 
					
						
							| 
									
										
										
										
											2017-07-11 15:58:18 +00:00
										 |  |  | 	case "html", "htm", "xml": | 
					
						
							|  |  |  | 		mode = "htmlmixed" | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 	case "js": | 
					
						
							|  |  |  | 		mode = "javascript" | 
					
						
							|  |  |  | 	case "go": | 
					
						
							|  |  |  | 		mode = "golang" | 
					
						
							| 
									
										
										
										
											2017-07-11 15:58:18 +00:00
										 |  |  | 	case "": | 
					
						
							|  |  |  | 		mode = "text" | 
					
						
							| 
									
										
										
										
											2017-06-29 09:17:35 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return mode | 
					
						
							|  |  |  | } |