mirror of
https://github.com/swisspol/GCDWebServer.git
synced 2026-04-24 00:00:04 +08:00
Added compatibility with OS X Finder for WebDAV
This commit is contained in:
@@ -47,6 +47,7 @@
|
|||||||
@property(nonatomic, copy) NSArray* allowedFileExtensions; // Default is nil i.e. all file extensions are allowed
|
@property(nonatomic, copy) NSArray* allowedFileExtensions; // Default is nil i.e. all file extensions are allowed
|
||||||
@property(nonatomic) BOOL showHiddenFiles; // Default is NO
|
@property(nonatomic) BOOL showHiddenFiles; // Default is NO
|
||||||
- (instancetype)initWithUploadDirectory:(NSString*)path;
|
- (instancetype)initWithUploadDirectory:(NSString*)path;
|
||||||
|
- (instancetype)initWithUploadDirectory:(NSString*)path macFinderMode:(BOOL)macFinderMode; // If Mac Finder mode is ON, WebDAV server can be mounted read-write instead of read-only in OS X Finder
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface GCDWebDAVServer (Subclassing)
|
@interface GCDWebDAVServer (Subclassing)
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ typedef NS_ENUM(NSInteger, DAVProperties) {
|
|||||||
@interface GCDWebDAVServer () {
|
@interface GCDWebDAVServer () {
|
||||||
@private
|
@private
|
||||||
NSString* _uploadDirectory;
|
NSString* _uploadDirectory;
|
||||||
|
BOOL _macMode;
|
||||||
id<GCDWebDAVServerDelegate> __unsafe_unretained _delegate;
|
id<GCDWebDAVServerDelegate> __unsafe_unretained _delegate;
|
||||||
NSArray* _allowedExtensions;
|
NSArray* _allowedExtensions;
|
||||||
BOOL _showHidden;
|
BOOL _showHidden;
|
||||||
@@ -66,29 +67,17 @@ typedef NS_ENUM(NSInteger, DAVProperties) {
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline BOOL _IsMacFinder(GCDWebServerRequest* request) {
|
||||||
|
NSString* userAgentHeader = [request.headers objectForKey:@"User-Agent"];
|
||||||
|
return ([userAgentHeader hasPrefix:@"WebDAVFS/"] || [userAgentHeader hasPrefix:@"WebDAVLib/"]); // OS X WebDAV client
|
||||||
|
}
|
||||||
|
|
||||||
- (GCDWebServerResponse*)performOPTIONS:(GCDWebServerRequest*)request {
|
- (GCDWebServerResponse*)performOPTIONS:(GCDWebServerRequest*)request {
|
||||||
GCDWebServerResponse* response = [GCDWebServerResponse response];
|
GCDWebServerResponse* response = [GCDWebServerResponse response];
|
||||||
|
if (_macMode && _IsMacFinder(request)) {
|
||||||
|
[response setValue:@"1, 2" forAdditionalHeader:@"DAV"]; // Classes 1 and 2
|
||||||
|
} else {
|
||||||
[response setValue:@"1" forAdditionalHeader:@"DAV"]; // Class 1
|
[response setValue:@"1" forAdditionalHeader:@"DAV"]; // Class 1
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (GCDWebServerResponse*)performHEAD:(GCDWebServerRequest*)request {
|
|
||||||
NSString* relativePath = request.path;
|
|
||||||
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath];
|
|
||||||
if (![absolutePath hasPrefix:_uploadDirectory] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath]) {
|
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSError* error = nil;
|
|
||||||
NSDictionary* attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:absolutePath error:&error];
|
|
||||||
if (!attributes) {
|
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound underlyingError:error message:@"Failed retrieving attributes for \"%@\"", relativePath];
|
|
||||||
}
|
|
||||||
|
|
||||||
GCDWebServerResponse* response = [GCDWebServerResponse response];
|
|
||||||
if ([[attributes fileType] isEqualToString:NSFileTypeRegular]) {
|
|
||||||
[response setValue:GCDWebServerGetMimeTypeForExtension([absolutePath pathExtension]) forAdditionalHeader:@"Content-Type"];
|
|
||||||
[response setValue:[NSString stringWithFormat:@"%llu", [attributes fileSize]] forAdditionalHeader:@"Content-Length"];
|
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@@ -374,6 +363,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
|
|
||||||
DAVProperties properties = 0;
|
DAVProperties properties = 0;
|
||||||
if (request.data.length) {
|
if (request.data.length) {
|
||||||
|
BOOL success = YES;
|
||||||
xmlDocPtr document = xmlReadMemory(request.data.bytes, (int)request.data.length, NULL, NULL, kXMLParseOptions);
|
xmlDocPtr document = xmlReadMemory(request.data.bytes, (int)request.data.length, NULL, NULL, kXMLParseOptions);
|
||||||
if (document) {
|
if (document) {
|
||||||
xmlNodePtr rootNode = _XMLChildWithName(document->children, (const xmlChar*)"propfind");
|
xmlNodePtr rootNode = _XMLChildWithName(document->children, (const xmlChar*)"propfind");
|
||||||
@@ -398,29 +388,38 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
node = node->next;
|
node = node->next;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
success = NO;
|
||||||
|
}
|
||||||
|
xmlFreeDoc(document);
|
||||||
|
} else {
|
||||||
|
success = NO;
|
||||||
|
}
|
||||||
|
if (!success) {
|
||||||
NSString* string = [[NSString alloc] initWithData:request.data encoding:NSUTF8StringEncoding];
|
NSString* string = [[NSString alloc] initWithData:request.data encoding:NSUTF8StringEncoding];
|
||||||
[self logError:@"Invalid DAV properties\n%@", string];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Invalid DAV properties:\n%@", string];
|
||||||
#if !__has_feature(objc_arc)
|
#if !__has_feature(objc_arc)
|
||||||
[string release];
|
[string release];
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
xmlFreeDoc(document);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
properties = kDAVAllProperties;
|
properties = kDAVAllProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString* relativePath = request.path;
|
NSString* relativePath = request.path;
|
||||||
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath];
|
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath];
|
||||||
if (![absolutePath hasPrefix:_uploadDirectory] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath]) {
|
BOOL isDirectory = NO;
|
||||||
|
if (![absolutePath hasPrefix:_uploadDirectory] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath isDirectory:&isDirectory]) {
|
||||||
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NSArray* items = nil;
|
||||||
|
if (isDirectory) {
|
||||||
NSError* error = nil;
|
NSError* error = nil;
|
||||||
NSArray* items = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:absolutePath error:&error];
|
items = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:absolutePath error:&error];
|
||||||
if (items == nil) {
|
if (items == nil) {
|
||||||
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed listing directory \"%@\"", relativePath];
|
return [GCDWebServerErrorResponse responseWithServerError:kGCDWebServerHTTPStatusCode_InternalServerError underlyingError:error message:@"Failed listing directory \"%@\"", relativePath];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NSMutableString* xmlString = [NSMutableString stringWithString:@"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"];
|
NSMutableString* xmlString = [NSMutableString stringWithString:@"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"];
|
||||||
[xmlString appendString:@"<D:multistatus xmlns:D=\"DAV:\">\n"];
|
[xmlString appendString:@"<D:multistatus xmlns:D=\"DAV:\">\n"];
|
||||||
@@ -446,6 +445,114 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (GCDWebServerResponse*)performLOCK:(GCDWebServerDataRequest*)request {
|
||||||
|
if (!_macMode || !_IsMacFinder(request)) {
|
||||||
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_MethodNotAllowed message:@"LOCK method only allowed for Mac Finder"];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString* relativePath = request.path;
|
||||||
|
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath];
|
||||||
|
if (![absolutePath hasPrefix:_uploadDirectory] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath]) {
|
||||||
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString* depthHeader = [request.headers objectForKey:@"Depth"];
|
||||||
|
NSString* timeoutHeader = [request.headers objectForKey:@"Timeout"];
|
||||||
|
NSString* scope = nil;
|
||||||
|
NSString* type = nil;
|
||||||
|
NSString* owner = nil;
|
||||||
|
NSString* token = nil;
|
||||||
|
BOOL success = YES;
|
||||||
|
xmlDocPtr document = xmlReadMemory(request.data.bytes, (int)request.data.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 {
|
||||||
|
success = NO;
|
||||||
|
}
|
||||||
|
xmlFreeDoc(document);
|
||||||
|
} else {
|
||||||
|
success = NO;
|
||||||
|
}
|
||||||
|
if (!success) {
|
||||||
|
NSString* string = [[NSString alloc] initWithData:request.data encoding:NSUTF8StringEncoding];
|
||||||
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Invalid DAV properties:\n%@", string];
|
||||||
|
#if !__has_feature(objc_arc)
|
||||||
|
[string release];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (![scope isEqualToString:@"exclusive"] || ![type isEqualToString:@"write"] || ![depthHeader isEqualToString:@"0"]) {
|
||||||
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_Forbidden message:@"Locking request \"%@/%@/%@\" for \"%@\" is not allowed", scope, type, depthHeader, relativePath];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
|
||||||
|
CFStringRef string = CFUUIDCreateString(kCFAllocatorDefault, uuid);
|
||||||
|
token = [NSString stringWithFormat:@"urn:uuid:%@", (__bridge NSString*)string];
|
||||||
|
CFRelease(string);
|
||||||
|
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", depthHeader];
|
||||||
|
if (owner) {
|
||||||
|
[xmlString appendFormat:@"<D:owner><D:href>%@</D:href></D:owner>\n", owner];
|
||||||
|
}
|
||||||
|
if (timeoutHeader) {
|
||||||
|
[xmlString appendFormat:@"<D:timeout>%@</D:timeout>\n", timeoutHeader];
|
||||||
|
}
|
||||||
|
[xmlString appendFormat:@"<D:locktoken><D:href>%@</D:href></D:locktoken>\n", token];
|
||||||
|
NSString* lockroot = [@"http://" stringByAppendingString:[[request.headers objectForKey:@"Host"] stringByAppendingString:[@"/" stringByAppendingString:relativePath]]];
|
||||||
|
[xmlString appendFormat:@"<D:lockroot><D:href>%@</D:href></D:lockroot>\n", lockroot];
|
||||||
|
[xmlString appendString:@"</D:activelock>\n</D:lockdiscovery>\n"];
|
||||||
|
[xmlString appendString:@"</D:prop>"];
|
||||||
|
|
||||||
|
[self logVerbose:@"WebDAV pretending to lock \"%@\"", relativePath];
|
||||||
|
GCDWebServerDataResponse* response = [GCDWebServerDataResponse responseWithData:[xmlString dataUsingEncoding:NSUTF8StringEncoding]
|
||||||
|
contentType:@"application/xml; charset=\"utf-8\""];
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (GCDWebServerResponse*)performUNLOCK:(GCDWebServerRequest*)request {
|
||||||
|
if (!_macMode || !_IsMacFinder(request)) {
|
||||||
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_MethodNotAllowed message:@"UNLOCK method only allowed for Mac Finder"];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString* relativePath = request.path;
|
||||||
|
NSString* absolutePath = [_uploadDirectory stringByAppendingPathComponent:relativePath];
|
||||||
|
if (![absolutePath hasPrefix:_uploadDirectory] || ![[NSFileManager defaultManager] fileExistsAtPath:absolutePath]) {
|
||||||
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_NotFound message:@"\"%@\" does not exist", relativePath];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString* tokenHeader = [request.headers objectForKey:@"Lock-Token"];
|
||||||
|
if (!tokenHeader.length) {
|
||||||
|
return [GCDWebServerErrorResponse responseWithClientError:kGCDWebServerHTTPStatusCode_BadRequest message:@"Missing 'Lock-Token' header"];
|
||||||
|
}
|
||||||
|
|
||||||
|
[self logVerbose:@"WebDAV pretending to unlock \"%@\"", relativePath];
|
||||||
|
return [GCDWebServerResponse responseWithStatusCode:kGCDWebServerHTTPStatusCode_NoContent];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation GCDWebDAVServer
|
@implementation GCDWebDAVServer
|
||||||
@@ -453,8 +560,13 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
@synthesize uploadDirectory=_uploadDirectory, delegate=_delegate, allowedFileExtensions=_allowedExtensions, showHiddenFiles=_showHidden;
|
@synthesize uploadDirectory=_uploadDirectory, delegate=_delegate, allowedFileExtensions=_allowedExtensions, showHiddenFiles=_showHidden;
|
||||||
|
|
||||||
- (instancetype)initWithUploadDirectory:(NSString*)path {
|
- (instancetype)initWithUploadDirectory:(NSString*)path {
|
||||||
|
return [self initWithUploadDirectory:path macFinderMode:NO];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithUploadDirectory:(NSString*)path macFinderMode:(BOOL)macFinderMode {
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_uploadDirectory = [[path stringByStandardizingPath] copy];
|
_uploadDirectory = [[path stringByStandardizingPath] copy];
|
||||||
|
_macMode = macFinderMode;
|
||||||
GCDWebDAVServer* __unsafe_unretained server = self;
|
GCDWebDAVServer* __unsafe_unretained server = self;
|
||||||
|
|
||||||
// 9.1 PROPFIND method
|
// 9.1 PROPFIND method
|
||||||
@@ -467,12 +579,7 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
return [server performMKCOL:(GCDWebServerDataRequest*)request];
|
return [server performMKCOL:(GCDWebServerDataRequest*)request];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
// 9.4 HEAD method
|
// 9.4 GET & HEAD methods
|
||||||
[self addDefaultHandlerForMethod:@"HEAD" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
|
||||||
return [server performHEAD:request];
|
|
||||||
}];
|
|
||||||
|
|
||||||
// 9.4 GET method
|
|
||||||
[self addDefaultHandlerForMethod:@"GET" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addDefaultHandlerForMethod:@"GET" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
||||||
return [server performGET:request];
|
return [server performGET:request];
|
||||||
}];
|
}];
|
||||||
@@ -497,6 +604,16 @@ static inline xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name
|
|||||||
return [server performCOPY:request isMove:YES];
|
return [server performCOPY:request isMove:YES];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
// 9.10 LOCK method
|
||||||
|
[self addDefaultHandlerForMethod:@"LOCK" requestClass:[GCDWebServerDataRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
||||||
|
return [server performLOCK:(GCDWebServerDataRequest*)request];
|
||||||
|
}];
|
||||||
|
|
||||||
|
// 9.11 UNLOCK method
|
||||||
|
[self addDefaultHandlerForMethod:@"UNLOCK" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
||||||
|
return [server performUNLOCK:request];
|
||||||
|
}];
|
||||||
|
|
||||||
// 10.1 OPTIONS method / DAV Header
|
// 10.1 OPTIONS method / DAV Header
|
||||||
[self addDefaultHandlerForMethod:@"OPTIONS" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
[self addDefaultHandlerForMethod:@"OPTIONS" requestClass:[GCDWebServerRequest class] processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
||||||
return [server performOPTIONS:request];
|
return [server performOPTIONS:request];
|
||||||
|
|||||||
+1
-1
@@ -97,7 +97,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 3: {
|
case 3: {
|
||||||
webServer = [[GCDWebDAVServer alloc] initWithUploadDirectory:[[NSFileManager defaultManager] currentDirectoryPath]];
|
webServer = [[GCDWebDAVServer alloc] initWithUploadDirectory:[[NSFileManager defaultManager] currentDirectoryPath] macFinderMode:YES];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ Extra built-in features:
|
|||||||
|
|
||||||
Included extensions:
|
Included extensions:
|
||||||
* [GCDWebUploader](GCDWebUploader/GCDWebUploader.h): subclass of GCDWebServer that implements an interface for uploading and downloading files from an iOS app's sandbox using a web browser
|
* [GCDWebUploader](GCDWebUploader/GCDWebUploader.h): subclass of GCDWebServer that implements an interface for uploading and downloading files from an iOS app's sandbox using a web browser
|
||||||
* [GCDWebDAVServer](GCDWebDAVServer/GCDWebDAVServer.h): subclass of GCDWebServer that implements a class 1 [WebDAV](https://en.wikipedia.org/wiki/WebDAV) server
|
* [GCDWebDAVServer](GCDWebDAVServer/GCDWebDAVServer.h): subclass of GCDWebServer that implements a class 1 [WebDAV](https://en.wikipedia.org/wiki/WebDAV) server (with partial class 2 support for OS X Finder)
|
||||||
|
|
||||||
What's not available out of the box but can be implemented on top of the API:
|
What's not available out of the box but can be implemented on top of the API:
|
||||||
* Authentication like [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
|
* Authentication like [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
|
||||||
@@ -87,7 +87,7 @@ Simply instantiate and run a GCDWebUploader instance then visit http://{YOUR-IOS
|
|||||||
WebDAV Server in iOS Apps
|
WebDAV Server in iOS Apps
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
GCDWebDAVServer is a subclass of GCDWebServer that provides a class 1 compliant [WebDAV](https://en.wikipedia.org/wiki/WebDAV) server. This lets users upload, download, delete files and create directories from a directory inside your iOS app's sandbox using any WebDAV client like [Transmit](https://panic.com/transmit/) (Mac), [ForkLift](http://binarynights.com/forklift/) (Mac) or [CyberDuck](http://cyberduck.io/) (Mac / Windows).
|
GCDWebDAVServer is a subclass of GCDWebServer that provides a class 1 compliant [WebDAV](https://en.wikipedia.org/wiki/WebDAV) server. This lets users upload, download, delete files and create directories from a directory inside your iOS app's sandbox using any WebDAV client like [Transmit](https://panic.com/transmit/) (Mac), [ForkLift](http://binarynights.com/forklift/) (Mac) or [CyberDuck](http://cyberduck.io/) (Mac / Windows). GCDWebDAVServer should also work with the [OS X Finder](http://support.apple.com/kb/PH13859) as it is partially class 2 compliant (but only when the client is the OS X WebDAV implementation).
|
||||||
|
|
||||||
Simply instantiate and run a GCDWebDAVServer instance then connect to http://{YOUR-IOS-DEVICE-IP-ADDRESS}/ using a WebDAV client:
|
Simply instantiate and run a GCDWebDAVServer instance then connect to http://{YOUR-IOS-DEVICE-IP-ADDRESS}/ using a WebDAV client:
|
||||||
|
|
||||||
@@ -249,6 +249,6 @@ NSString* websitePath = [[NSBundle mainBundle] pathForResource:@"Website" ofType
|
|||||||
Final Example: File Downloads and Uploads From iOS App
|
Final Example: File Downloads and Uploads From iOS App
|
||||||
======================================================
|
======================================================
|
||||||
|
|
||||||
GCDWebServer was originally written for the [ComicFlow](http://itunes.apple.com/us/app/comicflow/id409290355?mt=8) comic reader app for iPad. It lets users upload, download and organize comic files inside the app using their web browser directly over WiFi.
|
GCDWebServer was originally written for the [ComicFlow](http://itunes.apple.com/us/app/comicflow/id409290355?mt=8) comic reader app for iPad. It allow users to connect to their iPad with their web browser over WiFi and then upload, download and organize comic files inside the app.
|
||||||
|
|
||||||
ComicFlow is [entirely open-source](https://github.com/swisspol/ComicFlow) and you can see how it uses GCDWebUploader in the [WebServer.m](https://github.com/swisspol/ComicFlow/blob/master/Classes/WebServer.m) file.
|
ComicFlow is [entirely open-source](https://github.com/swisspol/ComicFlow) and you can see how it uses GCDWebUploader in the [WebServer.m](https://github.com/swisspol/ComicFlow/blob/master/Classes/WebServer.m) file.
|
||||||
|
|||||||
Reference in New Issue
Block a user