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) { //TODO use filepath.Walk //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 := os.ReadDir(p) if err != nil { panic(err) } for _, finfo := range fileInfos { info, err := finfo.Info() if err != nil { panic(err) } path := filepath.Join(name, info.Name()) var link string if info.Mode()&os.ModeSymlink == os.ModeSymlink { if link, err = os.Readlink(path); err != nil { panic(err) } } path = strings.TrimPrefix(path, "/") if info.IsDir() { handler.writeDirToTar(tw, info, path) } else { hdr, err := tar.FileInfoHeader(info, link) if err != nil { log.Fatal(err) } if err := tw.WriteHeader(hdr); err != nil { log.Fatal(err) } if !info.Mode().IsRegular() { continue } 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)) }