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