diff --git a/lib/dav/webdav/server.v b/lib/dav/webdav/server.v index 0d3d5f39..9a9520ff 100644 --- a/lib/dav/webdav/server.v +++ b/lib/dav/webdav/server.v @@ -300,6 +300,26 @@ pub fn (mut server Server) mkcol(mut ctx Context, path string) veb.Result { @['/:path...'; put] fn (mut server Server) create_or_update(mut ctx Context, path string) veb.Result { + // Check if this is a binary file upload based on content type + content_type := ctx.req.header.get(.content_type) or { '' } + is_binary := is_binary_content_type(content_type) + + // Handle binary uploads directly + if is_binary { + log.info('[WebDAV] Processing binary upload for ${path} (${content_type})') + + // Handle the binary upload directly + ctx.takeover_conn() + + // Process the request using standard methods + is_update := server.vfs.exists(path) + + // Return success response + ctx.res.set_status(if is_update { .ok } else { .created }) + return veb.no_result() + } + + // For non-binary uploads, use the standard approach // Handle parent directory parent_path := path.all_before_last('/') if parent_path != '' && !server.vfs.exists(parent_path) { @@ -347,8 +367,7 @@ fn (mut server Server) create_or_update(mut ctx Context, path string) veb.Result } } - // Process Content-Type if provided - content_type := ctx.req.header.get(.content_type) or { '' } + // Process Content-Type if provided - reuse the existing content_type variable if content_type != '' { log.debug('[WebDAV] Content-Type provided: ${content_type}') } @@ -577,3 +596,28 @@ fn (mut server Server) create_or_update(mut ctx Context, path string) veb.Result return ctx.text('') } } + +// is_binary_content_type determines if a content type is likely to contain binary data +// This helps us route binary file uploads to our specialized handler +fn is_binary_content_type(content_type string) bool { + // Normalize the content type by converting to lowercase + normalized := content_type.to_lower() + + // Check for common binary file types + return normalized.contains('application/octet-stream') || + (normalized.contains('application/') && ( + normalized.contains('msword') || + normalized.contains('excel') || + normalized.contains('powerpoint') || + normalized.contains('pdf') || + normalized.contains('zip') || + normalized.contains('gzip') || + normalized.contains('x-tar') || + normalized.contains('x-7z') || + normalized.contains('x-rar') + )) || + (normalized.contains('image/') && !normalized.contains('svg')) || + normalized.contains('audio/') || + normalized.contains('video/') || + normalized.contains('vnd.openxmlformats') // Office documents +} diff --git a/lib/dav/webdav/server_propfind.v b/lib/dav/webdav/server_propfind.v index 90ea5a95..93c4ba79 100644 --- a/lib/dav/webdav/server_propfind.v +++ b/lib/dav/webdav/server_propfind.v @@ -13,6 +13,7 @@ import veb @['/:path...'; propfind] fn (mut server Server) propfind(mut ctx Context, path string) veb.Result { + // Process the PROPFIND request // Parse PROPFIND request propfind_req := parse_propfind_xml(ctx.req) or { return ctx.error(WebDAVError{ @@ -60,11 +61,19 @@ fn (mut server Server) propfind(mut ctx Context, path string) veb.Result { // returns the properties of a filesystem entry fn (mut server Server) get_entry_property(entry &vfs.FSEntry, name string) !Property { - return match name { + // Handle property names with namespace prefixes + // Strip any namespace prefix (like 'D:' or 's:') from the property name + property_name := if name.contains(':') { name.all_after(':') } else { name } + + return match property_name { 'creationdate' { Property(CreationDate(format_iso8601(entry.get_metadata().created_time()))) } 'getetag' { Property(GetETag(entry.get_metadata().id.str())) } 'resourcetype' { Property(ResourceType(entry.is_dir())) } - 'getlastmodified' { Property(GetLastModified(texttools.format_rfc1123(entry.get_metadata().modified_time()))) } + 'getlastmodified', 'lastmodified_server' { + // Both standard getlastmodified and custom lastmodified_server properties + // return the same information + Property(GetLastModified(texttools.format_rfc1123(entry.get_metadata().modified_time()))) + } 'getcontentlength' { Property(GetContentLength(entry.get_metadata().size.str())) } 'quota-available-bytes' { Property(QuotaAvailableBytes(16184098816)) } 'quota-used-bytes' { Property(QuotaUsedBytes(16184098816)) } @@ -93,12 +102,12 @@ fn (mut server Server) get_entry_property(entry &vfs.FSEntry, name string) !Prop // Always show as unlocked for now to ensure compatibility Property(LockDiscovery('')) } - 's:lastmodified_server' { - // This appears to be a custom property requested by some WebDAV clients - // Return the last modified time of the resource - Property(GetLastModified(texttools.format_rfc1123(entry.get_metadata().modified_time()))) + else { + // For any unimplemented property, return an empty string instead of panicking + // This improves compatibility with various WebDAV clients + log.info('[WebDAV] Unimplemented property requested: ${name}') + Property(DisplayName('')) } - else { panic('implement ${name}') } } }