2 * Copyright (c) 2015 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-2014, Apple Inc. All rights reserved.
26 Responsibility: Jim Luther/Chris Linn
30 CFData read/write routines
33 #pragma GCC diagnostic push
34 #pragma GCC diagnostic ignored "-Wdeprecated"
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>
45 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
50 #include <sys/types.h>
53 #elif DEPLOYMENT_TARGET_WINDOWS
56 #include <sys/types.h>
60 #error Unknown or unspecified DEPLOYMENT_TARGET
62 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
66 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
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)
72 DEFINE_WEAK_CFNETWORK_FUNC_FAIL(Boolean
, _CFURLDestroyResource
, (CFURLRef A
, SInt32
*B
), (A
, B
), if(B
) *B
= kCFURLImproperArgumentsError
, false)
76 typedef struct __NSString__
*NSString
;
79 Pre-10.6 property keys
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")
91 CONST_STRING_DECL(kCFDataURLDataLength
, "kCFDataURLDataLength")
92 CONST_STRING_DECL(kCFDataURLMimeType
, "kCFDataURLMimeType")
93 CONST_STRING_DECL(kCFDataURLTextEncodingName
, "kCFDataURLTextEncodingName")
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")
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
106 /*************************/
107 /* file: access routines */
108 /*************************/
110 //#warning CF:For the moment file access failures are ill defined and set the error code to kCFURLUnknownError
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
;
117 CFMutableDictionaryRef propertyDict
= NULL
;
122 CFDateRef modTime
= NULL
, *modTimePtr
= NULL
;
123 CFArrayRef contents
= NULL
, *contentsPtr
= NULL
;
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
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
);
139 desiredProperties
= _allProps
;
142 arrayRange
.location
= 0;
143 arrayRange
.length
= CFArrayGetCount(desiredProperties
);
144 propertyDict
= CFDictionaryCreateMutable(alloc
, 0, & kCFTypeDictionaryKeyCallBacks
, & kCFTypeDictionaryValueCallBacks
);
145 if (arrayRange
.length
== 0) return propertyDict
;
147 if (CFArrayContainsValue(desiredProperties
, arrayRange
, kCFURLFileDirectoryContents
)) {
148 contentsPtr
= &contents
;
150 if (CFArrayContainsValue(desiredProperties
, arrayRange
, kCFURLFileLastModificationTime
)) {
151 modTimePtr
= &modTime
;
154 if (_CFGetFileProperties(alloc
, url
, &exists
, &posixMode
, &size
, modTimePtr
, &ownerID
, contentsPtr
) != 0) {
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
);
161 else if (errorCode
) {
162 *errorCode
= kCFURLUnknownError
;
167 for (idx
= 0; idx
< arrayRange
.length
; idx
++) {
168 CFStringRef key
= (CFMutableStringRef
)CFArrayGetValueAtIndex(desiredProperties
, idx
);
169 if (key
== kCFURLFilePOSIXMode
|| CFEqual(kCFURLFilePOSIXMode
, key
)) {
171 CFNumberRef num
= CFNumberCreate(alloc
, kCFNumberSInt32Type
, &posixMode
);
172 CFDictionarySetValue(propertyDict
, kCFURLFilePOSIXMode
, num
);
174 } else if (errorCode
) {
175 *errorCode
= kCFURLUnknownError
;
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
;
183 } else if (key
== kCFURLFileLength
|| CFEqual(kCFURLFileLength
, key
)) {
185 CFNumberRef num
= CFNumberCreate(alloc
, kCFNumberSInt64Type
, &size
);
186 CFDictionarySetValue(propertyDict
, kCFURLFileLength
, num
);
188 } else if (errorCode
) {
189 *errorCode
= kCFURLUnknownError
;
191 } else if (key
== kCFURLFileLastModificationTime
|| CFEqual(kCFURLFileLastModificationTime
, key
)) {
192 if (exists
&& modTime
) {
193 CFDictionarySetValue(propertyDict
, kCFURLFileLastModificationTime
, modTime
);
194 } else if (errorCode
) {
195 *errorCode
= kCFURLUnknownError
;
197 } else if (key
== kCFURLFileExists
|| CFEqual(kCFURLFileExists
, key
)) {
199 CFDictionarySetValue(propertyDict
, kCFURLFileExists
, kCFBooleanTrue
);
201 CFDictionarySetValue(propertyDict
, kCFURLFileExists
, kCFBooleanFalse
);
203 } else if (key
== kCFURLFileOwnerID
|| CFEqual(kCFURLFileOwnerID
, key
)) {
205 CFNumberRef num
= CFNumberCreate(alloc
, kCFNumberSInt32Type
, &ownerID
);
206 CFDictionarySetValue(propertyDict
, kCFURLFileOwnerID
, num
);
208 } else if (errorCode
) {
209 *errorCode
= kCFURLUnknownError
;
211 // Add more properties here
212 } else if (errorCode
) {
213 *errorCode
= kCFURLUnknownPropertyKeyError
;
216 if (modTime
) CFRelease(modTime
);
217 if (contents
) CFRelease(contents
);
221 static Boolean
_CFFileURLWritePropertiesToResource(CFURLRef url
, CFDictionaryRef propertyDict
, SInt32
*errorCode
) {
222 CFTypeRef buffer
[16];
225 Boolean result
= true;
227 char cPath
[CFMaxPathSize
];
229 if (!CFURLGetFileSystemRepresentation(url
, true, (unsigned char *)cPath
, CFMaxPathSize
)) {
230 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
234 count
= CFDictionaryGetCount(propertyDict
);
239 keys
= (CFTypeRef
*)CFAllocatorAllocate(CFGetAllocator(url
), sizeof(void *) * count
* 2, 0);
240 values
= keys
+ count
;
242 CFDictionaryGetKeysAndValues(propertyDict
, keys
, values
);
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
)) {
250 if (CFNumberGetTypeID() == CFGetTypeID(value
)) {
251 CFNumberRef modeNum
= (CFNumberRef
)value
;
252 CFNumberGetValue(modeNum
, kCFNumberSInt32Type
, &mode
);
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
259 #error Unknown or unspecified DEPLOYMENT_TARGET
261 const MODE_TYPE
*modePtr
= (const MODE_TYPE
*)CFDataGetBytePtr((CFDataRef
)value
);
264 err
= chmod(cPath
, mode
);
265 if (err
!= 0) result
= false;
271 if ((CFTypeRef
)keys
!= buffer
) CFAllocatorDeallocate(CFGetAllocator(url
), keys
);
273 if (errorCode
) *errorCode
= result
? 0 : kCFURLUnknownError
;
277 static Boolean
_CFFileURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc
, CFURLRef url
, CFDataRef
*fetchedData
, CFArrayRef desiredProperties
, CFDictionaryRef
*fetchedProperties
, SInt32
*errorCode
) {
278 Boolean success
= true;
280 if (errorCode
) *errorCode
= 0;
284 Boolean releaseAlloc
= false;
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());
292 if (!_CFReadBytesFromFile(alloc
, url
, &bytes
, &length
, 0, 0)) {
293 if (errorCode
) *errorCode
= kCFURLUnknownError
;
297 *fetchedData
= CFDataCreateWithBytesNoCopy(alloc
, (const UInt8
*)bytes
, length
, alloc
);
300 // Now the CFData should be hanging on to it.
305 if (fetchedProperties
) {
306 *fetchedProperties
= _CFFileURLCreatePropertiesFromResource(alloc
, url
, desiredProperties
, errorCode
);
307 if (!*fetchedProperties
)
311 if ( ! success
&& fetchedData
&& *fetchedData
) {
312 CFRelease( *fetchedData
);
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
326 const CFStringRef kCFDataURLDataLength;
328 const CFStringRef kCFDataURLMimeType;
330 const CFStringRef kCFDataURLTextEncodingName;
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. */
338 /* REMINDSMZ: From CFURLResponse.c */
339 static CFStringRef
mimeTypeFromContentTypeComponent(CFStringRef component
) {
340 CFIndex compLen
= CFStringGetLength(component
);
341 CFStringInlineBuffer buf
;
343 CFIndex firstChar
= -1, lastChar
= -1;
344 CFCharacterSetRef whitespaceSet
= CFCharacterSetGetPredefined(kCFCharacterSetWhitespace
);
345 CFStringInitInlineBuffer(component
, &buf
, CFRangeMake(0, compLen
));
347 for (idx
= 0; idx
< compLen
; idx
++) {
348 UniChar ch
= CFStringGetCharacterFromInlineBuffer(&buf
, idx
);
350 // Delimits the charset
352 } else if (firstChar
== -1) {
353 if (!CFCharacterSetIsCharacterMember(whitespaceSet
, ch
)) {
356 } else if (!CFCharacterSetIsCharacterMember(whitespaceSet
, ch
)) {
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));
366 CFStringDelete(newContentType
, CFRangeMake(0, firstChar
));
368 CFStringLowercase(newContentType
, NULL
);
369 return newContentType
;
374 /* REMINDSMZ: From CFURLResponse.c */
375 static CFStringRef
charsetFromContentTypeHeader(CFStringRef contentType
) {
376 // FIXME: Should this use KeyValuePair parsing to handle quoting properly?
378 CFIndex compLen
= CFStringGetLength(contentType
);
379 CFIndex start
, end
, idx
;
380 CFCharacterSetRef whitespaceSet
;
381 CFMutableStringRef result
;
383 CFStringRef kCFURLResponseCharsetPrefix
= CFSTR("charset=");
385 if (!CFStringFindWithOptions(contentType
, kCFURLResponseCharsetPrefix
, CFRangeMake(0, compLen
), kCFCompareCaseInsensitive
, &range
) || range
.length
== 0) return NULL
;
387 whitespaceSet
= CFCharacterSetGetPredefined(kCFCharacterSetWhitespace
);
390 for (idx
= range
.location
+ range
.length
; idx
< compLen
; idx
++) {
391 UniChar ch
= CFStringGetCharacterAtIndex(contentType
, idx
);
392 if (ch
== ';' || ch
== ',') break;
394 if (!CFCharacterSetIsCharacterMember(whitespaceSet
, ch
)) {
398 } else if (!CFCharacterSetIsCharacterMember(whitespaceSet
,ch
)) {
403 if (start
== -1) return NULL
;
405 result
= CFStringCreateMutableCopy(CFGetAllocator(contentType
), compLen
,contentType
);
406 if (end
!= compLen
) {
407 CFStringDelete(result
, CFRangeMake(end
+1, compLen
-end
-1));
409 CFStringDelete(result
, CFRangeMake(0, start
));
410 CFStringLowercase(result
, NULL
);
414 #define STATIC_BUFFER_SIZE 1024
416 static BOOL
isHexDigit(char c
)
418 return (c
>= '0' && c
<= '9') || (c
>= 'A' && c
<= 'F') || (c
>= 'a' && c
<= 'f');
421 static UInt8
hexDigitValue(char c
)
423 if (c
>= '0' && c
<= '9') {
426 if (c
>= 'A' && c
<= 'F') {
429 if (c
>= 'a' && c
<= 'f') {
432 // NSURL_ERROR("illegal hex digit");
436 static CFDataRef
percentEscapeDecodeBuffer(CFAllocatorRef alloc
, const UInt8
* srcBuffer
, CFRange range
, Boolean stripWhitespace
)
439 UInt8 staticDstBuffer
[STATIC_BUFFER_SIZE
];
441 if (range
.length
> STATIC_BUFFER_SIZE
) {
442 dstBuffer
= (UInt8
*) malloc(range
.length
);
444 dstBuffer
= staticDstBuffer
;
447 CFIndex end
= range
.location
+ range
.length
;
451 for (i
= range
.location
, j
= 0; i
< end
; ++i
) {
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]);
458 value
= srcBuffer
[i
];
461 if (!stripWhitespace
|| !isspace(value
)) {
462 dstBuffer
[j
++] = value
;
466 CFDataRef result
= CFDataCreate(alloc
, dstBuffer
, j
);
468 if (dstBuffer
!= staticDstBuffer
) {
476 // base 64 digits: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
478 static BOOL
isBase64Digit(char c
)
480 return (c
>= 'A' && c
<= 'Z') || (c
>= 'a' && c
<= 'z') || (c
>= '0' && c
<= '9') || (c
== '+') || (c
== '/');
483 static BOOL
isBase64DigitOrEqualSign(char c
)
485 return isBase64Digit(c
) || c
== '=';
488 static UInt8
base64DigitValue(char c
)
490 if (c
>= 'A' && c
<= 'Z') {
492 } else if (c
>= 'a' && c
<= 'z') {
494 } else if (c
>= '0' && c
<= '9') {
496 } else if (c
== '+') {
498 } else if (c
== '/') {
505 static CFDataRef
base64DecodeData(CFAllocatorRef alloc
, CFDataRef data
)
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
;
513 // base64 encoded data length must be multiple of 4
514 if (length
% 4 != 0) {
518 if (length
> STATIC_BUFFER_SIZE
) {
519 dstBuffer
= (UInt8
*) malloc(length
);
521 dstBuffer
= staticDstBuffer
;
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
) {
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);
541 if (srcBuffer
[i
+3] != '=') {
542 dstBuffer
[j
++] = ((base64DigitValue(srcBuffer
[i
+2]) & 0x3) << 6) + (base64DigitValue(srcBuffer
[i
+3]));
546 result
= CFDataCreate(alloc
, dstBuffer
, j
);
549 if (dstBuffer
!= staticDstBuffer
) {
556 static inline CFStringRef
percentExpandAndTrimContentType(CFAllocatorRef alloc
, CFStringRef str
, CFRange range
)
558 CFMutableStringRef contentTypeHeader
= NULL
;
559 CFStringRef contentTypeUnexpanded
= CFStringCreateWithSubstring(alloc
, str
, range
);
560 CFStringRef contentTypeExpanded
= CFURLCreateStringByReplacingPercentEscapes(alloc
, contentTypeUnexpanded
, CFSTR(""));
562 if (NULL
== contentTypeExpanded
) {
563 contentTypeHeader
= CFStringCreateMutableCopy(alloc
, 0, contentTypeUnexpanded
);
565 contentTypeHeader
= CFStringCreateMutableCopy(alloc
, 0, contentTypeExpanded
);
566 CFRelease(contentTypeExpanded
);
568 CFRelease(contentTypeUnexpanded
);
569 CFStringTrimWhitespace(contentTypeHeader
);
571 return contentTypeHeader
;
574 static Boolean
parseDataRequestURL(CFURLRef url
, CFDataRef
* outData
, CFStringRef
* outMimeType
, CFStringRef
* outTextEncodingName
)
576 Boolean result
= FALSE
;
577 CFAllocatorRef alloc
= CFGetAllocator(url
);
578 CFStringRef str
= CFURLCopyResourceSpecifier(url
);
580 CFRange commaRange
= CFStringFind(str
, CFSTR(","), 0);
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
);
587 Boolean base64
= CFStringFind(contentTypeHeader
, CFSTR(";base64"), kCFCompareCaseInsensitive
).location
!= kCFNotFound
;
589 if (mimeType
== NULL
) {
590 mimeType
= (CFStringRef
) CFRetain(CFSTR("text/plain"));
593 CFIndex bufferSize
= CFURLGetBytes(url
, NULL
, 0);
594 UInt8
* srcBuffer
= (UInt8
*) malloc(bufferSize
);
595 CFURLGetBytes(url
, srcBuffer
, bufferSize
);
597 CFRange dataRange
= CFURLGetByteRangeForComponent(url
, kCFURLComponentResourceSpecifier
, NULL
);
598 while (srcBuffer
[dataRange
.location
] != ',') {
599 dataRange
.location
++;
602 dataRange
.location
++;
605 CFDataRef dataRef
= NULL
;
608 dataRef
= percentEscapeDecodeBuffer(alloc
, srcBuffer
, dataRange
, false);
610 CFDataRef unescapedAndStripped
= percentEscapeDecodeBuffer(alloc
, srcBuffer
, dataRange
, true);
611 if (unescapedAndStripped
) {
612 dataRef
= base64DecodeData(alloc
, unescapedAndStripped
);
613 CFRelease(unescapedAndStripped
);
617 if (dataRef
!= NULL
) {
619 *outMimeType
= (CFStringRef
) mimeType
== NULL
? NULL
: CFStringCreateCopy(alloc
, mimeType
);
620 *outTextEncodingName
= (CFStringRef
) textEncodingName
== NULL
? NULL
: CFStringCreateCopy(alloc
, textEncodingName
);
626 if (contentTypeHeader
) CFRelease(contentTypeHeader
);
627 if (mimeType
) CFRelease(mimeType
);
628 if (textEncodingName
) CFRelease(textEncodingName
);
637 static Boolean
_CFDataURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc
, CFURLRef url
, CFDataRef
*fetchedData
, CFArrayRef desiredProperties
, CFDictionaryRef
*fetchedProperties
, SInt32
*errorCode
) {
638 Boolean success
= true;
640 if (errorCode
) *errorCode
= 0;
642 // We always need to fetch the data...
643 CFDataRef data
= NULL
;
644 CFStringRef mimeType
= NULL
;
645 CFStringRef textEncodingName
= NULL
;
647 if (! parseDataRequestURL(url
, &data
, &mimeType
, &textEncodingName
)) {
649 *errorCode
= kCFURLUnknownError
;
654 *fetchedData
= CFDataCreateCopy(alloc
, data
);
657 if (fetchedProperties
) {
658 const void* propKeys
[] = {
659 kCFDataURLDataLength
,
661 kCFDataURLTextEncodingName
,
663 const CFIndex propKeysCount
= sizeof(propKeys
) / sizeof(propKeys
[0]);
665 if (desiredProperties
== NULL
) {
666 static CFArrayRef sAllProps
= NULL
;
667 if (sAllProps
== NULL
) {
668 sAllProps
= CFArrayCreate(kCFAllocatorSystemDefault
, propKeys
, propKeysCount
, &kCFTypeArrayCallBacks
);
670 desiredProperties
= sAllProps
;
673 const void* vals
[propKeysCount
];
674 const void* keys
[propKeysCount
];
677 CFIndex count
= CFArrayGetCount(desiredProperties
);
678 for (CFIndex i
= 0; i
< count
; i
++) {
679 CFStringRef key
= (CFStringRef
) CFArrayGetValueAtIndex(desiredProperties
, i
);
681 if (CFEqual(key
, kCFDataURLDataLength
)) {
682 CFIndex len
= CFDataGetLength(data
);
684 vals
[ixVal
++] = CFNumberCreate(alloc
, kCFNumberCFIndexType
, &len
);
685 } else if (CFEqual(key
, kCFDataURLMimeType
)) {
686 if (mimeType
!= NULL
) {
688 vals
[ixVal
++] = CFStringCreateCopy(alloc
, mimeType
);
690 } else if (CFEqual(key
, kCFDataURLTextEncodingName
)) {
691 if (textEncodingName
!= NULL
) {
693 vals
[ixVal
++] = CFStringCreateCopy(alloc
, textEncodingName
);
698 *fetchedProperties
= CFDictionaryCreate(alloc
, keys
, vals
, ixVal
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
699 for (CFIndex i
= 0; i
< ixVal
; i
++)
701 if (*fetchedProperties
== NULL
)
705 if (data
) CFRelease(data
);
706 if (mimeType
) CFRelease(mimeType
);
707 if (textEncodingName
) CFRelease(textEncodingName
);
714 /*************************/
715 /* Public routines */
716 /*************************/
718 Boolean
CFURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc
, CFURLRef url
, CFDataRef
*fetchedData
, CFDictionaryRef
*fetchedProperties
, CFArrayRef desiredProperties
, SInt32
*errorCode
) {
719 CFStringRef scheme
= CFURLCopyScheme(url
);
722 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
723 if (fetchedData
) *fetchedData
= NULL
;
724 if (fetchedProperties
) *fetchedProperties
= NULL
;
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
);
733 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
734 result
= __CFNetwork__CFURLCreateDataAndPropertiesFromResource(alloc
, url
, fetchedData
, fetchedProperties
, desiredProperties
, errorCode
);
736 if (fetchedData
) *fetchedData
= NULL
;
737 if (fetchedProperties
) *fetchedProperties
= NULL
;
738 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;
747 CFTypeRef
CFURLCreatePropertyFromResource(CFAllocatorRef alloc
, CFURLRef url
, CFStringRef property
, SInt32
*errorCode
) {
748 CFArrayRef array
= CFArrayCreate(alloc
, (const void **)&property
, 1, &kCFTypeArrayCallBacks
);
749 CFDictionaryRef dict
;
751 if (CFURLCreateDataAndPropertiesFromResource(alloc
, url
, NULL
, &dict
, array
, errorCode
)) {
752 CFTypeRef result
= CFDictionaryGetValue(dict
, property
);
753 if (result
) CFRetain(result
);
758 if (dict
) CFRelease(dict
);
764 Boolean
CFURLWriteDataAndPropertiesToResource(CFURLRef url
, CFDataRef data
, CFDictionaryRef propertyDict
, SInt32
*errorCode
) {
765 CFStringRef scheme
= CFURLCopyScheme(url
);
767 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
769 } else if (CFStringCompare(scheme
, CFSTR("file"), 0) == kCFCompareEqualTo
) {
770 Boolean success
= true;
772 if (errorCode
) *errorCode
= 0;
774 if (CFURLHasDirectoryPath(url
)) {
775 // Create a directory
776 char cPath
[CFMaxPathSize
];
777 if (!CFURLGetFileSystemRepresentation(url
, true, (unsigned char *)cPath
, CFMaxPathSize
))
779 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
782 success
= _CFCreateDirectory(cPath
);
783 if (!success
&& errorCode
) *errorCode
= kCFURLUnknownError
;
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
;
794 if (!_CFFileURLWritePropertiesToResource(url
, propertyDict
, errorCode
))
800 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
801 Boolean result
= __CFNetwork__CFURLWriteDataAndPropertiesToResource(url
, data
, propertyDict
, errorCode
);
803 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;
807 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;
813 Boolean
CFURLDestroyResource(CFURLRef url
, SInt32
*errorCode
) {
814 CFStringRef scheme
= CFURLCopyScheme(url
);
815 char cPath
[CFMaxPathSize
];
818 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
820 } else if (CFStringCompare(scheme
, CFSTR("file"), 0) == kCFCompareEqualTo
) {
822 if (!CFURLGetFileSystemRepresentation(url
, true, (unsigned char *)cPath
, CFMaxPathSize
)) {
823 if (errorCode
) *errorCode
= kCFURLImproperArgumentsError
;
827 if (CFURLHasDirectoryPath(url
)) {
828 if (_CFRemoveDirectory(cPath
)) {
829 if (errorCode
) *errorCode
= 0;
832 if (errorCode
) *errorCode
= kCFURLUnknownError
;
836 if (_CFDeleteFile(cPath
)) {
837 if (errorCode
) *errorCode
= 0;
840 if (errorCode
) *errorCode
= kCFURLUnknownError
;
846 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
847 Boolean result
= __CFNetwork__CFURLDestroyResource(url
, errorCode
);
849 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;
853 if (errorCode
) *errorCode
= kCFURLUnknownSchemeError
;
858 #pragma GCC diagnostic pop