2 * Copyright (c) 2009 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-2009, 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
47 #include <sys/types.h>
51 #elif DEPLOYMENT_TARGET_WINDOWS
52 //#include <winsock2.h>
55 #include <sys/types.h>
60 #error Unknown or unspecified DEPLOYMENT_TARGET
64 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
67 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)
68 DEFINE_WEAK_CFNETWORK_FUNC_FAIL(Boolean
, _CFURLWriteDataAndPropertiesToResource
, (CFURLRef A
, CFDataRef B
, CFDictionaryRef C
, SInt32
*D
), (A
, B
, C
, D
), if (D
) *D
= kCFURLImproperArgumentsError
, false)
70 DEFINE_WEAK_CFNETWORK_FUNC_FAIL(Boolean
, _CFURLDestroyResource
, (CFURLRef A
, SInt32
*B
), (A
, B
), if(B
) *B
= kCFURLImproperArgumentsError
, false)
72 #elif DEPLOYMENT_TARGET_WINDOWS
74 #error Unknown or unspecified DEPLOYMENT_TARGET
77 typedef struct __NSString__
*NSString
;
80 Pre-10.6 property keys
83 CONST_STRING_DECL(kCFURLFileExists
, "kCFURLFileExists")
84 CONST_STRING_DECL(kCFURLFilePOSIXMode
, "kCFURLFilePOSIXMode")
85 CONST_STRING_DECL(kCFURLFileDirectoryContents
, "kCFURLFileDirectoryContents")
86 CONST_STRING_DECL(kCFURLFileLength
, "kCFURLFileLength")
87 CONST_STRING_DECL(kCFURLFileLastModificationTime
, "kCFURLFileLastModificationTime")
88 CONST_STRING_DECL(kCFURLFileOwnerID
, "kCFURLFileOwnerID")
89 CONST_STRING_DECL(kCFURLHTTPStatusCode
, "kCFURLHTTPStatusCode")
90 CONST_STRING_DECL(kCFURLHTTPStatusLine
, "kCFURLHTTPStatusLine")
92 CONST_STRING_DECL(kCFDataURLDataLength
, "kCFDataURLDataLength")
93 CONST_STRING_DECL(kCFDataURLMimeType
, "kCFDataURLMimeType")
94 CONST_STRING_DECL(kCFDataURLTextEncodingName
, "kCFDataURLTextEncodingName")
96 // Compatibility property strings -- we obsoleted these names pre-DP4. REW, 5/22/2000
97 CONST_STRING_DECL(kCFFileURLExists
, "kCFURLFileExists")
98 CONST_STRING_DECL(kCFFileURLPOSIXMode
, "kCFURLFilePOSIXMode")
99 CONST_STRING_DECL(kCFFileURLDirectoryContents
, "kCFURLFileDirectoryContents")
100 CONST_STRING_DECL(kCFFileURLSize
, "kCFURLFileLength")
101 CONST_STRING_DECL(kCFFileURLLastModificationTime
, "kCFURLFileLastModificationTime")
102 CONST_STRING_DECL(kCFHTTPURLStatusCode
, "kCFURLHTTPStatusCode")
103 CONST_STRING_DECL(kCFHTTPURLStatusLine
, "kCFURLHTTPStatusLine")
105 // 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
107 /*************************/
108 /* file: access routines */
109 /*************************/
111 //#warning CF:For the moment file access failures are ill defined and set the error code to kCFURLUnknownError
113 static CFDictionaryRef
_CFFileURLCreatePropertiesFromResource(CFAllocatorRef alloc
, CFURLRef url
, CFArrayRef desiredProperties
, SInt32
*errorCode
) {
114 // MF:!!! This could/should be changed to use _CFGetFileProperties() to do the actual figuring.
115 static CFArrayRef _allProps
= NULL
;
118 CFMutableDictionaryRef propertyDict
= NULL
;
123 CFDateRef modTime
= NULL
, *modTimePtr
= NULL
;
124 CFArrayRef contents
= NULL
, *contentsPtr
= NULL
;
127 if (errorCode
) *errorCode
= 0;
128 if (!desiredProperties
) {
129 // 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
131 const void *values
[9];
132 values
[0] = kCFURLFileExists
;
133 values
[1] = kCFURLFilePOSIXMode
;
134 values
[2] = kCFURLFileDirectoryContents
;
135 values
[3] = kCFURLFileLength
;
136 values
[4] = kCFURLFileLastModificationTime
;
137 values
[5] = kCFURLFileOwnerID
;
138 _allProps
= CFArrayCreate(kCFAllocatorSystemDefault
, values
, 6, &kCFTypeArrayCallBacks
);
140 desiredProperties
= _allProps
;
143 arrayRange
.location
= 0;
144 arrayRange
.length
= CFArrayGetCount(desiredProperties
);
145 propertyDict
= CFDictionaryCreateMutable(alloc
, 0, & kCFTypeDictionaryKeyCallBacks
, & kCFTypeDictionaryValueCallBacks
);
146 if (arrayRange
.length
== 0) return propertyDict
;
148 if (CFArrayContainsValue(desiredProperties
, arrayRange
, kCFURLFileDirectoryContents
)) {
149 contentsPtr
= &contents
;
151 if (CFArrayContainsValue(desiredProperties
, arrayRange
, kCFURLFileLastModificationTime
)) {
152 modTimePtr
= &modTime
;
155 if (_CFGetFileProperties(alloc
, url
, &exists
, &posixMode
, &size
, modTimePtr
, &ownerID
, contentsPtr
) != 0) {
157 // If all they've asked for is whether this file exists, then any error will just make
158 // this return kCFURLFileExists = kCFBooleanFalse, which handles the case where the filename is invalid or too long or something.
159 if ( arrayRange
.length
== 1 && CFArrayContainsValue( desiredProperties
, arrayRange
, kCFURLFileExists
) ) {
160 CFDictionarySetValue(propertyDict
, kCFURLFileExists
, kCFBooleanFalse
);
162 else if (errorCode
) {
163 *errorCode
= kCFURLUnknownError
;
168 for (idx
= 0; idx
< arrayRange
.length
; idx
++) {
169 CFStringRef key
= (CFMutableStringRef
)CFArrayGetValueAtIndex(desiredProperties
, idx
);
170 if (key
== kCFURLFilePOSIXMode
|| CFEqual(kCFURLFilePOSIXMode
, key
)) {
172 CFNumberRef num
= CFNumberCreate(alloc
, kCFNumberSInt32Type
, &posixMode
);
173 CFDictionarySetValue(propertyDict
, kCFURLFilePOSIXMode
, num
);
175 } else if (errorCode
) {
176 *errorCode
= kCFURLUnknownError
;
178 } else if (key
== kCFURLFileDirectoryContents
|| CFEqual(kCFURLFileDirectoryContents
, key
)) {
179 if (exists
&& (posixMode
& S_IFMT
) == S_IFDIR
&& contents
) {
180 CFDictionarySetValue(propertyDict
, kCFURLFileDirectoryContents
, contents
);
181 } else if (errorCode
) {
182 *errorCode
= kCFURLUnknownError
;
184 } else if (key
== kCFURLFileLength
|| CFEqual(kCFURLFileLength
, key
)) {
186 CFNumberRef num
= CFNumberCreate(alloc
, kCFNumberSInt64Type
, &size
);
187 CFDictionarySetValue(propertyDict
, kCFURLFileLength
, num
);
189 } else if (errorCode
) {
190 *errorCode
= kCFURLUnknownError
;
192 } else if (key
== kCFURLFileLastModificationTime
|| CFEqual(kCFURLFileLastModificationTime
, key
)) {
193 if (exists
&& modTime
) {
194 CFDictionarySetValue(propertyDict
, kCFURLFileLastModificationTime
, modTime
);
195 } else if (errorCode
) {
196 *errorCode
= kCFURLUnknownError
;
198 } else if (key
== kCFURLFileExists
|| CFEqual(kCFURLFileExists
, key
)) {
200 CFDictionarySetValue(propertyDict
, kCFURLFileExists
, kCFBooleanTrue
);
202 CFDictionarySetValue(propertyDict
, kCFURLFileExists
, kCFBooleanFalse
);
204 } else if (key
== kCFURLFileOwnerID
|| CFEqual(kCFURLFileOwnerID
, key
)) {
206 CFNumberRef num
= CFNumberCreate(alloc
, kCFNumberSInt32Type
, &ownerID
);
207 CFDictionarySetValue(propertyDict
, kCFURLFileOwnerID
, num
);
209 } else if (errorCode
) {
210 *errorCode
= kCFURLUnknownError
;
212 // Add more properties here
213 } else if (errorCode
) {
214 *errorCode
= kCFURLUnknownPropertyKeyError
;
217 if (modTime
) CFRelease(modTime
);
218 if (contents
) CFRelease(contents
);
222 static Boolean
_CFFileURLWritePropertiesToResource(CFURLRef url
, CFDictionaryRef propertyDict
, SInt32
*errorCode
) {
223 CFTypeRef buffer
[16];
226 Boolean result
= true;
228 char cPath
[CFMaxPathSize
];
230 if (!CFURLGetFileSystemRepresentation(url
, true, (unsigned char *)cPath
, CFMaxPathSize
)) {
231 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
235 count
= CFDictionaryGetCount(propertyDict
);
240 keys
= (CFTypeRef
*)CFAllocatorAllocate(CFGetAllocator(url
), sizeof(void *) * count
* 2, 0);
241 values
= keys
+ count
;
243 CFDictionaryGetKeysAndValues(propertyDict
, keys
, values
);
245 for (idx
= 0; idx
< count
; idx
++) {
246 CFStringRef key
= (CFStringRef
)keys
[idx
];
247 CFTypeRef value
= values
[idx
];
248 if (key
== kCFURLFilePOSIXMode
|| CFEqual(kCFURLFilePOSIXMode
, key
)) {
251 if (CFNumberGetTypeID() == CFGetTypeID(value
)) {
252 CFNumberRef modeNum
= (CFNumberRef
)value
;
253 CFNumberGetValue(modeNum
, kCFNumberSInt32Type
, &mode
);
255 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
256 #define MODE_TYPE mode_t
257 #elif DEPLOYMENT_TARGET_WINDOWS
258 #define MODE_TYPE uint16_t
260 #error Unknown or unspecified DEPLOYMENT_TARGET
262 const MODE_TYPE
*modePtr
= (const MODE_TYPE
*)CFDataGetBytePtr((CFDataRef
)value
);
265 err
= chmod(cPath
, mode
);
266 if (err
!= 0) result
= false;
272 if ((CFTypeRef
)keys
!= buffer
) CFAllocatorDeallocate(CFGetAllocator(url
), keys
);
274 if (errorCode
) *errorCode
= result
? 0 : kCFURLUnknownError
;
278 static Boolean
_CFFileURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc
, CFURLRef url
, CFDataRef
*fetchedData
, CFArrayRef desiredProperties
, CFDictionaryRef
*fetchedProperties
, SInt32
*errorCode
) {
279 Boolean success
= true;
281 if (errorCode
) *errorCode
= 0;
285 Boolean releaseAlloc
= false;
288 // We need a real allocator to pass to _CFReadBytesFromFile so that the CFDataRef we create with
289 // CFDataCreateWithBytesNoCopy() can free up the object _CFReadBytesFromFile() returns.
290 alloc
= (CFAllocatorRef
)CFRetain(__CFGetDefaultAllocator());
293 if (!_CFReadBytesFromFile(alloc
, url
, &bytes
, &length
, 0)) {
294 if (errorCode
) *errorCode
= kCFURLUnknownError
;
298 *fetchedData
= CFDataCreateWithBytesNoCopy(alloc
, (const UInt8
*)bytes
, length
, alloc
);
301 // Now the CFData should be hanging on to it.
306 if (fetchedProperties
) {
307 *fetchedProperties
= _CFFileURLCreatePropertiesFromResource(alloc
, url
, desiredProperties
, errorCode
);
308 if (!*fetchedProperties
)
312 if ( ! success
&& fetchedData
&& *fetchedData
) {
313 CFRelease( *fetchedData
);
321 * Support for data: URLs - RFC 2397
322 * Currently this is spi for CFNetwork, to make it API, just put these constants in CFURLAccess.h
327 const CFStringRef kCFDataURLDataLength;
329 const CFStringRef kCFDataURLMimeType;
331 const CFStringRef kCFDataURLTextEncodingName;
334 /* Properties for the data: scheme. */
335 /* kCFDataURLDataLength is a CFNumber giving the data's length in bytes. */
336 /* kCFDataURLMimeType is a CFString. */
337 /* kCFDataURLTextEncodingName is a CFString. */
339 /* REMINDSMZ: From CFURLResponse.c */
340 static CFStringRef
mimeTypeFromContentTypeComponent(CFStringRef component
) {
341 CFIndex compLen
= CFStringGetLength(component
);
342 CFStringInlineBuffer buf
;
344 CFIndex firstChar
= -1, lastChar
= -1;
345 CFCharacterSetRef whitespaceSet
= CFCharacterSetGetPredefined(kCFCharacterSetWhitespace
);
346 CFStringInitInlineBuffer(component
, &buf
, CFRangeMake(0, compLen
));
348 for (idx
= 0; idx
< compLen
; idx
++) {
349 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, idx
);
351 // Delimits the charset
353 } else if (firstChar
== -1) {
354 if (!CFCharacterSetIsCharacterMember(whitespaceSet
, ch
)) {
357 } else if (!CFCharacterSetIsCharacterMember(whitespaceSet
, ch
)) {
361 if (firstChar
!= -1 && lastChar
!= -1) {
362 CFMutableStringRef newContentType
= CFStringCreateMutableCopy(CFGetAllocator(component
), compLen
, component
);
363 if (lastChar
!= compLen
- 1) {
364 CFStringDelete(newContentType
, CFRangeMake(lastChar
+ 1, compLen
- lastChar
- 1));
367 CFStringDelete(newContentType
, CFRangeMake(0, firstChar
));
369 CFStringLowercase(newContentType
, NULL
);
370 return newContentType
;
375 /* REMINDSMZ: From CFURLResponse.c */
376 static CFStringRef
charsetFromContentTypeHeader(CFStringRef contentType
) {
377 // FIXME: Should this use KeyValuePair parsing to handle quoting properly?
379 CFIndex compLen
= CFStringGetLength(contentType
);
380 CFIndex start
, end
, idx
;
381 CFCharacterSetRef whitespaceSet
;
382 CFMutableStringRef result
;
384 CFStringRef kCFURLResponseCharsetPrefix
= CFSTR("charset=");
386 if (!CFStringFindWithOptions(contentType
, kCFURLResponseCharsetPrefix
, CFRangeMake(0, compLen
), kCFCompareCaseInsensitive
, &range
) || range
.length
== 0) return NULL
;
388 whitespaceSet
= CFCharacterSetGetPredefined(kCFCharacterSetWhitespace
);
391 for (idx
= range
.location
+ range
.length
; idx
< compLen
; idx
++) {
392 UniChar ch
= CFStringGetCharacterAtIndex(contentType
, idx
);
393 if (ch
== ';' || ch
== ',') break;
395 if (!CFCharacterSetIsCharacterMember(whitespaceSet
, ch
)) {
399 } else if (!CFCharacterSetIsCharacterMember(whitespaceSet
,ch
)) {
404 if (start
== -1) return NULL
;
406 result
= CFStringCreateMutableCopy(CFGetAllocator(contentType
), compLen
,contentType
);
407 if (end
!= compLen
) {
408 CFStringDelete(result
, CFRangeMake(end
+1, compLen
-end
-1));
410 CFStringDelete(result
, CFRangeMake(0, start
));
411 CFStringLowercase(result
, NULL
);
415 #define STATIC_BUFFER_SIZE 1024
417 static BOOL
isHexDigit(char c
)
419 return (c
>= '0' && c
<= '9') || (c
>= 'A' && c
<= 'F') || (c
>= 'a' && c
<= 'f');
422 static UInt8
hexDigitValue(char c
)
424 if (c
>= '0' && c
<= '9') {
427 if (c
>= 'A' && c
<= 'F') {
430 if (c
>= 'a' && c
<= 'f') {
433 // NSURL_ERROR("illegal hex digit");
437 static CFDataRef
percentEscapeDecodeBuffer(CFAllocatorRef alloc
, const UInt8
* srcBuffer
, CFRange range
, Boolean stripWhitespace
)
440 UInt8 staticDstBuffer
[STATIC_BUFFER_SIZE
];
442 if (range
.length
> STATIC_BUFFER_SIZE
) {
443 dstBuffer
= (UInt8
*) malloc(range
.length
);
445 dstBuffer
= staticDstBuffer
;
448 CFIndex end
= range
.location
+ range
.length
;
452 for (i
= range
.location
, j
= 0; i
< end
; ++i
) {
455 if (srcBuffer
[i
] == '%' && end
> i
+ 2 && isHexDigit(srcBuffer
[i
+1]) && isHexDigit(srcBuffer
[i
+2])) {
456 value
= hexDigitValue(srcBuffer
[i
+1]) * 16 + hexDigitValue(srcBuffer
[i
+2]);
459 value
= srcBuffer
[i
];
462 if (!stripWhitespace
|| !isspace(value
)) {
463 dstBuffer
[j
++] = value
;
467 CFDataRef result
= CFDataCreate(alloc
, dstBuffer
, j
);
469 if (dstBuffer
!= staticDstBuffer
) {
477 // base 64 digits: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
479 static BOOL
isBase64Digit(char c
)
481 return (c
>= 'A' && c
<= 'Z') || (c
>= 'a' && c
<= 'z') || (c
>= '0' && c
<= '9') || (c
== '+') || (c
== '/');
484 static BOOL
isBase64DigitOrEqualSign(char c
)
486 return isBase64Digit(c
) || c
== '=';
489 static UInt8
base64DigitValue(char c
)
491 if (c
>= 'A' && c
<= 'Z') {
493 } else if (c
>= 'a' && c
<= 'z') {
495 } else if (c
>= '0' && c
<= '9') {
497 } else if (c
== '+') {
499 } else if (c
== '/') {
506 static CFDataRef
base64DecodeData(CFAllocatorRef alloc
, CFDataRef data
)
508 const UInt8
*srcBuffer
= CFDataGetBytePtr(data
);
509 CFIndex length
= CFDataGetLength(data
);
510 UInt8
*dstBuffer
= NULL
;
511 UInt8 staticDstBuffer
[STATIC_BUFFER_SIZE
];
512 CFDataRef result
= NULL
;
514 // base64 encoded data length must be multiple of 4
515 if (length
% 4 != 0) {
519 if (length
> STATIC_BUFFER_SIZE
) {
520 dstBuffer
= (UInt8
*) malloc(length
);
522 dstBuffer
= staticDstBuffer
;
527 for (i
= 0, j
= 0; i
< length
; i
+=4) {
528 if (!(isBase64Digit(srcBuffer
[i
]) &&
529 isBase64Digit(srcBuffer
[i
+1]) &&
530 isBase64DigitOrEqualSign(srcBuffer
[i
+2]) &&
531 isBase64DigitOrEqualSign(srcBuffer
[i
+3]))) {
532 if (dstBuffer
!= staticDstBuffer
) {
538 dstBuffer
[j
++] = (base64DigitValue(srcBuffer
[i
]) << 2) + (base64DigitValue(srcBuffer
[i
+1]) >> 4);
539 if (srcBuffer
[i
+2] != '=') {
540 dstBuffer
[j
++] = ((base64DigitValue(srcBuffer
[i
+1]) & 0xf) << 4) + (base64DigitValue(srcBuffer
[i
+2]) >> 2);
542 if (srcBuffer
[i
+3] != '=') {
543 dstBuffer
[j
++] = ((base64DigitValue(srcBuffer
[i
+2]) & 0x3) << 6) + (base64DigitValue(srcBuffer
[i
+3]));
547 result
= CFDataCreate(alloc
, dstBuffer
, j
);
550 if (dstBuffer
!= staticDstBuffer
) {
557 static inline CFStringRef
percentExpandAndTrimContentType(CFAllocatorRef alloc
, CFStringRef str
, CFRange range
)
559 CFMutableStringRef contentTypeHeader
= NULL
;
560 CFStringRef contentTypeUnexpanded
= CFStringCreateWithSubstring(alloc
, str
, range
);
561 CFStringRef contentTypeExpanded
= CFURLCreateStringByReplacingPercentEscapes(alloc
, contentTypeUnexpanded
, CFSTR(""));
563 if (NULL
== contentTypeExpanded
) {
564 contentTypeHeader
= CFStringCreateMutableCopy(alloc
, 0, contentTypeUnexpanded
);
566 contentTypeHeader
= CFStringCreateMutableCopy(alloc
, 0, contentTypeExpanded
);
567 CFRelease(contentTypeExpanded
);
569 CFRelease(contentTypeUnexpanded
);
570 CFStringTrimWhitespace(contentTypeHeader
);
572 return contentTypeHeader
;
575 static Boolean
parseDataRequestURL(CFURLRef url
, CFDataRef
* outData
, CFStringRef
* outMimeType
, CFStringRef
* outTextEncodingName
)
577 Boolean result
= FALSE
;
578 CFAllocatorRef alloc
= CFGetAllocator(url
);
579 CFStringRef str
= CFURLCopyResourceSpecifier(url
);
581 CFRange commaRange
= CFStringFind(str
, CFSTR(","), 0);
583 if (commaRange
.location
!= kCFNotFound
) {
584 CFStringRef contentTypeHeader
= percentExpandAndTrimContentType(alloc
, str
, CFRangeMake(0, commaRange
.location
));
585 CFStringRef mimeType
= mimeTypeFromContentTypeComponent(contentTypeHeader
);
586 CFStringRef textEncodingName
= charsetFromContentTypeHeader(contentTypeHeader
);
588 Boolean base64
= CFStringFind(contentTypeHeader
, CFSTR(";base64"), kCFCompareCaseInsensitive
).location
!= kCFNotFound
;
590 if (mimeType
== NULL
) {
591 mimeType
= (CFStringRef
) CFRetain(CFSTR("text/plain"));
594 CFIndex bufferSize
= CFURLGetBytes(url
, NULL
, 0);
595 UInt8
* srcBuffer
= (UInt8
*) malloc(bufferSize
);
596 CFURLGetBytes(url
, srcBuffer
, bufferSize
);
598 CFRange dataRange
= CFURLGetByteRangeForComponent(url
, kCFURLComponentResourceSpecifier
, NULL
);
599 while (srcBuffer
[dataRange
.location
] != ',') {
600 dataRange
.location
++;
603 dataRange
.location
++;
606 CFDataRef dataRef
= NULL
;
609 dataRef
= percentEscapeDecodeBuffer(alloc
, srcBuffer
, dataRange
, false);
611 CFDataRef unescapedAndStripped
= percentEscapeDecodeBuffer(alloc
, srcBuffer
, dataRange
, true);
612 if (unescapedAndStripped
) {
613 dataRef
= base64DecodeData(alloc
, unescapedAndStripped
);
614 CFRelease(unescapedAndStripped
);
618 if (dataRef
!= NULL
) {
620 *outMimeType
= (CFStringRef
) mimeType
== NULL
? NULL
: CFStringCreateCopy(alloc
, mimeType
);
621 *outTextEncodingName
= (CFStringRef
) textEncodingName
== NULL
? NULL
: CFStringCreateCopy(alloc
, textEncodingName
);
627 if (contentTypeHeader
) CFRelease(contentTypeHeader
);
628 if (mimeType
) CFRelease(mimeType
);
629 if (textEncodingName
) CFRelease(textEncodingName
);
638 static Boolean
_CFDataURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc
, CFURLRef url
, CFDataRef
*fetchedData
, CFArrayRef desiredProperties
, CFDictionaryRef
*fetchedProperties
, SInt32
*errorCode
) {
639 Boolean success
= true;
641 if (errorCode
) *errorCode
= 0;
643 // We always need to fetch the data...
644 CFDataRef data
= NULL
;
645 CFStringRef mimeType
= NULL
;
646 CFStringRef textEncodingName
= NULL
;
648 if (! parseDataRequestURL(url
, &data
, &mimeType
, &textEncodingName
)) {
650 *errorCode
= kCFURLUnknownError
;
655 *fetchedData
= CFDataCreateCopy(alloc
, data
);
658 if (fetchedProperties
) {
659 const void* propKeys
[] = {
660 kCFDataURLDataLength
,
662 kCFDataURLTextEncodingName
,
664 const CFIndex propKeysCount
= sizeof(propKeys
) / sizeof(propKeys
[0]);
666 if (desiredProperties
== NULL
) {
667 static CFArrayRef sAllProps
= NULL
;
668 if (sAllProps
== NULL
) {
669 sAllProps
= CFArrayCreate(kCFAllocatorSystemDefault
, propKeys
, propKeysCount
, &kCFTypeArrayCallBacks
);
671 desiredProperties
= sAllProps
;
674 const void* vals
[propKeysCount
];
675 const void* keys
[propKeysCount
];
678 CFIndex count
= CFArrayGetCount(desiredProperties
);
679 for (CFIndex i
= 0; i
< count
; i
++) {
680 CFStringRef key
= (CFStringRef
) CFArrayGetValueAtIndex(desiredProperties
, i
);
682 if (CFEqual(key
, kCFDataURLDataLength
)) {
683 CFIndex len
= CFDataGetLength(data
);
685 vals
[ixVal
++] = CFNumberCreate(alloc
, kCFNumberCFIndexType
, &len
);
686 } else if (CFEqual(key
, kCFDataURLMimeType
)) {
687 if (mimeType
!= NULL
) {
689 vals
[ixVal
++] = CFStringCreateCopy(alloc
, mimeType
);
691 } else if (CFEqual(key
, kCFDataURLTextEncodingName
)) {
692 if (textEncodingName
!= NULL
) {
694 vals
[ixVal
++] = CFStringCreateCopy(alloc
, textEncodingName
);
699 *fetchedProperties
= CFDictionaryCreate(alloc
, keys
, vals
, ixVal
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
700 for (CFIndex i
= 0; i
< ixVal
; i
++)
702 if (*fetchedProperties
== NULL
)
706 if (data
) CFRelease(data
);
707 if (mimeType
) CFRelease(mimeType
);
708 if (textEncodingName
) CFRelease(textEncodingName
);
715 /*************************/
716 /* Public routines */
717 /*************************/
719 Boolean
CFURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc
, CFURLRef url
, CFDataRef
*fetchedData
, CFDictionaryRef
*fetchedProperties
, CFArrayRef desiredProperties
, SInt32
*errorCode
) {
720 CFStringRef scheme
= CFURLCopyScheme(url
);
723 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
724 if (fetchedData
) *fetchedData
= NULL
;
725 if (fetchedProperties
) *fetchedProperties
= NULL
;
729 if (CFStringCompare(scheme
, CFSTR("file"), kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
730 result
= _CFFileURLCreateDataAndPropertiesFromResource(alloc
, url
, fetchedData
, desiredProperties
, fetchedProperties
, errorCode
);
731 } else if (CFStringCompare(scheme
, CFSTR("data"), kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
732 result
= _CFDataURLCreateDataAndPropertiesFromResource(alloc
, url
, fetchedData
, desiredProperties
, fetchedProperties
, errorCode
);
734 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
735 result
= __CFNetwork__CFURLCreateDataAndPropertiesFromResource(alloc
, url
, fetchedData
, fetchedProperties
, desiredProperties
, errorCode
);
736 #elif DEPLOYMENT_TARGET_WINDOWS
737 if (fetchedData
) *fetchedData
= NULL
;
738 if (fetchedProperties
) *fetchedProperties
= NULL
;
739 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;
742 #error Unknown or unspecified DEPLOYMENT_TARGET
750 CFTypeRef
CFURLCreatePropertyFromResource(CFAllocatorRef alloc
, CFURLRef url
, CFStringRef property
, SInt32
*errorCode
) {
751 CFArrayRef array
= CFArrayCreate(alloc
, (const void **)&property
, 1, &kCFTypeArrayCallBacks
);
752 CFDictionaryRef dict
;
754 if (CFURLCreateDataAndPropertiesFromResource(alloc
, url
, NULL
, &dict
, array
, errorCode
)) {
755 CFTypeRef result
= CFDictionaryGetValue(dict
, property
);
756 if (result
) CFRetain(result
);
761 if (dict
) CFRelease(dict
);
767 Boolean
CFURLWriteDataAndPropertiesToResource(CFURLRef url
, CFDataRef data
, CFDictionaryRef propertyDict
, SInt32
*errorCode
) {
768 CFStringRef scheme
= CFURLCopyScheme(url
);
770 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
772 } else if (CFStringCompare(scheme
, CFSTR("file"), 0) == kCFCompareEqualTo
) {
773 Boolean success
= true;
775 if (errorCode
) *errorCode
= 0;
777 if (CFURLHasDirectoryPath(url
)) {
778 // Create a directory
779 #if DEPLOYMENT_TARGET_WINDOWS
780 wchar_t cPath
[CFMaxPathSize
];
781 if (!_CFURLGetWideFileSystemRepresentation(url
, true, cPath
, CFMaxPathSize
))
783 char cPath
[CFMaxPathSize
];
784 if (!CFURLGetFileSystemRepresentation(url
, true, (unsigned char *)cPath
, CFMaxPathSize
))
787 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
790 #if DEPLOYMENT_TARGET_WINDOWS
791 success
= _CFCreateDirectoryWide(cPath
);
793 success
= _CFCreateDirectory(cPath
);
795 if (!success
&& errorCode
) *errorCode
= kCFURLUnknownError
;
799 SInt32 length
= CFDataGetLength(data
);
800 const void *bytes
= (0 == length
) ? (const void *)"" : CFDataGetBytePtr(data
);
801 success
= _CFWriteBytesToFile(url
, bytes
, length
);
802 if (!success
&& errorCode
) *errorCode
= kCFURLUnknownError
;
806 if (!_CFFileURLWritePropertiesToResource(url
, propertyDict
, errorCode
))
812 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
813 Boolean result
= __CFNetwork__CFURLWriteDataAndPropertiesToResource(url
, data
, propertyDict
, errorCode
);
815 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;
818 #elif DEPLOYMENT_TARGET_WINDOWS
819 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;
822 #error Unknown or unspecified DEPLOYMENT_TARGET
827 Boolean
CFURLDestroyResource(CFURLRef url
, SInt32
*errorCode
) {
828 CFStringRef scheme
= CFURLCopyScheme(url
);
829 char cPath
[CFMaxPathSize
];
832 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
834 } else if (CFStringCompare(scheme
, CFSTR("file"), 0) == kCFCompareEqualTo
) {
836 if (!CFURLGetFileSystemRepresentation(url
, true, (unsigned char *)cPath
, CFMaxPathSize
)) {
837 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
841 if (CFURLHasDirectoryPath(url
)) {
842 if (_CFRemoveDirectory(cPath
)) {
843 if (errorCode
) *errorCode
= 0;
846 if (errorCode
) *errorCode
= kCFURLUnknownError
;
850 if (_CFDeleteFile(cPath
)) {
851 if (errorCode
) *errorCode
= 0;
854 if (errorCode
) *errorCode
= kCFURLUnknownError
;
860 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
861 Boolean result
= __CFNetwork__CFURLDestroyResource(url
, errorCode
);
863 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;
866 #elif DEPLOYMENT_TARGET_WINDOWS
867 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;
870 #error Unknown or unspecified DEPLOYMENT_TARGET