/**
* WebDAV XML generation utilities
*/
import type { IWebDAVResource, IWebDAVProperty, IWebDAVLock } from './webdav.types.js';
import { DAV_NAMESPACE } from './webdav.types.js';
/**
* Escape XML special characters
*/
function escapeXml(str: string): string {
return str
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
/**
* Format date for WebDAV (RFC 1123)
*/
function formatDate(date: Date): string {
return date.toUTCString();
}
/**
* Format date for creationdate (ISO 8601)
*/
function formatCreationDate(date: Date): string {
return date.toISOString();
}
/**
* Generate multistatus response for PROPFIND
*/
export function generateMultistatus(resources: IWebDAVResource[]): string {
const responses = resources.map(resource => generateResponse(resource)).join('\n');
return `
${responses}
`;
}
/**
* Generate single response element
*/
function generateResponse(resource: IWebDAVResource): string {
const props: string[] = [];
// Resource type
if (resource.isCollection) {
props.push('');
} else {
props.push('');
}
// Display name
if (resource.displayName) {
props.push(`${escapeXml(resource.displayName)}`);
}
// Content type
if (resource.contentType) {
props.push(`${escapeXml(resource.contentType)}`);
}
// Content length
if (resource.contentLength !== undefined) {
props.push(`${resource.contentLength}`);
}
// Last modified
if (resource.lastModified) {
props.push(`${formatDate(resource.lastModified)}`);
}
// Creation date
if (resource.creationDate) {
props.push(`${formatCreationDate(resource.creationDate)}`);
}
// ETag
if (resource.etag) {
props.push(`"${escapeXml(resource.etag)}"`);
}
// Supported lock
props.push(`
`);
// Custom properties
for (const prop of resource.properties) {
if (prop.value) {
props.push(`<${prop.name} xmlns="${prop.namespace}">${escapeXml(prop.value)}${prop.name}>`);
} else {
props.push(`<${prop.name} xmlns="${prop.namespace}"/>`);
}
}
return `
${escapeXml(resource.href)}
${props.join('\n ')}
HTTP/1.1 200 OK
`;
}
/**
* Generate lock discovery response
*/
export function generateLockDiscovery(locks: IWebDAVLock[]): string {
if (locks.length === 0) {
return '';
}
const lockEntries = locks.map(lock => `
${lock.depth}
${escapeXml(lock.owner)}
Second-${lock.timeout}
${escapeXml(lock.token)}
`).join('');
return `${lockEntries}
`;
}
/**
* Generate lock response
*/
export function generateLockResponse(lock: IWebDAVLock): string {
return `
${generateLockDiscovery([lock])}
`;
}
/**
* Generate error response
*/
export function generateError(status: number, message: string): string {
return `
`;
}
/**
* Parse PROPFIND request body to extract requested properties
*/
export function parsePropfindRequest(body: string): { allprop: boolean; propnames: string[] } {
// Simple XML parsing for PROPFIND
if (!body || body.includes(']*>([\s\S]*?)<\/(?:D:)?prop>/i);
if (propMatch) {
const propContent = propMatch[1];
const tagMatches = propContent.matchAll(/<(?:D:)?(\w+)[^>]*\/?>/gi);
for (const match of tagMatches) {
propnames.push(match[1]);
}
}
return { allprop: propnames.length === 0, propnames };
}