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