diff --git a/assets/templates/html/deletePasteSuccess.html.tmpl b/assets/templates/html/deletePasteSuccess.html.tmpl
new file mode 100644
index 0000000..334f2cb
--- /dev/null
+++ b/assets/templates/html/deletePasteSuccess.html.tmpl
@@ -0,0 +1,3 @@
+{{define "title"}}
+Success - rushlink
+{{end}}
\ No newline at end of file
diff --git a/assets/templates/txt/deletePasteSuccess.txt.tmpl b/assets/templates/txt/deletePasteSuccess.txt.tmpl
new file mode 100644
index 0000000..0ffae3c
--- /dev/null
+++ b/assets/templates/txt/deletePasteSuccess.txt.tmpl
@@ -0,0 +1 @@
+<{{.Request.Host}}/{{.Paste.Key}}> was succesfully deleted
diff --git a/assets/templates/txt/newFileUploadPasteSuccess.txt.tmpl b/assets/templates/txt/newFileUploadPasteSuccess.txt.tmpl
index bbceb50..dba998e 100644
--- a/assets/templates/txt/newFileUploadPasteSuccess.txt.tmpl
+++ b/assets/templates/txt/newFileUploadPasteSuccess.txt.tmpl
@@ -1,4 +1,5 @@
-https://{{.Request.Host}}/{{.Paste.Key}}
{{if .Request.PostForm.deleteToken -}}
https://{{.Request.Host}}/{{.Paste.Key}}?deleteToken={{.Paste.DeleteToken | urlquery}}
+{{else -}}
+https://{{.Request.Host}}/{{.Paste.Key}}
{{end -}}
diff --git a/assets/templates/txt/newRedirectPasteSuccess.txt.tmpl b/assets/templates/txt/newRedirectPasteSuccess.txt.tmpl
index bbceb50..dba998e 100644
--- a/assets/templates/txt/newRedirectPasteSuccess.txt.tmpl
+++ b/assets/templates/txt/newRedirectPasteSuccess.txt.tmpl
@@ -1,4 +1,5 @@
-https://{{.Request.Host}}/{{.Paste.Key}}
{{if .Request.PostForm.deleteToken -}}
https://{{.Request.Host}}/{{.Paste.Key}}?deleteToken={{.Paste.DeleteToken | urlquery}}
+{{else -}}
+https://{{.Request.Host}}/{{.Paste.Key}}
{{end -}}
diff --git a/fileupload.go b/fileupload.go
index bcef1e8..2925065 100644
--- a/fileupload.go
+++ b/fileupload.go
@@ -79,7 +79,7 @@ func (t fileUploadState) String() string {
func OpenFileStore(path string) error {
if path == "" {
- return errors.New("file store path not set")
+ return errors.New("file-store not set")
}
// Try to create the file store directory if it does not yet exist
@@ -158,11 +158,23 @@ func (fu *fileUpload) save(tx *bolt.Tx) error {
}
func (fu *fileUpload) delete(tx *bolt.Tx) error {
- // Replace the old paste with a new empty paste
- return (&fileUpload{
+ // Remove the file in the backend
+ filePath := fileStorePath(fu.ID, fu.FileName)
+ if err := os.Remove(filePath); err != nil {
+ return err
+ }
+
+ // Update the file in the server
+ if err := (&fileUpload{
ID: fu.ID,
State: fileUploadStateDeleted,
- }).save(tx)
+ }).save(tx); err != nil {
+ return err
+ }
+
+ // Cleanup the parent directory
+ wrap := "deletion succeeded, but removing the file directory has failed"
+ return errors.Wrap(os.Remove(path.Dir(filePath)), wrap)
}
func (fu *fileUpload) url() *url.URL {
diff --git a/handlers.go b/handlers.go
index 6de1de3..ca8075c 100644
--- a/handlers.go
+++ b/handlers.go
@@ -173,47 +173,34 @@ func viewPasteHandlerInner(w http.ResponseWriter, r *http.Request, flags viewPas
}
func newPasteHandler(w http.ResponseWriter, r *http.Request) {
- newPasteHandlerMultipart(w, r, func(w http.ResponseWriter, r *http.Request) {
- newPasteHandlerURLEncoded(w, r, func(w http.ResponseWriter, r *http.Request) {
- renderError(w, r, http.StatusBadRequest, "no form data in request\n")
- })
- })
-}
-
-// Try to parse the new-paste request as multi-part form.
-//
-// If there is no multi-part form in the request, this handler will call next.
-func newPasteHandlerMultipart(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
- reader, err := r.MultipartReader()
- if err != nil {
- next(w, r)
+ file, fileHeader, err := r.FormFile("file")
+ if err == nil {
+ newFileUploadPasteHandler(w, r, file, *fileHeader)
return
- }
-
- part, err := reader.NextPart()
- if err != nil {
- if err == io.EOF {
- renderError(w, r, http.StatusBadRequest, "multipart form is empty\n")
- return
- }
- panic(errors.Wrap(err, "multipart.Reader.NextPart"))
- }
- if part.FormName() != "file" {
- msg := fmt.Sprintf("invalid multipart form name: %v", part.FormName())
+ } else if err == http.ErrMissingFile {
+ // Fallthrough
+ } else {
+ msg := fmt.Sprintf("could not parse form: %v\n", err)
renderError(w, r, http.StatusBadRequest, msg)
return
}
- newFileUploadPasteHandler(w, r, *part)
+ shorten := r.FormValue("shorten")
+ if shorten != "" {
+ newRedirectPasteHandler(w, r, shorten)
+ return
+ }
+
+ renderError(w, r, http.StatusBadRequest, "no 'file' and no 'shorten' fields given in form\n")
}
-func newFileUploadPasteHandler(w http.ResponseWriter, r *http.Request, part multipart.Part) {
+func newFileUploadPasteHandler(w http.ResponseWriter, r *http.Request, file multipart.File, header multipart.FileHeader) {
var fu *fileUpload
var paste *paste
if err := DB.Update(func(tx *bolt.Tx) error {
var err error
// Create the fileUpload in the database
- fu, err = newFileUpload(tx, &part, part.FileName(), part.Header.Get("Content-Type"))
+ fu, err = newFileUpload(tx, file, header.Filename, header.Header.Get("Content-Type"))
if err != nil {
panic(errors.Wrap(err, "creating fileUpload"))
}
@@ -240,8 +227,7 @@ func newPasteHandlerURLEncoded(w http.ResponseWriter, r *http.Request, next http
newRedirectPasteHandler(w, r, shorten)
}
-func newRedirectPasteHandler(w http.ResponseWriter, r *http.Request, shorten string) {
- rawurl := r.PostForm.Get("shorten")
+func newRedirectPasteHandler(w http.ResponseWriter, r *http.Request, rawurl string) {
userURL, err := url.ParseRequestURI(rawurl)
if err != nil {
msg := fmt.Sprintf("invalid url (%v): %v", err, rawurl)
@@ -259,7 +245,6 @@ func newRedirectPasteHandler(w http.ResponseWriter, r *http.Request, shorten str
var paste *paste
if err := DB.Update(func(tx *bolt.Tx) error {
- // Generate a new delete token for this paste
var err error
paste, err = shortenURL(tx, userURL)
return err
@@ -282,22 +267,35 @@ func deletePasteHandler(w http.ResponseWriter, r *http.Request) {
}
var errorCode int
+ var paste paste
if err := DB.Update(func(tx *bolt.Tx) error {
p, err := getPaste(tx, key)
if err != nil {
errorCode = http.StatusNotFound
return err
}
- if subtle.ConstantTimeCompare([]byte(deleteToken), []byte(p.DeleteToken)) == 1 {
- p.delete(tx)
+ if p.State == pasteStateDeleted {
+ errorCode = http.StatusGone
+ return errors.New("already deleted")
}
- errorCode = http.StatusForbidden
- return errors.New("invalid delete token")
+ if subtle.ConstantTimeCompare([]byte(deleteToken), []byte(p.DeleteToken)) == 0 {
+ errorCode = http.StatusForbidden
+ return errors.New("invalid delete token")
+ }
+ if err := p.delete(tx); err != nil {
+ errorCode = http.StatusInternalServerError
+ return err
+ }
+ paste = *p
+ return nil
}); err != nil {
log.Printf("error: %v\n", err)
renderError(w, r, errorCode, fmt.Sprintf("error: %v\n", err))
return
}
+
+ data := map[string]interface{}{"Paste": paste}
+ render(w, r, "deletePasteSuccess", data)
}
// Add a new fileUpload redirect to the database
diff --git a/paste.go b/paste.go
index 28c6fef..7455cbe 100644
--- a/paste.go
+++ b/paste.go
@@ -7,6 +7,7 @@ import (
"strings"
"time"
+ "github.com/google/uuid"
"github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
)
@@ -44,6 +45,8 @@ func (t pasteType) String() string {
return "paste"
case pasteTypeRedirect:
return "redirect"
+ case pasteTypeFileUpload:
+ return "file"
default:
return "invalid"
}
@@ -94,12 +97,29 @@ func (p *paste) save(tx *bolt.Tx) error {
}
func (p *paste) delete(tx *bolt.Tx) error {
+ // Remove the (maybe) attached file
+ if p.Type == pasteTypeFileUpload {
+ fuID, err := uuid.FromBytes(p.Content)
+ if err != nil {
+ return errors.Wrap(err, "failed to parse uuid")
+ }
+ fu, err := getFileUpload(tx, fuID)
+ if err != nil {
+ return errors.Wrap(err, "failed to find file in database")
+ }
+ if err := fu.delete(tx); err != nil {
+ return errors.Wrap(err, "failed to remove file")
+ }
+ }
+
// Replace the old paste with a new empty paste
- return (&paste{
- Key: p.Key,
- State: pasteStateDeleted,
- DeleteToken: p.DeleteToken,
- }).save(tx)
+ p.Type = pasteTypeUndef
+ p.State = pasteStateDeleted
+ p.Content = []byte{}
+ if err := p.save(tx); err != nil {
+ return errors.Wrap(err, "failed to delete paste in database")
+ }
+ return nil
}
// Get the URL from this paste.