123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- package main
- import (
- "errors"
- "log"
- "net/http"
- "net/url"
- "path/filepath"
- "strings"
- fs "imuslab.com/arozos/mod/filesystem"
- "imuslab.com/arozos/mod/network/gzipmiddleware"
- )
- /*
- Media Server
- This function serve large file objects like video and audio file via asynchronize go routine :)
- Example usage:
- /media/?file=user:/Desktop/test/02.Orchestra- エミール (Addendum version).mp3
- /media/?file=user:/Desktop/test/02.Orchestra- エミール (Addendum version).mp3&download=true
- This will serve / download the file located at files/users/{username}/Desktop/test/02.Orchestra- エミール (Addendum version).mp3
- PLEASE ALWAYS USE URLENCODE IN THE LINK PASSED INTO THE /media ENDPOINT
- */
- //
- func mediaServer_init() {
- if *enable_gzip {
- http.HandleFunc("/media/", gzipmiddleware.CompressFunc(serverMedia))
- http.HandleFunc("/media/getMime/", gzipmiddleware.CompressFunc(serveMediaMime))
- } else {
- http.HandleFunc("/media/", serverMedia)
- http.HandleFunc("/media/getMime/", serveMediaMime)
- }
- }
- //This function validate the incoming media request and return the real path for the targed file
- func media_server_validateSourceFile(w http.ResponseWriter, r *http.Request) (string, error) {
- username, err := authAgent.GetUserName(w, r)
- if err != nil {
- return "", errors.New("User not logged in")
- }
- userinfo, _ := userHandler.GetUserInfoFromUsername(username)
- //Validate url valid
- if strings.Count(r.URL.String(), "?") > 1 {
- return "", errors.New("Invalid paramters. Multiple ? found")
- }
- targetfile, _ := mv(r, "file", false)
- targetfile, _ = url.QueryUnescape(targetfile)
- if targetfile == "" {
- return "", errors.New("Missing paramter 'file'")
- }
- //Translate the virtual directory to realpath
- realFilepath, err := userinfo.VirtualPathToRealPath(targetfile)
- if fileExists(realFilepath) && IsDir(realFilepath) {
- return "", errors.New("Given path is not a file.")
- }
- if err != nil {
- return "", errors.New("Unable to translate the given filepath")
- }
- if !fileExists(realFilepath) {
- //Sometime if url is not URL encoded, this error might be shown as well
- //Try to use manual segmentation
- originalURL := r.URL.String()
- //Must be pre-processed with system special URI Decode function to handle edge cases
- originalURL = fs.DecodeURI(originalURL)
- if strings.Contains(originalURL, "&download=true") {
- originalURL = strings.ReplaceAll(originalURL, "&download=true", "")
- } else if strings.Contains(originalURL, "download=true") {
- originalURL = strings.ReplaceAll(originalURL, "download=true", "")
- }
- if strings.Contains(originalURL, "&file=") {
- originalURL = strings.ReplaceAll(originalURL, "&file=", "file=")
- }
- urlInfo := strings.Split(originalURL, "file=")
- possibleVirtualFilePath := urlInfo[len(urlInfo)-1]
- possibleRealpath, err := userinfo.VirtualPathToRealPath(possibleVirtualFilePath)
- if err != nil {
- log.Println("Error when trying to serve file in compatibility mode", err.Error())
- return "", errors.New("Error when trying to serve file in compatibility mode")
- }
- if fileExists(possibleRealpath) {
- realFilepath = possibleRealpath
- log.Println("[Media Server] Serving file " + filepath.Base(possibleRealpath) + " in compatibility mode. Do not to use '&' or '+' sign in filename! ")
- return realFilepath, nil
- } else {
- return "", errors.New("File not exists")
- }
- }
- return realFilepath, nil
- }
- func serveMediaMime(w http.ResponseWriter, r *http.Request) {
- realFilepath, err := media_server_validateSourceFile(w, r)
- if err != nil {
- sendErrorResponse(w, err.Error())
- return
- }
- mime := "text/directory"
- if !IsDir(realFilepath) {
- m, _, err := fs.GetMime(realFilepath)
- if err != nil {
- mime = ""
- }
- mime = m
- }
- sendTextResponse(w, mime)
- }
- func serverMedia(w http.ResponseWriter, r *http.Request) {
- //Serve normal media files
- realFilepath, err := media_server_validateSourceFile(w, r)
- if err != nil {
- sendErrorResponse(w, err.Error())
- return
- }
- //Check if downloadMode
- downloadMode := false
- dw, _ := mv(r, "download", false)
- if dw == "true" {
- downloadMode = true
- }
- //Serve the file
- if downloadMode {
- userAgent := r.Header.Get("User-Agent")
- filename := strings.ReplaceAll(url.QueryEscape(filepath.Base(realFilepath)), "+", "%20")
- log.Println(r.Header.Get("User-Agent"))
- if strings.Contains(userAgent, "Safari/") {
- //This is Safari. Use speial header
- w.Header().Set("Content-Disposition", "attachment; filename="+filepath.Base(realFilepath))
- w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
- } else {
- //Fixing the header issue on Golang url encode lib problems
- w.Header().Set("Content-Disposition", "attachment; filename*=UTF-8''"+filename)
- w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
- }
- }
- http.ServeFile(w, r, realFilepath)
- }
|