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