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-2011, 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_LINUX
47 #include <sys/types.h>
50 #elif DEPLOYMENT_TARGET_WINDOWS
51 //#include <winsock2.h>
54 #include <sys/types.h>
59 #error Unknown or unspecified DEPLOYMENT_TARGET
61 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
65 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
68 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)
69 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 DEFINE_WEAK_CFNETWORK_FUNC_FAIL(Boolean
, _CFURLDestroyResource
, (CFURLRef A
, SInt32
*B
), (A
, B
), if(B
) *B
= kCFURLImproperArgumentsError
, false)
75 typedef struct __NSString__
*NSString
;
78 Pre-10.6 property keys
81 CONST_STRING_DECL(kCFURLFileExists
, "kCFURLFileExists")
82 CONST_STRING_DECL(kCFURLFilePOSIXMode
, "kCFURLFilePOSIXMode")
83 CONST_STRING_DECL(kCFURLFileDirectoryContents
, "kCFURLFileDirectoryContents")
84 CONST_STRING_DECL(kCFURLFileLength
, "kCFURLFileLength")
85 CONST_STRING_DECL(kCFURLFileLastModificationTime
, "kCFURLFileLastModificationTime")
86 CONST_STRING_DECL(kCFURLFileOwnerID
, "kCFURLFileOwnerID")
87 CONST_STRING_DECL(kCFURLHTTPStatusCode
, "kCFURLHTTPStatusCode")
88 CONST_STRING_DECL(kCFURLHTTPStatusLine
, "kCFURLHTTPStatusLine")
90 CONST_STRING_DECL(kCFDataURLDataLength
, "kCFDataURLDataLength")
91 CONST_STRING_DECL(kCFDataURLMimeType
, "kCFDataURLMimeType")
92 CONST_STRING_DECL(kCFDataURLTextEncodingName
, "kCFDataURLTextEncodingName")
94 // Compatibility property strings -- we obsoleted these names pre-DP4. REW, 5/22/2000
95 CONST_STRING_DECL(kCFFileURLExists
, "kCFURLFileExists")
96 CONST_STRING_DECL(kCFFileURLPOSIXMode
, "kCFURLFilePOSIXMode")
97 CONST_STRING_DECL(kCFFileURLDirectoryContents
, "kCFURLFileDirectoryContents")
98 CONST_STRING_DECL(kCFFileURLSize
, "kCFURLFileLength")
99 CONST_STRING_DECL(kCFFileURLLastModificationTime
, "kCFURLFileLastModificationTime")
100 CONST_STRING_DECL(kCFHTTPURLStatusCode
, "kCFURLHTTPStatusCode")
101 CONST_STRING_DECL(kCFHTTPURLStatusLine
, "kCFURLHTTPStatusLine")
103 // 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 /* file: access routines */
107 /*************************/
109 //#warning CF:For the moment file access failures are ill defined and set the error code to kCFURLUnknownError
111 static CFDictionaryRef
_CFFileURLCreatePropertiesFromResource(CFAllocatorRef alloc
, CFURLRef url
, CFArrayRef desiredProperties
, SInt32
*errorCode
) {
112 // MF:!!! This could/should be changed to use _CFGetFileProperties() to do the actual figuring.
113 static CFArrayRef _allProps
= NULL
;
116 CFMutableDictionaryRef propertyDict
= NULL
;
121 CFDateRef modTime
= NULL
, *modTimePtr
= NULL
;
122 CFArrayRef contents
= NULL
, *contentsPtr
= NULL
;
125 if (errorCode
) *errorCode
= 0;
126 if (!desiredProperties
) {
127 // 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 const void *values
[9];
130 values
[0] = kCFURLFileExists
;
131 values
[1] = kCFURLFilePOSIXMode
;
132 values
[2] = kCFURLFileDirectoryContents
;
133 values
[3] = kCFURLFileLength
;
134 values
[4] = kCFURLFileLastModificationTime
;
135 values
[5] = kCFURLFileOwnerID
;
136 _allProps
= CFArrayCreate(kCFAllocatorSystemDefault
, values
, 6, &kCFTypeArrayCallBacks
);
138 desiredProperties
= _allProps
;
141 arrayRange
.location
= 0;
142 arrayRange
.length
= CFArrayGetCount(desiredProperties
);
143 propertyDict
= CFDictionaryCreateMutable(alloc
, 0, & kCFTypeDictionaryKeyCallBacks
, & kCFTypeDictionaryValueCallBacks
);
144 if (arrayRange
.length
== 0) return propertyDict
;
146 if (CFArrayContainsValue(desiredProperties
, arrayRange
, kCFURLFileDirectoryContents
)) {
147 contentsPtr
= &contents
;
149 if (CFArrayContainsValue(desiredProperties
, arrayRange
, kCFURLFileLastModificationTime
)) {
150 modTimePtr
= &modTime
;
153 if (_CFGetFileProperties(alloc
, url
, &exists
, &posixMode
, &size
, modTimePtr
, &ownerID
, contentsPtr
) != 0) {
155 // If all they've asked for is whether this file exists, then any error will just make
156 // this return kCFURLFileExists = kCFBooleanFalse, which handles the case where the filename is invalid or too long or something.
157 if ( arrayRange
.length
== 1 && CFArrayContainsValue( desiredProperties
, arrayRange
, kCFURLFileExists
) ) {
158 CFDictionarySetValue(propertyDict
, kCFURLFileExists
, kCFBooleanFalse
);
160 else if (errorCode
) {
161 *errorCode
= kCFURLUnknownError
;
166 for (idx
= 0; idx
< arrayRange
.length
; idx
++) {
167 CFStringRef key
= (CFMutableStringRef
)CFArrayGetValueAtIndex(desiredProperties
, idx
);
168 if (key
== kCFURLFilePOSIXMode
|| CFEqual(kCFURLFilePOSIXMode
, key
)) {
170 CFNumberRef num
= CFNumberCreate(alloc
, kCFNumberSInt32Type
, &posixMode
);
171 CFDictionarySetValue(propertyDict
, kCFURLFilePOSIXMode
, num
);
173 } else if (errorCode
) {
174 *errorCode
= kCFURLUnknownError
;
176 } else if (key
== kCFURLFileDirectoryContents
|| CFEqual(kCFURLFileDirectoryContents
, key
)) {
177 if (exists
&& (posixMode
& S_IFMT
) == S_IFDIR
&& contents
) {
178 CFDictionarySetValue(propertyDict
, kCFURLFileDirectoryContents
, contents
);
179 } else if (errorCode
) {
180 *errorCode
= kCFURLUnknownError
;
182 } else if (key
== kCFURLFileLength
|| CFEqual(kCFURLFileLength
, key
)) {
184 CFNumberRef num
= CFNumberCreate(alloc
, kCFNumberSInt64Type
, &size
);
185 CFDictionarySetValue(propertyDict
, kCFURLFileLength
, num
);
187 } else if (errorCode
) {
188 *errorCode
= kCFURLUnknownError
;
190 } else if (key
== kCFURLFileLastModificationTime
|| CFEqual(kCFURLFileLastModificationTime
, key
)) {
191 if (exists
&& modTime
) {
192 CFDictionarySetValue(propertyDict
, kCFURLFileLastModificationTime
, modTime
);
193 } else if (errorCode
) {
194 *errorCode
= kCFURLUnknownError
;
196 } else if (key
== kCFURLFileExists
|| CFEqual(kCFURLFileExists
, key
)) {
198 CFDictionarySetValue(propertyDict
, kCFURLFileExists
, kCFBooleanTrue
);
200 CFDictionarySetValue(propertyDict
, kCFURLFileExists
, kCFBooleanFalse
);
202 } else if (key
== kCFURLFileOwnerID
|| CFEqual(kCFURLFileOwnerID
, key
)) {
204 CFNumberRef num
= CFNumberCreate(alloc
, kCFNumberSInt32Type
, &ownerID
);
205 CFDictionarySetValue(propertyDict
, kCFURLFileOwnerID
, num
);
207 } else if (errorCode
) {
208 *errorCode
= kCFURLUnknownError
;
210 // Add more properties here
211 } else if (errorCode
) {
212 *errorCode
= kCFURLUnknownPropertyKeyError
;
215 if (modTime
) CFRelease(modTime
);
216 if (contents
) CFRelease(contents
);
220 static Boolean
_CFFileURLWritePropertiesToResource(CFURLRef url
, CFDictionaryRef propertyDict
, SInt32
*errorCode
) {
221 CFTypeRef buffer
[16];
224 Boolean result
= true;
226 char cPath
[CFMaxPathSize
];
228 if (!CFURLGetFileSystemRepresentation(url
, true, (unsigned char *)cPath
, CFMaxPathSize
)) {
229 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
233 count
= CFDictionaryGetCount(propertyDict
);
238 keys
= (CFTypeRef
*)CFAllocatorAllocate(CFGetAllocator(url
), sizeof(void *) * count
* 2, 0);
239 values
= keys
+ count
;
241 CFDictionaryGetKeysAndValues(propertyDict
, keys
, values
);
243 for (idx
= 0; idx
< count
; idx
++) {
244 CFStringRef key
= (CFStringRef
)keys
[idx
];
245 CFTypeRef value
= values
[idx
];
246 if (key
== kCFURLFilePOSIXMode
|| CFEqual(kCFURLFilePOSIXMode
, key
)) {
249 if (CFNumberGetTypeID() == CFGetTypeID(value
)) {
250 CFNumberRef modeNum
= (CFNumberRef
)value
;
251 CFNumberGetValue(modeNum
, kCFNumberSInt32Type
, &mode
);
253 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX
254 #define MODE_TYPE mode_t
255 #elif DEPLOYMENT_TARGET_WINDOWS
256 #define MODE_TYPE uint16_t
258 #error Unknown or unspecified DEPLOYMENT_TARGET
260 const MODE_TYPE
*modePtr
= (const MODE_TYPE
*)CFDataGetBytePtr((CFDataRef
)value
);
263 err
= chmod(cPath
, mode
);
264 if (err
!= 0) result
= false;
270 if ((CFTypeRef
)keys
!= buffer
) CFAllocatorDeallocate(CFGetAllocator(url
), keys
);
272 if (errorCode
) *errorCode
= result
? 0 : kCFURLUnknownError
;
276 static Boolean
_CFFileURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc
, CFURLRef url
, CFDataRef
*fetchedData
, CFArrayRef desiredProperties
, CFDictionaryRef
*fetchedProperties
, SInt32
*errorCode
) {
277 Boolean success
= true;
279 if (errorCode
) *errorCode
= 0;
283 Boolean releaseAlloc
= false;
286 // We need a real allocator to pass to _CFReadBytesFromFile so that the CFDataRef we create with
287 // CFDataCreateWithBytesNoCopy() can free up the object _CFReadBytesFromFile() returns.
288 alloc
= (CFAllocatorRef
)CFRetain(__CFGetDefaultAllocator());
291 if (!_CFReadBytesFromFile(alloc
, url
, &bytes
, &length
, 0)) {
292 if (errorCode
) *errorCode
= kCFURLUnknownError
;
296 *fetchedData
= CFDataCreateWithBytesNoCopy(alloc
, (const UInt8
*)bytes
, length
, alloc
);
299 // Now the CFData should be hanging on to it.
304 if (fetchedProperties
) {
305 *fetchedProperties
= _CFFileURLCreatePropertiesFromResource(alloc
, url
, desiredProperties
, errorCode
);
306 if (!*fetchedProperties
)
310 if ( ! success
&& fetchedData
&& *fetchedData
) {
311 CFRelease( *fetchedData
);
319 * Support for data: URLs - RFC 2397
320 * Currently this is spi for CFNetwork, to make it API, just put these constants in CFURLAccess.h
325 const CFStringRef kCFDataURLDataLength;
327 const CFStringRef kCFDataURLMimeType;
329 const CFStringRef kCFDataURLTextEncodingName;
332 /* Properties for the data: scheme. */
333 /* kCFDataURLDataLength is a CFNumber giving the data's length in bytes. */
334 /* kCFDataURLMimeType is a CFString. */
335 /* kCFDataURLTextEncodingName is a CFString. */
337 /* REMINDSMZ: From CFURLResponse.c */
338 static CFStringRef
mimeTypeFromContentTypeComponent(CFStringRef component
) {
339 CFIndex compLen
= CFStringGetLength(component
);
340 CFStringInlineBuffer buf
;
342 CFIndex firstChar
= -1, lastChar
= -1;
343 CFCharacterSetRef whitespaceSet
= CFCharacterSetGetPredefined(kCFCharacterSetWhitespace
);
344 CFStringInitInlineBuffer(component
, &buf
, CFRangeMake(0, compLen
));
346 for (idx
= 0; idx
< compLen
; idx
++) {
347 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, idx
);
349 // Delimits the charset
351 } else if (firstChar
== -1) {
352 if (!CFCharacterSetIsCharacterMember(whitespaceSet
, ch
)) {
355 } else if (!CFCharacterSetIsCharacterMember(whitespaceSet
, ch
)) {
359 if (firstChar
!= -1 && lastChar
!= -1) {
360 CFMutableStringRef newContentType
= CFStringCreateMutableCopy(CFGetAllocator(component
), compLen
, component
);
361 if (lastChar
!= compLen
- 1) {
362 CFStringDelete(newContentType
, CFRangeMake(lastChar
+ 1, compLen
- lastChar
- 1));
365 CFStringDelete(newContentType
, CFRangeMake(0, firstChar
));
367 CFStringLowercase(newContentType
, NULL
);
368 return newContentType
;
373 /* REMINDSMZ: From CFURLResponse.c */
374 static CFStringRef
charsetFromContentTypeHeader(CFStringRef contentType
) {
375 // FIXME: Should this use KeyValuePair parsing to handle quoting properly?
377 CFIndex compLen
= CFStringGetLength(contentType
);
378 CFIndex start
, end
, idx
;
379 CFCharacterSetRef whitespaceSet
;
380 CFMutableStringRef result
;
382 CFStringRef kCFURLResponseCharsetPrefix
= CFSTR("charset=");
384 if (!CFStringFindWithOptions(contentType
, kCFURLResponseCharsetPrefix
, CFRangeMake(0, compLen
), kCFCompareCaseInsensitive
, &range
) || range
.length
== 0) return NULL
;
386 whitespaceSet
= CFCharacterSetGetPredefined(kCFCharacterSetWhitespace
);
389 for (idx
= range
.location
+ range
.length
; idx
< compLen
; idx
++) {
390 UniChar ch
= CFStringGetCharacterAtIndex(contentType
, idx
);
391 if (ch
== ';' || ch
== ',') break;
393 if (!CFCharacterSetIsCharacterMember(whitespaceSet
, ch
)) {
397 } else if (!CFCharacterSetIsCharacterMember(whitespaceSet
,ch
)) {
402 if (start
== -1) return NULL
;
404 result
= CFStringCreateMutableCopy(CFGetAllocator(contentType
), compLen
,contentType
);
405 if (end
!= compLen
) {
406 CFStringDelete(result
, CFRangeMake(end
+1, compLen
-end
-1));
408 CFStringDelete(result
, CFRangeMake(0, start
));
409 CFStringLowercase(result
, NULL
);
413 #define STATIC_BUFFER_SIZE 1024
415 static BOOL
isHexDigit(char c
)
417 return (c
>= '0' && c
<= '9') || (c
>= 'A' && c
<= 'F') || (c
>= 'a' && c
<= 'f');
420 static UInt8
hexDigitValue(char c
)
422 if (c
>= '0' && c
<= '9') {
425 if (c
>= 'A' && c
<= 'F') {
428 if (c
>= 'a' && c
<= 'f') {
431 // NSURL_ERROR("illegal hex digit");
435 static CFDataRef
percentEscapeDecodeBuffer(CFAllocatorRef alloc
, const UInt8
* srcBuffer
, CFRange range
, Boolean stripWhitespace
)
438 UInt8 staticDstBuffer
[STATIC_BUFFER_SIZE
];
440 if (range
.length
> STATIC_BUFFER_SIZE
) {
441 dstBuffer
= (UInt8
*) malloc(range
.length
);
443 dstBuffer
= staticDstBuffer
;
446 CFIndex end
= range
.location
+ range
.length
;
450 for (i
= range
.location
, j
= 0; i
< end
; ++i
) {
453 if (srcBuffer
[i
] == '%' && end
> i
+ 2 && isHexDigit(srcBuffer
[i
+1]) && isHexDigit(srcBuffer
[i
+2])) {
454 value
= hexDigitValue(srcBuffer
[i
+1]) * 16 + hexDigitValue(srcBuffer
[i
+2]);
457 value
= srcBuffer
[i
];
460 if (!stripWhitespace
|| !isspace(value
)) {
461 dstBuffer
[j
++] = value
;
465 CFDataRef result
= CFDataCreate(alloc
, dstBuffer
, j
);
467 if (dstBuffer
!= staticDstBuffer
) {
475 // base 64 digits: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
477 static BOOL
isBase64Digit(char c
)
479 return (c
>= 'A' && c
<= 'Z') || (c
>= 'a' && c
<= 'z') || (c
>= '0' && c
<= '9') || (c
== '+') || (c
== '/');
482 static BOOL
isBase64DigitOrEqualSign(char c
)
484 return isBase64Digit(c
) || c
== '=';
487 static UInt8
base64DigitValue(char c
)
489 if (c
>= 'A' && c
<= 'Z') {
491 } else if (c
>= 'a' && c
<= 'z') {
493 } else if (c
>= '0' && c
<= '9') {
495 } else if (c
== '+') {
497 } else if (c
== '/') {
504 static CFDataRef
base64DecodeData(CFAllocatorRef alloc
, CFDataRef data
)
506 const UInt8
*srcBuffer
= CFDataGetBytePtr(data
);
507 CFIndex length
= CFDataGetLength(data
);
508 UInt8
*dstBuffer
= NULL
;
509 UInt8 staticDstBuffer
[STATIC_BUFFER_SIZE
];
510 CFDataRef result
= NULL
;
512 // base64 encoded data length must be multiple of 4
513 if (length
% 4 != 0) {
517 if (length
> STATIC_BUFFER_SIZE
) {
518 dstBuffer
= (UInt8
*) malloc(length
);
520 dstBuffer
= staticDstBuffer
;
525 for (i
= 0, j
= 0; i
< length
; i
+=4) {
526 if (!(isBase64Digit(srcBuffer
[i
]) &&
527 isBase64Digit(srcBuffer
[i
+1]) &&
528 isBase64DigitOrEqualSign(srcBuffer
[i
+2]) &&
529 isBase64DigitOrEqualSign(srcBuffer
[i
+3]))) {
530 if (dstBuffer
!= staticDstBuffer
) {
536 dstBuffer
[j
++] = (base64DigitValue(srcBuffer
[i
]) << 2) + (base64DigitValue(srcBuffer
[i
+1]) >> 4);
537 if (srcBuffer
[i
+2] != '=') {
538 dstBuffer
[j
++] = ((base64DigitValue(srcBuffer
[i
+1]) & 0xf) << 4) + (base64DigitValue(srcBuffer
[i
+2]) >> 2);
540 if (srcBuffer
[i
+3] != '=') {
541 dstBuffer
[j
++] = ((base64DigitValue(srcBuffer
[i
+2]) & 0x3) << 6) + (base64DigitValue(srcBuffer
[i
+3]));
545 result
= CFDataCreate(alloc
, dstBuffer
, j
);
548 if (dstBuffer
!= staticDstBuffer
) {
555 static inline CFStringRef
percentExpandAndTrimContentType(CFAllocatorRef alloc
, CFStringRef str
, CFRange range
)
557 CFMutableStringRef contentTypeHeader
= NULL
;
558 CFStringRef contentTypeUnexpanded
= CFStringCreateWithSubstring(alloc
, str
, range
);
559 CFStringRef contentTypeExpanded
= CFURLCreateStringByReplacingPercentEscapes(alloc
, contentTypeUnexpanded
, CFSTR(""));
561 if (NULL
== contentTypeExpanded
) {
562 contentTypeHeader
= CFStringCreateMutableCopy(alloc
, 0, contentTypeUnexpanded
);
564 contentTypeHeader
= CFStringCreateMutableCopy(alloc
, 0, contentTypeExpanded
);
565 CFRelease(contentTypeExpanded
);
567 CFRelease(contentTypeUnexpanded
);
568 CFStringTrimWhitespace(contentTypeHeader
);
570 return contentTypeHeader
;
573 static Boolean
parseDataRequestURL(CFURLRef url
, CFDataRef
* outData
, CFStringRef
* outMimeType
, CFStringRef
* outTextEncodingName
)
575 Boolean result
= FALSE
;
576 CFAllocatorRef alloc
= CFGetAllocator(url
);
577 CFStringRef str
= CFURLCopyResourceSpecifier(url
);
579 CFRange commaRange
= CFStringFind(str
, CFSTR(","), 0);
581 if (commaRange
.location
!= kCFNotFound
) {
582 CFStringRef contentTypeHeader
= percentExpandAndTrimContentType(alloc
, str
, CFRangeMake(0, commaRange
.location
));
583 CFStringRef mimeType
= mimeTypeFromContentTypeComponent(contentTypeHeader
);
584 CFStringRef textEncodingName
= charsetFromContentTypeHeader(contentTypeHeader
);
586 Boolean base64
= CFStringFind(contentTypeHeader
, CFSTR(";base64"), kCFCompareCaseInsensitive
).location
!= kCFNotFound
;
588 if (mimeType
== NULL
) {
589 mimeType
= (CFStringRef
) CFRetain(CFSTR("text/plain"));
592 CFIndex bufferSize
= CFURLGetBytes(url
, NULL
, 0);
593 UInt8
* srcBuffer
= (UInt8
*) malloc(bufferSize
);
594 CFURLGetBytes(url
, srcBuffer
, bufferSize
);
596 CFRange dataRange
= CFURLGetByteRangeForComponent(url
, kCFURLComponentResourceSpecifier
, NULL
);
597 while (srcBuffer
[dataRange
.location
] != ',') {
598 dataRange
.location
++;
601 dataRange
.location
++;
604 CFDataRef dataRef
= NULL
;
607 dataRef
= percentEscapeDecodeBuffer(alloc
, srcBuffer
, dataRange
, false);
609 CFDataRef unescapedAndStripped
= percentEscapeDecodeBuffer(alloc
, srcBuffer
, dataRange
, true);
610 if (unescapedAndStripped
) {
611 dataRef
= base64DecodeData(alloc
, unescapedAndStripped
);
612 CFRelease(unescapedAndStripped
);
616 if (dataRef
!= NULL
) {
618 *outMimeType
= (CFStringRef
) mimeType
== NULL
? NULL
: CFStringCreateCopy(alloc
, mimeType
);
619 *outTextEncodingName
= (CFStringRef
) textEncodingName
== NULL
? NULL
: CFStringCreateCopy(alloc
, textEncodingName
);
625 if (contentTypeHeader
) CFRelease(contentTypeHeader
);
626 if (mimeType
) CFRelease(mimeType
);
627 if (textEncodingName
) CFRelease(textEncodingName
);
636 static Boolean
_CFDataURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc
, CFURLRef url
, CFDataRef
*fetchedData
, CFArrayRef desiredProperties
, CFDictionaryRef
*fetchedProperties
, SInt32
*errorCode
) {
637 Boolean success
= true;
639 if (errorCode
) *errorCode
= 0;
641 // We always need to fetch the data...
642 CFDataRef data
= NULL
;
643 CFStringRef mimeType
= NULL
;
644 CFStringRef textEncodingName
= NULL
;
646 if (! parseDataRequestURL(url
, &data
, &mimeType
, &textEncodingName
)) {
648 *errorCode
= kCFURLUnknownError
;
653 *fetchedData
= CFDataCreateCopy(alloc
, data
);
656 if (fetchedProperties
) {
657 const void* propKeys
[] = {
658 kCFDataURLDataLength
,
660 kCFDataURLTextEncodingName
,
662 const CFIndex propKeysCount
= sizeof(propKeys
) / sizeof(propKeys
[0]);
664 if (desiredProperties
== NULL
) {
665 static CFArrayRef sAllProps
= NULL
;
666 if (sAllProps
== NULL
) {
667 sAllProps
= CFArrayCreate(kCFAllocatorSystemDefault
, propKeys
, propKeysCount
, &kCFTypeArrayCallBacks
);
669 desiredProperties
= sAllProps
;
672 const void* vals
[propKeysCount
];
673 const void* keys
[propKeysCount
];
676 CFIndex count
= CFArrayGetCount(desiredProperties
);
677 for (CFIndex i
= 0; i
< count
; i
++) {
678 CFStringRef key
= (CFStringRef
) CFArrayGetValueAtIndex(desiredProperties
, i
);
680 if (CFEqual(key
, kCFDataURLDataLength
)) {
681 CFIndex len
= CFDataGetLength(data
);
683 vals
[ixVal
++] = CFNumberCreate(alloc
, kCFNumberCFIndexType
, &len
);
684 } else if (CFEqual(key
, kCFDataURLMimeType
)) {
685 if (mimeType
!= NULL
) {
687 vals
[ixVal
++] = CFStringCreateCopy(alloc
, mimeType
);
689 } else if (CFEqual(key
, kCFDataURLTextEncodingName
)) {
690 if (textEncodingName
!= NULL
) {
692 vals
[ixVal
++] = CFStringCreateCopy(alloc
, textEncodingName
);
697 *fetchedProperties
= CFDictionaryCreate(alloc
, keys
, vals
, ixVal
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
698 for (CFIndex i
= 0; i
< ixVal
; i
++)
700 if (*fetchedProperties
== NULL
)
704 if (data
) CFRelease(data
);
705 if (mimeType
) CFRelease(mimeType
);
706 if (textEncodingName
) CFRelease(textEncodingName
);
713 /*************************/
714 /* Public routines */
715 /*************************/
717 Boolean
CFURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc
, CFURLRef url
, CFDataRef
*fetchedData
, CFDictionaryRef
*fetchedProperties
, CFArrayRef desiredProperties
, SInt32
*errorCode
) {
718 CFStringRef scheme
= CFURLCopyScheme(url
);
721 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
722 if (fetchedData
) *fetchedData
= NULL
;
723 if (fetchedProperties
) *fetchedProperties
= NULL
;
727 if (CFStringCompare(scheme
, CFSTR("file"), kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
728 result
= _CFFileURLCreateDataAndPropertiesFromResource(alloc
, url
, fetchedData
, desiredProperties
, fetchedProperties
, errorCode
);
729 } else if (CFStringCompare(scheme
, CFSTR("data"), kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
730 result
= _CFDataURLCreateDataAndPropertiesFromResource(alloc
, url
, fetchedData
, desiredProperties
, fetchedProperties
, errorCode
);
732 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
733 result
= __CFNetwork__CFURLCreateDataAndPropertiesFromResource(alloc
, url
, fetchedData
, fetchedProperties
, desiredProperties
, errorCode
);
735 if (fetchedData
) *fetchedData
= NULL
;
736 if (fetchedProperties
) *fetchedProperties
= NULL
;
737 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;
746 CFTypeRef
CFURLCreatePropertyFromResource(CFAllocatorRef alloc
, CFURLRef url
, CFStringRef property
, SInt32
*errorCode
) {
747 CFArrayRef array
= CFArrayCreate(alloc
, (const void **)&property
, 1, &kCFTypeArrayCallBacks
);
748 CFDictionaryRef dict
;
750 if (CFURLCreateDataAndPropertiesFromResource(alloc
, url
, NULL
, &dict
, array
, errorCode
)) {
751 CFTypeRef result
= CFDictionaryGetValue(dict
, property
);
752 if (result
) CFRetain(result
);
757 if (dict
) CFRelease(dict
);
763 Boolean
CFURLWriteDataAndPropertiesToResource(CFURLRef url
, CFDataRef data
, CFDictionaryRef propertyDict
, SInt32
*errorCode
) {
764 CFStringRef scheme
= CFURLCopyScheme(url
);
766 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
768 } else if (CFStringCompare(scheme
, CFSTR("file"), 0) == kCFCompareEqualTo
) {
769 Boolean success
= true;
771 if (errorCode
) *errorCode
= 0;
773 if (CFURLHasDirectoryPath(url
)) {
774 // Create a directory
775 #if DEPLOYMENT_TARGET_WINDOWS
776 wchar_t cPath
[CFMaxPathSize
];
777 if (!_CFURLGetWideFileSystemRepresentation(url
, true, cPath
, CFMaxPathSize
))
779 char cPath
[CFMaxPathSize
];
780 if (!CFURLGetFileSystemRepresentation(url
, true, (unsigned char *)cPath
, CFMaxPathSize
))
783 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
786 #if DEPLOYMENT_TARGET_WINDOWS
787 success
= _CFCreateDirectoryWide(cPath
);
789 success
= _CFCreateDirectory(cPath
);
791 if (!success
&& errorCode
) *errorCode
= kCFURLUnknownError
;
795 SInt32 length
= CFDataGetLength(data
);
796 const void *bytes
= (0 == length
) ? (const void *)"" : CFDataGetBytePtr(data
);
797 success
= _CFWriteBytesToFile(url
, bytes
, length
);
798 if (!success
&& errorCode
) *errorCode
= kCFURLUnknownError
;
802 if (!_CFFileURLWritePropertiesToResource(url
, propertyDict
, errorCode
))
808 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
809 Boolean result
= __CFNetwork__CFURLWriteDataAndPropertiesToResource(url
, data
, propertyDict
, errorCode
);
811 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;
815 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;
821 Boolean
CFURLDestroyResource(CFURLRef url
, SInt32
*errorCode
) {
822 CFStringRef scheme
= CFURLCopyScheme(url
);
823 char cPath
[CFMaxPathSize
];
826 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
828 } else if (CFStringCompare(scheme
, CFSTR("file"), 0) == kCFCompareEqualTo
) {
830 if (!CFURLGetFileSystemRepresentation(url
, true, (unsigned char *)cPath
, CFMaxPathSize
)) {
831 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
835 if (CFURLHasDirectoryPath(url
)) {
836 if (_CFRemoveDirectory(cPath
)) {
837 if (errorCode
) *errorCode
= 0;
840 if (errorCode
) *errorCode
= kCFURLUnknownError
;
844 if (_CFDeleteFile(cPath
)) {
845 if (errorCode
) *errorCode
= 0;
848 if (errorCode
) *errorCode
= kCFURLUnknownError
;
854 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
855 Boolean result
= __CFNetwork__CFURLDestroyResource(url
, errorCode
);
857 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;
861 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;