aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Hurst <ark@mansus.space>2022-12-08 18:34:25 +0100
committerJulian Hurst <ark@mansus.space>2022-12-08 18:34:25 +0100
commit238d7a12de4ad095c297f214a76438110798208b (patch)
tree104f42f9f4a1bfc8b3a32723091a229e384e50a4
downloadautoindex-238d7a12de4ad095c297f214a76438110798208b.tar.gz
Initial commit
-rw-r--r--README.md26
-rw-r--r--autoindex.go270
-rw-r--r--config.go16
-rw-r--r--go.mod5
-rw-r--r--go.sum9
-rw-r--r--index.html90
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
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..2795e4c
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,5 @@
+module autoindex
+
+go 1.16
+
+require golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..4cbc8bb
--- /dev/null
+++ b/go.sum
@@ -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>