diff options
| -rw-r--r-- | README.md | 26 | ||||
| -rw-r--r-- | autoindex.go | 270 | ||||
| -rw-r--r-- | config.go | 16 | ||||
| -rw-r--r-- | go.mod | 5 | ||||
| -rw-r--r-- | go.sum | 9 | ||||
| -rw-r--r-- | index.html | 90 |
6 files changed, 416 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..89253a8 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# autoindex + +## Usage + Usage of ./autoindex: + -E Encrypt the data with a specified password + -b string + The autoindex executables directory. + -e Encrypt the data with a cryptographically secure generated hash + -p int + The port to bind to. (default 8080) + +## Build & install + +To build autoindex, install go and run: + + $ go build + +To install, you can run: + + # GOBIN=/usr/local/bin go install + +or + + $ GOBIN=$HOME/.local/bin go install + +The index.html file must be placed in $HOME/.local/share/autoindex. diff --git a/autoindex.go b/autoindex.go new file mode 100644 index 0000000..6a388a6 --- /dev/null +++ b/autoindex.go @@ -0,0 +1,270 @@ +package main + +import ( + "fmt" + "flag" + "archive/tar" + "compress/gzip" + "log" + "net/http" + "os" + "os/exec" + "path/filepath" + "html/template" + "io" + "io/ioutil" + "time" + "strings" +) + +const pwdlen int = 32 +var salt []byte = nil + +type IndexHandler struct { + docPath string + binPath string +} + +type File struct { + Info os.FileInfo + Path string +} + +func format(t time.Time) string { + return t.Format("2006-01-02 15:04") +} + +func (handler IndexHandler) writeDirToTar(tw *tar.Writer, file os.FileInfo, name string) { + //hdr := &tar.Header{ + //Name: name, + //Mode: int64(file.Mode()), + //ModTime: file.ModTime(), + //Typeflag: tar.TypeDir, + //} + //if err := tw.WriteHeader(hdr); err != nil { + //log.Fatal(err) + //} + p := filepath.Join(handler.docPath, name) + fileInfos, err := ioutil.ReadDir(p) + if err != nil { + panic(err) + } + for _, info := range fileInfos { + path := filepath.Join(name, info.Name()) + path = strings.TrimPrefix(path, "/") + if info.IsDir() { + handler.writeDirToTar(tw, info, path) + } else { + hdr := &tar.Header{ + Name: path, + Mode: int64(info.Mode()), + Size: info.Size(), + ModTime: info.ModTime(), + } + if err := tw.WriteHeader(hdr); err != nil { + log.Fatal(err) + } + absFilePath := filepath.Join(handler.docPath, path) + openFile, err := os.Open(absFilePath) + if err != nil { + panic(err) + } + defer openFile.Close() + _, err = io.Copy(tw, openFile) + if err != nil { + panic(err) + } + } + } +} + +func (handler IndexHandler) TarArchive(w io.Writer, r *http.Request, s string) { + p := filepath.Join(handler.docPath, s) + f, err := os.Stat(p) + if err != nil { + log.Println(err) + return + } + if f.IsDir() { + s = strings.TrimPrefix(s, "/") + //gw := gzip.NewWriter(w) + //defer gw.Close() + var tw *tar.Writer + tw = tar.NewWriter(w) + defer tw.Close() + handler.writeDirToTar(tw, f, s) + //w.WriteHeader(http.StatusCreated) + } else { + return + } +} + +func (handler IndexHandler) IsExistingIndex() (bool, string) { + ind := filepath.Join(handler.docPath, "index.html") + f, err := os.Stat(ind) + if err == nil && !f.IsDir() { + return true, ind + } + ind = filepath.Join(handler.docPath, "index.htm") + f, err = os.Stat(ind) + if err == nil && !f.IsDir() { + return true, ind + } + return false, "" +} + +func (handler IndexHandler) ServeDir(w http.ResponseWriter, r *http.Request, dir string) { + fileInfos, err := ioutil.ReadDir(dir) + if err != nil { + panic(err) + } + var files []File = nil + for _, info := range fileInfos { + path := filepath.Join(r.URL.Path, info.Name()) + files = append(files, File{info, path}) + } + funcs := template.FuncMap { + "format": format, + } + tmpl, err := template.New("index.html").Funcs(funcs).ParseFiles(GetIndexPath()) + if err != nil { + panic(err) + } + data := struct { + DocDir string + CurrentPath string + Parent string + Files []File + }{ + filepath.Base(handler.docPath), + r.URL.Path, + filepath.Dir(r.URL.Path), + files, + } + tmpl.Execute(w, data) +} + +func (handler IndexHandler) Execute(w http.ResponseWriter, r *http.Request, s string) { + b := filepath.Join(handler.binPath, s) + _, err := os.Stat(b) + if err != nil { + log.Println("stat") + log.Println(err) + return + } + log.Printf("Executing %s\n", b) + //out, err := exec.Command(b).CombinedOutput() + cmd := exec.Command(b) + out, err := cmd.StdoutPipe() + if err != nil { + log.Println(err) + return + } + if err := cmd.Start(); err != nil { + log.Println(err) + return + } + //_, err = w.Write(out) + _, err = io.Copy(w, out) + if err != nil { + log.Println(err) + return + } +} + +func (handler IndexHandler) ServeFile(w http.ResponseWriter, r *http.Request, p string) { + http.ServeFile(w, r, p) +} + +func (handler IndexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if strings.HasPrefix(r.URL.Path, "/tararchive") { + s := strings.TrimPrefix(r.URL.Path, "/tararchive") + contentDisposition := fmt.Sprintf("attachment; filename=\"%v\"", filepath.Base(s) + ".tar") + log.Println(contentDisposition) + w.Header().Set("Content-Disposition", contentDisposition) + handler.TarArchive(w, r, s) + return + } else if strings.HasPrefix(r.URL.Path, "/gzarchive") { + s := strings.TrimPrefix(r.URL.Path, "/gzarchive") + contentDisposition := fmt.Sprintf("attachment; filename=\"%v\"", filepath.Base(s) + ".tar.gz") + log.Println(contentDisposition) + w.Header().Set("Content-Disposition", contentDisposition) + gw := gzip.NewWriter(w) + defer gw.Close() + handler.TarArchive(gw, r, s) + return + } else if r.URL.Path == "/ai_execute" && handler.binPath != "" { + handler.ServeDir(w, r, handler.binPath) + return + } else if strings.HasPrefix(r.URL.Path, "/ai_execute") { + s := strings.TrimPrefix(r.URL.Path, "/ai_execute") + handler.Execute(w, r, s) + return + } + + p := filepath.Join(handler.docPath, r.URL.Path) + if r.URL.Path == "/" { + if ok, ind := handler.IsExistingIndex(); ok && r.URL.Path == "/" { + http.ServeFile(w, r, ind) + return + } + } + f, err := os.Stat(p) + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusNotFound) + return + } + if f.IsDir() { + fileInfos, err := ioutil.ReadDir(p) + if err != nil { + panic(err) + } + var files []File = nil + for _, info := range fileInfos { + path := filepath.Join(r.URL.Path, info.Name()) + files = append(files, File{info, path}) + } + funcs := template.FuncMap { + "format": format, + } + tmpl, err := template.New("index.html").Funcs(funcs).ParseFiles(GetIndexPath()) + if err != nil { + panic(err) + } + data := struct { + DocDir string + CurrentPath string + Parent string + Files []File + }{ + filepath.Base(handler.docPath), + r.URL.Path, + filepath.Dir(r.URL.Path), + files, + } + tmpl.Execute(w, data) + } else { + handler.ServeFile(w, r, p) + } +} + +func usage() { + fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [OPTIONS] pathtodocdir\n", os.Args[0]) + fmt.Fprint(flag.CommandLine.Output(), "\nOptions:\n") + flag.PrintDefaults() +} + +func main() { + port := flag.Int("p", 8080, "The port to bind to.") + binPath := flag.String("b", "", "The autoindex executables directory.") + flag.Parse() + args := flag.Args() + if len(args) != 1 { + usage() + os.Exit(1) + } + handler := IndexHandler{args[0], *binPath} + log.Printf("Listening on port %d with docPath=%s", *port, handler.docPath) + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", *port), handler)) +} diff --git a/config.go b/config.go new file mode 100644 index 0000000..4b253c9 --- /dev/null +++ b/config.go @@ -0,0 +1,16 @@ +package main + +import ( + "os" + "path/filepath" +) + +func GetIndexPath() string { + dataDir := os.Getenv("XDG_DATA_HOME") + if dataDir == "" { + home := os.Getenv("HOME") + dataDir = filepath.Join(home, ".local", "share") + } + indexPath := filepath.Join(dataDir, "autoindex", "index.html") + return indexPath +} @@ -0,0 +1,5 @@ +module autoindex + +go 1.16 + +require golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect @@ -0,0 +1,9 @@ +golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= +golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/index.html b/index.html new file mode 100644 index 0000000..81de88d --- /dev/null +++ b/index.html @@ -0,0 +1,90 @@ +<html> + <head> +<style> +html { + background: #222; + color: antiquewhite; + font-family: monospace; +} +table { + border-collapse: collapse; +} +th { + text-align: left; + border-bottom: 1px solid white; +} +td { + max-width: 1000px; + text-overflow: ellipsis; + overflow: hidden; + padding-right: 10px; + padding-bottom: 3px; + margin: 0; +} +tr > td:first-child { + min-width: 500px; +} +tr:hover { + background-color: #333; +} +a { + color: orange; +} +div { + float: right; +} +</style> + </head> + <body> + <h1>Index of {{.CurrentPath}}</h1> + <hr/> + <a style="float: right;" href="/ai_execute">Exec</a> + <table> + <!--<tr>--> + <!--<th>File</th>--> + <!--[><th>Export</th><]--> + <!--<th>Modtime</th>--> + <!--</tr>--> + <tr> + <td> + <a href="{{.CurrentPath}}">.</a> + </td> + <td> + [<a href="http://localhost:8080/gzarchive{{.CurrentPath}}">gzip</a>] + [<a href="/tararchive{{.CurrentPath}}">tar</a>] + </td> + <td></td> + </tr> + {{if ne .CurrentPath "/"}} + <tr> + <td> + <a href="{{.Parent}}">..</a> + </td> + <td> + [<a href="/gzarchive{{.Parent}}">gzip</a>] + [<a href="/tararchive{{.Parent}}">tar</a>] + </td> + <td></td> + </tr> + {{end}} + {{range .Files}} + <tr> + <td> + {{if .Info.IsDir}} + <a href="{{.Path}}">{{.Info.Name}}/</a> + {{else}} + <a href="{{.Path}}">{{.Info.Name}}</a> + {{end}} + </td> + <td> + {{if .Info.IsDir}} + [<a href="/gzarchive{{.Path}}">gzip</a>] + [<a href="/tararchive{{.Path}}">tar</a>] + {{end}} + </td> + <td>{{format .Info.ModTime}}</td> + </tr> + {{end}} + </table> + </body> +</html> |
