import type { GoogleDriveFile } from "./types"; export function escapeXml(str: string): string { return str.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); } export function generateListBucketResult(files: GoogleDriveFile[], bucket: string): string { const contents = files .map( (f) => ` ${escapeXml(f.name)} ${f.modifiedTime || new Date().toISOString()} "${f.md5Checksum || f.id}" ${f.size || 0} STANDARD `, ) .join(""); return ` ${escapeXml(bucket)} 1000 false ${contents} `; } export function initiateMultipartUploadResult(bucket: string, key: string, uploadId: string): string { return ` ${escapeXml(bucket)}${escapeXml(key)}${escapeXml(uploadId)}`; } export function completeMultipartUploadResult(bucket: string, key: string, etag: string): string { return ` /${escapeXml(bucket)}/${escapeXml(key)}${escapeXml(bucket)}${escapeXml(key)}"${escapeXml(etag)}"`; } export function listPartsResult(bucket: string, key: string, uploadId: string, result: { parts: Array<{ partNumber: number; size: number; etag: string; ts: number }>; nextMarker: number; truncated: boolean }): string { const parts = result.parts.map((part) => `${part.partNumber}${new Date(part.ts).toISOString()}"${escapeXml(part.etag)}"${part.size}`).join(""); return ` ${escapeXml(bucket)}${escapeXml(key)}${escapeXml(uploadId)}${result.nextMarker}${result.truncated}${parts}`; } export function listMultipartUploadsResult(bucket: string): string { return ` ${escapeXml(bucket)}1000false`; } const PART_RE = /]*>([\s\S]*?)<\/Part>/g; const NUM_RE = /\s*(\d+)\s*<\/PartNumber>/; const ETAG_RE = /\s*(?:"|")?([^<"&]*)(?:"|")?\s*<\/ETag>/; export function parseCompleteMultipartUpload(xml: string): Array<{ partNumber: number; etag: string }> { const parts: Array<{ partNumber: number; etag: string }> = []; for (const match of xml.matchAll(PART_RE)) { const numberMatch = NUM_RE.exec(match[1]); const etagMatch = ETAG_RE.exec(match[1]); if (!numberMatch || !etagMatch || parts.length >= 10_000) throw new Error("MalformedXML"); parts.push({ partNumber: Number(numberMatch[1]), etag: etagMatch[1].trim() }); } if (parts.length === 0 && !/]*>\s*<\/CompleteMultipartUpload>/.test(xml)) throw new Error("MalformedXML"); return parts; }