package dav import ( "encoding/xml" "fmt" "net/http" "net/url" "path" "path/filepath" "sort" "strings" "github.com/debridmediamanager.com/zurg/internal/config" "github.com/debridmediamanager.com/zurg/internal/torrent" "github.com/debridmediamanager.com/zurg/pkg/dav" "github.com/debridmediamanager.com/zurg/pkg/logutil" ) func HandlePropfindRequest(w http.ResponseWriter, r *http.Request, t *torrent.TorrentManager, c config.ConfigInterface) { log := logutil.NewLogger().Named("dav") requestPath := path.Clean(r.URL.Path) requestPath = strings.Trim(requestPath, "/") var output []byte var err error filteredSegments := strings.Split(requestPath, "/") switch { case len(filteredSegments) == 1 && filteredSegments[0] == "": output, err = handleRoot(c) case len(filteredSegments) == 1: output, err = handleListOfTorrents(requestPath, t) case len(filteredSegments) == 2: output, err = handleSingleTorrent(requestPath, t) default: log.Warnf("Request %s %s not found", r.Method, requestPath) http.Error(w, "Not Found", http.StatusNotFound) return } if err != nil { if strings.Contains(err.Error(), "cannot find") { http.Error(w, "Not Found", http.StatusNotFound) return } log.Errorf("Error processing request: %v", err) http.Error(w, "Server error", http.StatusInternalServerError) return } if output != nil { respBody := fmt.Sprintf("\n%s\n", output) w.Header().Set("Content-Type", "text/xml; charset=\"utf-8\"") w.WriteHeader(http.StatusMultiStatus) fmt.Fprint(w, respBody) } } func handleRoot(c config.ConfigInterface) ([]byte, error) { var responses []dav.Response responses = append(responses, dav.Directory("")) for _, directory := range c.GetDirectories() { responses = append(responses, dav.Directory(directory)) } rootResponse := dav.MultiStatus{ XMLNS: "DAV:", Response: responses, } return xml.Marshal(rootResponse) } func handleListOfTorrents(requestPath string, t *torrent.TorrentManager) ([]byte, error) { basePath := path.Base(requestPath) torrents, ok := t.DirectoryMap.Get(basePath) if !ok { return nil, fmt.Errorf("cannot find directory %s", basePath) } var responses []dav.Response // initial response is the directory itself responses = append(responses, dav.Directory(basePath)) var accessKeys []string torrents.IterCb(func(accessKey string, _ *torrent.Torrent) { accessKeys = append(accessKeys, accessKey) }) sort.Strings(accessKeys) for _, accessKey := range accessKeys { responses = append(responses, dav.Directory(filepath.Join(basePath, accessKey))) } resp := &dav.MultiStatus{ XMLNS: "DAV:", Response: responses, } return xml.Marshal(resp) } func handleSingleTorrent(requestPath string, t *torrent.TorrentManager) ([]byte, error) { basePath := path.Base(path.Dir(requestPath)) torrents, ok := t.DirectoryMap.Get(basePath) if !ok { return nil, fmt.Errorf("cannot find directory %s", basePath) } accessKey := path.Base(requestPath) tor, ok := torrents.Get(accessKey) if !ok { return nil, fmt.Errorf("cannot find torrent %s", accessKey) } var responses []dav.Response // initial response is the directory itself responses = append(responses, dav.Directory(requestPath)) tor.SelectedFiles.IterCb(func(filename string, file *torrent.File) { if file.Link == "" { // will be caught by torrent manager's repairAll // just skip it for now return } filePath := filepath.Join(requestPath, url.PathEscape(filename)) responses = append(responses, dav.File( filePath, file.Bytes, convertRFC3339toRFC1123(tor.LatestAdded), file.Link, )) }) resp := &dav.MultiStatus{ XMLNS: "DAV:", Response: responses, } return xml.Marshal(resp) }