diff --git a/internal/handlers/router.go b/internal/handlers/router.go index 7cd100d..6d47110 100644 --- a/internal/handlers/router.go +++ b/internal/handlers/router.go @@ -3,6 +3,7 @@ package handlers import ( "fmt" "net/http" + "net/url" "path/filepath" "strings" @@ -102,6 +103,7 @@ func AttachHandlers(router *chi.Mux, downloader *universal.Downloader, torMgr *t func (hs *Handlers) innerRootHandler(resp http.ResponseWriter, req *http.Request, handleFunc func(*torrent.TorrentManager) ([]byte, error), contentType string) { out, err := handleFunc(hs.torMgr) if err != nil && strings.Contains(contentType, "xml") { + hs.log.Debugf("Not implemented: %s %s", req.Method, req.URL) resp.WriteHeader(http.StatusNotImplemented) resp.Write([]byte("NotImplementedNot Implemented Method")) return @@ -133,9 +135,10 @@ func (hs *Handlers) handleInfuseRoot(resp http.ResponseWriter, req *http.Request // handle torrent list request func (hs *Handlers) innerTorrentsListHandler(resp http.ResponseWriter, req *http.Request, handleFunc func(string, *torrent.TorrentManager) ([]byte, error), contentType string) { - directory := chi.URLParam(req, "directory") + directory, _ := url.PathUnescape(chi.URLParam(req, "directory")) out, err := handleFunc(directory, hs.torMgr) if err != nil && strings.Contains(contentType, "xml") { + hs.log.Debugf("Not implemented: %s %s", req.Method, req.URL) resp.WriteHeader(http.StatusNotImplemented) resp.Write([]byte("NotImplementedNot Implemented Method")) return @@ -153,7 +156,7 @@ func (hs *Handlers) innerTorrentsListHandler(resp http.ResponseWriter, req *http } func (hs *Handlers) handleHttpTorrentsList(resp http.ResponseWriter, req *http.Request) { - directory := chi.URLParam(req, "directory") + directory, _ := url.PathUnescape(chi.URLParam(req, "directory")) handlerFunc := intHttp.ServeTorrentsList if directory == config.DOWNLOADS { handlerFunc = func(_ string, torMgr *torrent.TorrentManager) ([]byte, error) { @@ -164,7 +167,7 @@ func (hs *Handlers) handleHttpTorrentsList(resp http.ResponseWriter, req *http.R } func (hs *Handlers) handleDavTorrentsList(resp http.ResponseWriter, req *http.Request) { - directory := chi.URLParam(req, "directory") + directory, _ := url.PathUnescape(chi.URLParam(req, "directory")) handlerFunc := dav.ServeTorrentsList if directory == config.DOWNLOADS { handlerFunc = func(_ string, torMgr *torrent.TorrentManager) ([]byte, error) { @@ -175,7 +178,7 @@ func (hs *Handlers) handleDavTorrentsList(resp http.ResponseWriter, req *http.Re } func (hs *Handlers) handleInfuseTorrentsList(resp http.ResponseWriter, req *http.Request) { - directory := chi.URLParam(req, "directory") + directory, _ := url.PathUnescape(chi.URLParam(req, "directory")) handlerFunc := dav.ServeTorrentsListForInfuse if directory == config.DOWNLOADS { handlerFunc = func(_ string, torMgr *torrent.TorrentManager) ([]byte, error) { @@ -188,10 +191,11 @@ func (hs *Handlers) handleInfuseTorrentsList(resp http.ResponseWriter, req *http // handle files list request func (hs *Handlers) innerFilesListHandler(resp http.ResponseWriter, req *http.Request, handleFunc func(string, string, *torrent.TorrentManager) ([]byte, error), contentType string) { - directory := chi.URLParam(req, "directory") - torrentName := chi.URLParam(req, "torrent") + directory, _ := url.PathUnescape(chi.URLParam(req, "directory")) + torrentName, _ := url.PathUnescape(chi.URLParam(req, "torrent")) out, err := handleFunc(directory, torrentName, hs.torMgr) if err != nil && strings.Contains(contentType, "xml") { + hs.log.Debugf("Not implemented: %s %s", req.Method, req.URL) resp.WriteHeader(http.StatusNotImplemented) resp.Write([]byte("NotImplementedNot Implemented Method")) return @@ -246,9 +250,9 @@ func (hs *Handlers) handleInfuseDownloadsList(resp http.ResponseWriter, req *htt // handle delete request func (hs *Handlers) deleteFileHandler(resp http.ResponseWriter, req *http.Request) { - directory := chi.URLParam(req, "directory") - torrentName := chi.URLParam(req, "torrent") - fileName := chi.URLParam(req, "filename") + directory, _ := url.PathUnescape(chi.URLParam(req, "directory")) + torrentName, _ := url.PathUnescape(chi.URLParam(req, "torrent")) + fileName, _ := url.PathUnescape(chi.URLParam(req, "filename")) if dav.HandleDeleteFile(directory, torrentName, fileName, hs.torMgr) != nil { http.NotFound(resp, req) return @@ -257,8 +261,8 @@ func (hs *Handlers) deleteFileHandler(resp http.ResponseWriter, req *http.Reques } func (hs *Handlers) deleteTorrentHandler(resp http.ResponseWriter, req *http.Request) { - directory := chi.URLParam(req, "directory") - torrentName := chi.URLParam(req, "torrent") + directory, _ := url.PathUnescape(chi.URLParam(req, "directory")) + torrentName, _ := url.PathUnescape(chi.URLParam(req, "torrent")) if dav.HandleDeleteTorrent(directory, torrentName, hs.torMgr) != nil { http.NotFound(resp, req) return @@ -269,9 +273,9 @@ func (hs *Handlers) deleteTorrentHandler(resp http.ResponseWriter, req *http.Req // other handlers func (hs *Handlers) davCheckSingleFileHandler(resp http.ResponseWriter, req *http.Request) { - directory := chi.URLParam(req, "directory") - torrentName := chi.URLParam(req, "torrent") - fileName := chi.URLParam(req, "filename") + directory, _ := url.PathUnescape(chi.URLParam(req, "directory")) + torrentName, _ := url.PathUnescape(chi.URLParam(req, "torrent")) + fileName, _ := url.PathUnescape(chi.URLParam(req, "filename")) out, err := dav.HandleSingleFile(directory, torrentName, fileName, hs.torMgr) if err != nil { http.NotFound(resp, req) @@ -287,9 +291,9 @@ func (hs *Handlers) mkcolTorrentHandler(resp http.ResponseWriter, req *http.Requ } func (hs *Handlers) moveFileHandler(resp http.ResponseWriter, req *http.Request) { - directory := chi.URLParam(req, "directory") - torrentName := chi.URLParam(req, "torrent") - fileName := chi.URLParam(req, "filename") + directory, _ := url.PathUnescape(chi.URLParam(req, "directory")) + torrentName, _ := url.PathUnescape(chi.URLParam(req, "torrent")) + fileName, _ := url.PathUnescape(chi.URLParam(req, "filename")) newName := req.Header.Get("Destination") newName = filepath.Base(newName) if dav.HandleRenameFile(directory, torrentName, fileName, newName, hs.torMgr) != nil { @@ -300,8 +304,8 @@ func (hs *Handlers) moveFileHandler(resp http.ResponseWriter, req *http.Request) } func (hs *Handlers) moveTorrentHandler(resp http.ResponseWriter, req *http.Request) { - directory := chi.URLParam(req, "directory") - torrentName := chi.URLParam(req, "torrent") + directory, _ := url.PathUnescape(chi.URLParam(req, "directory")) + torrentName, _ := url.PathUnescape(chi.URLParam(req, "torrent")) newName := req.Header.Get("Destination") newName = filepath.Base(newName) if dav.HandleRenameTorrent(directory, torrentName, newName, hs.torMgr) != nil { @@ -314,14 +318,14 @@ func (hs *Handlers) moveTorrentHandler(resp http.ResponseWriter, req *http.Reque // universal handlers func (hs *Handlers) handleDownloadFile(resp http.ResponseWriter, req *http.Request) { - directory := chi.URLParam(req, "directory") - torrentName := chi.URLParam(req, "torrent") - fileName := chi.URLParam(req, "filename") + directory, _ := url.PathUnescape(chi.URLParam(req, "directory")) + torrentName, _ := url.PathUnescape(chi.URLParam(req, "torrent")) + fileName, _ := url.PathUnescape(chi.URLParam(req, "filename")) hs.downloader.DownloadFile(directory, torrentName, fileName, resp, req, hs.torMgr, hs.cfg, hs.log) } func (hs *Handlers) handleDownloadLink(resp http.ResponseWriter, req *http.Request) { - filename := chi.URLParam(req, "filename") + filename, _ := url.PathUnescape(chi.URLParam(req, "filename")) if download, ok := hs.torMgr.DownloadMap.Get(filename); ok { hs.downloader.DownloadLink(download.Filename, download.Download, resp, req, hs.torMgr, hs.cfg, hs.log) } else { @@ -330,14 +334,14 @@ func (hs *Handlers) handleDownloadLink(resp http.ResponseWriter, req *http.Reque } func (hs *Handlers) handleCheckFile(resp http.ResponseWriter, req *http.Request) { - directory := chi.URLParam(req, "directory") - torrentName := chi.URLParam(req, "torrent") - fileName := chi.URLParam(req, "filename") + directory, _ := url.PathUnescape(chi.URLParam(req, "directory")) + torrentName, _ := url.PathUnescape(chi.URLParam(req, "torrent")) + fileName, _ := url.PathUnescape(chi.URLParam(req, "filename")) universal.CheckFile(directory, torrentName, fileName, resp, req, hs.torMgr, hs.log) } func (hs *Handlers) handleCheckDownloadLink(resp http.ResponseWriter, req *http.Request) { - filename := chi.URLParam(req, "filename") + filename, _ := url.PathUnescape(chi.URLParam(req, "filename")) if download, ok := hs.torMgr.DownloadMap.Get(filename); ok { universal.CheckDownloadLink(download, resp, req, hs.torMgr, hs.log) } else { diff --git a/internal/universal/downloader.go b/internal/universal/downloader.go index b11cdb0..062dcf8 100644 --- a/internal/universal/downloader.go +++ b/internal/universal/downloader.go @@ -51,7 +51,6 @@ func (dl *Downloader) DownloadFile(directory, torrentName, fileName string, resp } link := file.Link - log.Debugf("Opening file %s from torrent %s (%s)", fileName, torrentName, link) unrestrict := torMgr.UnrestrictUntilOk(link) if unrestrict == nil { log.Warnf("File %s cannot be unrestricted (link=%s)", fileName, link) @@ -103,7 +102,6 @@ func (dl *Downloader) DownloadLink(fileName, link string, resp http.ResponseWrit return } - log.Debugf("Opening download %s", fileName) unrestrict := torMgr.UnrestrictUntilOk(link) if unrestrict == nil { log.Warnf("File %s cannot be unrestricted (link=%s)", fileName, link) @@ -151,8 +149,17 @@ func (dl *Downloader) streamFileToResponse(torrent *intTor.Torrent, file *intTor } // copy range header if it exists + rangeLog := "" if req.Header.Get("Range") != "" { dlReq.Header.Add("Range", req.Header.Get("Range")) + rangeLog = " (range: " + req.Header.Get("Range") + ")" + + } + + if torrent != nil { + log.Debugf("Downloading file %s from torrent %s (%s)%s", file.Path, torrent.Name, unrestrict.Link, rangeLog) + } else { + log.Debugf("Downloading file %s (%s)%s", file.Path, unrestrict.Link, rangeLog) } download, err := dl.client.Do(dlReq) diff --git a/pkg/dav/util.go b/pkg/dav/util.go index bacc107..9064316 100644 --- a/pkg/dav/util.go +++ b/pkg/dav/util.go @@ -9,15 +9,17 @@ func customPathEscape(input string) string { // First, URL-escape the path segments segments := strings.Split(input, "/") for i, segment := range segments { - segments[i] = url.PathEscape(segment) + escapedSegment := strings.ReplaceAll(segment, "%", "ZURG25") + escapedSegment = url.PathEscape(escapedSegment) + escapedSegment = strings.ReplaceAll(escapedSegment, "ZURG25", "%25") + segments[i] = escapedSegment } escapedPath := strings.Join(segments, "/") // Convert any XML-escaped sequences back to URL-escaped sequences - escapedPath = strings.Replace(escapedPath, "&", "%26", -1) // for & - escapedPath = strings.Replace(escapedPath, "<", "%3C", -1) // for < - escapedPath = strings.Replace(escapedPath, ">", "%3E", -1) // for > - escapedPath = strings.Replace(escapedPath, "\"", "%22", -1) // for " - escapedPath = strings.Replace(escapedPath, "'", "%27", -1) // for ' - escapedPath = strings.Replace(escapedPath, ":", "%3A", -1) // for : + escapedPath = strings.ReplaceAll(escapedPath, "$", "%24") + escapedPath = strings.ReplaceAll(escapedPath, "&", "%26") + escapedPath = strings.ReplaceAll(escapedPath, "+", "%2B") + escapedPath = strings.ReplaceAll(escapedPath, ":", "%3A") + escapedPath = strings.ReplaceAll(escapedPath, "@", "%40") return escapedPath } diff --git a/pkg/http/client.go b/pkg/http/client.go index 99c30e9..6141243 100644 --- a/pkg/http/client.go +++ b/pkg/http/client.go @@ -177,7 +177,7 @@ func (r *HTTPClient) Do(req *http.Request) (*http.Response, error) { } r.replaceHostIfNeeded(req) // check if Range header is set - hasRangeHeader := req.Header.Get("Range") != "" + hasRangeHeader := req.Header.Get("Range") != "" && req.Header.Get("Range") != "bytes=0-" var resp *http.Response var err error diff --git a/pkg/realdebrid/types.go b/pkg/realdebrid/types.go index 600c4d0..184bf4f 100644 --- a/pkg/realdebrid/types.go +++ b/pkg/realdebrid/types.go @@ -87,10 +87,16 @@ type TorrentInfo struct { } func (i *TorrentInfo) IsDone() bool { + selectedCount := 0 + for _, file := range i.Files { + if file.Selected == 1 { + selectedCount++ + } + } if i.Progress != 100 { return false } - return len(i.Files) == len(i.Links) && len(i.Files) > 0 + return selectedCount == len(i.Links) && len(i.Links) > 0 } func (i *TorrentInfo) MarshalJSON() ([]byte, error) {