2 * Copyright (c) 2012 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 Copyright (c) 1999-2012, Apple Inc. All rights reserved.
26 Responsibility: Chris Linn
30 CFData read/write routines
33 #include "CFInternal.h"
34 #include <CoreFoundation/CFBase.h>
35 #include <CoreFoundation/CFURL.h>
36 #include <CoreFoundation/CFDictionary.h>
37 #include <CoreFoundation/CFURLAccess.h>
38 #include <CoreFoundation/CFDate.h>
39 #include <CoreFoundation/CFNumber.h>
42 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
47 #include <sys/types.h>
50 #elif DEPLOYMENT_TARGET_WINDOWS
53 #include <sys/types.h>
57 #error Unknown or unspecified DEPLOYMENT_TARGET
59 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
63 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
66 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)
67 DEFINE_WEAK_CFNETWORK_FUNC_FAIL(Boolean
, _CFURLWriteDataAndPropertiesToResource
, (CFURLRef A
, CFDataRef B
, CFDictionaryRef C
, SInt32
*D
), (A
, B
, C
, D
), if (D
) *D
= kCFURLImproperArgumentsError
, false)
69 DEFINE_WEAK_CFNETWORK_FUNC_FAIL(Boolean
, _CFURLDestroyResource
, (CFURLRef A
, SInt32
*B
), (A
, B
), if(B
) *B
= kCFURLImproperArgumentsError
, false)
73 typedef struct __NSString__
*NSString
;
76 Pre-10.6 property keys
79 CONST_STRING_DECL(kCFURLFileExists
, "kCFURLFileExists")
80 CONST_STRING_DECL(kCFURLFilePOSIXMode
, "kCFURLFilePOSIXMode")
81 CONST_STRING_DECL(kCFURLFileDirectoryContents
, "kCFURLFileDirectoryContents")
82 CONST_STRING_DECL(kCFURLFileLength
, "kCFURLFileLength")
83 CONST_STRING_DECL(kCFURLFileLastModificationTime
, "kCFURLFileLastModificationTime")
84 CONST_STRING_DECL(kCFURLFileOwnerID
, "kCFURLFileOwnerID")
85 CONST_STRING_DECL(kCFURLHTTPStatusCode
, "kCFURLHTTPStatusCode")
86 CONST_STRING_DECL(kCFURLHTTPStatusLine
, "kCFURLHTTPStatusLine")
88 CONST_STRING_DECL(kCFDataURLDataLength
, "kCFDataURLDataLength")
89 CONST_STRING_DECL(kCFDataURLMimeType
, "kCFDataURLMimeType")
90 CONST_STRING_DECL(kCFDataURLTextEncodingName
, "kCFDataURLTextEncodingName")
92 // Compatibility property strings -- we obsoleted these names pre-DP4. REW, 5/22/2000
93 CONST_STRING_DECL(kCFFileURLExists
, "kCFURLFileExists")
94 CONST_STRING_DECL(kCFFileURLPOSIXMode
, "kCFURLFilePOSIXMode")
95 CONST_STRING_DECL(kCFFileURLDirectoryContents
, "kCFURLFileDirectoryContents")
96 CONST_STRING_DECL(kCFFileURLSize
, "kCFURLFileLength")
97 CONST_STRING_DECL(kCFFileURLLastModificationTime
, "kCFURLFileLastModificationTime")
98 CONST_STRING_DECL(kCFHTTPURLStatusCode
, "kCFURLHTTPStatusCode")
99 CONST_STRING_DECL(kCFHTTPURLStatusLine
, "kCFURLHTTPStatusLine")
101 // 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
103 /*************************/
104 /* file: access routines */
105 /*************************/
107 //#warning CF:For the moment file access failures are ill defined and set the error code to kCFURLUnknownError
109 static CFDictionaryRef
_CFFileURLCreatePropertiesFromResource(CFAllocatorRef alloc
, CFURLRef url
, CFArrayRef desiredProperties
, SInt32
*errorCode
) {
110 // MF:!!! This could/should be changed to use _CFGetFileProperties() to do the actual figuring.
111 static CFArrayRef _allProps
= NULL
;
114 CFMutableDictionaryRef propertyDict
= NULL
;
119 CFDateRef modTime
= NULL
, *modTimePtr
= NULL
;
120 CFArrayRef contents
= NULL
, *contentsPtr
= NULL
;
123 if (errorCode
) *errorCode
= 0;
124 if (!desiredProperties
) {
125 // 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
127 const void *values
[9];
128 values
[0] = kCFURLFileExists
;
129 values
[1] = kCFURLFilePOSIXMode
;
130 values
[2] = kCFURLFileDirectoryContents
;
131 values
[3] = kCFURLFileLength
;
132 values
[4] = kCFURLFileLastModificationTime
;
133 values
[5] = kCFURLFileOwnerID
;
134 _allProps
= CFArrayCreate(kCFAllocatorSystemDefault
, values
, 6, &kCFTypeArrayCallBacks
);
136 desiredProperties
= _allProps
;
139 arrayRange
.location
= 0;
140 arrayRange
.length
= CFArrayGetCount(desiredProperties
);
141 propertyDict
= CFDictionaryCreateMutable(alloc
, 0, & kCFTypeDictionaryKeyCallBacks
, & kCFTypeDictionaryValueCallBacks
);
142 if (arrayRange
.length
== 0) return propertyDict
;
144 if (CFArrayContainsValue(desiredProperties
, arrayRange
, kCFURLFileDirectoryContents
)) {
145 contentsPtr
= &contents
;
147 if (CFArrayContainsValue(desiredProperties
, arrayRange
, kCFURLFileLastModificationTime
)) {
148 modTimePtr
= &modTime
;
151 if (_CFGetFileProperties(alloc
, url
, &exists
, &posixMode
, &size
, modTimePtr
, &ownerID
, contentsPtr
) != 0) {
153 // If all they've asked for is whether this file exists, then any error will just make
154 // this return kCFURLFileExists = kCFBooleanFalse, which handles the case where the filename is invalid or too long or something.
155 if ( arrayRange
.length
== 1 && CFArrayContainsValue( desiredProperties
, arrayRange
, kCFURLFileExists
) ) {
156 CFDictionarySetValue(propertyDict
, kCFURLFileExists
, kCFBooleanFalse
);
158 else if (errorCode
) {
159 *errorCode
= kCFURLUnknownError
;
164 for (idx
= 0; idx
< arrayRange
.length
; idx
++) {
165 CFStringRef key
= (CFMutableStringRef
)CFArrayGetValueAtIndex(desiredProperties
, idx
);
166 if (key
== kCFURLFilePOSIXMode
|| CFEqual(kCFURLFilePOSIXMode
, key
)) {
168 CFNumberRef num
= CFNumberCreate(alloc
, kCFNumberSInt32Type
, &posixMode
);
169 CFDictionarySetValue(propertyDict
, kCFURLFilePOSIXMode
, num
);
171 } else if (errorCode
) {
172 *errorCode
= kCFURLUnknownError
;
174 } else if (key
== kCFURLFileDirectoryContents
|| CFEqual(kCFURLFileDirectoryContents
, key
)) {
175 if (exists
&& (posixMode
& S_IFMT
) == S_IFDIR
&& contents
) {
176 CFDictionarySetValue(propertyDict
, kCFURLFileDirectoryContents
, contents
);
177 } else if (errorCode
) {
178 *errorCode
= kCFURLUnknownError
;
180 } else if (key
== kCFURLFileLength
|| CFEqual(kCFURLFileLength
, key
)) {
182 CFNumberRef num
= CFNumberCreate(alloc
, kCFNumberSInt64Type
, &size
);
183 CFDictionarySetValue(propertyDict
, kCFURLFileLength
, num
);
185 } else if (errorCode
) {
186 *errorCode
= kCFURLUnknownError
;
188 } else if (key
== kCFURLFileLastModificationTime
|| CFEqual(kCFURLFileLastModificationTime
, key
)) {
189 if (exists
&& modTime
) {
190 CFDictionarySetValue(propertyDict
, kCFURLFileLastModificationTime
, modTime
);
191 } else if (errorCode
) {
192 *errorCode
= kCFURLUnknownError
;
194 } else if (key
== kCFURLFileExists
|| CFEqual(kCFURLFileExists
, key
)) {
196 CFDictionarySetValue(propertyDict
, kCFURLFileExists
, kCFBooleanTrue
);
198 CFDictionarySetValue(propertyDict
, kCFURLFileExists
, kCFBooleanFalse
);
200 } else if (key
== kCFURLFileOwnerID
|| CFEqual(kCFURLFileOwnerID
, key
)) {
202 CFNumberRef num
= CFNumberCreate(alloc
, kCFNumberSInt32Type
, &ownerID
);
203 CFDictionarySetValue(propertyDict
, kCFURLFileOwnerID
, num
);
205 } else if (errorCode
) {
206 *errorCode
= kCFURLUnknownError
;
208 // Add more properties here
209 } else if (errorCode
) {
210 *errorCode
= kCFURLUnknownPropertyKeyError
;
213 if (modTime
) CFRelease(modTime
);
214 if (contents
) CFRelease(contents
);
218 static Boolean
_CFFileURLWritePropertiesToResource(CFURLRef url
, CFDictionaryRef propertyDict
, SInt32
*errorCode
) {
219 CFTypeRef buffer
[16];
222 Boolean result
= true;
224 char cPath
[CFMaxPathSize
];
226 if (!CFURLGetFileSystemRepresentation(url
, true, (unsigned char *)cPath
, CFMaxPathSize
)) {
227 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
231 count
= CFDictionaryGetCount(propertyDict
);
236 keys
= (CFTypeRef
*)CFAllocatorAllocate(CFGetAllocator(url
), sizeof(void *) * count
* 2, 0);
237 values
= keys
+ count
;
239 CFDictionaryGetKeysAndValues(propertyDict
, keys
, values
);
241 for (idx
= 0; idx
< count
; idx
++) {
242 CFStringRef key
= (CFStringRef
)keys
[idx
];
243 CFTypeRef value
= values
[idx
];
244 if (key
== kCFURLFilePOSIXMode
|| CFEqual(kCFURLFilePOSIXMode
, key
)) {
247 if (CFNumberGetTypeID() == CFGetTypeID(value
)) {
248 CFNumberRef modeNum
= (CFNumberRef
)value
;
249 CFNumberGetValue(modeNum
, kCFNumberSInt32Type
, &mode
);
251 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
252 #define MODE_TYPE mode_t
253 #elif DEPLOYMENT_TARGET_WINDOWS
254 #define MODE_TYPE uint16_t
256 #error Unknown or unspecified DEPLOYMENT_TARGET
258 const MODE_TYPE
*modePtr
= (const MODE_TYPE
*)CFDataGetBytePtr((CFDataRef
)value
);
261 err
= chmod(cPath
, mode
);
262 if (err
!= 0) result
= false;
268 if ((CFTypeRef
)keys
!= buffer
) CFAllocatorDeallocate(CFGetAllocator(url
), keys
);
270 if (errorCode
) *errorCode
= result
? 0 : kCFURLUnknownError
;
274 static Boolean
_CFFileURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc
, CFURLRef url
, CFDataRef
*fetchedData
, CFArrayRef desiredProperties
, CFDictionaryRef
*fetchedProperties
, SInt32
*errorCode
) {
275 Boolean success
= true;
277 if (errorCode
) *errorCode
= 0;
281 Boolean releaseAlloc
= false;
284 // We need a real allocator to pass to _CFReadBytesFromFile so that the CFDataRef we create with
285 // CFDataCreateWithBytesNoCopy() can free up the object _CFReadBytesFromFile() returns.
286 alloc
= (CFAllocatorRef
)CFRetain(__CFGetDefaultAllocator());
289 if (!_CFReadBytesFromFile(alloc
, url
, &bytes
, &length
, 0, 0)) {
290 if (errorCode
) *errorCode
= kCFURLUnknownError
;
294 *fetchedData
= CFDataCreateWithBytesNoCopy(alloc
, (const UInt8
*)bytes
, length
, alloc
);
297 // Now the CFData should be hanging on to it.
302 if (fetchedProperties
) {
303 *fetchedProperties
= _CFFileURLCreatePropertiesFromResource(alloc
, url
, desiredProperties
, errorCode
);
304 if (!*fetchedProperties
)
308 if ( ! success
&& fetchedData
&& *fetchedData
) {
309 CFRelease( *fetchedData
);
317 * Support for data: URLs - RFC 2397
318 * Currently this is spi for CFNetwork, to make it API, just put these constants in CFURLAccess.h
323 const CFStringRef kCFDataURLDataLength;
325 const CFStringRef kCFDataURLMimeType;
327 const CFStringRef kCFDataURLTextEncodingName;
330 /* Properties for the data: scheme. */
331 /* kCFDataURLDataLength is a CFNumber giving the data's length in bytes. */
332 /* kCFDataURLMimeType is a CFString. */
333 /* kCFDataURLTextEncodingName is a CFString. */
335 /* REMINDSMZ: From CFURLResponse.c */
336 static CFStringRef
mimeTypeFromContentTypeComponent(CFStringRef component
) {
337 CFIndex compLen
= CFStringGetLength(component
);
338 CFStringInlineBuffer buf
;
340 CFIndex firstChar
= -1, lastChar
= -1;
341 CFCharacterSetRef whitespaceSet
= CFCharacterSetGetPredefined(kCFCharacterSetWhitespace
);
342 CFStringInitInlineBuffer(component
, &buf
, CFRangeMake(0, compLen
));
344 for (idx
= 0; idx
< compLen
; idx
++) {
345 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, idx
);
347 // Delimits the charset
349 } else if (firstChar
== -1) {
350 if (!CFCharacterSetIsCharacterMember(whitespaceSet
, ch
)) {
353 } else if (!CFCharacterSetIsCharacterMember(whitespaceSet
, ch
)) {
357 if (firstChar
!= -1 && lastChar
!= -1) {
358 CFMutableStringRef newContentType
= CFStringCreateMutableCopy(CFGetAllocator(component
), compLen
, component
);
359 if (lastChar
!= compLen
- 1) {
360 CFStringDelete(newContentType
, CFRangeMake(lastChar
+ 1, compLen
- lastChar
- 1));
363 CFStringDelete(newContentType
, CFRangeMake(0, firstChar
));
365 CFStringLowercase(newContentType
, NULL
);
366 return newContentType
;
371 /* REMINDSMZ: From CFURLResponse.c */
372 static CFStringRef
charsetFromContentTypeHeader(CFStringRef contentType
) {
373 // FIXME: Should this use KeyValuePair parsing to handle quoting properly?
375 CFIndex compLen
= CFStringGetLength(contentType
);
376 CFIndex start
, end
, idx
;
377 CFCharacterSetRef whitespaceSet
;
378 CFMutableStringRef result
;
380 CFStringRef kCFURLResponseCharsetPrefix
= CFSTR("charset=");
382 if (!CFStringFindWithOptions(contentType
, kCFURLResponseCharsetPrefix
, CFRangeMake(0, compLen
), kCFCompareCaseInsensitive
, &range
) || range
.length
== 0) return NULL
;
384 whitespaceSet
= CFCharacterSetGetPredefined(kCFCharacterSetWhitespace
);
387 for (idx
= range
.location
+ range
.length
; idx
< compLen
; idx
++) {
388 UniChar ch
= CFStringGetCharacterAtIndex(contentType
, idx
);
389 if (ch
== ';' || ch
== ',') break;
391 if (!CFCharacterSetIsCharacterMember(whitespaceSet
, ch
)) {
395 } else if (!CFCharacterSetIsCharacterMember(whitespaceSet
,ch
)) {
400 if (start
== -1) return NULL
;
402 result
= CFStringCreateMutableCopy(CFGetAllocator(contentType
), compLen
,contentType
);
403 if (end
!= compLen
) {
404 CFStringDelete(result
, CFRangeMake(end
+1, compLen
-end
-1));
406 CFStringDelete(result
, CFRangeMake(0, start
));
407 CFStringLowercase(result
, NULL
);
411 #define STATIC_BUFFER_SIZE 1024
413 static BOOL
isHexDigit(char c
)
415 return (c
>= '0' && c
<= '9') || (c
>= 'A' && c
<= 'F') || (c
>= 'a' && c
<= 'f');
418 static UInt8
hexDigitValue(char c
)
420 if (c
>= '0' && c
<= '9') {
423 if (c
>= 'A' && c
<= 'F') {
426 if (c
>= 'a' && c
<= 'f') {
429 // NSURL_ERROR("illegal hex digit");
433 static CFDataRef
percentEscapeDecodeBuffer(CFAllocatorRef alloc
, const UInt8
* srcBuffer
, CFRange range
, Boolean stripWhitespace
)
436 UInt8 staticDstBuffer
[STATIC_BUFFER_SIZE
];
438 if (range
.length
> STATIC_BUFFER_SIZE
) {
439 dstBuffer
= (UInt8
*) malloc(range
.length
);
441 dstBuffer
= staticDstBuffer
;
444 CFIndex end
= range
.location
+ range
.length
;
448 for (i
= range
.location
, j
= 0; i
< end
; ++i
) {
451 if (srcBuffer
[i
] == '%' && end
> i
+ 2 && isHexDigit(srcBuffer
[i
+1]) && isHexDigit(srcBuffer
[i
+2])) {
452 value
= hexDigitValue(srcBuffer
[i
+1]) * 16 + hexDigitValue(srcBuffer
[i
+2]);
455 value
= srcBuffer
[i
];
458 if (!stripWhitespace
|| !isspace(value
)) {
459 dstBuffer
[j
++] = value
;
463 CFDataRef result
= CFDataCreate(alloc
, dstBuffer
, j
);
465 if (dstBuffer
!= staticDstBuffer
) {
473 // base 64 digits: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
475 static BOOL
isBase64Digit(char c
)
477 return (c
>= 'A' && c
<= 'Z') || (c
>= 'a' && c
<= 'z') || (c
>= '0' && c
<= '9') || (c
== '+') || (c
== '/');
480 static BOOL
isBase64DigitOrEqualSign(char c
)
482 return isBase64Digit(c
) || c
== '=';
485 static UInt8
base64DigitValue(char c
)
487 if (c
>= 'A' && c
<= 'Z') {
489 } else if (c
>= 'a' && c
<= 'z') {
491 } else if (c
>= '0' && c
<= '9') {
493 } else if (c
== '+') {
495 } else if (c
== '/') {
502 static CFDataRef
base64DecodeData(CFAllocatorRef alloc
, CFDataRef data
)
504 const UInt8
*srcBuffer
= CFDataGetBytePtr(data
);
505 CFIndex length
= CFDataGetLength(data
);
506 UInt8
*dstBuffer
= NULL
;
507 UInt8 staticDstBuffer
[STATIC_BUFFER_SIZE
];
508 CFDataRef result
= NULL
;
510 // base64 encoded data length must be multiple of 4
511 if (length
% 4 != 0) {
515 if (length
> STATIC_BUFFER_SIZE
) {
516 dstBuffer
= (UInt8
*) malloc(length
);
518 dstBuffer
= staticDstBuffer
;
523 for (i
= 0, j
= 0; i
< length
; i
+=4) {
524 if (!(isBase64Digit(srcBuffer
[i
]) &&
525 isBase64Digit(srcBuffer
[i
+1]) &&
526 isBase64DigitOrEqualSign(srcBuffer
[i
+2]) &&
527 isBase64DigitOrEqualSign(srcBuffer
[i
+3]))) {
528 if (dstBuffer
!= staticDstBuffer
) {
534 dstBuffer
[j
++] = (base64DigitValue(srcBuffer
[i
]) << 2) + (base64DigitValue(srcBuffer
[i
+1]) >> 4);
535 if (srcBuffer
[i
+2] != '=') {
536 dstBuffer
[j
++] = ((base64DigitValue(srcBuffer
[i
+1]) & 0xf) << 4) + (base64DigitValue(srcBuffer
[i
+2]) >> 2);
538 if (srcBuffer
[i
+3] != '=') {
539 dstBuffer
[j
++] = ((base64DigitValue(srcBuffer
[i
+2]) & 0x3) << 6) + (base64DigitValue(srcBuffer
[i
+3]));
543 result
= CFDataCreate(alloc
, dstBuffer
, j
);
546 if (dstBuffer
!= staticDstBuffer
) {
553 static inline CFStringRef
percentExpandAndTrimContentType(CFAllocatorRef alloc
, CFStringRef str
, CFRange range
)
555 CFMutableStringRef contentTypeHeader
= NULL
;
556 CFStringRef contentTypeUnexpanded
= CFStringCreateWithSubstring(alloc
, str
, range
);
557 CFStringRef contentTypeExpanded
= CFURLCreateStringByReplacingPercentEscapes(alloc
, contentTypeUnexpanded
, CFSTR(""));
559 if (NULL
== contentTypeExpanded
) {
560 contentTypeHeader
= CFStringCreateMutableCopy(alloc
, 0, contentTypeUnexpanded
);
562 contentTypeHeader
= CFStringCreateMutableCopy(alloc
, 0, contentTypeExpanded
);
563 CFRelease(contentTypeExpanded
);
565 CFRelease(contentTypeUnexpanded
);
566 CFStringTrimWhitespace(contentTypeHeader
);
568 return contentTypeHeader
;
571 static Boolean
parseDataRequestURL(CFURLRef url
, CFDataRef
* outData
, CFStringRef
* outMimeType
, CFStringRef
* outTextEncodingName
)
573 Boolean result
= FALSE
;
574 CFAllocatorRef alloc
= CFGetAllocator(url
);
575 CFStringRef str
= CFURLCopyResourceSpecifier(url
);
577 CFRange commaRange
= CFStringFind(str
, CFSTR(","), 0);
579 if (commaRange
.location
!= kCFNotFound
) {
580 CFStringRef contentTypeHeader
= percentExpandAndTrimContentType(alloc
, str
, CFRangeMake(0, commaRange
.location
));
581 CFStringRef mimeType
= mimeTypeFromContentTypeComponent(contentTypeHeader
);
582 CFStringRef textEncodingName
= charsetFromContentTypeHeader(contentTypeHeader
);
584 Boolean base64
= CFStringFind(contentTypeHeader
, CFSTR(";base64"), kCFCompareCaseInsensitive
).location
!= kCFNotFound
;
586 if (mimeType
== NULL
) {
587 mimeType
= (CFStringRef
) CFRetain(CFSTR("text/plain"));
590 CFIndex bufferSize
= CFURLGetBytes(url
, NULL
, 0);
591 UInt8
* srcBuffer
= (UInt8
*) malloc(bufferSize
);
592 CFURLGetBytes(url
, srcBuffer
, bufferSize
);
594 CFRange dataRange
= CFURLGetByteRangeForComponent(url
, kCFURLComponentResourceSpecifier
, NULL
);
595 while (srcBuffer
[dataRange
.location
] != ',') {
596 dataRange
.location
++;
599 dataRange
.location
++;
602 CFDataRef dataRef
= NULL
;
605 dataRef
= percentEscapeDecodeBuffer(alloc
, srcBuffer
, dataRange
, false);
607 CFDataRef unescapedAndStripped
= percentEscapeDecodeBuffer(alloc
, srcBuffer
, dataRange
, true);
608 if (unescapedAndStripped
) {
609 dataRef
= base64DecodeData(alloc
, unescapedAndStripped
);
610 CFRelease(unescapedAndStripped
);
614 if (dataRef
!= NULL
) {
616 *outMimeType
= (CFStringRef
) mimeType
== NULL
? NULL
: CFStringCreateCopy(alloc
, mimeType
);
617 *outTextEncodingName
= (CFStringRef
) textEncodingName
== NULL
? NULL
: CFStringCreateCopy(alloc
, textEncodingName
);
623 if (contentTypeHeader
) CFRelease(contentTypeHeader
);
624 if (mimeType
) CFRelease(mimeType
);
625 if (textEncodingName
) CFRelease(textEncodingName
);
634 static Boolean
_CFDataURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc
, CFURLRef url
, CFDataRef
*fetchedData
, CFArrayRef desiredProperties
, CFDictionaryRef
*fetchedProperties
, SInt32
*errorCode
) {
635 Boolean success
= true;
637 if (errorCode
) *errorCode
= 0;
639 // We always need to fetch the data...
640 CFDataRef data
= NULL
;
641 CFStringRef mimeType
= NULL
;
642 CFStringRef textEncodingName
= NULL
;
644 if (! parseDataRequestURL(url
, &data
, &mimeType
, &textEncodingName
)) {
646 *errorCode
= kCFURLUnknownError
;
651 *fetchedData
= CFDataCreateCopy(alloc
, data
);
654 if (fetchedProperties
) {
655 const void* propKeys
[] = {
656 kCFDataURLDataLength
,
658 kCFDataURLTextEncodingName
,
660 const CFIndex propKeysCount
= sizeof(propKeys
) / sizeof(propKeys
[0]);
662 if (desiredProperties
== NULL
) {
663 static CFArrayRef sAllProps
= NULL
;
664 if (sAllProps
== NULL
) {
665 sAllProps
= CFArrayCreate(kCFAllocatorSystemDefault
, propKeys
, propKeysCount
, &kCFTypeArrayCallBacks
);
667 desiredProperties
= sAllProps
;
670 const void* vals
[propKeysCount
];
671 const void* keys
[propKeysCount
];
674 CFIndex count
= CFArrayGetCount(desiredProperties
);
675 for (CFIndex i
= 0; i
< count
; i
++) {
676 CFStringRef key
= (CFStringRef
) CFArrayGetValueAtIndex(desiredProperties
, i
);
678 if (CFEqual(key
, kCFDataURLDataLength
)) {
679 CFIndex len
= CFDataGetLength(data
);
681 vals
[ixVal
++] = CFNumberCreate(alloc
, kCFNumberCFIndexType
, &len
);
682 } else if (CFEqual(key
, kCFDataURLMimeType
)) {
683 if (mimeType
!= NULL
) {
685 vals
[ixVal
++] = CFStringCreateCopy(alloc
, mimeType
);
687 } else if (CFEqual(key
, kCFDataURLTextEncodingName
)) {
688 if (textEncodingName
!= NULL
) {
690 vals
[ixVal
++] = CFStringCreateCopy(alloc
, textEncodingName
);
695 *fetchedProperties
= CFDictionaryCreate(alloc
, keys
, vals
, ixVal
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
696 for (CFIndex i
= 0; i
< ixVal
; i
++)
698 if (*fetchedProperties
== NULL
)
702 if (data
) CFRelease(data
);
703 if (mimeType
) CFRelease(mimeType
);
704 if (textEncodingName
) CFRelease(textEncodingName
);
711 /*************************/
712 /* Public routines */
713 /*************************/
715 Boolean
CFURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc
, CFURLRef url
, CFDataRef
*fetchedData
, CFDictionaryRef
*fetchedProperties
, CFArrayRef desiredProperties
, SInt32
*errorCode
) {
716 CFStringRef scheme
= CFURLCopyScheme(url
);
719 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
720 if (fetchedData
) *fetchedData
= NULL
;
721 if (fetchedProperties
) *fetchedProperties
= NULL
;
725 if (CFStringCompare(scheme
, CFSTR("file"), kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
726 result
= _CFFileURLCreateDataAndPropertiesFromResource(alloc
, url
, fetchedData
, desiredProperties
, fetchedProperties
, errorCode
);
727 } else if (CFStringCompare(scheme
, CFSTR("data"), kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
728 result
= _CFDataURLCreateDataAndPropertiesFromResource(alloc
, url
, fetchedData
, desiredProperties
, fetchedProperties
, errorCode
);
730 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
731 result
= __CFNetwork__CFURLCreateDataAndPropertiesFromResource(alloc
, url
, fetchedData
, fetchedProperties
, desiredProperties
, errorCode
);
733 if (fetchedData
) *fetchedData
= NULL
;
734 if (fetchedProperties
) *fetchedProperties
= NULL
;
735 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;
744 CFTypeRef
CFURLCreatePropertyFromResource(CFAllocatorRef alloc
, CFURLRef url
, CFStringRef property
, SInt32
*errorCode
) {
745 CFArrayRef array
= CFArrayCreate(alloc
, (const void **)&property
, 1, &kCFTypeArrayCallBacks
);
746 CFDictionaryRef dict
;
748 if (CFURLCreateDataAndPropertiesFromResource(alloc
, url
, NULL
, &dict
, array
, errorCode
)) {
749 CFTypeRef result
= CFDictionaryGetValue(dict
, property
);
750 if (result
) CFRetain(result
);
755 if (dict
) CFRelease(dict
);
761 Boolean
CFURLWriteDataAndPropertiesToResource(CFURLRef url
, CFDataRef data
, CFDictionaryRef propertyDict
, SInt32
*errorCode
) {
762 CFStringRef scheme
= CFURLCopyScheme(url
);
764 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
766 } else if (CFStringCompare(scheme
, CFSTR("file"), 0) == kCFCompareEqualTo
) {
767 Boolean success
= true;
769 if (errorCode
) *errorCode
= 0;
771 if (CFURLHasDirectoryPath(url
)) {
772 // Create a directory
773 char cPath
[CFMaxPathSize
];
774 if (!CFURLGetFileSystemRepresentation(url
, true, (unsigned char *)cPath
, CFMaxPathSize
))
776 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
779 success
= _CFCreateDirectory(cPath
);
780 if (!success
&& errorCode
) *errorCode
= kCFURLUnknownError
;
784 SInt32 length
= CFDataGetLength(data
);
785 const void *bytes
= (0 == length
) ? (const void *)"" : CFDataGetBytePtr(data
);
786 success
= _CFWriteBytesToFile(url
, bytes
, length
);
787 if (!success
&& errorCode
) *errorCode
= kCFURLUnknownError
;
791 if (!_CFFileURLWritePropertiesToResource(url
, propertyDict
, errorCode
))
797 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
798 Boolean result
= __CFNetwork__CFURLWriteDataAndPropertiesToResource(url
, data
, propertyDict
, errorCode
);
800 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;
804 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;
810 Boolean
CFURLDestroyResource(CFURLRef url
, SInt32
*errorCode
) {
811 CFStringRef scheme
= CFURLCopyScheme(url
);
812 char cPath
[CFMaxPathSize
];
815 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
817 } else if (CFStringCompare(scheme
, CFSTR("file"), 0) == kCFCompareEqualTo
) {
819 if (!CFURLGetFileSystemRepresentation(url
, true, (unsigned char *)cPath
, CFMaxPathSize
)) {
820 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
824 if (CFURLHasDirectoryPath(url
)) {
825 if (_CFRemoveDirectory(cPath
)) {
826 if (errorCode
) *errorCode
= 0;
829 if (errorCode
) *errorCode
= kCFURLUnknownError
;
833 if (_CFDeleteFile(cPath
)) {
834 if (errorCode
) *errorCode
= 0;
837 if (errorCode
) *errorCode
= kCFURLUnknownError
;
843 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
844 Boolean result
= __CFNetwork__CFURLDestroyResource(url
, errorCode
);
846 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;
850 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;