| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 | 
							- #import <libxml/parser.h>
 
- #import "DAVResponse.h"
 
- #import "HTTPLogging.h"
 
- // WebDAV specifications: http://webdav.org/specs/rfc4918.html
 
- typedef enum {
 
-   kDAVProperty_ResourceType = (1 << 0),
 
-   kDAVProperty_CreationDate = (1 << 1),
 
-   kDAVProperty_LastModified = (1 << 2),
 
-   kDAVProperty_ContentLength = (1 << 3),
 
-   kDAVAllProperties = kDAVProperty_ResourceType | kDAVProperty_CreationDate | kDAVProperty_LastModified | kDAVProperty_ContentLength
 
- } DAVProperties;
 
- #define kXMLParseOptions (XML_PARSE_NONET | XML_PARSE_RECOVER | XML_PARSE_NOBLANKS | XML_PARSE_COMPACT | XML_PARSE_NOWARNING | XML_PARSE_NOERROR)
 
- static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
 
- @implementation DAVResponse
 
- static void _AddPropertyResponse(NSString* itemPath, NSString* resourcePath, DAVProperties properties, NSMutableString* xmlString) {
 
-   CFStringRef escapedPath = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)resourcePath, NULL,
 
-                                                                     CFSTR("<&>?+"), kCFStringEncodingUTF8);
 
-   if (escapedPath) {
 
-     NSDictionary* attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:itemPath error:NULL];
 
-     BOOL isDirectory = [[attributes fileType] isEqualToString:NSFileTypeDirectory];
 
-     [xmlString appendString:@"<D:response>"];
 
-       [xmlString appendFormat:@"<D:href>%@</D:href>", escapedPath];
 
-       [xmlString appendString:@"<D:propstat>"];
 
-         [xmlString appendString:@"<D:prop>"];
 
-         
 
-           if (properties & kDAVProperty_ResourceType) {
 
-             if (isDirectory) {
 
-               [xmlString appendString:@"<D:resourcetype><D:collection/></D:resourcetype>"];
 
-             } else {
 
-               [xmlString appendString:@"<D:resourcetype/>"];
 
-             }
 
-           }
 
-           
 
-           if ((properties & kDAVProperty_CreationDate) && [attributes objectForKey:NSFileCreationDate]) {
 
-             NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
 
-             formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
 
-             formatter.timeZone = [NSTimeZone timeZoneWithName:@"GMT"];
 
-             formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'+00:00'";
 
-             [xmlString appendFormat:@"<D:creationdate>%@</D:creationdate>", [formatter stringFromDate:[attributes fileCreationDate]]];
 
-           }
 
-           
 
-           if ((properties & kDAVProperty_LastModified) && [attributes objectForKey:NSFileModificationDate]) {
 
-             NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
 
-             formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
 
-             formatter.timeZone = [NSTimeZone timeZoneWithName:@"GMT"];
 
-             formatter.dateFormat = @"EEE', 'd' 'MMM' 'yyyy' 'HH:mm:ss' GMT'";
 
-             [xmlString appendFormat:@"<D:getlastmodified>%@</D:getlastmodified>", [formatter stringFromDate:[attributes fileModificationDate]]];
 
-           }
 
-           
 
-           if ((properties & kDAVProperty_ContentLength) && !isDirectory && [attributes objectForKey:NSFileSize]) {
 
-             [xmlString appendFormat:@"<D:getcontentlength>%qu</D:getcontentlength>", [attributes fileSize]];
 
-           }
 
-         
 
-         [xmlString appendString:@"</D:prop>"];
 
-         [xmlString appendString:@"<D:status>HTTP/1.1 200 OK</D:status>"];
 
-       [xmlString appendString:@"</D:propstat>"];
 
-     [xmlString appendString:@"</D:response>\n"];
 
-     CFRelease(escapedPath);
 
-   }
 
- }
 
