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