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