- static xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name) {
 
-   while (child) {
 
-     if ((child->type == XML_ELEMENT_NODE) && !xmlStrcmp(child->name, name)) {
 
-       return child;
 
-     }
 
-     child = child->next;
 
-   }
 
-   return NULL;
 
- }
 
- - (id) initWithMethod:(NSString*)method headers:(NSDictionary*)headers bodyData:(NSData*)body resourcePath:(NSString*)resourcePath rootPath:(NSString*)rootPath {
 
-   if ((self = [super init])) {
 
-     _status = 200;
 
-     _headers = [[NSMutableDictionary alloc] init];
 
-     
 
-     // 10.1 DAV Header
 
-     if ([method isEqualToString:@"OPTIONS"]) {
 
-       if ([[headers objectForKey:@"User-Agent"] hasPrefix:@"WebDAVFS/"]) {  // Mac OS X WebDAV support
 
-         [_headers setObject:@"1, 2" forKey:@"DAV"];
 
-       } else {
 
-         [_headers setObject:@"1" forKey:@"DAV"];
 
-       }
 
-     }
 
-     
 
-     // 9.1 PROPFIND Method
 
-     if ([method isEqualToString:@"PROPFIND"]) {
 
-       NSInteger depth;
 
-       NSString* depthHeader = [headers objectForKey:@"Depth"];
 
-       if ([depthHeader isEqualToString:@"0"]) {
 
-         depth = 0;
 
-       } else if ([depthHeader isEqualToString:@"1"]) {
 
-         depth = 1;
 
-       } else {
 
-         HTTPLogError(@"Unsupported DAV depth \"%@\"", depthHeader);
 
-         return nil;
 
-       }
 
-       
 
-       DAVProperties properties = 0;
 
-       xmlDocPtr document = xmlReadMemory(body.bytes, (int)body.length, NULL, NULL, kXMLParseOptions);
 
-       if (document) {
 
-         xmlNodePtr node = _XMLChildWithName(document->children, (const xmlChar*)"propfind");
 
-         if (node) {
 
-           node = _XMLChildWithName(node->children, (const xmlChar*)"prop");
 
-         }
 
-         if (node) {
 
-           node = node->children;
 
-           while (node) {
 
-             if (!xmlStrcmp(node->name, (const xmlChar*)"resourcetype")) {
 
-               properties |= kDAVProperty_ResourceType;
 
-             } else if (!xmlStrcmp(node->name, (const xmlChar*)"creationdate")) {
 
-               properties |= kDAVProperty_CreationDate;
 
-             } else if (!xmlStrcmp(node->name, (const xmlChar*)"getlastmodified")) {
 
-               properties |= kDAVProperty_LastModified;
 
-             } else if (!xmlStrcmp(node->name, (const xmlChar*)"getcontentlength")) {
 
-               properties |= kDAVProperty_ContentLength;
 
-             } else {
 
-               HTTPLogWarn(@"Unknown DAV property requested \"%s\"", node->name);
 
-             }
 
-             node = node->next;
 
-           }
 
-         } else {
 
-           HTTPLogWarn(@"HTTP Server: Invalid DAV properties\n%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
 
-         }
 
-         xmlFreeDoc(document);
 
-       }
 
-       if (!properties) {
 
-         properties = kDAVAllProperties;
 
-       }
 
-       
 
-       NSString* basePath = [rootPath stringByAppendingPathComponent:resourcePath];
 
-       if (![basePath hasPrefix:rootPath] || ![[NSFileManager defaultManager] fileExistsAtPath:basePath]) {
 
-         return nil;
 
-       }
 
-       
 
-       NSMutableString* xmlString = [NSMutableString stringWithString:@"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"];
 
-       [xmlString appendString:@"<D:multistatus xmlns:D=\"DAV:\">\n"];
 
-       if (![resourcePath hasPrefix:@"/"]) {
 
-         resourcePath = [@"/" stringByAppendingString:resourcePath];
 
-       }
 
-       _AddPropertyResponse(basePath, resourcePath, properties, xmlString);
 
-       if (depth == 1) {
 
-         if (![resourcePath hasSuffix:@"/"]) {
 
-           resourcePath = [resourcePath stringByAppendingString:@"/"];
 
-         }
 
-         NSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager] enumeratorAtPath:basePath];
 
-         NSString* path;
 
-         while ((path = [enumerator nextObject])) {
 
-           _AddPropertyResponse([basePath stringByAppendingPathComponent:path], [resourcePath stringByAppendingString:path], properties, xmlString);
 
-           [enumerator skipDescendents];
 
-         }
 
-       }
 
-       [xmlString appendString:@"</D:multistatus>"];
 
-       
 
-       [_headers setObject:@"application/xml; charset=\"utf-8\"" forKey:@"Content-Type"];
 
-       _data = [xmlString dataUsingEncoding:NSUTF8StringEncoding];
 
-       _status = 207;
 
-     }
 
-     
 
-     // 9.3 MKCOL Method
 
-     if ([method isEqualToString:@"MKCOL"]) {
 
-       NSString* path = [rootPath stringByAppendingPathComponent:resourcePath];
 
-       if (![path hasPrefix:rootPath]) {
 
-         return nil;
 
-       }
 
-       
 
-       if (![[NSFileManager defaultManager] fileExistsAtPath:[path stringByDeletingLastPathComponent]]) {
 
-         HTTPLogError(@"Missing intermediate collection(s) at \"%@\"", path);
 
-         _status = 409;
 
-       } else if (![[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:NO attributes:nil error:NULL]) {
 
-         HTTPLogError(@"Failed creating collection at \"%@\"", path);
 
-         _status = 405;
 
-       }
 
-     }
 
-     
 
-     // 9.8 COPY Method
 
-     // 9.9 MOVE Method
 
-     if ([method isEqualToString:@"MOVE"] || [method isEqualToString:@"COPY"]) {
 
-       if ([method isEqualToString:@"COPY"] && ![[headers objectForKey:@"Depth"] isEqualToString:@"infinity"]) {
 
-         HTTPLogError(@"Unsupported DAV depth \"%@\"", [headers objectForKey:@"Depth"]);
 
-         return nil;
 
-       }
 
-       
 
-       NSString* sourcePath = [rootPath stringByAppendingPathComponent:resourcePath];
 
-       if (![sourcePath hasPrefix:rootPath] || ![[NSFileManager defaultManager] fileExistsAtPath:sourcePath]) {
 
-         return nil;
 
-       }
 
-       
 
-       NSString* destination = [headers objectForKey:@"Destination"];
 
-       NSRange range = [destination rangeOfString:[headers objectForKey:@"Host"]];
 
-       if (range.location == NSNotFound) {
 
-         return nil;
 
-       }
 
-       NSString* destinationPath = [rootPath stringByAppendingPathComponent:
 
-         [[destination substringFromIndex:(range.location + range.length)] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
 
-       if (![destinationPath hasPrefix:rootPath] || [[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) {
 
-         return nil;
 
-       }
 
-       
 
-       BOOL isDirectory;
 
-       if (![[NSFileManager defaultManager] fileExistsAtPath:[destinationPath stringByDeletingLastPathComponent] isDirectory:&isDirectory] || !isDirectory) {
 
-         HTTPLogError(@"Invalid destination path \"%@\"", destinationPath);
 
-         _status = 409;
 
-       } else {
 
-         BOOL existing = [[NSFileManager defaultManager] fileExistsAtPath:destinationPath];
 
-         if (existing && [[headers objectForKey:@"Overwrite"] isEqualToString:@"F"]) {
 
-           HTTPLogError(@"Pre-existing destination path \"%@\"", destinationPath);
 
-           _status = 412;
 
-         } else {
 
-           if ([method isEqualToString:@"COPY"]) {
 
-             if ([[NSFileManager defaultManager] copyItemAtPath:sourcePath toPath:destinationPath error:NULL]) {
 
-               _status = existing ? 204 : 201;
 
-             } else {
 
-               HTTPLogError(@"Failed copying \"%@\" to \"%@\"", sourcePath, destinationPath);
 
-               _status = 403;
 
-             }
 
-           } else {
 
-             if ([[NSFileManager defaultManager] moveItemAtPath:sourcePath toPath:destinationPath error:NULL]) {
 
-               _status = existing ? 204 : 201;
 
-             } else {
 
-               HTTPLogError(@"Failed moving \"%@\" to \"%@\"", sourcePath, destinationPath);
 
-               _status = 403;
 
-             }
 
-           }
 
-         }
 
-       }
 
-     }
 
-     
 
-     // 9.10 LOCK Method - TODO: Actually lock the resource
 
-     if ([method isEqualToString:@"LOCK"]) {
 
-       NSString* path = [rootPath stringByAppendingPathComponent:resourcePath];
 
-       if (![path hasPrefix:rootPath]) {
 
-         return nil;
 
-       }
 
-       
 
-       NSString* depth = [headers objectForKey:@"Depth"];
 
-       NSString* scope = nil;
 
-       NSString* type = nil;
 
-       NSString* owner = nil;
 
-       NSString* token = nil;
 
-       xmlDocPtr document = xmlReadMemory(body.bytes, (int)body.length, NULL, NULL, kXMLParseOptions);
 
-       if (document) {
 
-         xmlNodePtr node = _XMLChildWithName(document->children, (const xmlChar*)"lockinfo");
 
-         if (node) {
 
-           xmlNodePtr scopeNode = _XMLChildWithName(node->children, (const xmlChar*)"lockscope");
 
-           if (scopeNode && scopeNode->children && scopeNode->children->name) {
 
-             scope = [NSString stringWithUTF8String:(const char*)scopeNode->children->name];
 
-           }
 
-           xmlNodePtr typeNode = _XMLChildWithName(node->children, (const xmlChar*)"locktype");
 
-           if (typeNode && typeNode->children && typeNode->children->name) {
 
-             type = [NSString stringWithUTF8String:(const char*)typeNode->children->name];
 
-           }
 
-           xmlNodePtr ownerNode = _XMLChildWithName(node->children, (const xmlChar*)"owner");
 
-           if (ownerNode) {
 
-             ownerNode = _XMLChildWithName(ownerNode->children, (const xmlChar*)"href");
 
-             if (ownerNode && ownerNode->children && ownerNode->children->content) {
 
-               owner = [NSString stringWithUTF8String:(const char*)ownerNode->children->content];
 
-             }
 
-           }
 
-         } else {
 
-           HTTPLogWarn(@"HTTP Server: Invalid DAV properties\n%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
 
-         }
 
-         xmlFreeDoc(document);
 
-       } else {
 
- 		  // No body, see if they're trying to refresh an existing lock.  If so, then just fake up the scope, type and depth so we fall
 
- 		  // into the lock create case.
 
- 		  NSString* lockToken;
 
- 		  if ((lockToken = [headers objectForKey:@"If"]) != nil) {
 
- 			  scope = @"exclusive";
 
- 			  type = @"write";
 
- 			  depth = @"0";
 
- 			  token = [lockToken stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"(<>)"]];
 
- 		  }
 
- 	  }
 
-       if ([scope isEqualToString:@"exclusive"] && [type isEqualToString:@"write"] && [depth isEqualToString:@"0"] &&
 
-         ([[NSFileManager defaultManager] fileExistsAtPath:path] || [[NSData data] writeToFile:path atomically:YES])) {
 
-         NSString* timeout = [headers objectForKey:@"Timeout"];
 
- 		if (!token) {
 
-           CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
 
-           NSString *uuidStr = (__bridge_transfer NSString *)CFUUIDCreateString(kCFAllocatorDefault, uuid);
 
-           token = [NSString stringWithFormat:@"urn:uuid:%@", uuidStr];
 
-           CFRelease(uuid);
 
- 		}
 
-         
 
-         NSMutableString* xmlString = [NSMutableString stringWithString:@"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"];
 
-         [xmlString appendString:@"<D:prop xmlns:D=\"DAV:\">\n"];
 
-         [xmlString appendString:@"<D:lockdiscovery>\n<D:activelock>\n"];
 
-         [xmlString appendFormat:@"<D:locktype><D:%@/></D:locktype>\n", type];
 
-         [xmlString appendFormat:@"<D:lockscope><D:%@/></D:lockscope>\n", scope];
 
-         [xmlString appendFormat:@"<D:depth>%@</D:depth>\n", depth];
 
-         if (owner) {
 
-           [xmlString appendFormat:@"<D:owner><D:href>%@</D:href></D:owner>\n", owner];
 
-         }
 
-         if (timeout) {
 
-           [xmlString appendFormat:@"<D:timeout>%@</D:timeout>\n", timeout];
 
-         }
 
-         [xmlString appendFormat:@"<D:locktoken><D:href>%@</D:href></D:locktoken>\n", token];
 
- 		NSString* lockroot = [@"http://" stringByAppendingString:[[headers objectForKey:@"Host"] stringByAppendingString:[@"/" stringByAppendingString:resourcePath]]];
 
-         [xmlString appendFormat:@"<D:lockroot><D:href>%@</D:href></D:lockroot>\n", lockroot];
 
-         [xmlString appendString:@"</D:activelock>\n</D:lockdiscovery>\n"];
 
-         [xmlString appendString:@"</D:prop>"];
 
-         
 
-         [_headers setObject:@"application/xml; charset=\"utf-8\"" forKey:@"Content-Type"];
 
-         _data = [xmlString dataUsingEncoding:NSUTF8StringEncoding];
 
-         _status = 200;
 
-         HTTPLogVerbose(@"Pretending to lock \"%@\"", resourcePath);
 
-       } else {
 
-         HTTPLogError(@"Locking request \"%@/%@/%@\" for \"%@\" is not allowed", scope, type, depth, resourcePath);
 
-         _status = 403;
 
-       }
 
-     }
 
-     
 
-     // 9.11 UNLOCK Method - TODO: Actually unlock the resource
 
-     if ([method isEqualToString:@"UNLOCK"]) {
 
-       NSString* path = [rootPath stringByAppendingPathComponent:resourcePath];
 
-       if (![path hasPrefix:rootPath] || ![[NSFileManager defaultManager] fileExistsAtPath:path]) {
 
-         return nil;
 
-       }
 
-       
 
-       NSString* token = [headers objectForKey:@"Lock-Token"];
 
-       _status = token ? 204 : 400;
 
-       HTTPLogVerbose(@"Pretending to unlock \"%@\"", resourcePath);
 
-     }
 
-     
 
-   }
 
-   return self;
 
- }
 
- - (UInt64) contentLength {
 
-   return _data ? _data.length : 0;
 
- }
 
- - (UInt64) offset {
 
-   return _offset;
 
- }
 
- - (void) setOffset:(UInt64)offset {
 
-   _offset = offset;
 
- }
 
- - (NSData*) readDataOfLength:(NSUInteger)lengthParameter {
 
-   if (_data) {
 
-     NSUInteger remaining = _data.length - (NSUInteger)_offset;
 
-     NSUInteger length = lengthParameter < remaining ? lengthParameter : remaining;
 
-     void* bytes = (void*)(_data.bytes + _offset);
 
-     _offset += length;
 
-     return [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:NO];
 
-   }
 
-   return nil;
 
- }
 
- - (BOOL) isDone {
 
-   return _data ? _offset == _data.length : YES;
 
- }
 
- - (NSInteger) status {
 
-   return _status;
 
- }
 
- - (NSDictionary*) httpHeaders {
 
-   return _headers;
 
- }
 
- @end
 
 
  |