package archetype import ( "errors" "io/ioutil" "net/http" "os" "path/filepath" "strings" ) type SimpleFileManager struct { Root string ShowHtml bool ShowHidden bool } type FileData struct { Error string Path string Name string IsDir bool } type FileListing struct { Error string Root string Up string SubDirs []string Files []string } type FileManager interface { Init(cfg *Config) error // ListTree() FileListing ListSubTree(root string) FileListing GetFileData(slug string) FileData AddFile(path string, req *http.Request) error // MkDir(path string) error Remove(path string) error // Rename(old, new string) error } func (self *SimpleFileManager) Init(cfg *Config) error { self.Root = filepath.Clean(cfg.StaticRoot) self.ShowHtml = cfg.StaticShowHtml self.ShowHidden = cfg.StaticShowHidden return nil } func (self *SimpleFileManager) ListSubTree(root string) FileListing { list := FileListing{} if strings.Contains(root, "../") || strings.Contains(root, "..\\") { list.Error = "You cannot escape!" return list } fullPath := filepath.Join(self.Root, root) files, err := ioutil.ReadDir(fullPath) if err != nil { list.Error = err.Error() return list } list.Root = root if !strings.HasSuffix(list.Root, "/") { list.Root += "/" } if !strings.HasPrefix(list.Root, "/") { list.Root = "/" + list.Root } levels := strings.Split(root, "/") if list.Root != "/" { list.Up = "/" } if len(levels) >= 2 { list.Up = "/" + strings.Join(levels[:len(levels)-1], "/") } for _, file := range files { if !self.ShowHidden && strings.HasPrefix(file.Name(), ".") { continue } if file.IsDir() { list.SubDirs = append(list.SubDirs, file.Name()) } else { if !self.ShowHtml && strings.HasSuffix(file.Name(), ".html") { continue } list.Files = append(list.Files, file.Name()) } } return list } func (self *SimpleFileManager) GetFileData(slug string) FileData { fullPath := filepath.Join(self.Root, slug) fileInfo, err := os.Stat(fullPath) if err != nil { return FileData{ Error: err.Error(), } } if !strings.HasPrefix(fullPath, self.Root) { return FileData{ Error: "You cannot escape!", } } cleanedSlug := filepath.Clean(slug) fileBase := filepath.Base(cleanedSlug) return FileData{ Path: filepath.Clean(slug), Name: fileBase, IsDir: fileInfo.IsDir(), } } func (self *SimpleFileManager) Remove(slug string) error { fullPath := filepath.Join(self.Root, slug) _, err := os.Stat(fullPath) if err != nil { return err } if !strings.HasPrefix(fullPath, self.Root) { return errors.New("You cannot escape!") } return os.RemoveAll(fullPath) } func (self *SimpleFileManager) AddFile(path string, req *http.Request) error { fullPath := filepath.Join(self.Root, path) _, err := os.Stat(fullPath) if err != nil { return err } if !strings.HasPrefix(fullPath, filepath.Clean(self.Root)) { return errors.New("You cannot escape!") } req.ParseMultipartForm(250 << 20) file, header, err := req.FormFile("file") if err != nil { return err } fileData, err := ioutil.ReadAll(file) if err != nil { return err } destPath := filepath.Join(fullPath, header.Filename) dest, err := os.Create(destPath) if err != nil { return err } defer dest.Close() dest.Write(fileData) return nil }