53 lines
1.4 KiB
Go
53 lines
1.4 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"path/filepath"
|
|
)
|
|
|
|
// ServeMedia serves media files with path traversal protection
|
|
func (h *Handlers) ServeMedia(w http.ResponseWriter, r *http.Request) {
|
|
// Expected path format: /api/media/{accountID}/{filename}
|
|
path := r.URL.Path[len("/api/media/"):]
|
|
|
|
// Split path into accountID and filename
|
|
var accountID, filename string
|
|
for i, ch := range path {
|
|
if ch == '/' {
|
|
accountID = path[:i]
|
|
filename = path[i+1:]
|
|
break
|
|
}
|
|
}
|
|
|
|
if accountID == "" || filename == "" {
|
|
http.Error(w, "Invalid media path", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
// Construct full file path
|
|
filePath := filepath.Join(h.config.Media.DataPath, accountID, filename)
|
|
|
|
// Security check: ensure the resolved path is within the media directory
|
|
mediaDir := filepath.Join(h.config.Media.DataPath, accountID)
|
|
absFilePath, err := filepath.Abs(filePath)
|
|
if err != nil {
|
|
http.Error(w, "Invalid file path", http.StatusBadRequest)
|
|
return
|
|
}
|
|
absMediaDir, err := filepath.Abs(mediaDir)
|
|
if err != nil {
|
|
http.Error(w, "Invalid media directory", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// Check if file path is within media directory (prevent directory traversal)
|
|
if len(absFilePath) < len(absMediaDir) || absFilePath[:len(absMediaDir)] != absMediaDir {
|
|
http.Error(w, "Access denied", http.StatusForbidden)
|
|
return
|
|
}
|
|
|
|
// Serve the file
|
|
http.ServeFile(w, r, absFilePath)
|
|
}
|