diff --git a/main.go b/main.go
index 2a90f5f..762a714 100644
--- a/main.go
+++ b/main.go
@@ -275,6 +275,7 @@ func searchHandler(w http.ResponseWriter, r *http.Request) {
func servePDF(w http.ResponseWriter, r *http.Request) {
hash := chi.URLParam(r, "hash")
+ download := r.URL.Query().Get("download") == "1"
// Try PDF first, then TXT
for _, ext := range []string{".pdf", ".txt"} {
@@ -285,6 +286,13 @@ func servePDF(w http.ResponseWriter, r *http.Request) {
} else {
w.Header().Set("Content-Type", "text/plain")
}
+ if download {
+ filename := hash + ext
+ if doc, err := GetDocument(hash); err == nil && doc.Title != "" {
+ filename = sanitizeFilename(doc.Title) + ext
+ }
+ w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filename))
+ }
http.ServeFile(w, r, path)
return
}
@@ -293,6 +301,13 @@ func servePDF(w http.ResponseWriter, r *http.Request) {
// Try without extension
path := filepath.Join(storeDir, hash)
if _, err := os.Stat(path); err == nil {
+ if download {
+ filename := hash
+ if doc, err := GetDocument(hash); err == nil && doc.Title != "" {
+ filename = sanitizeFilename(doc.Title)
+ }
+ w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filename))
+ }
http.ServeFile(w, r, path)
return
}
@@ -300,6 +315,12 @@ func servePDF(w http.ResponseWriter, r *http.Request) {
http.Error(w, "File not found", http.StatusNotFound)
}
+// sanitizeFilename removes characters unsafe for use in Content-Disposition filenames.
+func sanitizeFilename(name string) string {
+ replacer := strings.NewReplacer(`"`, "'", "/", "-", "\\", "-", "\n", " ", "\r", "")
+ return replacer.Replace(name)
+}
+
// API handlers
func apiSearchHandler(w http.ResponseWriter, r *http.Request) {
diff --git a/templates/document.html b/templates/document.html
index 718472c..62e1637 100644
--- a/templates/document.html
+++ b/templates/document.html
@@ -39,7 +39,7 @@
Share
{{if .Document.PDFPath}}
-
+