1/* 2 * Copyright (c) 2014 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-2013, Apple Inc. All rights reserved. 26 Responsibility: Chris Linn 27*/ 28 29/*------ 30CFData read/write routines 31-------*/ 32 33#pragma GCC diagnostic push 34#pragma GCC diagnostic ignored "-Wdeprecated" 35 36#include "CFInternal.h" 37#include <CoreFoundation/CFBase.h> 38#include <CoreFoundation/CFURL.h> 39#include <CoreFoundation/CFDictionary.h> 40#include <CoreFoundation/CFURLAccess.h> 41#include <CoreFoundation/CFDate.h> 42#include <CoreFoundation/CFNumber.h> 43#include <string.h> 44#include <ctype.h> 45#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX 46#include <stdlib.h> 47#include <unistd.h> 48#include <dirent.h> 49#include <sys/stat.h> 50#include <sys/types.h> 51#include <pwd.h> 52#include <fcntl.h> 53#elif DEPLOYMENT_TARGET_WINDOWS 54#include <io.h> 55#include <sys/stat.h> 56#include <sys/types.h> 57#include <fcntl.h> 58#include <ctype.h> 59#else 60#error Unknown or unspecified DEPLOYMENT_TARGET 61#endif 62#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 63#include <dlfcn.h> 64#endif 65 66#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 67 68 69DEFINE_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) 70DEFINE_WEAK_CFNETWORK_FUNC_FAIL(Boolean, _CFURLWriteDataAndPropertiesToResource, (CFURLRef A, CFDataRef B, CFDictionaryRef C, SInt32 *D), (A, B, C, D), if (D) *D = kCFURLImproperArgumentsError, false) 71 72DEFINE_WEAK_CFNETWORK_FUNC_FAIL(Boolean, _CFURLDestroyResource, (CFURLRef A, SInt32 *B), (A, B), if(B) *B = kCFURLImproperArgumentsError, false) 73 74#endif 75 76typedef struct __NSString__ *NSString; 77 78/* 79 Pre-10.6 property keys 80*/ 81 82CONST_STRING_DECL(kCFURLFileExists, "kCFURLFileExists") 83CONST_STRING_DECL(kCFURLFilePOSIXMode, "kCFURLFilePOSIXMode") 84CONST_STRING_DECL(kCFURLFileDirectoryContents, "kCFURLFileDirectoryContents") 85CONST_STRING_DECL(kCFURLFileLength, "kCFURLFileLength") 86CONST_STRING_DECL(kCFURLFileLastModificationTime, "kCFURLFileLastModificationTime") 87CONST_STRING_DECL(kCFURLFileOwnerID, "kCFURLFileOwnerID") 88CONST_STRING_DECL(kCFURLHTTPStatusCode, "kCFURLHTTPStatusCode") 89CONST_STRING_DECL(kCFURLHTTPStatusLine, "kCFURLHTTPStatusLine") 90 91CONST_STRING_DECL(kCFDataURLDataLength, "kCFDataURLDataLength") 92CONST_STRING_DECL(kCFDataURLMimeType, "kCFDataURLMimeType") 93CONST_STRING_DECL(kCFDataURLTextEncodingName, "kCFDataURLTextEncodingName") 94 95// Compatibility property strings -- we obsoleted these names pre-DP4. REW, 5/22/2000 96CONST_STRING_DECL(kCFFileURLExists, "kCFURLFileExists") 97CONST_STRING_DECL(kCFFileURLPOSIXMode, "kCFURLFilePOSIXMode") 98CONST_STRING_DECL(kCFFileURLDirectoryContents, "kCFURLFileDirectoryContents") 99CONST_STRING_DECL(kCFFileURLSize, "kCFURLFileLength") 100CONST_STRING_DECL(kCFFileURLLastModificationTime, "kCFURLFileLastModificationTime") 101CONST_STRING_DECL(kCFHTTPURLStatusCode, "kCFURLHTTPStatusCode") 102CONST_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 112static 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 221static 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 || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX 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 277static 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, 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/* 325CF_EXPORT 326const CFStringRef kCFDataURLDataLength; 327CF_EXPORT 328const CFStringRef kCFDataURLMimeType; 329CF_EXPORT 330const 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 */ 339static 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 */ 375static 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 416static BOOL isHexDigit(char c) 417{ 418 return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); 419} 420 421static 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 436static 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 478static BOOL isBase64Digit(char c) 479{ 480 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c == '+') || (c == '/'); 481} 482 483static BOOL isBase64DigitOrEqualSign(char c) 484{ 485 return isBase64Digit(c) || c == '='; 486} 487 488static 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 505static 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 548done: 549 if (dstBuffer != staticDstBuffer) { 550 free(dstBuffer); 551 } 552 553 return result; 554} 555 556static 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 574static 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 637static 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 718Boolean 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#else 736 if (fetchedData) *fetchedData = NULL; 737 if (fetchedProperties) *fetchedProperties = NULL; 738 if (errorCode) *errorCode = kCFURLUnknownSchemeError; 739 result = false; 740#endif 741 } 742 CFRelease(scheme); 743 return result; 744 } 745} 746 747CFTypeRef CFURLCreatePropertyFromResource(CFAllocatorRef alloc, CFURLRef url, CFStringRef property, SInt32 *errorCode) { 748 CFArrayRef array = CFArrayCreate(alloc, (const void **)&property, 1, &kCFTypeArrayCallBacks); 749 CFDictionaryRef dict; 750 751 if (CFURLCreateDataAndPropertiesFromResource(alloc, url, NULL, &dict, array, errorCode)) { 752 CFTypeRef result = CFDictionaryGetValue(dict, property); 753 if (result) CFRetain(result); 754 CFRelease(array); 755 CFRelease(dict); 756 return result; 757 } else { 758 if (dict) CFRelease(dict); 759 CFRelease(array); 760 return NULL; 761 } 762} 763 764Boolean CFURLWriteDataAndPropertiesToResource(CFURLRef url, CFDataRef data, CFDictionaryRef propertyDict, SInt32 *errorCode) { 765 CFStringRef scheme = CFURLCopyScheme(url); 766 if (!scheme) { 767 if (errorCode) *errorCode = kCFURLImproperArgumentsError; 768 return false; 769 } else if (CFStringCompare(scheme, CFSTR("file"), 0) == kCFCompareEqualTo) { 770 Boolean success = true; 771 CFRelease(scheme); 772 if (errorCode) *errorCode = 0; 773 if (data) { 774 if (CFURLHasDirectoryPath(url)) { 775 // Create a directory 776 char cPath[CFMaxPathSize]; 777 if (!CFURLGetFileSystemRepresentation(url, true, (unsigned char *)cPath, CFMaxPathSize)) 778 { 779 if (errorCode) *errorCode = kCFURLImproperArgumentsError; 780 success = false; 781 } else { 782 success = _CFCreateDirectory(cPath); 783 if (!success && errorCode) *errorCode = kCFURLUnknownError; 784 } 785 } else { 786 // Write data 787 SInt32 length = CFDataGetLength(data); 788 const void *bytes = (0 == length) ? (const void *)"" : CFDataGetBytePtr(data); 789 success = _CFWriteBytesToFile(url, bytes, length); 790 if (!success && errorCode) *errorCode = kCFURLUnknownError; 791 } 792 } 793 if (propertyDict) { 794 if (!_CFFileURLWritePropertiesToResource(url, propertyDict, errorCode)) 795 success = false; 796 } 797 return success; 798 } else { 799 CFRelease(scheme); 800#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 801 Boolean result = __CFNetwork__CFURLWriteDataAndPropertiesToResource(url, data, propertyDict, errorCode); 802 if (!result) { 803 if (errorCode) *errorCode = kCFURLUnknownSchemeError; 804 } 805 return result; 806#else 807 if (errorCode) *errorCode = kCFURLUnknownSchemeError; 808 return false; 809#endif 810 } 811} 812 813Boolean CFURLDestroyResource(CFURLRef url, SInt32 *errorCode) { 814 CFStringRef scheme = CFURLCopyScheme(url); 815 char cPath[CFMaxPathSize]; 816 817 if (!scheme) { 818 if (errorCode) *errorCode = kCFURLImproperArgumentsError; 819 return false; 820 } else if (CFStringCompare(scheme, CFSTR("file"), 0) == kCFCompareEqualTo) { 821 CFRelease(scheme); 822 if (!CFURLGetFileSystemRepresentation(url, true, (unsigned char *)cPath, CFMaxPathSize)) { 823 if (errorCode) *errorCode = kCFURLImproperArgumentsError; 824 return false; 825 } 826 827 if (CFURLHasDirectoryPath(url)) { 828 if (_CFRemoveDirectory(cPath)) { 829 if (errorCode) *errorCode = 0; 830 return true; 831 } else { 832 if (errorCode) *errorCode = kCFURLUnknownError; 833 return false; 834 } 835 } else { 836 if (_CFDeleteFile(cPath)) { 837 if (errorCode) *errorCode = 0; 838 return true; 839 } else { 840 if (errorCode) *errorCode = kCFURLUnknownError; 841 return false; 842 } 843 } 844 } else { 845 CFRelease(scheme); 846#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 847 Boolean result = __CFNetwork__CFURLDestroyResource(url, errorCode); 848 if (!result) { 849 if (errorCode) *errorCode = kCFURLUnknownSchemeError; 850 } 851 return result; 852#else 853 if (errorCode) *errorCode = kCFURLUnknownSchemeError; 854 return false; 855#endif 856 } 857} 858#pragma GCC diagnostic pop 859 860 861