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