rhodes 7.4.1 → 7.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. checksums.yaml +5 -5
  2. data/CREDITS +3 -1
  3. data/LICENSE +1 -1
  4. data/README.md +3 -1
  5. data/Rakefile +52 -43
  6. data/SECURITY.md +19 -0
  7. data/appveyor.yml +60 -7
  8. data/azure-pipelines.yml +67 -0
  9. data/extensions/rhoconnect-push/ext/rhoconnect-push/platform/android/src/com/rhomobile/rhoelements/ans/ANSManager.java +2 -2
  10. data/lib/commonAPI/barcode/ext/platform/android/AndroidManifest.xml +3 -1
  11. data/lib/commonAPI/barcode/ext/platform/android/src/com/google/barcodereader/BarcodeCaptureActivity.java +11 -13
  12. data/lib/commonAPI/barcode/ext/platform/android/src/com/google/barcodereader/BarcodeGraphicTracker.java +1 -1
  13. data/lib/commonAPI/barcode/ext/platform/android/src/com/google/barcodereader/ui/camera/CameraSource.java +3 -3
  14. data/lib/commonAPI/barcode/ext/platform/android/src/com/google/barcodereader/ui/camera/CameraSourcePreview.java +1 -1
  15. data/lib/commonAPI/barcode/ext.yml +4 -8
  16. data/lib/commonAPI/coreapi/ext/platform/android/Rakefile +0 -12
  17. data/lib/commonAPI/coreapi/ext/platform/android/src/com/rho/notification/Notification.java +4 -2
  18. data/lib/commonAPI/coreapi/ext/platform/android/src/com/rho/notification/NotificationScheduler.java +3 -3
  19. data/lib/commonAPI/coreapi/ext/platform/android/src/com/rho/notification/NotificationSingleton.java +1 -1
  20. data/lib/commonAPI/coreapi/ext/push.xml +5 -2
  21. data/lib/commonAPI/mediacapture/ext/camera.xml +4 -9
  22. data/lib/commonAPI/mediacapture/ext/platform/android/ApplicationFileProvider.erb +1 -1
  23. data/lib/commonAPI/mediacapture/ext/platform/android/ext_java.files +1 -9
  24. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraExtension.java +0 -2
  25. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraFactory.java +19 -24
  26. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraObject.java +317 -729
  27. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraRhoListener.java +240 -434
  28. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/{CameraSingletonObject.java → CameraSingleton.java} +68 -74
  29. data/lib/commonAPI/printing_zebra/ext/platform/android/src/com/rhomobile/printing/zebra/impl/ZebraPrintingProviderImpl.java +2 -1
  30. data/lib/extensions/fcm-push/ext/android/ApplicationManifestAdds.erb +15 -17
  31. data/lib/extensions/fcm-push/ext/android/Rakefile +59 -34
  32. data/lib/extensions/fcm-push/ext/android/ext_java.files +0 -1
  33. data/lib/extensions/fcm-push/ext/android/src/com/rhomobile/rhodes/fcm/FCMFacade.java +13 -17
  34. data/lib/extensions/fcm-push/ext/android/src/com/rhomobile/rhodes/fcm/FCMIntentService.java +81 -80
  35. data/lib/extensions/fcm-push/ext/android/src/com/rhomobile/rhodes/fcm/FCMListener.java +0 -16
  36. data/lib/extensions/fcm-push/ext.yml +1 -1
  37. data/lib/extensions/gmaps/ext/platform/android/ApplicationManifestAdds.erb +1 -0
  38. data/lib/extensions/gmaps/ext/platform/android/src/com/rhomobile/rhodes/gmaps/GMapActivity.java +12 -4
  39. data/lib/extensions/gmaps/ext.yml +3 -1
  40. data/platform/android/Rhodes/jni/src/MethodResultJni.cpp +4 -0
  41. data/platform/android/Rhodes/src/com/rhomobile/rhodes/RhodesActivity.java +3 -3
  42. data/platform/android/Rhodes/src/com/rhomobile/rhodes/RhodesService.java +3 -3
  43. data/platform/android/Rhodes/src/com/rhomobile/rhodes/alert/StatusNotification.java +1 -1
  44. data/platform/android/Rhodes/src/com/rhomobile/rhodes/geolocation/GeoLocation.java +14 -14
  45. data/platform/android/Rhodes/src/com/rhomobile/rhodes/geolocation/GeoLocationImpl.java +18 -9
  46. data/platform/android/Rhodes/src/com/rhomobile/rhodes/osfunctionality/AndroidFunctionality26.java +1 -1
  47. data/platform/android/build/{aab_builder.rb → aapt2_helper.rb} +94 -37
  48. data/platform/android/build/android.rake +124 -191
  49. data/platform/android/build/android_tools.rb +9 -9
  50. data/platform/android/build/androidcommon.rb +18 -7
  51. data/platform/android/build/build_tools_finder.rb +46 -0
  52. data/platform/android/build/config.yml +5 -0
  53. data/platform/android/build/dex_builder.rb +88 -0
  54. data/platform/android/build/hostplatform.rb +9 -0
  55. data/platform/android/build/manifest_generator.rb +1 -0
  56. data/platform/android/build/maven_deps_extractor.rb +22 -21
  57. data/platform/android/build/ndkwrapper.rb +80 -51
  58. data/platform/iphone/Classes/AppManager/AppManager.m +3 -1
  59. data/platform/iphone/Classes/Camera/PickImageDelegate.h +2 -0
  60. data/platform/iphone/Classes/Camera/PickImageDelegate.m +45 -23
  61. data/platform/iphone/Classes/CocoaServer/CCocoaServer.h +27 -0
  62. data/platform/iphone/Classes/CocoaServer/CCocoaServer.m +107 -0
  63. data/platform/iphone/Classes/CocoaServer/RhoHTTPConnection.h +16 -0
  64. data/platform/iphone/Classes/CocoaServer/RhoHTTPConnection.m +226 -0
  65. data/platform/iphone/Classes/RhoWKWebView.mm +78 -11
  66. data/platform/iphone/Classes/RhoWebViewFabrique.m +6 -1
  67. data/platform/iphone/Classes/Rhodes.m +3 -0
  68. data/platform/iphone/CocoaHTTPServer/Core/Categories/DDData.h +14 -0
  69. data/platform/iphone/CocoaHTTPServer/Core/Categories/DDData.m +158 -0
  70. data/platform/iphone/CocoaHTTPServer/Core/Categories/DDNumber.h +12 -0
  71. data/platform/iphone/CocoaHTTPServer/Core/Categories/DDNumber.m +88 -0
  72. data/platform/iphone/CocoaHTTPServer/Core/Categories/DDRange.h +56 -0
  73. data/platform/iphone/CocoaHTTPServer/Core/Categories/DDRange.m +104 -0
  74. data/platform/iphone/CocoaHTTPServer/Core/HTTPAuthenticationRequest.h +45 -0
  75. data/platform/iphone/CocoaHTTPServer/Core/HTTPAuthenticationRequest.m +195 -0
  76. data/platform/iphone/CocoaHTTPServer/Core/HTTPConnection.h +120 -0
  77. data/platform/iphone/CocoaHTTPServer/Core/HTTPConnection.m +2708 -0
  78. data/platform/iphone/CocoaHTTPServer/Core/HTTPLogging.h +136 -0
  79. data/platform/iphone/CocoaHTTPServer/Core/HTTPMessage.h +48 -0
  80. data/platform/iphone/CocoaHTTPServer/Core/HTTPMessage.m +113 -0
  81. data/platform/iphone/CocoaHTTPServer/Core/HTTPResponse.h +149 -0
  82. data/platform/iphone/CocoaHTTPServer/Core/HTTPServer.h +205 -0
  83. data/platform/iphone/CocoaHTTPServer/Core/HTTPServer.m +772 -0
  84. data/platform/iphone/CocoaHTTPServer/Core/Mime/MultipartFormDataParser.h +65 -0
  85. data/platform/iphone/CocoaHTTPServer/Core/Mime/MultipartFormDataParser.m +529 -0
  86. data/platform/iphone/CocoaHTTPServer/Core/Mime/MultipartMessageHeader.h +33 -0
  87. data/platform/iphone/CocoaHTTPServer/Core/Mime/MultipartMessageHeader.m +86 -0
  88. data/platform/iphone/CocoaHTTPServer/Core/Mime/MultipartMessageHeaderField.h +23 -0
  89. data/platform/iphone/CocoaHTTPServer/Core/Mime/MultipartMessageHeaderField.m +211 -0
  90. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPAsyncFileResponse.h +75 -0
  91. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPAsyncFileResponse.m +405 -0
  92. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPDataResponse.h +13 -0
  93. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPDataResponse.m +79 -0
  94. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPDynamicFileResponse.h +52 -0
  95. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPDynamicFileResponse.m +292 -0
  96. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPErrorResponse.h +9 -0
  97. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPErrorResponse.m +38 -0
  98. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPFileResponse.h +25 -0
  99. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPFileResponse.m +237 -0
  100. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPRedirectResponse.h +12 -0
  101. data/platform/iphone/CocoaHTTPServer/Core/Responses/HTTPRedirectResponse.m +73 -0
  102. data/platform/iphone/CocoaHTTPServer/Core/WebSocket.h +105 -0
  103. data/platform/iphone/CocoaHTTPServer/Core/WebSocket.m +791 -0
  104. data/platform/iphone/CocoaHTTPServer/Extensions/WebDAV/DAVConnection.h +7 -0
  105. data/platform/iphone/CocoaHTTPServer/Extensions/WebDAV/DAVConnection.m +160 -0
  106. data/platform/iphone/CocoaHTTPServer/Extensions/WebDAV/DAVResponse.h +11 -0
  107. data/platform/iphone/CocoaHTTPServer/Extensions/WebDAV/DAVResponse.m +372 -0
  108. data/platform/iphone/CocoaHTTPServer/Extensions/WebDAV/DELETEResponse.h +7 -0
  109. data/platform/iphone/CocoaHTTPServer/Extensions/WebDAV/DELETEResponse.m +49 -0
  110. data/platform/iphone/CocoaHTTPServer/Extensions/WebDAV/PUTResponse.h +8 -0
  111. data/platform/iphone/CocoaHTTPServer/Extensions/WebDAV/PUTResponse.m +69 -0
  112. data/platform/iphone/CocoaHTTPServer/LICENSE.txt +18 -0
  113. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaAsyncSocket/About.txt +4 -0
  114. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaAsyncSocket/GCDAsyncSocket.h +1226 -0
  115. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaAsyncSocket/GCDAsyncSocket.m +8528 -0
  116. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/About.txt +33 -0
  117. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDASLLogger.h +41 -0
  118. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDASLLogger.m +99 -0
  119. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDAbstractDatabaseLogger.h +102 -0
  120. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDAbstractDatabaseLogger.m +727 -0
  121. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDFileLogger.h +334 -0
  122. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDFileLogger.m +1353 -0
  123. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDLog.h +601 -0
  124. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDLog.m +1083 -0
  125. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDTTYLogger.h +167 -0
  126. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/DDTTYLogger.m +1479 -0
  127. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/Extensions/ContextFilterLogFormatter.h +65 -0
  128. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/Extensions/ContextFilterLogFormatter.m +191 -0
  129. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/Extensions/DispatchQueueLogFormatter.h +116 -0
  130. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/Extensions/DispatchQueueLogFormatter.m +251 -0
  131. data/platform/iphone/CocoaHTTPServer/Vendor/CocoaLumberjack/Extensions/README.txt +7 -0
  132. data/platform/iphone/Framework/Rhodes/Rhodes.xcodeproj/project.pbxproj +1 -1
  133. data/platform/iphone/RhoAppBaseLib/RhoAppBaseLib.xcodeproj/project.pbxproj +364 -0
  134. data/platform/iphone/rbuild/iphone.rake +4 -0
  135. data/platform/osx/bin/RhoSimulator/RhoSimulator.app.zip +0 -0
  136. data/platform/shared/common/RhodesApp.cpp +26 -1
  137. data/platform/shared/net/HttpServer.cpp +20 -0
  138. data/platform/shared/qt/RhoSimulator.pro +1 -1
  139. data/platform/shared/ruby/osx/ruby/config.h +2 -0
  140. data/platform/shared/ruby/win32/win32.c +10 -3
  141. data/platform/win32/RhoSimulator/RhoSimulator.exe +0 -0
  142. data/rakefile.rb +52 -43
  143. data/res/build-tools/RhoRuby.exe +0 -0
  144. data/res/build-tools/aapt2/linux/aapt2 +0 -0
  145. data/res/build-tools/aapt2/osx/aapt2 +0 -0
  146. data/res/generators/templates/application/AndroidManifest.erb +2 -1
  147. data/res/generators/templates/application/build.yml +4 -1
  148. data/res/generators/templates/application/rhoconfig.txt +5 -0
  149. data/rhobuild.yml.example +4 -4
  150. data/rhodes.gemspec +1 -2
  151. data/version +1 -1
  152. metadata +80 -35
  153. data/lib/commonAPI/mediacapture/ext/platform/android/adds/res/drawable/camera_btn.xml +0 -14
  154. data/lib/commonAPI/mediacapture/ext/platform/android/adds/res/layout/camera_land.xml +0 -23
  155. data/lib/commonAPI/mediacapture/ext/platform/android/adds/res/layout/camera_port.xml +0 -23
  156. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraActivity.java +0 -156
  157. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraEclair.java +0 -227
  158. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraGingerbread.java +0 -152
  159. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraPreview.java +0 -183
  160. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraSingletonEclair.java +0 -14
  161. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/CameraSingletonGingerbread.java +0 -60
  162. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/ICameraObject.java +0 -20
  163. data/lib/commonAPI/mediacapture/ext/platform/android/src/com/rho/camera/ICameraSingletonObject.java +0 -8
  164. data/lib/extensions/fcm-push/ext/android/src/com/rhomobile/rhodes/fcm/FCMTokenRefresherService.java +0 -27
  165. data/platform/android/Rhodes/AndroidManifest.xml.erb +0 -89
