]> git.saurik.com Git - apple/cf.git/blob - CFURLAccess.c
CF-855.14.tar.gz
[apple/cf.git] / CFURLAccess.c
1 /*
2 * Copyright (c) 2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /* CFURLAccess.c
25 Copyright (c) 1999-2013, Apple Inc. All rights reserved.
26 Responsibility: Chris Linn
27 */
28
29 /*------
30 CFData read/write routines
31 -------*/
32
33 #pragma GCC diagnostic push
34 #pragma GCC diagnostic ignored "-Wdeprecated"
35
36 #include "CFInternal.h"
37 #include <CoreFoundation/CFBase.h>
38 #include <CoreFoundation/CFURL.h>
39 #include <CoreFoundation/CFDictionary.h>
40 #include <CoreFoundation/CFURLAccess.h>
41 #include <CoreFoundation/CFDate.h>
42 #include <CoreFoundation/CFNumber.h>
43 #include <string.h>
44 #include <ctype.h>
45 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
46 #include <stdlib.h>
47 #include <unistd.h>
48 #include <dirent.h>
49 #include <sys/stat.h>
50 #include <sys/types.h>
51 #include <pwd.h>
52 #include <fcntl.h>
53 #elif DEPLOYMENT_TARGET_WINDOWS
54 #include <io.h>
55 #include <sys/stat.h>
56 #include <sys/types.h>
57 #include <fcntl.h>
58 #include <ctype.h>
59 #else
60 #error Unknown or unspecified DEPLOYMENT_TARGET
61 #endif
62 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
63 #include <dlfcn.h>
64 #endif
65
66 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
67
68
69 DEFINE_WEAK_CFNETWORK_FUNC_FAIL(Boolean, _CFURLCreateDataAndPropertiesFromResource, (CFAllocatorRef A, CFURLRef B, CFDataRef *C, CFDictionaryRef *D, CFArrayRef E, SInt32 *F), (A, B, C, D, E, F), { if(C) *C=NULL; if (D) *D=NULL; if(F) *F=kCFURLImproperArgumentsError; }, false)
70 DEFINE_WEAK_CFNETWORK_FUNC_FAIL(Boolean, _CFURLWriteDataAndPropertiesToResource, (CFURLRef A, CFDataRef B, CFDictionaryRef C, SInt32 *D), (A, B, C, D), if (D) *D = kCFURLImproperArgumentsError, false)
71
72 DEFINE_WEAK_CFNETWORK_FUNC_FAIL(Boolean, _CFURLDestroyResource, (CFURLRef A, SInt32 *B), (A, B), if(B) *B = kCFURLImproperArgumentsError, false)
73
74 #endif
75
76 typedef struct __NSString__ *NSString;
77
78 /*
79 Pre-10.6 property keys
80 */
81
82 CONST_STRING_DECL(kCFURLFileExists, "kCFURLFileExists")
83 CONST_STRING_DECL(kCFURLFilePOSIXMode, "kCFURLFilePOSIXMode")
84 CONST_STRING_DECL(kCFURLFileDirectoryContents, "kCFURLFileDirectoryContents")
85 CONST_STRING_DECL(kCFURLFileLength, "kCFURLFileLength")
86 CONST_STRING_DECL(kCFURLFileLastModificationTime, "kCFURLFileLastModificationTime")
87 CONST_STRING_DECL(kCFURLFileOwnerID, "kCFURLFileOwnerID")
88 CONST_STRING_DECL(kCFURLHTTPStatusCode, "kCFURLHTTPStatusCode")
89 CONST_STRING_DECL(kCFURLHTTPStatusLine, "kCFURLHTTPStatusLine")
90
91 CONST_STRING_DECL(kCFDataURLDataLength, "kCFDataURLDataLength")
92 CONST_STRING_DECL(kCFDataURLMimeType, "kCFDataURLMimeType")
93 CONST_STRING_DECL(kCFDataURLTextEncodingName, "kCFDataURLTextEncodingName")
94
95 // Compatibility property strings -- we obsoleted these names pre-DP4. REW, 5/22/2000
96 CONST_STRING_DECL(kCFFileURLExists, "kCFURLFileExists")
97 CONST_STRING_DECL(kCFFileURLPOSIXMode, "kCFURLFilePOSIXMode")
98 CONST_STRING_DECL(kCFFileURLDirectoryContents, "kCFURLFileDirectoryContents")
99 CONST_STRING_DECL(kCFFileURLSize, "kCFURLFileLength")
100 CONST_STRING_DECL(kCFFileURLLastModificationTime, "kCFURLFileLastModificationTime")
101 CONST_STRING_DECL(kCFHTTPURLStatusCode, "kCFURLHTTPStatusCode")
102 CONST_STRING_DECL(kCFHTTPURLStatusLine, "kCFURLHTTPStatusLine")
103
104 // Copied pretty much verbatim from NSData; note that files are still special cased in this code. Ultimately, we probably want to treat file URLs the same way as any other URL (go through the URL Access layer). -- REW, 10/21/98
105
106 /*************************/
107 /* file: access routines */
108 /*************************/
109
110 //#warning CF:For the moment file access failures are ill defined and set the error code to kCFURLUnknownError
111
112 static CFDictionaryRef _CFFileURLCreatePropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFArrayRef desiredProperties, SInt32 *errorCode) {
113 // MF:!!! This could/should be changed to use _CFGetFileProperties() to do the actual figuring.
114 static CFArrayRef _allProps = NULL;
115 CFRange arrayRange;
116 SInt32 idx;
117 CFMutableDictionaryRef propertyDict = NULL;
118
119 Boolean exists;
120 SInt32 posixMode;
121 int64_t size;
122 CFDateRef modTime = NULL, *modTimePtr = NULL;
123 CFArrayRef contents = NULL, *contentsPtr = NULL;
124 SInt32 ownerID;
125
126 if (errorCode) *errorCode = 0;
127 if (!desiredProperties) {
128 // Cheap and dirty hack to make this work for the moment; ultimately we need to do something more sophisticated. This will result in an error return whenever a property key is defined which isn't applicable to all file URLs. REW, 3/2/99
129 if (!_allProps) {
130 const void *values[9];
131 values[0] = kCFURLFileExists;
132 values[1] = kCFURLFilePOSIXMode;
133 values[2] = kCFURLFileDirectoryContents;
134 values[3] = kCFURLFileLength;
135 values[4] = kCFURLFileLastModificationTime;
136 values[5] = kCFURLFileOwnerID;
137 _allProps = CFArrayCreate(kCFAllocatorSystemDefault, values, 6, &kCFTypeArrayCallBacks);
138 }
139 desiredProperties = _allProps;
140 }
141
142 arrayRange.location = 0;
143 arrayRange.length = CFArrayGetCount(desiredProperties);
144 propertyDict = CFDictionaryCreateMutable(alloc, 0, & kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks);
145 if (arrayRange.length == 0) return propertyDict;
146
147 if (CFArrayContainsValue(desiredProperties, arrayRange, kCFURLFileDirectoryContents)) {
148 contentsPtr = &contents;
149 }
150 if (CFArrayContainsValue(desiredProperties, arrayRange, kCFURLFileLastModificationTime)) {
151 modTimePtr = &modTime;
152 }
153
154 if (_CFGetFileProperties(alloc, url, &exists, &posixMode, &size, modTimePtr, &ownerID, contentsPtr) != 0) {
155
156 // If all they've asked for is whether this file exists, then any error will just make
157 // this return kCFURLFileExists = kCFBooleanFalse, which handles the case where the filename is invalid or too long or something.
158 if ( arrayRange.length == 1 && CFArrayContainsValue( desiredProperties, arrayRange, kCFURLFileExists ) ) {
159 CFDictionarySetValue(propertyDict, kCFURLFileExists, kCFBooleanFalse);
160 }
161 else if (errorCode) {
162 *errorCode = kCFURLUnknownError;
163 }
164 return propertyDict;
165 }
166
167 for (idx = 0; idx < arrayRange.length; idx ++) {
168 CFStringRef key = (CFMutableStringRef )CFArrayGetValueAtIndex(desiredProperties, idx);
169 if (key == kCFURLFilePOSIXMode || CFEqual(kCFURLFilePOSIXMode, key)) {
170 if (exists) {
171 CFNumberRef num = CFNumberCreate(alloc, kCFNumberSInt32Type, &posixMode);
172 CFDictionarySetValue(propertyDict, kCFURLFilePOSIXMode, num);
173 CFRelease(num);
174 } else if (errorCode) {
175 *errorCode = kCFURLUnknownError;
176 }
177 } else if (key == kCFURLFileDirectoryContents || CFEqual(kCFURLFileDirectoryContents, key)) {
178 if (exists && (posixMode & S_IFMT) == S_IFDIR && contents) {
179 CFDictionarySetValue(propertyDict, kCFURLFileDirectoryContents, contents);
180 } else if (errorCode) {
181 *errorCode = kCFURLUnknownError;
182 }
183 } else if (key == kCFURLFileLength || CFEqual(kCFURLFileLength, key)) {
184 if (exists) {
185 CFNumberRef num = CFNumberCreate(alloc, kCFNumberSInt64Type, &size);
186 CFDictionarySetValue(propertyDict, kCFURLFileLength, num);
187 CFRelease(num);
188 } else if (errorCode) {
189 *errorCode = kCFURLUnknownError;
190 }
191 } else if (key == kCFURLFileLastModificationTime || CFEqual(kCFURLFileLastModificationTime, key)) {
192 if (exists && modTime) {
193 CFDictionarySetValue(propertyDict, kCFURLFileLastModificationTime, modTime);
194 } else if (errorCode) {
195 *errorCode = kCFURLUnknownError;
196 }
197 } else if (key == kCFURLFileExists || CFEqual(kCFURLFileExists, key)) {
198 if (exists) {
199 CFDictionarySetValue(propertyDict, kCFURLFileExists, kCFBooleanTrue);
200 } else {
201 CFDictionarySetValue(propertyDict, kCFURLFileExists, kCFBooleanFalse);
202 }
203 } else if (key == kCFURLFileOwnerID || CFEqual(kCFURLFileOwnerID, key)) {
204 if (exists) {
205 CFNumberRef num = CFNumberCreate(alloc, kCFNumberSInt32Type, &ownerID);
206 CFDictionarySetValue(propertyDict, kCFURLFileOwnerID, num);
207 CFRelease(num);
208 } else if (errorCode) {
209 *errorCode = kCFURLUnknownError;
210 }
211 // Add more properties here
212 } else if (errorCode) {
213 *errorCode = kCFURLUnknownPropertyKeyError;
214 }
215 }
216 if (modTime) CFRelease(modTime);
217 if (contents) CFRelease(contents);
218 return propertyDict;
219 }
220
221 static Boolean _CFFileURLWritePropertiesToResource(CFURLRef url, CFDictionaryRef propertyDict, SInt32 *errorCode) {
222 CFTypeRef buffer[16];
223 CFTypeRef *keys;
224 CFTypeRef *values;
225 Boolean result = true;
226 SInt32 idx, count;
227 char cPath[CFMaxPathSize];
228
229 if (!CFURLGetFileSystemRepresentation(url, true, (unsigned char *)cPath, CFMaxPathSize)) {
230 if (errorCode) *errorCode = kCFURLImproperArgumentsError;
231 return false;
232 }
233
234 count = CFDictionaryGetCount(propertyDict);
235 if (count < 8) {
236 keys = buffer;
237 values = buffer+8;
238 } else {
239 keys = (CFTypeRef *)CFAllocatorAllocate(CFGetAllocator(url), sizeof(void *) * count * 2, 0);
240 values = keys + count;
241 }
242 CFDictionaryGetKeysAndValues(propertyDict, keys, values);
243
244 for (idx = 0; idx < count; idx ++) {
245 CFStringRef key = (CFStringRef)keys[idx];
246 CFTypeRef value = values[idx];
247 if (key == kCFURLFilePOSIXMode || CFEqual(kCFURLFilePOSIXMode, key)) {
248 SInt32 mode;
249 int err;
250 if (CFNumberGetTypeID() == CFGetTypeID(value)) {
251 CFNumberRef modeNum = (CFNumberRef)value;
252 CFNumberGetValue(modeNum, kCFNumberSInt32Type, &mode);
253 } else {
254 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
255 #define MODE_TYPE mode_t
256 #elif DEPLOYMENT_TARGET_WINDOWS
257 #define MODE_TYPE uint16_t
258 #else
259 #error Unknown or unspecified DEPLOYMENT_TARGET
260 #endif
261 const MODE_TYPE *modePtr = (const MODE_TYPE *)CFDataGetBytePtr((CFDataRef)value);
262 mode = *modePtr;
263 }
264 err = chmod(cPath, mode);
265 if (err != 0) result = false;
266 } else {
267 result = false;
268 }
269 }
270
271 if ((CFTypeRef)keys != buffer) CFAllocatorDeallocate(CFGetAllocator(url), keys);
272
273 if (errorCode) *errorCode = result ? 0 : kCFURLUnknownError;
274 return result;
275 }
276
277 static Boolean _CFFileURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFDataRef *fetchedData, CFArrayRef desiredProperties, CFDictionaryRef *fetchedProperties, SInt32 *errorCode) {
278 Boolean success = true;
279
280 if (errorCode) *errorCode = 0;
281 if (fetchedData) {
282 void *bytes;
283 CFIndex length;
284 Boolean releaseAlloc = false;
285
286 if (alloc == NULL) {
287 // We need a real allocator to pass to _CFReadBytesFromFile so that the CFDataRef we create with
288 // CFDataCreateWithBytesNoCopy() can free up the object _CFReadBytesFromFile() returns.
289 alloc = (CFAllocatorRef)CFRetain(__CFGetDefaultAllocator());
290 releaseAlloc = true;
291 }
292 if (!_CFReadBytesFromFile(alloc, url, &bytes, &length, 0, 0)) {
293 if (errorCode) *errorCode = kCFURLUnknownError;
294 *fetchedData = NULL;
295 success = false;
296 } else {
297 *fetchedData = CFDataCreateWithBytesNoCopy(alloc, (const UInt8 *)bytes, length, alloc);
298 }
299 if (releaseAlloc) {
300 // Now the CFData should be hanging on to it.
301 CFRelease(alloc);
302 }
303 }
304
305 if (fetchedProperties) {
306 *fetchedProperties = _CFFileURLCreatePropertiesFromResource(alloc, url, desiredProperties, errorCode);
307 if (!*fetchedProperties)
308 success = false;
309 }
310
311 if ( ! success && fetchedData && *fetchedData ) {
312 CFRelease( *fetchedData );
313 *fetchedData = NULL;
314 }
315
316 return success;
317 }
318
319 /*
320 * Support for data: URLs - RFC 2397
321 * Currently this is spi for CFNetwork, to make it API, just put these constants in CFURLAccess.h
322 */
323
324 /*
325 CF_EXPORT
326 const CFStringRef kCFDataURLDataLength;
327 CF_EXPORT
328 const CFStringRef kCFDataURLMimeType;
329 CF_EXPORT
330 const CFStringRef kCFDataURLTextEncodingName;
331 */
332
333 /* Properties for the data: scheme. */
334 /* kCFDataURLDataLength is a CFNumber giving the data's length in bytes. */
335 /* kCFDataURLMimeType is a CFString. */
336 /* kCFDataURLTextEncodingName is a CFString. */
337
338 /* REMINDSMZ: From CFURLResponse.c */
339 static CFStringRef mimeTypeFromContentTypeComponent(CFStringRef component) {
340 CFIndex compLen = CFStringGetLength(component);
341 CFStringInlineBuffer buf;
342 CFIndex idx;
343 CFIndex firstChar = -1, lastChar = -1;
344 CFCharacterSetRef whitespaceSet = CFCharacterSetGetPredefined(kCFCharacterSetWhitespace);
345 CFStringInitInlineBuffer(component, &buf, CFRangeMake(0, compLen));
346
347 for (idx = 0; idx < compLen; idx ++) {
348 UniChar ch = CFStringGetCharacterFromInlineBuffer(&buf, idx);
349 if (ch == ';') {
350 // Delimits the charset
351 break;
352 } else if (firstChar == -1) {
353 if (!CFCharacterSetIsCharacterMember(whitespaceSet, ch)) {
354 firstChar = idx;
355 }
356 } else if (!CFCharacterSetIsCharacterMember(whitespaceSet, ch)) {
357 lastChar = idx;
358 }
359 }
360 if (firstChar != -1 && lastChar != -1) {
361 CFMutableStringRef newContentType = CFStringCreateMutableCopy(CFGetAllocator(component), compLen, component);
362 if (lastChar != compLen - 1) {
363 CFStringDelete(newContentType, CFRangeMake(lastChar + 1, compLen - lastChar - 1));
364 }
365 if (firstChar > 0) {
366 CFStringDelete(newContentType, CFRangeMake(0, firstChar));
367 }
368 CFStringLowercase(newContentType, NULL);
369 return newContentType;
370 }
371 return NULL;
372 }
373
374 /* REMINDSMZ: From CFURLResponse.c */
375 static CFStringRef charsetFromContentTypeHeader(CFStringRef contentType) {
376 // FIXME: Should this use KeyValuePair parsing to handle quoting properly?
377 CFRange range;
378 CFIndex compLen = CFStringGetLength(contentType);
379 CFIndex start, end, idx;
380 CFCharacterSetRef whitespaceSet;
381 CFMutableStringRef result;
382
383 CFStringRef kCFURLResponseCharsetPrefix = CFSTR("charset=");
384
385 if (!CFStringFindWithOptions(contentType, kCFURLResponseCharsetPrefix, CFRangeMake(0, compLen), kCFCompareCaseInsensitive, &range) || range.length == 0) return NULL;
386
387 whitespaceSet = CFCharacterSetGetPredefined(kCFCharacterSetWhitespace);
388 start = -1;
389 end = -1;
390 for (idx = range.location + range.length; idx < compLen; idx ++) {
391 UniChar ch = CFStringGetCharacterAtIndex(contentType, idx);
392 if (ch == ';' || ch == ',') break;
393 if (start == -1) {
394 if (!CFCharacterSetIsCharacterMember(whitespaceSet, ch)) {
395 start = idx;
396 end = idx;
397 }
398 } else if (!CFCharacterSetIsCharacterMember(whitespaceSet,ch)) {
399 end = idx;
400 }
401 }
402
403 if (start == -1) return NULL;
404
405 result = CFStringCreateMutableCopy(CFGetAllocator(contentType), compLen,contentType);
406 if (end != compLen) {
407 CFStringDelete(result, CFRangeMake(end+1, compLen-end-1));
408 }
409 CFStringDelete(result, CFRangeMake(0, start));
410 CFStringLowercase(result, NULL);
411 return result;
412 }
413
414 #define STATIC_BUFFER_SIZE 1024
415
416 static BOOL isHexDigit(char c)
417 {
418 return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
419 }
420
421 static UInt8 hexDigitValue(char c)
422 {
423 if (c >= '0' && c <= '9') {
424 return c - '0';
425 }
426 if (c >= 'A' && c <= 'F') {
427 return c - 'A' + 10;
428 }
429 if (c >= 'a' && c <= 'f') {
430 return c - 'a' + 10;
431 }
432 // NSURL_ERROR("illegal hex digit");
433 return 0;
434 }
435
436 static CFDataRef percentEscapeDecodeBuffer(CFAllocatorRef alloc, const UInt8* srcBuffer, CFRange range, Boolean stripWhitespace)
437 {
438 UInt8* dstBuffer;
439 UInt8 staticDstBuffer[STATIC_BUFFER_SIZE];
440
441 if (range.length > STATIC_BUFFER_SIZE) {
442 dstBuffer = (UInt8*) malloc(range.length);
443 } else {
444 dstBuffer = staticDstBuffer;
445 }
446
447 CFIndex end = range.location + range.length;
448
449 CFIndex i;
450 CFIndex j;
451 for (i = range.location, j = 0; i < end; ++i) {
452 char value;
453
454 if (srcBuffer[i] == '%' && end > i + 2 && isHexDigit(srcBuffer[i+1]) && isHexDigit(srcBuffer[i+2])) {
455 value = hexDigitValue(srcBuffer[i+1]) * 16 + hexDigitValue(srcBuffer[i+2]);
456 i += 2;
457 } else {
458 value = srcBuffer[i];
459 }
460
461 if (!stripWhitespace || !isspace(value)) {
462 dstBuffer[j++] = value;
463 }
464 }
465
466 CFDataRef result = CFDataCreate(alloc, dstBuffer, j);
467
468 if (dstBuffer != staticDstBuffer) {
469 free(dstBuffer);
470 }
471
472 return result;
473 }
474
475
476 // base 64 digits: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
477
478 static BOOL isBase64Digit(char c)
479 {
480 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c == '+') || (c == '/');
481 }
482
483 static BOOL isBase64DigitOrEqualSign(char c)
484 {
485 return isBase64Digit(c) || c == '=';
486 }
487
488 static UInt8 base64DigitValue(char c)
489 {
490 if (c >= 'A' && c <= 'Z') {
491 return c - 'A';
492 } else if (c >= 'a' && c <= 'z') {
493 return 26 + c - 'a';
494 } else if (c >= '0' && c <= '9') {
495 return 52 + c - '0';
496 } else if (c == '+') {
497 return 62;
498 } else if (c == '/') {
499 return 63;
500 } else {
501 return 0;
502 }
503 }
504
505 static CFDataRef base64DecodeData(CFAllocatorRef alloc, CFDataRef data)
506 {
507 const UInt8 *srcBuffer = CFDataGetBytePtr(data);
508 CFIndex length = CFDataGetLength(data);
509 UInt8 *dstBuffer = NULL;
510 UInt8 staticDstBuffer[STATIC_BUFFER_SIZE];
511 CFDataRef result = NULL;
512
513 // base64 encoded data length must be multiple of 4
514 if (length % 4 != 0) {
515 goto done;
516 }
517
518 if (length > STATIC_BUFFER_SIZE) {
519 dstBuffer = (UInt8*) malloc(length);
520 } else {
521 dstBuffer = staticDstBuffer;
522 }
523
524 CFIndex i;
525 CFIndex j;
526 for (i = 0, j = 0; i < length; i+=4) {
527 if (!(isBase64Digit(srcBuffer[i]) &&
528 isBase64Digit(srcBuffer[i+1]) &&
529 isBase64DigitOrEqualSign(srcBuffer[i+2]) &&
530 isBase64DigitOrEqualSign(srcBuffer[i+3]))) {
531 if (dstBuffer != staticDstBuffer) {
532 free(dstBuffer);
533 }
534 return NULL;
535 }
536
537 dstBuffer[j++] = (base64DigitValue(srcBuffer[i]) << 2) + (base64DigitValue(srcBuffer[i+1]) >> 4);
538 if (srcBuffer[i+2] != '=') {
539 dstBuffer[j++] = ((base64DigitValue(srcBuffer[i+1]) & 0xf) << 4) + (base64DigitValue(srcBuffer[i+2]) >> 2);
540 }
541 if (srcBuffer[i+3] != '=') {
542 dstBuffer[j++] = ((base64DigitValue(srcBuffer[i+2]) & 0x3) << 6) + (base64DigitValue(srcBuffer[i+3]));
543 }
544 }
545
546 result = CFDataCreate(alloc, dstBuffer, j);
547
548 done:
549 if (dstBuffer != staticDstBuffer) {
550 free(dstBuffer);
551 }
552
553 return result;
554 }
555
556 static inline CFStringRef percentExpandAndTrimContentType(CFAllocatorRef alloc, CFStringRef str, CFRange range)
557 {
558 CFMutableStringRef contentTypeHeader = NULL;
559 CFStringRef contentTypeUnexpanded = CFStringCreateWithSubstring(alloc, str, range);
560 CFStringRef contentTypeExpanded = CFURLCreateStringByReplacingPercentEscapes(alloc, contentTypeUnexpanded, CFSTR(""));
561
562 if (NULL == contentTypeExpanded) {
563 contentTypeHeader = CFStringCreateMutableCopy(alloc, 0, contentTypeUnexpanded);
564 } else {
565 contentTypeHeader = CFStringCreateMutableCopy(alloc, 0, contentTypeExpanded);
566 CFRelease(contentTypeExpanded);
567 }
568 CFRelease(contentTypeUnexpanded);
569 CFStringTrimWhitespace(contentTypeHeader);
570
571 return contentTypeHeader;
572 }
573
574 static Boolean parseDataRequestURL(CFURLRef url, CFDataRef* outData, CFStringRef* outMimeType, CFStringRef* outTextEncodingName)
575 {
576 Boolean result = FALSE;
577 CFAllocatorRef alloc = CFGetAllocator(url);
578 CFStringRef str = CFURLCopyResourceSpecifier(url);
579 if (str != NULL) {
580 CFRange commaRange = CFStringFind(str, CFSTR(","), 0);
581
582 if (commaRange.location != kCFNotFound) {
583 CFStringRef contentTypeHeader = percentExpandAndTrimContentType(alloc, str, CFRangeMake(0, commaRange.location));
584 CFStringRef mimeType = mimeTypeFromContentTypeComponent(contentTypeHeader);
585 CFStringRef textEncodingName = charsetFromContentTypeHeader(contentTypeHeader);
586
587 Boolean base64 = CFStringFind(contentTypeHeader, CFSTR(";base64"), kCFCompareCaseInsensitive).location != kCFNotFound;
588
589 if (mimeType == NULL) {
590 mimeType = (CFStringRef) CFRetain(CFSTR("text/plain"));
591 }
592
593 CFIndex bufferSize = CFURLGetBytes(url, NULL, 0);
594 UInt8* srcBuffer = (UInt8*) malloc(bufferSize);
595 CFURLGetBytes(url, srcBuffer, bufferSize);
596
597 CFRange dataRange = CFURLGetByteRangeForComponent(url, kCFURLComponentResourceSpecifier, NULL);
598 while (srcBuffer[dataRange.location] != ',') {
599 dataRange.location++;
600 dataRange.length--;
601 }
602 dataRange.location++;
603 dataRange.length--;
604
605 CFDataRef dataRef = NULL;
606
607 if (! base64) {
608 dataRef = percentEscapeDecodeBuffer(alloc, srcBuffer, dataRange, false);
609 } else {
610 CFDataRef unescapedAndStripped = percentEscapeDecodeBuffer(alloc, srcBuffer, dataRange, true);
611 if (unescapedAndStripped) {
612 dataRef = base64DecodeData(alloc, unescapedAndStripped);
613 CFRelease(unescapedAndStripped);
614 }
615 }
616
617 if (dataRef != NULL) {
618 *outData = dataRef;
619 *outMimeType = (CFStringRef) mimeType == NULL? NULL : CFStringCreateCopy(alloc, mimeType);
620 *outTextEncodingName = (CFStringRef) textEncodingName == NULL? NULL : CFStringCreateCopy(alloc, textEncodingName);
621 result = true;
622 }
623
624 free(srcBuffer);
625
626 if (contentTypeHeader) CFRelease(contentTypeHeader);
627 if (mimeType) CFRelease(mimeType);
628 if (textEncodingName) CFRelease(textEncodingName);
629 }
630
631 CFRelease(str);
632 }
633
634 return result;
635 }
636
637 static Boolean _CFDataURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFDataRef *fetchedData, CFArrayRef desiredProperties, CFDictionaryRef *fetchedProperties, SInt32 *errorCode) {
638 Boolean success = true;
639
640 if (errorCode) *errorCode = 0;
641
642 // We always need to fetch the data...
643 CFDataRef data = NULL;
644 CFStringRef mimeType = NULL;
645 CFStringRef textEncodingName = NULL;
646
647 if (! parseDataRequestURL(url, &data, &mimeType, &textEncodingName)) {
648 if (errorCode)
649 *errorCode = kCFURLUnknownError;
650 *fetchedData = NULL;
651 success = false;
652 } else {
653 if (fetchedData) {
654 *fetchedData = CFDataCreateCopy(alloc, data);
655 }
656
657 if (fetchedProperties) {
658 const void* propKeys[] = {
659 kCFDataURLDataLength,
660 kCFDataURLMimeType,
661 kCFDataURLTextEncodingName,
662 };
663 const CFIndex propKeysCount = sizeof(propKeys) / sizeof(propKeys[0]);
664
665 if (desiredProperties == NULL) {
666 static CFArrayRef sAllProps = NULL;
667 if (sAllProps == NULL) {
668 sAllProps = CFArrayCreate(kCFAllocatorSystemDefault, propKeys, propKeysCount, &kCFTypeArrayCallBacks);
669 }
670 desiredProperties = sAllProps;
671 }
672
673 const void* vals[propKeysCount];
674 const void* keys[propKeysCount];
675 int ixVal = 0;
676
677 CFIndex count = CFArrayGetCount(desiredProperties);
678 for (CFIndex i = 0; i < count; i++) {
679 CFStringRef key = (CFStringRef) CFArrayGetValueAtIndex(desiredProperties, i);
680
681 if (CFEqual(key, kCFDataURLDataLength)) {
682 CFIndex len = CFDataGetLength(data);
683 keys[ixVal] = key;
684 vals[ixVal++] = CFNumberCreate(alloc, kCFNumberCFIndexType, &len);
685 } else if (CFEqual(key, kCFDataURLMimeType)) {
686 if (mimeType != NULL) {
687 keys[ixVal] = key;
688 vals[ixVal++] = CFStringCreateCopy(alloc, mimeType);
689 }
690 } else if (CFEqual(key, kCFDataURLTextEncodingName)) {
691 if (textEncodingName != NULL) {
692 keys[ixVal] = key;
693 vals[ixVal++] = CFStringCreateCopy(alloc, textEncodingName);
694 }
695 }
696 }
697
698 *fetchedProperties = CFDictionaryCreate(alloc, keys, vals, ixVal, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
699 for (CFIndex i = 0; i < ixVal; i++)
700 CFRelease(vals[i]);
701 if (*fetchedProperties == NULL)
702 success = false;
703 }
704
705 if (data) CFRelease(data);
706 if (mimeType) CFRelease(mimeType);
707 if (textEncodingName) CFRelease(textEncodingName);
708 }
709
710
711 return success;
712 }
713
714 /*************************/
715 /* Public routines */
716 /*************************/
717
718 Boolean CFURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFDataRef *fetchedData, CFDictionaryRef *fetchedProperties, CFArrayRef desiredProperties, SInt32 *errorCode) {
719 CFStringRef scheme = CFURLCopyScheme(url);
720
721 if (!scheme) {
722 if (errorCode) *errorCode = kCFURLImproperArgumentsError;
723 if (fetchedData) *fetchedData = NULL;
724 if (fetchedProperties) *fetchedProperties = NULL;
725 return false;
726 } else {
727 Boolean result;
728 if (CFStringCompare(scheme, CFSTR("file"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
729 result = _CFFileURLCreateDataAndPropertiesFromResource(alloc, url, fetchedData, desiredProperties, fetchedProperties, errorCode);
730 } else if (CFStringCompare(scheme, CFSTR("data"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
731 result = _CFDataURLCreateDataAndPropertiesFromResource(alloc, url, fetchedData, desiredProperties, fetchedProperties, errorCode);
732 } else {
733 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
734 result = __CFNetwork__CFURLCreateDataAndPropertiesFromResource(alloc, url, fetchedData, fetchedProperties, desiredProperties, errorCode);
735 #else
736 if (fetchedData) *fetchedData = NULL;
737 if (fetchedProperties) *fetchedProperties = NULL;
738 if (errorCode) *errorCode = kCFURLUnknownSchemeError;
739 result = false;
740 #endif
741 }
742 CFRelease(scheme);
743 return result;
744 }
745 }
746
747 CFTypeRef CFURLCreatePropertyFromResource(CFAllocatorRef alloc, CFURLRef url, CFStringRef property, SInt32 *errorCode) {
748 CFArrayRef array = CFArrayCreate(alloc, (const void **)&property, 1, &kCFTypeArrayCallBacks);
749 CFDictionaryRef dict;
750
751 if (CFURLCreateDataAndPropertiesFromResource(alloc, url, NULL, &dict, array, errorCode)) {
752 CFTypeRef result = CFDictionaryGetValue(dict, property);
753 if (result) CFRetain(result);
754 CFRelease(array);
755 CFRelease(dict);
756 return result;
757 } else {
758 if (dict) CFRelease(dict);
759 CFRelease(array);
760 return NULL;
761 }
762 }
763
764 Boolean CFURLWriteDataAndPropertiesToResource(CFURLRef url, CFDataRef data, CFDictionaryRef propertyDict, SInt32 *errorCode) {
765 CFStringRef scheme = CFURLCopyScheme(url);
766 if (!scheme) {
767 if (errorCode) *errorCode = kCFURLImproperArgumentsError;
768 return false;
769 } else if (CFStringCompare(scheme, CFSTR("file"), 0) == kCFCompareEqualTo) {
770 Boolean success = true;
771 CFRelease(scheme);
772 if (errorCode) *errorCode = 0;
773 if (data) {
774 if (CFURLHasDirectoryPath(url)) {
775 // Create a directory
776 char cPath[CFMaxPathSize];
777 if (!CFURLGetFileSystemRepresentation(url, true, (unsigned char *)cPath, CFMaxPathSize))
778 {
779 if (errorCode) *errorCode = kCFURLImproperArgumentsError;
780 success = false;
781 } else {
782 success = _CFCreateDirectory(cPath);
783 if (!success && errorCode) *errorCode = kCFURLUnknownError;
784 }
785 } else {
786 // Write data
787 SInt32 length = CFDataGetLength(data);
788 const void *bytes = (0 == length) ? (const void *)"" : CFDataGetBytePtr(data);
789 success = _CFWriteBytesToFile(url, bytes, length);
790 if (!success && errorCode) *errorCode = kCFURLUnknownError;
791 }
792 }
793 if (propertyDict) {
794 if (!_CFFileURLWritePropertiesToResource(url, propertyDict, errorCode))
795 success = false;
796 }
797 return success;
798 } else {
799 CFRelease(scheme);
800 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
801 Boolean result = __CFNetwork__CFURLWriteDataAndPropertiesToResource(url, data, propertyDict, errorCode);
802 if (!result) {
803 if (errorCode) *errorCode = kCFURLUnknownSchemeError;
804 }
805 return result;
806 #else
807 if (errorCode) *errorCode = kCFURLUnknownSchemeError;
808 return false;
809 #endif
810 }
811 }
812
813 Boolean CFURLDestroyResource(CFURLRef url, SInt32 *errorCode) {
814 CFStringRef scheme = CFURLCopyScheme(url);
815 char cPath[CFMaxPathSize];
816
817 if (!scheme) {
818 if (errorCode) *errorCode = kCFURLImproperArgumentsError;
819 return false;
820 } else if (CFStringCompare(scheme, CFSTR("file"), 0) == kCFCompareEqualTo) {
821 CFRelease(scheme);
822 if (!CFURLGetFileSystemRepresentation(url, true, (unsigned char *)cPath, CFMaxPathSize)) {
823 if (errorCode) *errorCode = kCFURLImproperArgumentsError;
824 return false;
825 }
826
827 if (CFURLHasDirectoryPath(url)) {
828 if (_CFRemoveDirectory(cPath)) {
829 if (errorCode) *errorCode = 0;
830 return true;
831 } else {
832 if (errorCode) *errorCode = kCFURLUnknownError;
833 return false;
834 }
835 } else {
836 if (_CFDeleteFile(cPath)) {
837 if (errorCode) *errorCode = 0;
838 return true;
839 } else {
840 if (errorCode) *errorCode = kCFURLUnknownError;
841 return false;
842 }
843 }
844 } else {
845 CFRelease(scheme);
846 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
847 Boolean result = __CFNetwork__CFURLDestroyResource(url, errorCode);
848 if (!result) {
849 if (errorCode) *errorCode = kCFURLUnknownSchemeError;
850 }
851 return result;
852 #else
853 if (errorCode) *errorCode = kCFURLUnknownSchemeError;
854 return false;
855 #endif
856 }
857 }
858 #pragma GCC diagnostic pop
859
860