parent
5e6ce9c2be
commit
245dd64f82
96
handlers.go
96
handlers.go
@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.hashru.nl/dsprenkels/rushlink/internal/db"
|
||||
@ -60,53 +61,6 @@ func (rl *rushlink) indexGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||
rl.render(w, r, "index", map[string]interface{}{})
|
||||
}
|
||||
|
||||
func (rl *rushlink) uploadFileGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
id := vars["id"]
|
||||
|
||||
var fu *db.FileUpload
|
||||
var badID bool
|
||||
if err := rl.db.Bolt.View(func(tx *bolt.Tx) error {
|
||||
fuID, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
badID = true
|
||||
return err
|
||||
}
|
||||
fu, err = db.GetFileUpload(tx, fuID)
|
||||
return err
|
||||
}); err != nil {
|
||||
if badID {
|
||||
rl.renderError(w, r, http.StatusNotFound, "malformed file id")
|
||||
return
|
||||
}
|
||||
// unexpected error
|
||||
panic(err)
|
||||
}
|
||||
|
||||
filePath := fu.Path(rl.fs)
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
log.Printf("error: '%v' should exist according to the database, but it doesn't", filePath)
|
||||
rl.renderError(w, r, http.StatusNotFound, "file not found")
|
||||
return
|
||||
}
|
||||
// unexpected error
|
||||
panic(err)
|
||||
}
|
||||
info, err := file.Stat()
|
||||
var modtime time.Time
|
||||
if err != nil {
|
||||
log.Printf("error: %v", errors.Wrapf(err, "could not stat file '%v'", filePath))
|
||||
} else {
|
||||
modtime = info.ModTime()
|
||||
}
|
||||
// We use http.ServeContent (instead of http.ServeFile) because we cannot
|
||||
// use http.ServeFile together with the assertion that the file exists,
|
||||
// without introducing a TOCTOU flaw.
|
||||
http.ServeContent(w, r, fu.FileName, modtime, file)
|
||||
}
|
||||
|
||||
func (rl *rushlink) viewPasteHandler(w http.ResponseWriter, r *http.Request) {
|
||||
rl.viewPasteHandlerFlags(w, r, 0)
|
||||
}
|
||||
@ -158,31 +112,61 @@ func (rl *rushlink) viewPasteHandlerInner(w http.ResponseWriter, r *http.Request
|
||||
|
||||
switch p.State {
|
||||
case db.PasteStatePresent:
|
||||
var location string
|
||||
switch p.Type {
|
||||
case db.PasteTypeFileUpload:
|
||||
if fu == nil {
|
||||
panic(fmt.Sprintf("file for id %v does not exist in database\n", string(p.Content)))
|
||||
}
|
||||
location = fu.URL().String()
|
||||
break
|
||||
rl.viewFileUploadHandler(w, r, fu)
|
||||
return
|
||||
case db.PasteTypeRedirect:
|
||||
location = p.RedirectURL().String()
|
||||
break
|
||||
if flags&viewNoRedirect == 0 {
|
||||
http.Redirect(w, r, p.RedirectURL().String(), http.StatusSeeOther)
|
||||
}
|
||||
return
|
||||
default:
|
||||
panic("paste type unsupported")
|
||||
}
|
||||
if flags&viewNoRedirect == 0 {
|
||||
http.Redirect(w, r, location, http.StatusSeeOther)
|
||||
}
|
||||
fmt.Fprint(w, location)
|
||||
|
||||
case db.PasteStateDeleted:
|
||||
rl.renderError(w, r, http.StatusGone, "paste has been deleted\n")
|
||||
return
|
||||
default:
|
||||
panic(errors.Errorf("invalid paste.State (%v) for key '%v'", p.State, p.Key))
|
||||
}
|
||||
}
|
||||
|
||||
func (rl *rushlink) viewFileUploadHandler(w http.ResponseWriter, r *http.Request, fu *db.FileUpload) {
|
||||
filePath := fu.Path(rl.fs)
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
log.Printf("error: '%v' should exist according to the database, but it doesn't", filePath)
|
||||
rl.renderError(w, r, http.StatusNotFound, "file not found")
|
||||
return
|
||||
}
|
||||
// unexpected error
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var modtime time.Time
|
||||
info, err := file.Stat()
|
||||
if err != nil {
|
||||
log.Printf("error: %v", errors.Wrapf(err, "could not stat file '%v'", filePath))
|
||||
} else {
|
||||
modtime = info.ModTime()
|
||||
}
|
||||
|
||||
// Provide the real filename to the client (to be used in Ctrl+S etc.)
|
||||
quotedName := strings.ReplaceAll(fu.FileName, "\"", "\\\"")
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"%s\"", quotedName))
|
||||
|
||||
// We use http.ServeContent (instead of http.ServeFile) because we cannot
|
||||
// use http.ServeFile together with the assertion that the file exists,
|
||||
// without introducing a TOCTOU flaw.
|
||||
http.ServeContent(w, r, fu.FileName, modtime, file)
|
||||
}
|
||||
|
||||
func (rl *rushlink) viewPasteHandlerInnerMeta(w http.ResponseWriter, r *http.Request, p *db.Paste, fu *db.FileUpload) {
|
||||
var cd canDelete
|
||||
deleteToken := getDeleteTokenFromRequest(r)
|
||||
|
@ -99,7 +99,6 @@ func StartMainServer(addr string, db *db.Database, fs *db.FileStore, rawRootURL
|
||||
router := mux.NewRouter()
|
||||
router.Use(rl.recoveryMiddleware)
|
||||
router.Use(rl.metricsMiddleware)
|
||||
router.HandleFunc("/uploads/{id:[A-Za-z0-9-_]+}/{filename:.+}", rl.uploadFileGetHandler).Methods("GET", "HEAD")
|
||||
router.HandleFunc("/{path:img/"+staticFilenameExpr+"}", rl.staticGetHandler).Methods("GET", "HEAD")
|
||||
router.HandleFunc("/{path:css/"+staticFilenameExpr+"}", rl.staticGetHandler).Methods("GET", "HEAD")
|
||||
router.HandleFunc("/{path:js/"+staticFilenameExpr+"}", rl.staticGetHandler).Methods("GET", "HEAD")
|
||||
|
Loading…
Reference in New Issue
Block a user