@@ -0,0 +1,7 @@
1
+ #import "HTTPConnection.h"
2
+
3
+ @interface DAVConnection : HTTPConnection {
4
+ id requestContentBody;
5
+ NSOutputStream* requestContentStream;
6
+ }
7
+ @end
@@ -0,0 +1,160 @@
1
+ #import "DAVConnection.h"
2
+ #import "HTTPMessage.h"
3
+ #import "HTTPFileResponse.h"
4
+ #import "HTTPAsyncFileResponse.h"
5
+ #import "PUTResponse.h"
6
+ #import "DELETEResponse.h"
7
+ #import "DAVResponse.h"
8
+ #import "HTTPLogging.h"
9
+
10
+ #define HTTP_BODY_MAX_MEMORY_SIZE (1024 * 1024)
11
+ #define HTTP_ASYNC_FILE_RESPONSE_THRESHOLD (16 * 1024 * 1024)
12
+
13
+ static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
14
+
15
+ @implementation DAVConnection
16
+
17
+ - (void) dealloc {
18
+ [requestContentStream close];
19
+
20
+ }
21
+
22
+ - (BOOL) supportsMethod:(NSString*)method atPath:(NSString*)path {
23
+ // HTTPFileResponse & HTTPAsyncFileResponse
24
+ if ([method isEqualToString:@"GET"]) return YES;
25
+ if ([method isEqualToString:@"HEAD"]) return YES;
26
+
27
+ // PUTResponse
28
+ if ([method isEqualToString:@"PUT"]) return YES;
29
+
30
+ // DELETEResponse
31
+ if ([method isEqualToString:@"DELETE"]) return YES;
32
+
33
+ // DAVResponse
34
+ if ([method isEqualToString:@"OPTIONS"]) return YES;
35
+ if ([method isEqualToString:@"PROPFIND"]) return YES;
36
+ if ([method isEqualToString:@"MKCOL"]) return YES;
37
+ if ([method isEqualToString:@"MOVE"]) return YES;
38
+ if ([method isEqualToString:@"COPY"]) return YES;
39
+ if ([method isEqualToString:@"LOCK"]) return YES;
40
+ if ([method isEqualToString:@"UNLOCK"]) return YES;
41
+
42
+ return NO;
43
+ }
44
+
45
+ - (BOOL) expectsRequestBodyFromMethod:(NSString*)method atPath:(NSString*)path {
46
+ // PUTResponse
47
+ if ([method isEqualToString:@"PUT"]) {
48
+ return YES;
49
+ }
50
+
51
+ // DAVResponse
52
+ if ([method isEqual:@"PROPFIND"] || [method isEqual:@"MKCOL"]) {
53
+ return [request headerField:@"Content-Length"] ? YES : NO;
54
+ }
55
+ if ([method isEqual:@"LOCK"]) {
56
+ return YES;
57
+ }
58
+
59
+ return NO;
60
+ }
61
+
62
+ - (void) prepareForBodyWithSize:(UInt64)contentLength {
63
+ NSAssert(requestContentStream == nil, @"requestContentStream should be nil");
64
+ NSAssert(requestContentBody == nil, @"requestContentBody should be nil");
65
+
66
+ if (contentLength > HTTP_BODY_MAX_MEMORY_SIZE) {
67
+ requestContentBody = [[NSTemporaryDirectory() stringByAppendingString:[[NSProcessInfo processInfo] globallyUniqueString]] copy];
68
+ requestContentStream = [[NSOutputStream alloc] initToFileAtPath:requestContentBody append:NO];
69
+ [requestContentStream open];
70
+ } else {
71
+ requestContentBody = [[NSMutableData alloc] initWithCapacity:(NSUInteger)contentLength];
72
+ requestContentStream = nil;
73
+ }
74
+ }
75
+
76
+ - (void) processBodyData:(NSData*)postDataChunk {
77
+ NSAssert(requestContentBody != nil, @"requestContentBody should not be nil");
78
+ if (requestContentStream) {
79
+ [requestContentStream write:[postDataChunk bytes] maxLength:[postDataChunk length]];
80
+ } else {
81
+ [(NSMutableData*)requestContentBody appendData:postDataChunk];
82
+ }
83
+ }
84
+
85
+ - (void) finishBody {
86
+ NSAssert(requestContentBody != nil, @"requestContentBody should not be nil");
87
+ if (requestContentStream) {
88
+ [requestContentStream close];
89
+ requestContentStream = nil;
90
+ }
91
+ }
92
+
93
+ - (void)finishResponse {
94
+ NSAssert(requestContentStream == nil, @"requestContentStream should be nil");
95
+ requestContentBody = nil;
96
+
97
+ [super finishResponse];
98
+ }
99
+
100
+ - (NSObject<HTTPResponse>*) httpResponseForMethod:(NSString*)method URI:(NSString*)path {
101
+ if ([method isEqualToString:@"HEAD"] || [method isEqualToString:@"GET"]) {
102
+ NSString* filePath = [self filePathForURI:path allowDirectory:NO];
103
+ if (filePath) {
104
+ NSDictionary* fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:NULL];
105
+ if (fileAttributes) {
106
+ if ([[fileAttributes objectForKey:NSFileSize] unsignedLongLongValue] > HTTP_ASYNC_FILE_RESPONSE_THRESHOLD) {
107
+ return [[HTTPAsyncFileResponse alloc] initWithFilePath:filePath forConnection:self];
108
+ } else {
109
+ return [[HTTPFileResponse alloc] initWithFilePath:filePath forConnection:self];
110
+ }
111
+ }
112
+ }
113
+ }
114
+
115
+ if ([method isEqualToString:@"PUT"]) {
116
+ NSString* filePath = [self filePathForURI:path allowDirectory:YES];
117
+ if (filePath) {
118
+ if ([requestContentBody isKindOfClass:[NSString class]]) {
119
+ return [[PUTResponse alloc] initWithFilePath:filePath headers:[request allHeaderFields] bodyFile:requestContentBody];
120
+ } else if ([requestContentBody isKindOfClass:[NSData class]]) {
121
+ return [[PUTResponse alloc] initWithFilePath:filePath headers:[request allHeaderFields] bodyData:requestContentBody];
122
+ } else {
123
+ HTTPLogError(@"Internal error");
124
+ }
125
+ }
126
+ }
127
+
128
+ if ([method isEqualToString:@"DELETE"]) {
129
+ NSString* filePath = [self filePathForURI:path allowDirectory:YES];
130
+ if (filePath) {
131
+ return [[DELETEResponse alloc] initWithFilePath:filePath];
132
+ }
133
+ }
134
+
135
+ if ([method isEqualToString:@"OPTIONS"] || [method isEqualToString:@"PROPFIND"] || [method isEqualToString:@"MKCOL"] ||
136
+ [method isEqualToString:@"MOVE"] || [method isEqualToString:@"COPY"] || [method isEqualToString:@"LOCK"] || [method isEqualToString:@"UNLOCK"]) {
137
+ NSString* filePath = [self filePathForURI:path allowDirectory:YES];
138
+ if (filePath) {
139
+ NSString* rootPath = [config documentRoot];
140
+ NSString* resourcePath = [filePath substringFromIndex:([rootPath length] + 1)];
141
+ if (requestContentBody) {
142
+ if ([requestContentBody isKindOfClass:[NSString class]]) {
143
+ requestContentBody = [NSData dataWithContentsOfFile:requestContentBody];
144
+ } else if (![requestContentBody isKindOfClass:[NSData class]]) {
145
+ HTTPLogError(@"Internal error");
146
+ return nil;
147
+ }
148
+ }
149
+ return [[DAVResponse alloc] initWithMethod:method
150
+ headers:[request allHeaderFields]
151
+ bodyData:requestContentBody
152
+ resourcePath:resourcePath
153
+ rootPath:rootPath];
154
+ }
155
+ }
156
+
157
+ return nil;
158
+ }
159
+
160
+ @end
@@ -0,0 +1,11 @@
1
+ #import "HTTPResponse.h"
2
+
3
+ @interface DAVResponse : NSObject <HTTPResponse> {
4
+ @private
5
+ UInt64 _offset;
6
+ NSMutableDictionary* _headers;
7
+ NSData* _data;
8
+ NSInteger _status;
9
+ }
10
+ - (id) initWithMethod:(NSString*)method headers:(NSDictionary*)headers bodyData:(NSData*)body resourcePath:(NSString*)resourcePath rootPath:(NSString*)rootPath;
11
+ @end
@@ -0,0 +1,372 @@
1
+ #import <libxml/parser.h>
2
+
3
+ #import "DAVResponse.h"
4
+ #import "HTTPLogging.h"
5
+
6
+ // WebDAV specifications: http://webdav.org/specs/rfc4918.html
7
+
8
+ typedef enum {
9
+ kDAVProperty_ResourceType = (1 << 0),
10
+ kDAVProperty_CreationDate = (1 << 1),
11
+ kDAVProperty_LastModified = (1 << 2),
12
+ kDAVProperty_ContentLength = (1 << 3),
13
+ kDAVAllProperties = kDAVProperty_ResourceType | kDAVProperty_CreationDate | kDAVProperty_LastModified | kDAVProperty_ContentLength
14
+ } DAVProperties;
15
+
16
+ #define kXMLParseOptions (XML_PARSE_NONET | XML_PARSE_RECOVER | XML_PARSE_NOBLANKS | XML_PARSE_COMPACT | XML_PARSE_NOWARNING | XML_PARSE_NOERROR)
17
+
18
+ static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
19
+
20
+ @implementation DAVResponse
21
+
22
+ static void _AddPropertyResponse(NSString* itemPath, NSString* resourcePath, DAVProperties properties, NSMutableString* xmlString) {
23
+ CFStringRef escapedPath = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)resourcePath, NULL,
24
+ CFSTR("<&>?+"), kCFStringEncodingUTF8);
25
+ if (escapedPath) {
26
+ NSDictionary* attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:itemPath error:NULL];
27
+ BOOL isDirectory = [[attributes fileType] isEqualToString:NSFileTypeDirectory];
28
+ [xmlString appendString:@"<D:response>"];
29
+ [xmlString appendFormat:@"<D:href>%@</D:href>", escapedPath];
30
+ [xmlString appendString:@"<D:propstat>"];
31
+ [xmlString appendString:@"<D:prop>"];
32
+
33
+ if (properties & kDAVProperty_ResourceType) {
34
+ if (isDirectory) {
35
+ [xmlString appendString:@"<D:resourcetype><D:collection/></D:resourcetype>"];
36
+ } else {
37
+ [xmlString appendString:@"<D:resourcetype/>"];
38
+ }
39
+ }
40
+
41
+ if ((properties & kDAVProperty_CreationDate) && [attributes objectForKey:NSFileCreationDate]) {
42
+ NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
43
+ formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
44
+ formatter.timeZone = [NSTimeZone timeZoneWithName:@"GMT"];
45
+ formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'+00:00'";
46
+ [xmlString appendFormat:@"<D:creationdate>%@</D:creationdate>", [formatter stringFromDate:[attributes fileCreationDate]]];
47
+ }
48
+
49
+ if ((properties & kDAVProperty_LastModified) && [attributes objectForKey:NSFileModificationDate]) {
50
+ NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
51
+ formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
52
+ formatter.timeZone = [NSTimeZone timeZoneWithName:@"GMT"];
53
+ formatter.dateFormat = @"EEE', 'd' 'MMM' 'yyyy' 'HH:mm:ss' GMT'";
54
+ [xmlString appendFormat:@"<D:getlastmodified>%@</D:getlastmodified>", [formatter stringFromDate:[attributes fileModificationDate]]];
55
+ }
56
+
57
+ if ((properties & kDAVProperty_ContentLength) && !isDirectory && [attributes objectForKey:NSFileSize]) {
58
+ [xmlString appendFormat:@"<D:getcontentlength>%qu</D:getcontentlength>", [attributes fileSize]];
59
+ }
60
+
61
+ [xmlString appendString:@"</D:prop>"];
62
+ [xmlString appendString:@"<D:status>HTTP/1.1 200 OK</D:status>"];
63
+ [xmlString appendString:@"</D:propstat>"];
64
+ [xmlString appendString:@"</D:response>\n"];
65
+ CFRelease(escapedPath);
66
+ }
67
+ }
68
+
69
+ static xmlNodePtr _XMLChildWithName(xmlNodePtr child, const xmlChar* name) {
70
+ while (child) {
71
+ if ((child->type == XML_ELEMENT_NODE) && !xmlStrcmp(child->name, name)) {
72
+ return child;
73
+ }
74
+ child = child->next;
75
+ }
76
+ return NULL;
77
+ }
78
+
79
+ - (id) initWithMethod:(NSString*)method headers:(NSDictionary*)headers bodyData:(NSData*)body resourcePath:(NSString*)resourcePath rootPath:(NSString*)rootPath {
80
+ if ((self = [super init])) {
81
+ _status = 200;
82
+ _headers = [[NSMutableDictionary alloc] init];
83
+
84
+ // 10.1 DAV Header
85
+ if ([method isEqualToString:@"OPTIONS"]) {
86
+ if ([[headers objectForKey:@"User-Agent"] hasPrefix:@"WebDAVFS/"]) { // Mac OS X WebDAV support
87
+ [_headers setObject:@"1, 2" forKey:@"DAV"];
88
+ } else {
89
+ [_headers setObject:@"1" forKey:@"DAV"];
90
+ }
91
+ }
92
+
93
+ // 9.1 PROPFIND Method
94
+ if ([method isEqualToString:@"PROPFIND"]) {
95
+ NSInteger depth;
96
+ NSString* depthHeader = [headers objectForKey:@"Depth"];
97
+ if ([depthHeader isEqualToString:@"0"]) {
98
+ depth = 0;
99
+ } else if ([depthHeader isEqualToString:@"1"]) {
100
+ depth = 1;
101
+ } else {
102
+ HTTPLogError(@"Unsupported DAV depth \"%@\"", depthHeader);
103
+ return nil;
104
+ }
105
+
106
+ DAVProperties properties = 0;
107
+ xmlDocPtr document = xmlReadMemory(body.bytes, (int)body.length, NULL, NULL, kXMLParseOptions);
108
+ if (document) {
109
+ xmlNodePtr node = _XMLChildWithName(document->children, (const xmlChar*)"propfind");
110
+ if (node) {
111
+ node = _XMLChildWithName(node->children, (const xmlChar*)"prop");
112
+ }
113
+ if (node) {
114
+ node = node->children;
115
+ while (node) {
116
+ if (!xmlStrcmp(node->name, (const xmlChar*)"resourcetype")) {
117
+ properties |= kDAVProperty_ResourceType;
118
+ } else if (!xmlStrcmp(node->name, (const xmlChar*)"creationdate")) {
119
+ properties |= kDAVProperty_CreationDate;
120
+ } else if (!xmlStrcmp(node->name, (const xmlChar*)"getlastmodified")) {
121
+ properties |= kDAVProperty_LastModified;
122
+ } else if (!xmlStrcmp(node->name, (const xmlChar*)"getcontentlength")) {
123
+ properties |= kDAVProperty_ContentLength;
124
+ } else {
125
+ HTTPLogWarn(@"Unknown DAV property requested \"%s\"", node->name);
126
+ }
127
+ node = node->next;
128
+ }
129
+ } else {
130
+ HTTPLogWarn(@"HTTP Server: Invalid DAV properties\n%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
131
+ }
132
+ xmlFreeDoc(document);
133
+ }
134
+ if (!properties) {
135
+ properties = kDAVAllProperties;
136
+ }
137
+
138
+ NSString* basePath = [rootPath stringByAppendingPathComponent:resourcePath];
139
+ if (![basePath hasPrefix:rootPath] || ![[NSFileManager defaultManager] fileExistsAtPath:basePath]) {
140
+ return nil;
141
+ }
142
+
143
+ NSMutableString* xmlString = [NSMutableString stringWithString:@"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"];
144
+ [xmlString appendString:@"<D:multistatus xmlns:D=\"DAV:\">\n"];
145
+ if (![resourcePath hasPrefix:@"/"]) {
146
+ resourcePath = [@"/" stringByAppendingString:resourcePath];
147
+ }
148
+ _AddPropertyResponse(basePath, resourcePath, properties, xmlString);
149
+ if (depth == 1) {
150
+ if (![resourcePath hasSuffix:@"/"]) {
151
+ resourcePath = [resourcePath stringByAppendingString:@"/"];
152
+ }
153
+ NSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager] enumeratorAtPath:basePath];
154
+ NSString* path;
155
+ while ((path = [enumerator nextObject])) {
156
+ _AddPropertyResponse([basePath stringByAppendingPathComponent:path], [resourcePath stringByAppendingString:path], properties, xmlString);
157
+ [enumerator skipDescendents];
158
+ }
159
+ }
160
+ [xmlString appendString:@"</D:multistatus>"];
161
+
162
+ [_headers setObject:@"application/xml; charset=\"utf-8\"" forKey:@"Content-Type"];
163
+ _data = [xmlString dataUsingEncoding:NSUTF8StringEncoding];
164
+ _status = 207;
165
+ }
166
+
167
+ // 9.3 MKCOL Method
168
+ if ([method isEqualToString:@"MKCOL"]) {
169
+ NSString* path = [rootPath stringByAppendingPathComponent:resourcePath];
170
+ if (![path hasPrefix:rootPath]) {
171
+ return nil;
172
+ }
173
+
174
+ if (![[NSFileManager defaultManager] fileExistsAtPath:[path stringByDeletingLastPathComponent]]) {
175
+ HTTPLogError(@"Missing intermediate collection(s) at \"%@\"", path);
176
+ _status = 409;
177
+ } else if (![[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:NO attributes:nil error:NULL]) {
178
+ HTTPLogError(@"Failed creating collection at \"%@\"", path);
179
+ _status = 405;
180
+ }
181
+ }
182
+
183
+ // 9.8 COPY Method
184
+ // 9.9 MOVE Method
185
+ if ([method isEqualToString:@"MOVE"] || [method isEqualToString:@"COPY"]) {
186
+ if ([method isEqualToString:@"COPY"] && ![[headers objectForKey:@"Depth"] isEqualToString:@"infinity"]) {
187
+ HTTPLogError(@"Unsupported DAV depth \"%@\"", [headers objectForKey:@"Depth"]);
188
+ return nil;
189
+ }
190
+
191
+ NSString* sourcePath = [rootPath stringByAppendingPathComponent:resourcePath];
192
+ if (![sourcePath hasPrefix:rootPath] || ![[NSFileManager defaultManager] fileExistsAtPath:sourcePath]) {
193
+ return nil;
194
+ }
195
+
196
+ NSString* destination = [headers objectForKey:@"Destination"];
197
+ NSRange range = [destination rangeOfString:[headers objectForKey:@"Host"]];
198
+ if (range.location == NSNotFound) {
199
+ return nil;
200
+ }
201
+ NSString* destinationPath = [rootPath stringByAppendingPathComponent:
202
+ [[destination substringFromIndex:(range.location + range.length)] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
203
+ if (![destinationPath hasPrefix:rootPath] || [[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) {
204
+ return nil;
205
+ }
206
+
207
+ BOOL isDirectory;
208
+ if (![[NSFileManager defaultManager] fileExistsAtPath:[destinationPath stringByDeletingLastPathComponent] isDirectory:&isDirectory] || !isDirectory) {
209
+ HTTPLogError(@"Invalid destination path \"%@\"", destinationPath);
210
+ _status = 409;
211
+ } else {
212
+ BOOL existing = [[NSFileManager defaultManager] fileExistsAtPath:destinationPath];
213
+ if (existing && [[headers objectForKey:@"Overwrite"] isEqualToString:@"F"]) {
214
+ HTTPLogError(@"Pre-existing destination path \"%@\"", destinationPath);
215
+ _status = 412;
216
+ } else {
217
+ if ([method isEqualToString:@"COPY"]) {
218
+ if ([[NSFileManager defaultManager] copyItemAtPath:sourcePath toPath:destinationPath error:NULL]) {
219
+ _status = existing ? 204 : 201;
220
+ } else {
221
+ HTTPLogError(@"Failed copying \"%@\" to \"%@\"", sourcePath, destinationPath);
222
+ _status = 403;
223
+ }
224
+ } else {
225
+ if ([[NSFileManager defaultManager] moveItemAtPath:sourcePath toPath:destinationPath error:NULL]) {
226
+ _status = existing ? 204 : 201;
227
+ } else {
228
+ HTTPLogError(@"Failed moving \"%@\" to \"%@\"", sourcePath, destinationPath);
229
+ _status = 403;
230
+ }
231
+ }
232
+ }
233
+ }
234
+ }
235
+
236
+ // 9.10 LOCK Method - TODO: Actually lock the resource
237
+ if ([method isEqualToString:@"LOCK"]) {
238
+ NSString* path = [rootPath stringByAppendingPathComponent:resourcePath];
239
+ if (![path hasPrefix:rootPath]) {
240
+ return nil;
241
+ }
242
+
243
+ NSString* depth = [headers objectForKey:@"Depth"];
244
+ NSString* scope = nil;
245
+ NSString* type = nil;
246
+ NSString* owner = nil;
247
+ NSString* token = nil;
248
+ xmlDocPtr document = xmlReadMemory(body.bytes, (int)body.length, NULL, NULL, kXMLParseOptions);
249
+ if (document) {
250
+ xmlNodePtr node = _XMLChildWithName(document->children, (const xmlChar*)"lockinfo");
251
+ if (node) {
252
+ xmlNodePtr scopeNode = _XMLChildWithName(node->children, (const xmlChar*)"lockscope");
253
+ if (scopeNode && scopeNode->children && scopeNode->children->name) {
254
+ scope = [NSString stringWithUTF8String:(const char*)scopeNode->children->name];
255
+ }
256
+ xmlNodePtr typeNode = _XMLChildWithName(node->children, (const xmlChar*)"locktype");
257
+ if (typeNode && typeNode->children && typeNode->children->name) {
258
+ type = [NSString stringWithUTF8String:(const char*)typeNode->children->name];
259
+ }
260
+ xmlNodePtr ownerNode = _XMLChildWithName(node->children, (const xmlChar*)"owner");
261
+ if (ownerNode) {
262
+ ownerNode = _XMLChildWithName(ownerNode->children, (const xmlChar*)"href");
263
+ if (ownerNode && ownerNode->children && ownerNode->children->content) {
264
+ owner = [NSString stringWithUTF8String:(const char*)ownerNode->children->content];
265
+ }
266
+ }
267
+ } else {
268
+ HTTPLogWarn(@"HTTP Server: Invalid DAV properties\n%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
269
+ }
270
+ xmlFreeDoc(document);
271
+ } else {
272
+ // 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
273
+ // into the lock create case.
274
+ NSString* lockToken;
275
+ if ((lockToken = [headers objectForKey:@"If"]) != nil) {
276
+ scope = @"exclusive";
277
+ type = @"write";
278
+ depth = @"0";
279
+ token = [lockToken stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"(<>)"]];
280
+ }
281
+ }
282
+ if ([scope isEqualToString:@"exclusive"] && [type isEqualToString:@"write"] && [depth isEqualToString:@"0"] &&
283
+ ([[NSFileManager defaultManager] fileExistsAtPath:path] || [[NSData data] writeToFile:path atomically:YES])) {
284
+ NSString* timeout = [headers objectForKey:@"Timeout"];
285
+ if (!token) {
286
+ CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
287
+ NSString *uuidStr = (__bridge_transfer NSString *)CFUUIDCreateString(kCFAllocatorDefault, uuid);
288
+ token = [NSString stringWithFormat:@"urn:uuid:%@", uuidStr];
289
+ CFRelease(uuid);
290
+ }
291
+
292
+ NSMutableString* xmlString = [NSMutableString stringWithString:@"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"];
293
+ [xmlString appendString:@"<D:prop xmlns:D=\"DAV:\">\n"];
294
+ [xmlString appendString:@"<D:lockdiscovery>\n<D:activelock>\n"];
295
+ [xmlString appendFormat:@"<D:locktype><D:%@/></D:locktype>\n", type];
296
+ [xmlString appendFormat:@"<D:lockscope><D:%@/></D:lockscope>\n", scope];
297
+ [xmlString appendFormat:@"<D:depth>%@</D:depth>\n", depth];
298
+ if (owner) {
299
+ [xmlString appendFormat:@"<D:owner><D:href>%@</D:href></D:owner>\n", owner];
300
+ }
301
+ if (timeout) {
302
+ [xmlString appendFormat:@"<D:timeout>%@</D:timeout>\n", timeout];
303
+ }
304
+ [xmlString appendFormat:@"<D:locktoken><D:href>%@</D:href></D:locktoken>\n", token];
305
+ NSString* lockroot = [@"http://" stringByAppendingString:[[headers objectForKey:@"Host"] stringByAppendingString:[@"/" stringByAppendingString:resourcePath]]];
306
+ [xmlString appendFormat:@"<D:lockroot><D:href>%@</D:href></D:lockroot>\n", lockroot];
307
+ [xmlString appendString:@"</D:activelock>\n</D:lockdiscovery>\n"];
308
+ [xmlString appendString:@"</D:prop>"];
309
+
310
+ [_headers setObject:@"application/xml; charset=\"utf-8\"" forKey:@"Content-Type"];
311
+ _data = [xmlString dataUsingEncoding:NSUTF8StringEncoding];
312
+ _status = 200;
313
+ HTTPLogVerbose(@"Pretending to lock \"%@\"", resourcePath);
314
+ } else {
315
+ HTTPLogError(@"Locking request \"%@/%@/%@\" for \"%@\" is not allowed", scope, type, depth, resourcePath);
316
+ _status = 403;
317
+ }
318
+ }
319
+
320
+ // 9.11 UNLOCK Method - TODO: Actually unlock the resource
321
+ if ([method isEqualToString:@"UNLOCK"]) {
322
+ NSString* path = [rootPath stringByAppendingPathComponent:resourcePath];
323
+ if (![path hasPrefix:rootPath] || ![[NSFileManager defaultManager] fileExistsAtPath:path]) {
324
+ return nil;
325
+ }
326
+
327
+ NSString* token = [headers objectForKey:@"Lock-Token"];
328
+ _status = token ? 204 : 400;
329
+ HTTPLogVerbose(@"Pretending to unlock \"%@\"", resourcePath);
330
+ }
331
+
332
+ }
333
+ return self;
334
+ }
335
+
336
+
337
+ - (UInt64) contentLength {
338
+ return _data ? _data.length : 0;
339
+ }
340
+
341
+ - (UInt64) offset {
342
+ return _offset;
343
+ }
344
+
345
+ - (void) setOffset:(UInt64)offset {
346
+ _offset = offset;
347
+ }
348
+
349
+ - (NSData*) readDataOfLength:(NSUInteger)lengthParameter {
350
+ if (_data) {
351
+ NSUInteger remaining = _data.length - (NSUInteger)_offset;
352
+ NSUInteger length = lengthParameter < remaining ? lengthParameter : remaining;
353
+ void* bytes = (void*)(_data.bytes + _offset);
354
+ _offset += length;
355
+ return [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:NO];
356
+ }
357
+ return nil;
358
+ }
359
+
360
+ - (BOOL) isDone {
361
+ return _data ? _offset == _data.length : YES;
362
+ }
363
+
364
+ - (NSInteger) status {
365
+ return _status;
366
+ }
367
+
368
+ - (NSDictionary*) httpHeaders {
369
+ return _headers;
370
+ }
371
+
372
+ @end
@@ -0,0 +1,7 @@
1
+ #import "HTTPResponse.h"
2
+
3
+ @interface DELETEResponse : NSObject <HTTPResponse> {
4
+ NSInteger _status;
5
+ }
6
+ - (id) initWithFilePath:(NSString*)path;
7
+ @end
@@ -0,0 +1,49 @@
1
+ #import "DELETEResponse.h"
2
+ #import "HTTPLogging.h"
3
+
4
+ // HTTP methods: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
5
+ // HTTP headers: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
6
+ // HTTP status codes: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
7
+
8
+ static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
9
+
10
+ @implementation DELETEResponse
11
+
12
+ - (id) initWithFilePath:(NSString*)path {
13
+ if ((self = [super init])) {
14
+ BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path];
15
+ if ([[NSFileManager defaultManager] removeItemAtPath:path error:NULL]) {
16
+ _status = exists ? 200 : 204;
17
+ } else {
18
+ HTTPLogError(@"Failed deleting \"%@\"", path);
19
+ _status = 404;
20
+ }
21
+ }
22
+ return self;
23
+ }
24
+
25
+ - (UInt64) contentLength {
26
+ return 0;
27
+ }
28
+
29
+ - (UInt64) offset {
30
+ return 0;
31
+ }
32
+
33
+ - (void)setOffset:(UInt64)offset {
34
+ ;
35
+ }
36
+
37
+ - (NSData*) readDataOfLength:(NSUInteger)length {
38
+ return nil;
39
+ }
40
+
41
+ - (BOOL) isDone {
42
+ return YES;
43
+ }
44
+
45
+ - (NSInteger) status {
46
+ return _status;
47
+ }
48
+
49
+ @end
@@ -0,0 +1,8 @@
1
+ #import "HTTPResponse.h"
2
+
3
+ @interface PUTResponse : NSObject <HTTPResponse> {
4
+ NSInteger _status;
5
+ }
6
+ - (id) initWithFilePath:(NSString*)path headers:(NSDictionary*)headers bodyData:(NSData*)body;
7
+ - (id) initWithFilePath:(NSString*)path headers:(NSDictionary*)headers bodyFile:(NSString*)body;
8
+ @end
@@ -0,0 +1,69 @@
1
+ #import "PUTResponse.h"
2
+ #import "HTTPLogging.h"
3
+
4
+ // HTTP methods: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
5
+ // HTTP headers: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
6
+ // HTTP status codes: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
7
+
8
+ static const int httpLogLevel = HTTP_LOG_LEVEL_WARN;
9
+
10
+ @implementation PUTResponse
11
+
12
+ - (id) initWithFilePath:(NSString*)path headers:(NSDictionary*)headers body:(id)body {
13
+ if ((self = [super init])) {
14
+ if ([headers objectForKey:@"Content-Range"]) {
15
+ HTTPLogError(@"Content-Range not supported for upload to \"%@\"", path);
16
+ _status = 400;
17
+ } else {
18
+ BOOL overwrite = [[NSFileManager defaultManager] fileExistsAtPath:path];
19
+ BOOL success;
20
+ if ([body isKindOfClass:[NSString class]]) {
21
+ [[NSFileManager defaultManager] removeItemAtPath:path error:NULL];
22
+ success = [[NSFileManager defaultManager] moveItemAtPath:body toPath:path error:NULL];
23
+ } else {
24
+ success = [body writeToFile:path atomically:YES];
25
+ }
26
+ if (success) {
27
+ _status = overwrite ? 200 : 201;
28
+ } else {
29
+ HTTPLogError(@"Failed writing upload to \"%@\"", path);
30
+ _status = 403;
31
+ }
32
+ }
33
+ }
34
+ return self;
35
+ }
36
+
37
+ - (id) initWithFilePath:(NSString*)path headers:(NSDictionary*)headers bodyData:(NSData*)body {
38
+ return [self initWithFilePath:path headers:headers body:body];
39
+ }
40
+
41
+ - (id) initWithFilePath:(NSString*)path headers:(NSDictionary*)headers bodyFile:(NSString*)body {
42
+ return [self initWithFilePath:path headers:headers body:body];
43
+ }
44
+
45
+ - (UInt64) contentLength {
46
+ return 0;
47
+ }
48
+
49
+ - (UInt64) offset {
50
+ return 0;
51
+ }
52
+
53
+ - (void) setOffset:(UInt64)offset {
54
+ ;
55
+ }
56
+
57
+ - (NSData*) readDataOfLength:(NSUInteger)length {
58
+ return nil;
59
+ }
60
+
61
+ - (BOOL) isDone {
62
+ return YES;
63
+ }
64
+
65
+ - (NSInteger) status {
66
+ return _status;
67
+ }
68
+
69
+ @end