import { stripHostPrefix } from '../utils/url.js'; /** * Parse a PROPFIND `multistatus` XML document into a listing keyed by name. * The entry whose URI matches the requested URL is keyed as `.` (the current * collection). */ export function parsePropfindListing(xml, ctx) { const files = {}; const compareUrl = ctx.url.replace(/\/+$/, ''); xml.querySelectorAll('response').forEach((node) => { const href = node.querySelector('href'); if (!href || !href.textContent) { return; } const itemUri = href.textContent; const compareItemUri = itemUri.replace(/\/+$/, ''); // Find the first 200 propstat; ignore 404s etc. let propsNode = null; node.querySelectorAll('propstat').forEach((propstat) => { if (propsNode) { return; } const status = propstat.querySelector('status'); if (status && /200/.test(status.textContent || '')) { propsNode = propstat; } }); if (!propsNode) { console.error('Cannot find properties for: ' + itemUri); return; } // Alias for narrow-friendly access below. const props = propsNode; // Displayname falls back to the last path segment. let name = itemUri.replace(/\/$/, '').split('/').pop() || ''; try { name = decodeURIComponent(name); } catch { // leave name as-is on malformed encoding } const displayname = props.querySelector('displayname'); if (displayname && displayname.textContent) { name = displayname.textContent; } // Some servers (e.g. lighttpd) prefix each name with the hostname. let host = null; try { host = new URL(ctx.baseUrl).hostname; } catch { host = null; } name = stripHostPrefix(name, host); const isDir = !!node.querySelector('resourcetype collection'); const lengthEl = props.querySelector('getcontentlength'); const mimeEl = props.querySelector('getcontenttype'); const modifiedEl = props.querySelector('getlastmodified'); const permsEl = props.querySelector('permissions'); const entry = { uri: itemUri, url: itemUri, path: itemUri.substring(ctx.baseUrl.length), name, size: !isDir && lengthEl?.textContent ? parseInt(lengthEl.textContent, 10) : null, mime: !isDir && mimeEl?.textContent ? mimeEl.textContent : null, modified: modifiedEl?.textContent ? new Date(modifiedEl.textContent) : null, isDir, permissions: permsEl?.textContent ? permsEl.textContent : null, }; const key = compareItemUri === compareUrl ? '.' : name; files[key] = entry; }); return files; } //# sourceMappingURL=parse.js.map