hold-this/handlers.go

126 lines
2.7 KiB
Go

package main
import (
"fmt"
"io"
"io/fs"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"github.com/gofrs/uuid"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
const dirCreateMode = 0o750
func deriveFile(r *http.Request) (string, error) {
absRoot, err := filepath.Abs(cfg.RootDir)
if err != nil {
return "", errors.Wrap(err, "getting absolute root")
}
file := filepath.Clean(path.Join(absRoot, strings.TrimLeft(r.URL.Path, "/")))
if !strings.HasPrefix(file, path.Join(absRoot, "")) {
return "", errors.New("break-out attempt")
}
return file, nil
}
func genericHTTPError(w http.ResponseWriter, reqID string, err error, desc string) {
logrus.WithField("req_id", reqID).WithError(err).Error(desc)
http.Error(w, fmt.Sprintf("something went wrong: %s", reqID), http.StatusInternalServerError)
}
func handleDelete(w http.ResponseWriter, r *http.Request) {
reqID := uuid.Must(uuid.NewV4()).String()
f, err := deriveFile(r)
if err != nil {
genericHTTPError(w, reqID, err, "deriving file for request")
return
}
stat, err := os.Stat(f)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
w.WriteHeader(http.StatusNoContent)
return
}
genericHTTPError(w, reqID, err, "stating file")
return
}
if stat.IsDir() {
genericHTTPError(w, reqID, errors.New("is directory"), "refusing to delete dir")
return
}
if err = os.Remove(f); err != nil {
genericHTTPError(w, reqID, err, "deleting file")
return
}
w.WriteHeader(http.StatusNoContent)
}
func handleGet(w http.ResponseWriter, r *http.Request) {
reqID := uuid.Must(uuid.NewV4()).String()
f, err := deriveFile(r)
if err != nil {
genericHTTPError(w, reqID, err, "deriving file for request")
return
}
stat, err := os.Stat(f)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
http.Error(w, "not found", http.StatusNotFound)
return
}
genericHTTPError(w, reqID, err, "stating file")
return
}
if stat.IsDir() {
genericHTTPError(w, reqID, errors.New("is directory"), "refusing to get dir")
return
}
http.ServeFile(w, r, f)
}
func handlePut(w http.ResponseWriter, r *http.Request) {
reqID := uuid.Must(uuid.NewV4()).String()
f, err := deriveFile(r)
if err != nil {
genericHTTPError(w, reqID, err, "deriving file for request")
return
}
if err = os.MkdirAll(path.Dir(f), dirCreateMode); err != nil {
genericHTTPError(w, reqID, err, "ensuring directory for file")
return
}
out, err := os.Create(f)
if err != nil {
genericHTTPError(w, reqID, err, "creating file")
return
}
defer out.Close()
if _, err = io.Copy(out, r.Body); err != nil {
genericHTTPError(w, reqID, err, "copying file contents")
return
}
w.WriteHeader(http.StatusCreated)
}