commit 4de4f078687e80c5c6b3c65fb82e65906a80801c Author: dedal.qq Date: Sun Sep 23 16:40:46 2018 +0300 commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/handler.go b/handler.go new file mode 100644 index 0000000..d188c9e --- /dev/null +++ b/handler.go @@ -0,0 +1,158 @@ +package main + +import ( + "fmt" + "html/template" + "net/http" + "net/url" + "os" + "path" + "sort" + "strings" +) + +func renderError(w http.ResponseWriter, msg string, code int) { + http.Error(w, msg, 404) +} + +func byteCountBinary(b int64) string { + const unit = 1024 + if b < unit { + return fmt.Sprintf("%d B", b) + } + div, exp := int64(unit), 0 + for n := b / unit; n >= unit; n /= unit { + div *= unit + exp++ + } + return fmt.Sprintf("%.1f %ciB", float64(b)/float64(div), "KMGTPE"[exp]) +} + +var htmlReplacer = strings.NewReplacer( + "&", "&", + "<", "<", + ">", ">", + // """ is shorter than """. + `"`, """, + // "'" is shorter than "'" and apos was not in HTML until HTML5. + "'", "'", +) + +func localRedirect(w http.ResponseWriter, r *http.Request, newPath string) { + if q := r.URL.RawQuery; q != "" { + newPath += "?" + q + } + w.Header().Set("Location", newPath) + w.WriteHeader(http.StatusMovedPermanently) +} + +func toHTTPError(err error) (msg string, httpStatus int) { + if os.IsNotExist(err) { + return "404 page not found", http.StatusNotFound + } + if os.IsPermission(err) { + return "403 Forbidden", http.StatusForbidden + } + // Default: + return "500 Internal Server Error", http.StatusInternalServerError +} + +func serveFile(w http.ResponseWriter, r *http.Request, fs http.FileSystem, name string) { + + f, err := fs.Open(name) + if err != nil { + msg, code := toHTTPError(err) + renderError(w, msg, code) + return + } + defer f.Close() + + d, err := f.Stat() + if err != nil { + msg, code := toHTTPError(err) + renderError(w, msg, code) + return + } + + // redirect if the directory name doesn't end in a slash + if d.IsDir() { + url := r.URL.Path + if url[len(url)-1] != '/' { + localRedirect(w, r, path.Base(url)+"/") + return + } + } + + // Still a directory? (we didn't find an index.html file) + if d.IsDir() { + dirList(w, r, f, name) + return + } + + // serveContent will check modification time + // sizeFunc := func() (int64, error) { return d.Size(), nil } + http.ServeContent(w, r, d.Name(), d.ModTime(), f) +} + +func dirList(w http.ResponseWriter, r *http.Request, f http.File, name string) { + dirs, err := f.Readdir(-1) + if err != nil { + renderError(w, "Error reading directory", http.StatusInternalServerError) + return + } + sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() }) + + w.Header().Set("Content-Type", "text/html; charset=utf-8") + + t, err := template.New("main.html").ParseFiles("main.html") + if err != nil { + fmt.Printf(err.Error()) + return + } + + data := &Data{ + Title: name, + Files: make([]*File, 0, len(dirs)), + } + + for _, d := range dirs { + name := d.Name() + icon := "file" + size := byteCountBinary(d.Size()) + if d.IsDir() { + name += "/" + icon = "folder" + size = "-" + } + + url := url.URL{Path: name} + + data.Files = append(data.Files, &File{ + Icon: icon, + URL: url.String(), + Name: htmlReplacer.Replace(name), + Size: size, + Date: d.ModTime().Format("02.01.2006 15:04:05"), + }) + + } + + err = t.Execute(w, data) + if err != nil { + fmt.Printf(err.Error()) + } + +} + +type fileHandler struct { + root string +} + +func (fh *fileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + upath := r.URL.Path + if !strings.HasPrefix(upath, "/") { + upath = "/" + upath + r.URL.Path = upath + } + serveFile(w, r, http.Dir(fh.root), path.Clean(upath)) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..4f5a584 --- /dev/null +++ b/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "flag" + "fmt" + "net/http" + "os" +) + +var port uint +var folderPath string + +func init() { + flag.StringVar(&folderPath, "p", "", "Path to folder (required)") + flag.UintVar(&port, "l", 8080, "Listening port (default 8080)") + flag.Parse() +} + +func main() { + + if folderPath == "" { + fmt.Fprintf(os.Stderr, "Error: parametr 'path' is required") + os.Exit(1) + } + + server := &http.Server{ + Addr: fmt.Sprintf(":%d", port), + Handler: &fileHandler{ + root: folderPath, + }, + } + + server.ListenAndServe() +} diff --git a/main.html b/main.html new file mode 100644 index 0000000..f753889 --- /dev/null +++ b/main.html @@ -0,0 +1,44 @@ + + + + + + {{.Title}} + + + + + + + + +
+ +
+ + + + + + + + + {{range .Files}} + + + + + + + {{end}} +
 NameSizeDate
{{.Name}}{{.Size}}{{.Date}}
+
+ + + diff --git a/struct.go b/struct.go new file mode 100644 index 0000000..4c6b7ee --- /dev/null +++ b/struct.go @@ -0,0 +1,17 @@ +package main + +// File - +type File struct { + Icon string + URL string + Name string + Size string + Date string +} + +// Data - +type Data struct { + Title string + IsRoot bool + Files []*File +}