package main import ( "fmt" "log" "net/http" "os" "io" "html/template" "flag" "path" "syscall" ) var tmpl *template.Template func handleGet( w http.ResponseWriter, r *http.Request, headOnly bool) { filename := r.URL.Path[1:] if filename == "" { filename = "." } fmt.Printf("Trying to get [%s]\n", filename) stat, err := os.Stat(filename) switch { case err != nil || stat == nil : if os.IsNotExist(err) { w.WriteHeader(http.StatusNotFound) if ! headOnly { fmt.Fprintln(w, "File not found") fmt.Fprintln(w, "") fmt.Fprintln(w, "Sry bro") } return } case stat.IsDir() : // If it's a collection list return the list of the collection type Entry struct { Name string Size int } var files []Entry f, err := os.Open(filename) if err != nil { w.WriteHeader(http.StatusNotFound) if !headOnly { fmt.Fprintln(w, "File not found") fmt.Fprintln(w, "") fmt.Fprintln(w, "Sry bro") } return } fileInfo, err := f.Readdir(-1) f.Close() if err != nil { w.WriteHeader(http.StatusNotFound) if !headOnly { fmt.Fprintln(w, "File not found") fmt.Fprintln(w, "") fmt.Fprintln(w, "Sry bro") } } for _, file := range fileInfo { files = append(files, Entry{file.Name(), 0}) } type Collection struct { Name string ListEntries []Entry } tmpl.Execute(w, Collection{ "Title" , files}) case stat.Mode().IsRegular(): if !headOnly { http.ServeFile(w, r, filename) } default: } } func handleDelete( w http.ResponseWriter, r *http.Request) { } func handlePut( w http.ResponseWriter, r *http.Request) { filename := r.URL.Path[1:] if filename == "" { filename = "." } stat, err := os.Stat(path.Dir(filename)) if err == syscall.ENOENT { w.WriteHeader(http.StatusConflict) fmt.Fprintf(w, "Can't find [%s]\n)", path.Dir(filename)) return } else if err != nil{ w.WriteHeader(http.StatusInternalServerError) fmt.Fprintln(w, err.Error()) return } else if ! stat.IsDir(){ w.WriteHeader(http.StatusConflict) fmt.Fprintln(w, "¯\\_(ツ)_/¯") return } stat, err = os.Stat(filename) if err != nil { if !os.IsNotExist(err) { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintln(w, err.Error()) return } } else if stat.IsDir() { w.WriteHeader(http.StatusMethodNotAllowed) fmt.Fprintln(w, "Cannot write a file over a existing collection") return } file, err := os.Create(filename) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintln(w, err.Error()) return } defer file.Close() s, err := io.Copy(file, r.Body) if err == io.EOF { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintln(w, err.Error()) return } fmt.Fprintf(w, "%d bytes written\n", s) } func handleMkcol( w http.ResponseWriter, r *http.Request) { filename := r.URL.Path[1:] if filename == "" { filename = "." } fmt.Printf("Trying to Mkcol [%s]\n", filename) fmt.Printf(" Base [%s]\n", path.Base(filename)) fmt.Printf(" Dir [%s]\n", path.Dir(filename)) err := os.Mkdir(filename, 0755) if err != nil { w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, "Sry for the error : %s", err.Error()) } } func handleOptions( w http.ResponseWriter, r *http.Request) { } func handleCopy( w http.ResponseWriter, r *http.Request) { } func handleMove( w http.ResponseWriter, r *http.Request) { filename := r.URL.Path[1:] oups := "" if filename == "" || filename == "." { w.WriteHeader(http.StatusBadRequest) fmt.Fprintln(w, "Cannot move root directory") return } fmt.Printf("Trying to move [%s] to [%s]\n", filename, oups) } func handleMethod( w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: handleGet(w, r, false) case http.MethodHead: handleGet(w, r, true) case http.MethodPut: handlePut(w, r) case http.MethodDelete: handleDelete(w, r) case http.MethodOptions: handleOptions(w, r) case "MOVE": handleDelete(w, r) case "MKCOL": handleMkcol(w, r) case "LOCK": fallthrough case "UNLOCK": fallthrough case http.MethodPost: w.WriteHeader(http.StatusTeapot) fmt.Fprintln(w, "Sry, will be implemented \"soon\"") fmt.Fprintln(w, "") fmt.Fprintln(w, "Here's a unicorn. 🦄") default: w.WriteHeader(http.StatusTeapot) fmt.Fprintln(w, "Sry, i'm a teapot >///<") fmt.Fprintln(w, "") fmt.Fprintln(w, "Here's some tea. 🍵") } } func main() { var dir string var help, version bool helpString := `This is the help` versionString := "0.0.0" const ( defaultDir = "." usageDir = "the directory to serve" usageHelp = "show some help" usageVersion = "show the version" ) flag.StringVar(&dir, "directory", defaultDir, usageDir) flag.StringVar(&dir, "d", defaultDir, usageDir+" (shorthand)") flag.BoolVar(&help, "help", false, usageHelp) flag.BoolVar(&help, "h", false, usageHelp+" (shorthand)") flag.BoolVar(&version, "version", false, usageVersion) flag.BoolVar(&version, "v", false, usageVersion+" (shorthand)") flag.Parse() if dir == "" { dir = defaultDir } if help { fmt.Println(helpString) return } if version { fmt.Printf("davy %s\n", versionString) return } tmpl = template.Must(template.ParseFiles("dir.html")) if err := os.Chdir(dir) ; err != nil { fmt.Println("Error with directory [" + dir + "]") return } fmt.Println("Launch server") http.HandleFunc("/", handleMethod) log.Fatal(http.ListenAndServe(":8080", nil)) fmt.Println("Bye bye") }