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/* CFURL.c 25 Copyright (c) 1998-2013, Apple Inc. All rights reserved. 26 Responsibility: John Iarocci 27*/ 28 29#include <CoreFoundation/CFURL.h> 30#include <CoreFoundation/CFPriv.h> 31#include <CoreFoundation/CFCharacterSetPriv.h> 32#include <CoreFoundation/CFNumber.h> 33#include "CFInternal.h" 34#include <CoreFoundation/CFStringEncodingConverter.h> 35#include <limits.h> 36#include <stdlib.h> 37#include <stdio.h> 38#include <string.h> 39#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX 40#if DEPLOYMENT_TARGET_MACOSX 41#include <CoreFoundation/CFNumberFormatter.h> 42#endif 43#include <unistd.h> 44#include <sys/stat.h> 45#include <sys/types.h> 46#include <sys/syslog.h> 47#include <CoreFoundation/CFURLPriv.h> 48#endif 49 50#ifndef DEBUG_URL_MEMORY_USAGE 51// enables various statistical counters which can be displayed with __CFURLDumpMemRecord(). 52#define DEBUG_URL_MEMORY_USAGE 0 53#endif 54 55#ifndef DEBUG_URL_INITIALIZER_LOGGING 56// enables logging in URL initializer. You get to see the inputs and output for each URL created. 57#define DEBUG_URL_INITIALIZER_LOGGING 0 58#endif 59 60 61#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 62static CFArrayRef HFSPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir); 63static CFStringRef HFSPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDir, Boolean isAbsolute); 64#endif 65static CFArrayRef WindowsPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir, Boolean isAbsolute); 66static CFStringRef WindowsPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDir, Boolean isAbsolute); 67static CFStringRef POSIXPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDirectory, Boolean isAbsolute, Boolean *posixAndUrlPathsMatch); 68static CFStringRef CreateStringFromFileSystemRepresentationByAddingPercentEscapes(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex numBytes, Boolean isDirectory, Boolean isAbsolute, Boolean windowsPath, Boolean *addedPercentEncoding); 69CFStringRef CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator, CFURLRef anURL, CFURLPathStyle fsType, Boolean resolveAgainstBase); 70CF_EXPORT CFURLRef _CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator); 71#if DEPLOYMENT_TARGET_MACOSX 72static Boolean _CFURLHasFileURLScheme(CFURLRef url, Boolean *hasScheme); 73#endif 74 75 76 77// When __CONSTANT_CFSTRINGS__ is not defined, we have separate macros for static and exported constant strings, but 78// when it is defined, we must prefix with static to prevent the string from being exported 79#ifdef __CONSTANT_CFSTRINGS__ 80static CONST_STRING_DECL(kCFURLHTTPScheme, "http") 81static CONST_STRING_DECL(kCFURLHTTPSScheme, "https") 82static CONST_STRING_DECL(kCFURLFileScheme, "file") 83static CONST_STRING_DECL(kCFURLDataScheme, "data") 84static CONST_STRING_DECL(kCFURLFTPScheme, "ftp") 85static CONST_STRING_DECL(kCFURLLocalhost, "localhost") 86#else 87CONST_STRING_DECL(kCFURLHTTPScheme, "http") 88CONST_STRING_DECL(kCFURLHTTPSScheme, "https") 89CONST_STRING_DECL(kCFURLFileScheme, "file") 90CONST_STRING_DECL(kCFURLDataScheme, "data") 91CONST_STRING_DECL(kCFURLFTPScheme, "ftp") 92CONST_STRING_DECL(kCFURLLocalhost, "localhost") 93#endif 94 95#if DEBUG_URL_MEMORY_USAGE 96static uint numURLs = 0; // number of URLs allocated 97static uint numDealloced = 0; // number of URLs deallocated 98static uint numFileURLsParsed = 0; // number of URLs created from a string which had to be parsed 99static uint numExtraDataAllocated = 0; // number of URLs with additional data -- either because URLHandle was used, or because a sanitizedString was needed 100static uint numURLsWithBaseURL = 0; // number of URLs with a baseURL 101static uint numNonUTF8EncodedURLs = 0; // number of URLs that don't have UTF8 encoding 102#endif 103 104/* The bit flags in myURL->_flags */ 105// component bits 106#define HAS_SCHEME (0x00000001) 107#define HAS_USER (0x00000002) 108#define HAS_PASSWORD (0x00000004) 109#define HAS_HOST (0x00000008) 110#define HAS_PORT (0x00000010) 111#define HAS_PATH (0x00000020) 112#define HAS_PARAMETERS (0x00000040) 113#define HAS_QUERY (0x00000080) 114#define HAS_FRAGMENT (0x00000100) 115// various boolean flags 116#define IS_IPV6_ENCODED (0x00000400) 117#define IS_DIRECTORY (0x00000800) 118#define IS_CANONICAL_FILE_URL (0x00001000) // if set, the URL is a file URL in the form "file://<absolute_percent_encoded_path>" (it was created from a file system path or representation) 119#define PATH_HAS_FILE_ID (0x00002000) 120#define IS_DECOMPOSABLE (0x00004000) 121#define POSIX_AND_URL_PATHS_MATCH (0x00008000) // POSIX_AND_URL_PATHS_MATCH will only be true if the URL path and the POSIX path are identical, character for character, except for the presence/absence of a trailing slash on directories 122#define ORIGINAL_AND_URL_STRINGS_MATCH (0x00010000) 123// scheme bits and amount to shift it to translate to the kXXXXScheme enums 124#define SCHEME_TYPE_MASK (0xE0000000) 125#define SCHEME_SHIFT 29 126enum { 127 kHasUncommonScheme = 0, 128 kHasHttpScheme = 1, 129 kHasHttpsScheme = 2, 130 kHasFileScheme = 3, 131 kHasDataScheme = 4, 132 kHasFtpScheme = 5, 133 kMaxScheme 134}; 135// accessors for the scheme bits in _flags 136CF_INLINE UInt32 _getSchemeTypeFromFlags(UInt32 flags); 137CF_INLINE void _setSchemeTypeInFlags(UInt32 *flags, UInt32 schemeType); 138 139// Other useful defines 140#define NET_LOCATION_MASK (HAS_HOST | HAS_USER | HAS_PASSWORD | HAS_PORT) 141#define RESOURCE_SPECIFIER_MASK (HAS_PARAMETERS | HAS_QUERY | HAS_FRAGMENT) 142// These flags can be compared for equality since these are all set once when the CFURL is created. 143// IS_CANONICAL_FILE_URL cannot be compared since we don't always create the URL string. 144// POSIX_AND_URL_PATHS_MATCH cannot be compared because it may not be set 145// ORIGINAL_AND_URL_STRINGS_MATCH cannot be compared because it gets set on demand later. 146#define EQUAL_FLAGS_MASK (HAS_SCHEME | HAS_USER | HAS_PASSWORD | HAS_HOST | HAS_PORT | HAS_PATH | HAS_PARAMETERS | HAS_QUERY | HAS_FRAGMENT | IS_IPV6_ENCODED | IS_DIRECTORY | PATH_HAS_FILE_ID | IS_DECOMPOSABLE | SCHEME_TYPE_MASK ) 147 148// The value of FULL_URL_REPRESENTATION must not be in the CFURLPathStyle enums. Also, its value is exposed via _CFURLCopyPropertyListRepresentation to the Finder so don't change it. 149#define FULL_URL_REPRESENTATION (0xF) 150 151/* The bit flags in _CFURLAdditionalData->_additionalDataFlags */ 152/* If ORIGINAL_AND_URL_STRINGS_MATCH in myURL->_flags is false, these bits determine where they differ. XXXX_DIFFERS must match the HAS_XXXX */ 153#define SCHEME_DIFFERS HAS_SCHEME // Scheme can actually never differ because if there were escaped characters prior to the colon, we'd interpret the string as a relative path 154#define USER_DIFFERS HAS_USER 155#define PASSWORD_DIFFERS HAS_PASSWORD 156#define HOST_DIFFERS HAS_HOST 157#define PORT_DIFFERS HAS_PORT // Port can actually never differ because if there were a non-digit following a colon in the net location, we'd interpret the whole net location as the host 158#define PATH_DIFFERS HAS_PATH // unused 159#define PARAMETERS_DIFFER HAS_PARAMETERS // unused 160#define QUERY_DIFFER HAS_QUERY // unused 161#define FRAGMENT_DIFFER HAS_FRAGMENT // unused 162 163#define FILE_ID_PREFIX ".file" 164#define FILE_ID_KEY "id" 165#define FILE_ID_PREAMBLE "/.file/id=" 166#define FILE_ID_PREAMBLE_LENGTH 10 167 168#define FILE_PREFIX "file://" 169static const UInt8 fileURLPrefix[] = FILE_PREFIX; 170 171// FILE_PREFIX_WITH_AUTHORITY and fileURLPrefixWithAuthority are only needed because some code incorrectly expects file URLs to have a host of "localhost", so if the application is linked on or before OS X 10.9 or iOS 7.0, we add "localhost" to file path URLs we create. 172#define FILE_PREFIX_WITH_AUTHORITY "file://localhost" 173static const UInt8 fileURLPrefixWithAuthority[] = FILE_PREFIX_WITH_AUTHORITY; 174 175static Boolean AddAuthorityToFileURL(void) 176{ 177 static Boolean result = false; 178 return ( result ); 179} 180 181// In order to reduce the sizeof ( __CFURL ), move these items into a seperate structure which is 182// only allocated when necessary. In my tests, it's almost never needed -- very rarely does a CFURL have 183// either a sanitized string or a reserved pointer for URLHandle. 184struct _CFURLAdditionalData { 185 void *_reserved; // Reserved for URLHandle's use. 186 CFStringRef _sanitizedString; // The fully compliant RFC string. This is only non-NULL if ORIGINAL_AND_URL_STRINGS_MATCH is false. 187 UInt32 _additionalDataFlags; // these flags only apply to things we need to keep state for in _CFURLAdditionalData (like the XXXX_DIFFERS flags) 188}; 189 190struct __CFURL { 191 CFRuntimeBase _cfBase; 192 UInt32 _flags; 193 CFStringEncoding _encoding; // The encoding to use when asked to remove percent escapes 194 CFStringRef _string; // Never NULL 195 CFURLRef _base; 196 CFRange *_ranges; 197 struct _CFURLAdditionalData* _extra; 198 void *_resourceInfo; // For use by CoreServicesInternal to cache property values. Retained and released by CFURL. 199}; 200 201 202CF_INLINE void* _getReserved ( const struct __CFURL* url ) 203{ 204 if ( url && url->_extra ) { 205 return ( url->_extra->_reserved ); 206 } 207 else { 208 return ( NULL ); 209 } 210} 211 212CF_INLINE CFStringRef _getSanitizedString(const struct __CFURL* url) 213{ 214 if ( url && url->_extra ) { 215 return ( url->_extra->_sanitizedString ); 216 } 217 else { 218 return ( NULL ); 219 } 220} 221 222CF_INLINE UInt32 _getAdditionalDataFlags(const struct __CFURL* url) 223{ 224 if ( url && url->_extra ) { 225 return ( url->_extra->_additionalDataFlags ); 226 } 227 else { 228 return ( 0 ); 229 } 230} 231 232CF_INLINE void* _getResourceInfo ( const struct __CFURL* url ) 233{ 234 if ( url ) { 235 return url->_resourceInfo; 236 } 237 else { 238 return NULL; 239 } 240} 241 242static void _CFURLAllocateExtraDataspace( struct __CFURL* url ) 243{ 244 if ( url && ! url->_extra ) 245 { struct _CFURLAdditionalData* extra = (struct _CFURLAdditionalData*) CFAllocatorAllocate( CFGetAllocator( url), sizeof( struct _CFURLAdditionalData ), __kCFAllocatorGCScannedMemory); 246 247 extra->_reserved = _getReserved( url ); 248 extra->_additionalDataFlags = _getAdditionalDataFlags(url); 249 extra->_sanitizedString = _getSanitizedString(url); 250 251 url->_extra = extra; 252 253 #if DEBUG_URL_MEMORY_USAGE 254 numExtraDataAllocated ++; 255 #endif 256 } 257} 258 259CF_INLINE void _setReserved ( struct __CFURL* url, void* reserved ) 260{ 261 if ( url ) 262 { 263 // Don't allocate extra space if we're just going to be storing NULL 264 if ( !url->_extra && reserved ) 265 _CFURLAllocateExtraDataspace( url ); 266 267 if ( url->_extra ) 268 __CFAssignWithWriteBarrier((void **)&url->_extra->_reserved, reserved); 269 } 270} 271 272CF_INLINE void _setSanitizedString( struct __CFURL* url, CFMutableStringRef sanitizedString ) 273{ 274 if ( url ) 275 { 276 // Don't allocate extra space if we're just going to be storing NULL 277 if ( !url->_extra && sanitizedString ) { 278 _CFURLAllocateExtraDataspace( url ); 279 } 280 281 if ( url->_extra ) { 282 if ( url->_extra->_sanitizedString ) { 283 CFRelease(url->_extra->_sanitizedString); 284 } 285 url->_extra->_sanitizedString = CFStringCreateCopy(CFGetAllocator(url), sanitizedString); 286 287 } 288 } 289} 290 291CF_INLINE void _setAdditionalDataFlags(struct __CFURL* url, UInt32 additionalDataFlags) 292{ 293 if ( url ) 294 { 295 // Don't allocate extra space if we're just going to be storing 0 296 if ( !url->_extra && (additionalDataFlags != 0) ) { 297 _CFURLAllocateExtraDataspace( url ); 298 } 299 300 if ( url->_extra ) { 301 url->_extra->_additionalDataFlags = additionalDataFlags; 302 } 303 } 304} 305 306CF_INLINE void _setResourceInfo ( struct __CFURL* url, void* resourceInfo ) 307{ 308 // Must be atomic 309 // Never a GC object 310 if ( url && OSAtomicCompareAndSwapPtrBarrier( NULL, resourceInfo, &url->_resourceInfo )) { 311 CFRetain( resourceInfo ); 312 } 313} 314 315CF_INLINE UInt32 _getSchemeTypeFromFlags(UInt32 flags) 316{ 317 return ( (flags & SCHEME_TYPE_MASK) >> SCHEME_SHIFT ); 318} 319 320CF_INLINE void _setSchemeTypeInFlags(UInt32 *flags, UInt32 schemeType) 321{ 322 CFAssert2((schemeType >= kHasUncommonScheme) && (schemeType < kMaxScheme), __kCFLogAssertion, "%s(): Received bad schemeType %d", __PRETTY_FUNCTION__, schemeType); 323 *flags = (*flags & ~SCHEME_TYPE_MASK) + (schemeType << SCHEME_SHIFT); 324} 325 326static Boolean _pathHasFileIDPrefix(CFStringRef path); 327static CFStringRef _resolveFileSystemPaths(CFStringRef relativePath, CFStringRef basePath, Boolean baseIsDir, CFURLPathStyle fsType, CFAllocatorRef alloc); 328static void _parseComponents(CFAllocatorRef alloc, CFStringRef string, CFURLRef base, UInt32 *flags, CFRange **range); 329static CFRange _rangeForComponent(UInt32 flags, CFRange *ranges, UInt32 compFlag); 330static CFRange _netLocationRange(UInt32 flags, CFRange *ranges); 331static UInt32 _firstResourceSpecifierFlag(UInt32 flags); 332static void computeSanitizedString(CFURLRef url); 333static CFStringRef correctedComponent(CFStringRef component, UInt32 compFlag, CFStringEncoding enc); 334static CFMutableStringRef resolveAbsoluteURLString(CFAllocatorRef alloc, CFStringRef relString, UInt32 relFlags, CFRange *relRanges, CFStringRef baseString, UInt32 baseFlags, CFRange *baseRanges); 335static CFStringRef _resolvedPath(UniChar *pathStr, UniChar *end, UniChar pathDelimiter, Boolean stripLeadingDotDots, Boolean stripTrailingDelimiter, CFAllocatorRef alloc); 336 337 338CF_INLINE void _parseComponentsOfURL(CFURLRef url) { 339 _parseComponents(CFGetAllocator(url), url->_string, url->_base, &(((struct __CFURL *)url)->_flags), &(((struct __CFURL *)url)->_ranges)); 340} 341 342enum { 343 VALID = 1, 344 ALPHA = 2, 345 PATHVALID = 4, 346 SCHEME = 8, 347 HEXDIGIT = 16 348}; 349 350static const unsigned char sURLValidCharacters[128] = { 351 /* nul 0 */ 0, 352 /* soh 1 */ 0, 353 /* stx 2 */ 0, 354 /* etx 3 */ 0, 355 /* eot 4 */ 0, 356 /* enq 5 */ 0, 357 /* ack 6 */ 0, 358 /* bel 7 */ 0, 359 /* bs 8 */ 0, 360 /* ht 9 */ 0, 361 /* nl 10 */ 0, 362 /* vt 11 */ 0, 363 /* np 12 */ 0, 364 /* cr 13 */ 0, 365 /* so 14 */ 0, 366 /* si 15 */ 0, 367 /* dle 16 */ 0, 368 /* dc1 17 */ 0, 369 /* dc2 18 */ 0, 370 /* dc3 19 */ 0, 371 /* dc4 20 */ 0, 372 /* nak 21 */ 0, 373 /* syn 22 */ 0, 374 /* etb 23 */ 0, 375 /* can 24 */ 0, 376 /* em 25 */ 0, 377 /* sub 26 */ 0, 378 /* esc 27 */ 0, 379 /* fs 28 */ 0, 380 /* gs 29 */ 0, 381 /* rs 30 */ 0, 382 /* us 31 */ 0, 383 /* sp 32 */ 0, 384 /* '!' 33 */ VALID | PATHVALID , 385 /* '"' 34 */ 0, 386 /* '#' 35 */ 0, 387 /* '$' 36 */ VALID | PATHVALID , 388 /* '%' 37 */ 0, 389 /* '&' 38 */ VALID | PATHVALID , 390 /* ''' 39 */ VALID | PATHVALID , 391 /* '(' 40 */ VALID | PATHVALID , 392 /* ')' 41 */ VALID | PATHVALID , 393 /* '*' 42 */ VALID | PATHVALID , 394 /* '+' 43 */ VALID | SCHEME | PATHVALID , 395 /* ',' 44 */ VALID | PATHVALID , 396 /* '-' 45 */ VALID | SCHEME | PATHVALID , 397 /* '.' 46 */ VALID | SCHEME | PATHVALID , 398 /* '/' 47 */ VALID | PATHVALID , 399 /* '0' 48 */ VALID | SCHEME | PATHVALID | HEXDIGIT , 400 /* '1' 49 */ VALID | SCHEME | PATHVALID | HEXDIGIT , 401 /* '2' 50 */ VALID | SCHEME | PATHVALID | HEXDIGIT , 402 /* '3' 51 */ VALID | SCHEME | PATHVALID | HEXDIGIT , 403 /* '4' 52 */ VALID | SCHEME | PATHVALID | HEXDIGIT , 404 /* '5' 53 */ VALID | SCHEME | PATHVALID | HEXDIGIT , 405 /* '6' 54 */ VALID | SCHEME | PATHVALID | HEXDIGIT , 406 /* '7' 55 */ VALID | SCHEME | PATHVALID | HEXDIGIT , 407 /* '8' 56 */ VALID | SCHEME | PATHVALID | HEXDIGIT , 408 /* '9' 57 */ VALID | SCHEME | PATHVALID | HEXDIGIT , 409 /* ':' 58 */ VALID , 410 /* ';' 59 */ VALID , 411 /* '<' 60 */ 0, 412 /* '=' 61 */ VALID | PATHVALID , 413 /* '>' 62 */ 0, 414 /* '?' 63 */ VALID , 415 /* '@' 64 */ VALID , 416 /* 'A' 65 */ VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT , 417 /* 'B' 66 */ VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT , 418 /* 'C' 67 */ VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT , 419 /* 'D' 68 */ VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT , 420 /* 'E' 69 */ VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT , 421 /* 'F' 70 */ VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT , 422 /* 'G' 71 */ VALID | ALPHA | SCHEME | PATHVALID , 423 /* 'H' 72 */ VALID | ALPHA | SCHEME | PATHVALID , 424 /* 'I' 73 */ VALID | ALPHA | SCHEME | PATHVALID , 425 /* 'J' 74 */ VALID | ALPHA | SCHEME | PATHVALID , 426 /* 'K' 75 */ VALID | ALPHA | SCHEME | PATHVALID , 427 /* 'L' 76 */ VALID | ALPHA | SCHEME | PATHVALID , 428 /* 'M' 77 */ VALID | ALPHA | SCHEME | PATHVALID , 429 /* 'N' 78 */ VALID | ALPHA | SCHEME | PATHVALID , 430 /* 'O' 79 */ VALID | ALPHA | SCHEME | PATHVALID , 431 /* 'P' 80 */ VALID | ALPHA | SCHEME | PATHVALID , 432 /* 'Q' 81 */ VALID | ALPHA | SCHEME | PATHVALID , 433 /* 'R' 82 */ VALID | ALPHA | SCHEME | PATHVALID , 434 /* 'S' 83 */ VALID | ALPHA | SCHEME | PATHVALID , 435 /* 'T' 84 */ VALID | ALPHA | SCHEME | PATHVALID , 436 /* 'U' 85 */ VALID | ALPHA | SCHEME | PATHVALID , 437 /* 'V' 86 */ VALID | ALPHA | SCHEME | PATHVALID , 438 /* 'W' 87 */ VALID | ALPHA | SCHEME | PATHVALID , 439 /* 'X' 88 */ VALID | ALPHA | SCHEME | PATHVALID , 440 /* 'Y' 89 */ VALID | ALPHA | SCHEME | PATHVALID , 441 /* 'Z' 90 */ VALID | ALPHA | SCHEME | PATHVALID , 442 /* '[' 91 */ 0, 443 /* '\' 92 */ 0, 444 /* ']' 93 */ 0, 445 /* '^' 94 */ 0, 446 /* '_' 95 */ VALID | PATHVALID , 447 /* '`' 96 */ 0, 448 /* 'a' 97 */ VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT , 449 /* 'b' 98 */ VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT , 450 /* 'c' 99 */ VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT , 451 /* 'd' 100 */ VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT , 452 /* 'e' 101 */ VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT , 453 /* 'f' 102 */ VALID | ALPHA | SCHEME | PATHVALID | HEXDIGIT , 454 /* 'g' 103 */ VALID | ALPHA | SCHEME | PATHVALID , 455 /* 'h' 104 */ VALID | ALPHA | SCHEME | PATHVALID , 456 /* 'i' 105 */ VALID | ALPHA | SCHEME | PATHVALID , 457 /* 'j' 106 */ VALID | ALPHA | SCHEME | PATHVALID , 458 /* 'k' 107 */ VALID | ALPHA | SCHEME | PATHVALID , 459 /* 'l' 108 */ VALID | ALPHA | SCHEME | PATHVALID , 460 /* 'm' 109 */ VALID | ALPHA | SCHEME | PATHVALID , 461 /* 'n' 110 */ VALID | ALPHA | SCHEME | PATHVALID , 462 /* 'o' 111 */ VALID | ALPHA | SCHEME | PATHVALID , 463 /* 'p' 112 */ VALID | ALPHA | SCHEME | PATHVALID , 464 /* 'q' 113 */ VALID | ALPHA | SCHEME | PATHVALID , 465 /* 'r' 114 */ VALID | ALPHA | SCHEME | PATHVALID , 466 /* 's' 115 */ VALID | ALPHA | SCHEME | PATHVALID , 467 /* 't' 116 */ VALID | ALPHA | SCHEME | PATHVALID , 468 /* 'u' 117 */ VALID | ALPHA | SCHEME | PATHVALID , 469 /* 'v' 118 */ VALID | ALPHA | SCHEME | PATHVALID , 470 /* 'w' 119 */ VALID | ALPHA | SCHEME | PATHVALID , 471 /* 'x' 120 */ VALID | ALPHA | SCHEME | PATHVALID , 472 /* 'y' 121 */ VALID | ALPHA | SCHEME | PATHVALID , 473 /* 'z' 122 */ VALID | ALPHA | SCHEME | PATHVALID , 474 /* '{' 123 */ 0, 475 /* '|' 124 */ 0, 476 /* '}' 125 */ 0, 477 /* '~' 126 */ VALID | PATHVALID , 478 /* del 127 */ 0, 479}; 480 481CF_INLINE Boolean isURLLegalCharacter(UniChar ch) { 482 return (ch <= 127) ? ((sURLValidCharacters[ch] & VALID) != 0) : false; 483} 484 485CF_INLINE Boolean scheme_valid(UniChar ch) { 486 return (ch <= 127) ? ((sURLValidCharacters[ch] & SCHEME) != 0) : false; 487} 488 489CF_INLINE Boolean isALPHA(UniChar ch) { 490 return (ch <= 127) ? ((sURLValidCharacters[ch] & ALPHA) != 0) : false; 491} 492 493CF_INLINE Boolean isPathLegalCharacter(UniChar ch) { 494 return (ch <= 127) ? ((sURLValidCharacters[ch] & PATHVALID) != 0) : false; 495} 496 497CF_INLINE Boolean isHexDigit(UniChar ch) { 498 return (ch <= 127) ? ((sURLValidCharacters[ch] & HEXDIGIT) != 0) : false; 499} 500 501// Returns false if ch1 or ch2 isn't properly formatted 502CF_INLINE Boolean _translateBytes(UniChar ch1, UniChar ch2, uint8_t *result) { 503 *result = 0; 504 if (ch1 >= '0' && ch1 <= '9') *result += (ch1 - '0'); 505 else if (ch1 >= 'a' && ch1 <= 'f') *result += 10 + ch1 - 'a'; 506 else if (ch1 >= 'A' && ch1 <= 'F') *result += 10 + ch1 - 'A'; 507 else return false; 508 509 *result = (*result) << 4; 510 if (ch2 >= '0' && ch2 <= '9') *result += (ch2 - '0'); 511 else if (ch2 >= 'a' && ch2 <= 'f') *result += 10 + ch2 - 'a'; 512 else if (ch2 >= 'A' && ch2 <= 'F') *result += 10 + ch2 - 'A'; 513 else return false; 514 515 return true; 516} 517 518CF_INLINE Boolean _haveTestedOriginalString(CFURLRef url) { 519 return ((url->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) != 0) || (_getSanitizedString(url) != NULL); 520} 521 522enum { 523 IS_PCHAR = 0x01, 524}; 525 526static const unsigned char sURLValidBytes[256] = { 527 /* nul 0 */ 0, 528 /* soh 1 */ 0, 529 /* stx 2 */ 0, 530 /* etx 3 */ 0, 531 /* eot 4 */ 0, 532 /* enq 5 */ 0, 533 /* ack 6 */ 0, 534 /* bel 7 */ 0, 535 /* bs 8 */ 0, 536 /* ht 9 */ 0, 537 /* nl 10 */ 0, 538 /* vt 11 */ 0, 539 /* np 12 */ 0, 540 /* cr 13 */ 0, 541 /* so 14 */ 0, 542 /* si 15 */ 0, 543 /* dle 16 */ 0, 544 /* dc1 17 */ 0, 545 /* dc2 18 */ 0, 546 /* dc3 19 */ 0, 547 /* dc4 20 */ 0, 548 /* nak 21 */ 0, 549 /* syn 22 */ 0, 550 /* etb 23 */ 0, 551 /* can 24 */ 0, 552 /* em 25 */ 0, 553 /* sub 26 */ 0, 554 /* esc 27 */ 0, 555 /* fs 28 */ 0, 556 /* gs 29 */ 0, 557 /* rs 30 */ 0, 558 /* us 31 */ 0, 559 /* sp 32 */ 0, 560 /* '!' 33 */ IS_PCHAR, 561 /* '"' 34 */ 0, 562 /* '#' 35 */ 0, 563 /* '$' 36 */ IS_PCHAR, 564 /* '%' 37 */ 0, 565 /* '&' 38 */ IS_PCHAR, 566 /* ''' 39 */ IS_PCHAR, 567 /* '(' 40 */ IS_PCHAR, 568 /* ')' 41 */ IS_PCHAR, 569 /* '*' 42 */ IS_PCHAR, 570 /* '+' 43 */ IS_PCHAR, 571 /* ',' 44 */ IS_PCHAR, 572 /* '-' 45 */ IS_PCHAR, 573 /* '.' 46 */ IS_PCHAR, 574 /* '/' 47 */ IS_PCHAR, // not really a pchar -- it's the segment delimiter 575 /* '0' 48 */ IS_PCHAR, 576 /* '1' 49 */ IS_PCHAR, 577 /* '2' 50 */ IS_PCHAR, 578 /* '3' 51 */ IS_PCHAR, 579 /* '4' 52 */ IS_PCHAR, 580 /* '5' 53 */ IS_PCHAR, 581 /* '6' 54 */ IS_PCHAR, 582 /* '7' 55 */ IS_PCHAR, 583 /* '8' 56 */ IS_PCHAR, 584 /* '9' 57 */ IS_PCHAR, 585 /* ':' 58 */ IS_PCHAR, 586 /* ';' 59 */ 0, // we need to percent-escape ';' in file system paths so it won't be mistaken for the start of the obsolete param rule (rfc2396) that CFURL still supports 587 /* '<' 60 */ 0, 588 /* '=' 61 */ IS_PCHAR, 589 /* '>' 62 */ 0, 590 /* '?' 63 */ 0, 591 /* '@' 64 */ IS_PCHAR, 592 /* 'A' 65 */ IS_PCHAR, 593 /* 'B' 66 */ IS_PCHAR, 594 /* 'C' 67 */ IS_PCHAR, 595 /* 'D' 68 */ IS_PCHAR, 596 /* 'E' 69 */ IS_PCHAR, 597 /* 'F' 70 */ IS_PCHAR, 598 /* 'G' 71 */ IS_PCHAR, 599 /* 'H' 72 */ IS_PCHAR, 600 /* 'I' 73 */ IS_PCHAR, 601 /* 'J' 74 */ IS_PCHAR, 602 /* 'K' 75 */ IS_PCHAR, 603 /* 'L' 76 */ IS_PCHAR, 604 /* 'M' 77 */ IS_PCHAR, 605 /* 'N' 78 */ IS_PCHAR, 606 /* 'O' 79 */ IS_PCHAR, 607 /* 'P' 80 */ IS_PCHAR, 608 /* 'Q' 81 */ IS_PCHAR, 609 /* 'R' 82 */ IS_PCHAR, 610 /* 'S' 83 */ IS_PCHAR, 611 /* 'T' 84 */ IS_PCHAR, 612 /* 'U' 85 */ IS_PCHAR, 613 /* 'V' 86 */ IS_PCHAR, 614 /* 'W' 87 */ IS_PCHAR, 615 /* 'X' 88 */ IS_PCHAR, 616 /* 'Y' 89 */ IS_PCHAR, 617 /* 'Z' 90 */ IS_PCHAR, 618 /* '[' 91 */ 0, 619 /* '\' 92 */ 0, 620 /* ']' 93 */ 0, 621 /* '^' 94 */ 0, 622 /* '_' 95 */ IS_PCHAR, 623 /* '`' 96 */ 0, 624 /* 'a' 97 */ IS_PCHAR, 625 /* 'b' 98 */ IS_PCHAR, 626 /* 'c' 99 */ IS_PCHAR, 627 /* 'd' 100 */ IS_PCHAR, 628 /* 'e' 101 */ IS_PCHAR, 629 /* 'f' 102 */ IS_PCHAR, 630 /* 'g' 103 */ IS_PCHAR, 631 /* 'h' 104 */ IS_PCHAR, 632 /* 'i' 105 */ IS_PCHAR, 633 /* 'j' 106 */ IS_PCHAR, 634 /* 'k' 107 */ IS_PCHAR, 635 /* 'l' 108 */ IS_PCHAR, 636 /* 'm' 109 */ IS_PCHAR, 637 /* 'n' 110 */ IS_PCHAR, 638 /* 'o' 111 */ IS_PCHAR, 639 /* 'p' 112 */ IS_PCHAR, 640 /* 'q' 113 */ IS_PCHAR, 641 /* 'r' 114 */ IS_PCHAR, 642 /* 's' 115 */ IS_PCHAR, 643 /* 't' 116 */ IS_PCHAR, 644 /* 'u' 117 */ IS_PCHAR, 645 /* 'v' 118 */ IS_PCHAR, 646 /* 'w' 119 */ IS_PCHAR, 647 /* 'x' 120 */ IS_PCHAR, 648 /* 'y' 121 */ IS_PCHAR, 649 /* 'z' 122 */ IS_PCHAR, 650 /* '{' 123 */ 0, 651 /* '|' 124 */ 0, 652 /* '}' 125 */ 0, 653 /* '~' 126 */ IS_PCHAR, 654 /* del 127 */ 0, 655 0, 656 0, 657 0, 658 0, 659 0, 660 0, 661 0, 662 0, 663 0, 664 0, 665 0, 666 0, 667 0, 668 0, 669 0, 670 0, 671 0, 672 0, 673 0, 674 0, 675 0, 676 0, 677 0, 678 0, 679 0, 680 0, 681 0, 682 0, 683 0, 684 0, 685 0, 686 0, 687 0, 688 0, 689 0, 690 0, 691 0, 692 0, 693 0, 694 0, 695 0, 696 0, 697 0, 698 0, 699 0, 700 0, 701 0, 702 0, 703 0, 704 0, 705 0, 706 0, 707 0, 708 0, 709 0, 710 0, 711 0, 712 0, 713 0, 714 0, 715 0, 716 0, 717 0, 718 0, 719 0, 720 0, 721 0, 722 0, 723 0, 724 0, 725 0, 726 0, 727 0, 728 0, 729 0, 730 0, 731 0, 732 0, 733 0, 734 0, 735 0, 736 0, 737 0, 738 0, 739 0, 740 0, 741 0, 742 0, 743 0, 744 0, 745 0, 746 0, 747 0, 748 0, 749 0, 750 0, 751 0, 752 0, 753 0, 754 0, 755 0, 756 0, 757 0, 758 0, 759 0, 760 0, 761 0, 762 0, 763 0, 764 0, 765 0, 766 0, 767 0, 768 0, 769 0, 770 0, 771 0, 772 0, 773 0, 774 0, 775 0, 776 0, 777 0, 778 0, 779 0, 780 0, 781 0, 782 0, 783}; 784 785CF_INLINE Boolean is_pchar(unsigned char ch) { 786 return ( (sURLValidBytes[ch] & IS_PCHAR) != 0 ); 787} 788 789 790/* 791 CreateStringFromFileSystemRepresentationByAddingPercentEscapes creates a CFString 792 for the path-absolute form of a URI path component from the native file system representation. 793 Note: this code uses '/' path separators 794 795 The rules for path-absolute from rfc3986 are: 796 path-absolute = "/" [ segment-nz *( "/" segment ) ] 797 segment = *pchar 798 segment-nz = 1*pchar 799 pchar = unreserved / pct-encoded / sub-delims / ":" / "@" 800 pct-encoded = "%" HEXDIG HEXDIG 801 unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" 802 sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" 803 */ 804static CFStringRef CreateStringFromFileSystemRepresentationByAddingPercentEscapes(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex numBytes, Boolean isDirectory, Boolean isAbsolute, Boolean windowsPath, Boolean *addedPercentEncoding) 805{ 806 static const UInt8 hexchars[] = "0123456789ABCDEF"; 807 const UInt8 *fileURLPrefixPtr; 808 size_t fileURLPrefixLength; 809 if ( AddAuthorityToFileURL() ) { 810 fileURLPrefixPtr = fileURLPrefixWithAuthority; 811 fileURLPrefixLength = sizeof(fileURLPrefixWithAuthority); 812 } 813 else { 814 fileURLPrefixPtr = fileURLPrefix; 815 fileURLPrefixLength = sizeof(fileURLPrefix); 816 } 817 STACK_BUFFER_DECL(UInt8, stackBuf, (PATH_MAX * 3) + (isAbsolute ? fileURLPrefixLength : 0) + (isDirectory ? 1 : 0)); // worst case is every byte needs to be percent-escaped 818 UInt8 *bufStartPtr; 819 UInt8 *bufBytePtr; 820 const UInt8 *bytePtr = bytes; 821 CFIndex idx; 822 CFStringRef result; 823 Boolean addedPercent = FALSE; 824 825 // choose a buffer to percent-escape into. 826 if ( numBytes <= PATH_MAX ) { 827 bufStartPtr = &stackBuf[0]; 828 } 829 else { 830 // worst case is every byte needs to be percent-escaped 831 bufStartPtr = (UInt8 *)malloc((numBytes * 3) + (isAbsolute ? fileURLPrefixLength : 0) + (isDirectory ? 1 : 0)); 832 } 833 834 if ( bufStartPtr != NULL ) { 835 if ( isAbsolute ) { 836 // start with the fileURLPrefix 837 strcpy((char *)bufStartPtr, (char *)fileURLPrefixPtr); 838 bufBytePtr = bufStartPtr + fileURLPrefixLength - 1; 839 } 840 else { 841 bufBytePtr = bufStartPtr; 842 } 843 844 if ( !windowsPath ) { 845 for ( idx = 0; (idx < numBytes) && (*bytePtr != 0); ++idx ) { 846 if ( is_pchar(*bytePtr) ) { 847 *bufBytePtr++ = *bytePtr; 848 } 849 else { 850 *bufBytePtr++ = '%'; 851 *bufBytePtr++ = hexchars[*bytePtr >> 4]; 852 *bufBytePtr++ = hexchars[*bytePtr & 0x0f]; 853 addedPercent = TRUE; 854 } 855 ++bytePtr; 856 } 857 } 858 else { 859 for ( idx = 0; (idx < numBytes) && (*bytePtr != 0); ++idx ) { 860 if ( is_pchar(*bytePtr) && (*bytePtr != '/') ) { // percent-escape the forward slash if this is a windowsPath 861 *bufBytePtr++ = *bytePtr; 862 } 863 else { 864 *bufBytePtr++ = '%'; 865 *bufBytePtr++ = hexchars[*bytePtr >> 4]; 866 *bufBytePtr++ = hexchars[*bytePtr & 0x0f]; 867 addedPercent = TRUE; 868 } 869 ++bytePtr; 870 } 871 } 872 873 // did we convert numBytes? 874 if ( idx != numBytes ) { 875 // no, but it's OK if the remaining bytes are all nul (embedded nul bytes are not allowed) 876 const UInt8 *nullBytePtr = bytePtr; 877 for ( /* start where we left off */; (idx < numBytes) && (*nullBytePtr == '\0'); ++idx, ++nullBytePtr ) { 878 // do nothing 879 } 880 } 881 882 if ( idx == numBytes ) { 883 if ( isDirectory ) { 884 // if it is a directory and it doesn't end with PATH_SEP, append a PATH_SEP. 885 if ( bytes[numBytes-1] != '/' ) { 886 *bufBytePtr++ = '/'; 887 } 888 } 889 else { 890 // it is not a directory: remove any pathDelim characters at end (leaving at least one character) 891 while ( (numBytes > 1) && (bytes[numBytes-1] == '/') ) { 892 --bufBytePtr; 893 --numBytes; 894 } 895 } 896 897 // create the result 898 result = CFStringCreateWithBytes(alloc, bufStartPtr, (CFIndex)(bufBytePtr-bufStartPtr), kCFStringEncodingUTF8, FALSE); 899 } 900 else { 901 // the remaining bytes were not all nul 902 result = NULL; 903 } 904 905 // free the buffer if we malloc'd it 906 if ( bufStartPtr != &stackBuf[0] ) { 907 free(bufStartPtr); 908 } 909 } 910 else { 911 result = NULL; 912 } 913 914 if ( addedPercentEncoding ) { 915 *addedPercentEncoding = addedPercent; 916 } 917 918 return ( result ); 919} 920 921// Returns NULL if str cannot be converted for whatever reason, str if str contains no characters in need of escaping, or a newly-created string with the appropriate % escape codes in place. Caller must always release the returned string. 922CF_INLINE CFStringRef _replacePathIllegalCharacters(CFStringRef str, CFAllocatorRef alloc, Boolean preserveSlashes) { 923 CFStringRef result = NULL; 924 STACK_BUFFER_DECL(char, buffer, PATH_MAX); 925 if ( CFStringGetCString(str, buffer, PATH_MAX, kCFStringEncodingUTF8) ) { 926 result = CreateStringFromFileSystemRepresentationByAddingPercentEscapes(kCFAllocatorDefault, (const UInt8 *)buffer, strlen(buffer), FALSE, FALSE, !preserveSlashes, NULL); 927 } 928 return result; 929} 930 931static Boolean _appendPercentEscapesForCharacter(UniChar ch, CFStringEncoding encoding, CFMutableStringRef str) { 932 uint8_t bytes[6]; // 6 bytes is the maximum a single character could require in UTF8 (most common case); other encodings could require more 933 uint8_t *bytePtr = bytes, *currByte; 934 CFIndex byteLength; 935 CFAllocatorRef alloc = NULL; 936 if (CFStringEncodingUnicodeToBytes(encoding, 0, &ch, 1, NULL, bytePtr, 6, &byteLength) != kCFStringEncodingConversionSuccess) { 937 byteLength = CFStringEncodingByteLengthForCharacters(encoding, 0, &ch, 1); 938 if (byteLength <= 6) { 939 // The encoding cannot accomodate the character 940 return false; 941 } 942 alloc = CFGetAllocator(str); 943 bytePtr = (uint8_t *)CFAllocatorAllocate(alloc, byteLength, 0); 944 if (!bytePtr || CFStringEncodingUnicodeToBytes(encoding, 0, &ch, 1, NULL, bytePtr, byteLength, &byteLength) != kCFStringEncodingConversionSuccess) { 945 if (bytePtr) CFAllocatorDeallocate(alloc, bytePtr); 946 return false; 947 } 948 } 949 for (currByte = bytePtr; currByte < bytePtr + byteLength; currByte ++) { 950 UniChar escapeSequence[3] = {'%', '\0', '\0'}; 951 unsigned char high, low; 952 high = ((*currByte) & 0xf0) >> 4; 953 low = (*currByte) & 0x0f; 954 escapeSequence[1] = (high < 10) ? '0' + high : 'A' + high - 10; 955 escapeSequence[2] = (low < 10) ? '0' + low : 'A' + low - 10; 956 CFStringAppendCharacters(str, escapeSequence, 3); 957 } 958 if (bytePtr != bytes) { 959 CFAllocatorDeallocate(alloc, bytePtr); 960 } 961 return true; 962} 963 964static CFStringRef UnescapeAllWithUTF8(CFAllocatorRef alloc, CFStringRef originalString) 965{ 966 CFStringRef result = NULL; 967 CFIndex strLength = CFStringGetLength(originalString); 968 CFIndex maxBufferSize = CFStringGetMaximumSizeForEncoding(strLength, kCFStringEncodingUTF8); 969 CFIndex stackBufferSize = 2096; 970 STACK_BUFFER_DECL(UInt8, escapedStackBuf, stackBufferSize *2); 971 UInt8 *escapedBuf; 972 UInt8 *unescapedBuf; 973 // choose a buffer to percent-escape into. 974 if ( maxBufferSize <= stackBufferSize ) { 975 escapedBuf = &escapedStackBuf[0]; 976 } 977 else { 978 escapedBuf = (UInt8 *)malloc(maxBufferSize * 2); 979 } 980 if ( escapedBuf ) { 981 CFIndex charsConverted; 982 CFIndex usedBufLen; 983 unescapedBuf = &escapedBuf[maxBufferSize]; 984 charsConverted = CFStringGetBytes(originalString, CFRangeMake(0, strLength), kCFStringEncodingUTF8, 0, false, escapedBuf, maxBufferSize, &usedBufLen); 985 if ( charsConverted ) { 986 // 0x80 marks invalid hex digits so this table can validate the digits while getting the values 987 static const UInt8 hexvalues[] = { 988 /* 00 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 989 /* 08 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 990 /* 10 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 991 /* 18 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 992 /* 20 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 993 /* 28 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 994 /* 30 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 995 /* 38 */ 0x08, 0x09, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 996 /* 40 */ 0x80, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x80, 997 /* 48 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 998 /* 50 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 999 /* 58 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1000 /* 60 */ 0x80, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x80, 1001 /* 68 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1002 /* 70 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1003 /* 78 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1004 1005 /* 80 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1006 /* 88 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1007 /* 90 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1008 /* 98 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1009 /* A0 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1010 /* A8 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1011 /* B0 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1012 /* B8 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1013 /* C0 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1014 /* C8 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1015 /* D0 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1016 /* D8 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1017 /* E0 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1018 /* E8 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1019 /* F0 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1020 /* F8 */ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 1021 }; 1022 UInt8 *bufStartPtr; 1023 UInt8 *bufPtr; 1024 const UInt8 *bytePtr = escapedBuf; 1025 CFIndex idx; 1026 1027 bufPtr = bufStartPtr = unescapedBuf; 1028 Boolean conversionOK = TRUE; 1029 1030 for ( idx = 0; (idx < usedBufLen) && conversionOK; ++idx ) { 1031 switch ( *bytePtr ) { 1032 case '%': 1033 idx += 2; 1034 if ( idx < usedBufLen ) { 1035 UInt8 hex1, hex2; 1036 // skip over % 1037 bytePtr++; 1038 // get the hex digits 1039 hex1 = hexvalues[*bytePtr++]; 1040 hex2 = hexvalues[*bytePtr++]; 1041 // validate them 1042 if ( ((hex1 | hex2) & 0x80) == 0 ) { 1043 // convert hex digits 1044 *bufPtr = (hex1 << 4) + hex2; 1045 } 1046 else { 1047 conversionOK = FALSE; 1048 } 1049 } 1050 else { 1051 conversionOK = FALSE; 1052 } 1053 break; 1054 default: 1055 // copy everything else 1056 *bufPtr = *bytePtr++; 1057 break; 1058 } 1059 ++bufPtr; 1060 } 1061 if ( conversionOK ) { 1062 result = CFStringCreateWithBytes(alloc, unescapedBuf, bufPtr - bufStartPtr, kCFStringEncodingUTF8, false); 1063 } 1064 } 1065 1066 // free the buffer if we malloc'd it 1067 if ( escapedBuf != &escapedStackBuf[0] ) { 1068 free(escapedBuf); 1069 } 1070 } 1071 return ( result ); 1072} 1073 1074// Uses UTF-8 to translate all percent escape sequences; returns NULL if it encounters a format failure. May return the original string. 1075CFStringRef CFURLCreateStringByReplacingPercentEscapes(CFAllocatorRef alloc, CFStringRef originalString, CFStringRef charactersToLeaveEscaped) { 1076 CFMutableStringRef newStr = NULL; 1077 CFIndex length; 1078 CFIndex mark = 0; 1079 CFRange percentRange, searchRange; 1080 CFStringRef escapedStr = NULL; 1081 CFMutableStringRef strForEscapedChar = NULL; 1082 UniChar escapedChar; 1083 Boolean escapeAll = (charactersToLeaveEscaped && CFStringGetLength(charactersToLeaveEscaped) == 0); 1084 Boolean failed = false; 1085 1086 if (!originalString) return NULL; 1087 1088 length = CFStringGetLength(originalString); 1089 1090 if ((length == 0) || (charactersToLeaveEscaped == NULL)) { 1091 return (CFStringRef)CFStringCreateCopy(alloc, originalString); 1092 } 1093 1094 if ( escapeAll ) { 1095 return ( UnescapeAllWithUTF8(alloc, originalString) ); 1096 } 1097 1098 searchRange = CFRangeMake(0, length); 1099 1100 while (!failed && CFStringFindWithOptions(originalString, CFSTR("%"), searchRange, 0, &percentRange)) { 1101 uint8_t bytes[4]; // Single UTF-8 character could require up to 4 bytes. 1102 uint8_t numBytesExpected; 1103 UniChar ch1, ch2; 1104 1105 escapedStr = NULL; 1106 // Make sure we have at least 2 more characters 1107 if (length - percentRange.location < 3) { failed = true; break; } 1108 1109 // if we don't have at least 2 more characters, we can't interpret the percent escape code, 1110 // so we assume the percent character is legit, and let it pass into the string 1111 ch1 = CFStringGetCharacterAtIndex(originalString, percentRange.location+1); 1112 ch2 = CFStringGetCharacterAtIndex(originalString, percentRange.location+2); 1113 if (!_translateBytes(ch1, ch2, bytes)) { failed = true; break; } 1114 if (!(bytes[0] & 0x80)) { 1115 numBytesExpected = 1; 1116 } else if (!(bytes[0] & 0x20)) { 1117 numBytesExpected = 2; 1118 } else if (!(bytes[0] & 0x10)) { 1119 numBytesExpected = 3; 1120 } else { 1121 numBytesExpected = 4; 1122 } 1123 if (numBytesExpected == 1) { 1124 // one byte sequence (most common case); handle this specially 1125 escapedChar = bytes[0]; 1126 if (!strForEscapedChar) { 1127 strForEscapedChar = CFStringCreateMutableWithExternalCharactersNoCopy(alloc, &escapedChar, 1, 1, kCFAllocatorNull); 1128 } 1129 escapedStr = (CFStringRef)CFRetain(strForEscapedChar); 1130 } else { 1131 CFIndex j; 1132 // Make sure up front that we have enough characters 1133 if (length < percentRange.location + numBytesExpected * 3) { failed = true; break; } 1134 for (j = 1; j < numBytesExpected; j ++) { 1135 if (CFStringGetCharacterAtIndex(originalString, percentRange.location + 3*j) != '%') { failed = true; break; } 1136 ch1 = CFStringGetCharacterAtIndex(originalString, percentRange.location + 3*j + 1); 1137 ch2 = CFStringGetCharacterAtIndex(originalString, percentRange.location + 3*j + 2); 1138 if (!_translateBytes(ch1, ch2, bytes+j)) { failed = true; break; } 1139 } 1140 1141 // FIXME: This uses about 1/3 of the time spent in CFURLCreateStringByReplacingPercentEscapes 1142 // !!! We should do the low-level bit-twiddling ourselves; this is expensive! REW, 6/10/99 1143 escapedStr = CFStringCreateWithBytes(alloc, bytes, numBytesExpected, kCFStringEncodingUTF8, false); 1144 if (!escapedStr) { 1145 failed = true; 1146 } else if (CFStringGetLength(escapedStr) == 0 && numBytesExpected == 3 && bytes[0] == 0xef && bytes[1] == 0xbb && bytes[2] == 0xbf) { 1147 // Somehow, the UCS-2 BOM got translated in to a UTF8 string 1148 escapedChar = 0xfeff; 1149 if (!strForEscapedChar) { 1150 strForEscapedChar = CFStringCreateMutableWithExternalCharactersNoCopy(alloc, &escapedChar, 1, 1, kCFAllocatorNull); 1151 } 1152 CFRelease(escapedStr); 1153 escapedStr = (CFStringRef)CFRetain(strForEscapedChar); 1154 } 1155 if (failed) break; 1156 } 1157 1158 // The new character is in escapedChar; the number of percent escapes it took is in numBytesExpected. 1159 searchRange.location = percentRange.location + 3 * numBytesExpected; 1160 searchRange.length = length - searchRange.location; 1161 1162 if (!escapeAll) { 1163 if (CFStringFind(charactersToLeaveEscaped, escapedStr, 0).location != kCFNotFound) { 1164 if (escapedStr) { 1165 CFRelease(escapedStr); 1166 escapedStr = NULL; 1167 } 1168 continue; 1169 } 1170 } 1171 1172 if (!newStr) { 1173 newStr = CFStringCreateMutable(alloc, length); 1174 } 1175 if (percentRange.location - mark > 0) { 1176 // FIXME: The creation of this temporary string is unfortunate. 1177 CFStringRef substring = CFStringCreateWithSubstring(alloc, originalString, CFRangeMake(mark, percentRange.location - mark)); 1178 CFStringAppend(newStr, substring); 1179 CFRelease(substring); 1180 } 1181 CFStringAppend(newStr, escapedStr); 1182 if (escapedStr) { 1183 CFRelease(escapedStr); 1184 escapedStr = NULL; 1185 } 1186 mark = searchRange.location;// We need mark to be the index of the first character beyond the escape sequence 1187 } 1188 1189 if (escapedStr) CFRelease(escapedStr); 1190 if (strForEscapedChar) CFRelease(strForEscapedChar); 1191 if (failed) { 1192 if (newStr) CFRelease(newStr); 1193 return NULL; 1194 } else if (newStr) { 1195 if (mark < length) { 1196 // Need to cat on the remainder of the string 1197 CFStringRef substring = CFStringCreateWithSubstring(alloc, originalString, CFRangeMake(mark, length - mark)); 1198 CFStringAppend(newStr, substring); 1199 CFRelease(substring); 1200 } 1201 return newStr; 1202 } else { 1203 return (CFStringRef)CFStringCreateCopy(alloc, originalString); 1204 } 1205} 1206 1207CF_EXPORT 1208CFStringRef CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFAllocatorRef alloc, CFStringRef originalString, CFStringRef charactersToLeaveEscaped, CFStringEncoding enc) { 1209 if (enc == kCFStringEncodingUTF8) { 1210 return CFURLCreateStringByReplacingPercentEscapes(alloc, originalString, charactersToLeaveEscaped); 1211 } else { 1212 CFMutableStringRef newStr = NULL; 1213 CFMutableStringRef escapedStr = NULL; 1214 CFIndex length; 1215 CFIndex mark = 0; 1216 CFRange percentRange, searchRange; 1217 Boolean escapeAll = (charactersToLeaveEscaped && CFStringGetLength(charactersToLeaveEscaped) == 0); 1218 Boolean failed = false; 1219 uint8_t byteBuffer[8]; 1220 uint8_t *bytes = byteBuffer; 1221 int capacityOfBytes = 8; 1222 1223 if (!originalString) return NULL; 1224 1225 if (charactersToLeaveEscaped == NULL) { 1226 return (CFStringRef)CFStringCreateCopy(alloc, originalString); 1227 } 1228 1229 length = CFStringGetLength(originalString); 1230 searchRange = CFRangeMake(0, length); 1231 1232 while (!failed && CFStringFindWithOptions(originalString, CFSTR("%"), searchRange, 0, &percentRange)) { 1233 UniChar ch1, ch2; 1234 CFIndex percentLoc = percentRange.location; 1235 CFStringRef convertedString; 1236 int numBytesUsed = 0; 1237 do { 1238 // Make sure we have at least 2 more characters 1239 if (length - percentLoc < 3) { failed = true; break; } 1240 1241 if (numBytesUsed == capacityOfBytes) { 1242 if (bytes == byteBuffer) { 1243 bytes = (uint8_t *)CFAllocatorAllocate(alloc, 16 * sizeof(uint8_t), 0); 1244 memmove(bytes, byteBuffer, capacityOfBytes); 1245 capacityOfBytes = 16; 1246 } else { 1247 void *oldbytes = bytes; 1248 int oldcap = capacityOfBytes; 1249 capacityOfBytes = 2*capacityOfBytes; 1250 bytes = (uint8_t *)CFAllocatorAllocate(alloc, capacityOfBytes * sizeof(uint8_t), 0); 1251 memmove(bytes, oldbytes, oldcap); 1252 CFAllocatorDeallocate(alloc, oldbytes); 1253 } 1254 } 1255 percentLoc ++; 1256 ch1 = CFStringGetCharacterAtIndex(originalString, percentLoc); 1257 percentLoc ++; 1258 ch2 = CFStringGetCharacterAtIndex(originalString, percentLoc); 1259 percentLoc ++; 1260 if (!_translateBytes(ch1, ch2, bytes + numBytesUsed)) { failed = true; break; } 1261 numBytesUsed ++; 1262 } while (CFStringGetCharacterAtIndex(originalString, percentLoc) == '%'); 1263 searchRange.location = percentLoc; 1264 searchRange.length = length - searchRange.location; 1265 1266 if (failed) break; 1267 convertedString = CFStringCreateWithBytes(alloc, bytes, numBytesUsed, enc, false); 1268 if (!convertedString) { 1269 failed = true; 1270 break; 1271 } 1272 1273 if (!newStr) { 1274 newStr = CFStringCreateMutable(alloc, length); 1275 } 1276 if (percentRange.location - mark > 0) { 1277 // FIXME: The creation of this temporary string is unfortunate. 1278 CFStringRef substring = CFStringCreateWithSubstring(alloc, originalString, CFRangeMake(mark, percentRange.location - mark)); 1279 CFStringAppend(newStr, substring); 1280 CFRelease(substring); 1281 } 1282 1283 if (escapeAll) { 1284 CFStringAppend(newStr, convertedString); 1285 } else { 1286 CFIndex i, c = CFStringGetLength(convertedString); 1287 if (!escapedStr) { 1288 escapedStr = CFStringCreateMutableWithExternalCharactersNoCopy(alloc, &ch1, 1, 1, kCFAllocatorNull); 1289 } 1290 for (i = 0; i < c; i ++) { 1291 ch1 = CFStringGetCharacterAtIndex(convertedString, i); 1292 if (CFStringFind(charactersToLeaveEscaped, escapedStr, 0).location == kCFNotFound) { 1293 CFStringAppendCharacters(newStr, &ch1, 1); 1294 } else { 1295 // Must regenerate the escape sequence for this character; because we started with percent escapes, we know this call cannot fail 1296 _appendPercentEscapesForCharacter(ch1, enc, newStr); 1297 } 1298 } 1299 } 1300 CFRelease(convertedString); 1301 mark = searchRange.location;// We need mark to be the index of the first character beyond the escape sequence 1302 } 1303 1304 if (escapedStr) CFRelease(escapedStr); 1305 if (bytes != byteBuffer) CFAllocatorDeallocate(alloc, bytes); 1306 if (failed) { 1307 if (newStr) CFRelease(newStr); 1308 return NULL; 1309 } else if (newStr) { 1310 if (mark < length) { 1311 // Need to cat on the remainder of the string 1312 CFStringRef substring = CFStringCreateWithSubstring(alloc, originalString, CFRangeMake(mark, length - mark)); 1313 CFStringAppend(newStr, substring); 1314 CFRelease(substring); 1315 } 1316 return newStr; 1317 } else { 1318 return (CFStringRef)CFStringCreateCopy(alloc, originalString); 1319 } 1320 } 1321} 1322 1323static Boolean _stringContainsCharacter(CFStringRef string, UniChar ch) { 1324 CFIndex i, c = CFStringGetLength(string); 1325 CFStringInlineBuffer buf; 1326 CFStringInitInlineBuffer(string, &buf, CFRangeMake(0, c)); 1327 for (i = 0; i < c; i ++) if (__CFStringGetCharacterFromInlineBufferQuick(&buf, i) == ch) return true; 1328 return false; 1329} 1330 1331// Note: charactersToLeaveUnescaped and legalURLCharactersToBeEscaped only work for characters which can be represented as a single UTF16 codepoint. 1332CF_EXPORT CFStringRef CFURLCreateStringByAddingPercentEscapes(CFAllocatorRef allocator, CFStringRef originalString, CFStringRef charactersToLeaveUnescaped, CFStringRef legalURLCharactersToBeEscaped, CFStringEncoding encoding) { 1333 CFMutableStringRef newString = NULL; 1334 CFIndex idx, length; 1335 CFStringInlineBuffer buf; 1336 enum { 1337 kCharBufferMax = 4096, 1338 }; 1339 STACK_BUFFER_DECL(UniChar, charBuffer, kCharBufferMax); 1340 CFIndex charBufferIndex = 0; 1341 1342 if (!originalString) return NULL; 1343 length = CFStringGetLength(originalString); 1344 if (length == 0) return (CFStringRef)CFStringCreateCopy(allocator, originalString); 1345 CFStringInitInlineBuffer(originalString, &buf, CFRangeMake(0, length)); 1346 1347 for (idx = 0; idx < length; idx ++) { 1348 UniChar ch = __CFStringGetCharacterFromInlineBufferQuick(&buf, idx); 1349 Boolean shouldReplace = (isURLLegalCharacter(ch) == false); 1350 if (shouldReplace) { 1351 if (charactersToLeaveUnescaped && _stringContainsCharacter(charactersToLeaveUnescaped, ch)) { 1352 shouldReplace = false; 1353 } 1354 } else if (legalURLCharactersToBeEscaped && _stringContainsCharacter(legalURLCharactersToBeEscaped, ch)) { 1355 shouldReplace = true; 1356 } 1357 1358 if (shouldReplace) { 1359 enum { 1360 kMaxBytesPerUniChar = 8, // 8 bytes is the maximum a single UniChar can require in any current encodings; future encodings could require more 1361 kMaxPercentEncodedUniChars = kMaxBytesPerUniChar * 3 1362 }; 1363 1364 static const UInt8 hexchars[] = "0123456789ABCDEF"; 1365 uint8_t bytes[kMaxBytesPerUniChar]; 1366 uint8_t *bytePtr = bytes; 1367 uint8_t *currByte; 1368 uint8_t *endPtr; 1369 CFIndex byteLength; 1370 1371 // Perform the replacement 1372 if ( !newString ) { 1373 newString = CFStringCreateMutableCopy(CFGetAllocator(originalString), 0, originalString); 1374 CFStringDelete(newString, CFRangeMake(idx, length-idx)); 1375 } 1376 // make sure charBuffer has enough room 1377 if ( charBufferIndex >= (kCharBufferMax - kMaxPercentEncodedUniChars) ) { 1378 // make room 1379 CFStringAppendCharacters(newString, charBuffer, charBufferIndex); 1380 charBufferIndex = 0; 1381 } 1382 1383 // convert the UniChar to bytes 1384 if ( CFStringEncodingUnicodeToBytes(encoding, 0, &ch, 1, NULL, bytePtr, 8, &byteLength) == kCFStringEncodingConversionSuccess ) { 1385 // percent-encode the bytes 1386 endPtr = bytePtr + byteLength; 1387 for ( currByte = bytePtr; currByte < endPtr; currByte++ ) { 1388 charBuffer[charBufferIndex++] = '%'; 1389 charBuffer[charBufferIndex++] = hexchars[*currByte >> 4]; 1390 charBuffer[charBufferIndex++] = hexchars[*currByte & 0x0f]; 1391 } 1392 } 1393 else { 1394 // FIXME: once CFString supports finding glyph boundaries walk by glyph boundaries instead of by unichars 1395 if ( encoding == kCFStringEncodingUTF8 && CFCharacterSetIsSurrogateHighCharacter(ch) && idx + 1 < length && CFCharacterSetIsSurrogateLowCharacter(__CFStringGetCharacterFromInlineBufferQuick(&buf, idx+1)) ) { 1396 UniChar surrogate[2]; 1397 uint8_t *currByte; 1398 1399 surrogate[0] = ch; 1400 surrogate[1] = __CFStringGetCharacterFromInlineBufferQuick(&buf, idx+1); 1401 // Aki sez it should never take more than 6 bytes 1402 if (CFStringEncodingUnicodeToBytes(kCFStringEncodingUTF8, 0, surrogate, 2, NULL, bytes, 6, &byteLength) == kCFStringEncodingConversionSuccess) { 1403 endPtr = bytePtr + byteLength; 1404 for ( currByte = bytes; currByte < endPtr; currByte++ ) { 1405 charBuffer[charBufferIndex++] = '%'; 1406 charBuffer[charBufferIndex++] = hexchars[*currByte >> 4]; 1407 charBuffer[charBufferIndex++] = hexchars[*currByte & 0x0f]; 1408 } 1409 idx++; // We consumed 2 characters, not 1 1410 } 1411 else { 1412 // surrogate pair conversion failed 1413 break; 1414 } 1415 } else { 1416 // not a surrogate pair 1417 break; 1418 } 1419 } 1420 } else if (newString) { 1421 charBuffer[charBufferIndex++] = ch; 1422 if ( charBufferIndex == kCharBufferMax ) { 1423 CFStringAppendCharacters(newString, charBuffer, charBufferIndex); 1424 charBufferIndex = 0; 1425 } 1426 } 1427 } 1428 if (idx < length) { 1429 // Ran into an encoding failure 1430 if (newString) CFRelease(newString); 1431 return NULL; 1432 } else if (newString) { 1433 if ( charBufferIndex != 0 ) { 1434 CFStringAppendCharacters(newString, charBuffer, charBufferIndex); 1435 } 1436 return newString; 1437 } else { 1438 return (CFStringRef)CFStringCreateCopy(CFGetAllocator(originalString), originalString); 1439 } 1440} 1441 1442static Boolean __CFURLEqual(CFTypeRef cf1, CFTypeRef cf2) { 1443 Boolean result; 1444 CFURLRef url1 = (CFURLRef)cf1; 1445 CFURLRef url2 = (CFURLRef)cf2; 1446 1447 __CFGenericValidateType(cf1, CFURLGetTypeID()); 1448 __CFGenericValidateType(cf2, CFURLGetTypeID()); 1449 1450 if ( url1 == url2 ) { 1451 result = true; 1452 } 1453 else { 1454 if ( (url1->_flags & EQUAL_FLAGS_MASK) != (url2->_flags & EQUAL_FLAGS_MASK) ) { 1455 result = false; 1456 } 1457 else { 1458 if ( (url1->_base && !url2->_base) || 1459 (!url1->_base && url2->_base) || 1460 (url1->_base && url2->_base && !CFEqual(url1->_base, url2->_base)) ) { 1461 result = false; 1462 } 1463 else { 1464 // no base urls, so compare the URL strings 1465 // Do not compare the original strings; compare the sanatized strings. 1466 result = CFEqual(CFURLGetString(url1), CFURLGetString(url2)); 1467 } 1468 } 1469 } 1470 return ( result ) ; 1471} 1472 1473static CFHashCode __CFURLHash(CFTypeRef cf) 1474{ 1475 CFHashCode result; 1476 1477 if ( cf ) { 1478 // use the CFHashCode of the URL 1479 result = CFHash(CFURLGetString((CFURLRef)cf)); 1480 } 1481 else { 1482 // no object, no hashcode 1483 result = 0; 1484 } 1485 1486 return ( result ); 1487} 1488 1489static CFStringRef CreateTruncatedURLString(CFAllocatorRef alloc, CFStringRef urlString, CFIndex maxLength, CFIndex suffixLength) 1490{ 1491 CFStringRef result; 1492 1493 CFIndex len = CFStringGetLength(urlString); 1494 if ( len <= maxLength ) { 1495 // return the retained urlString 1496 result = CFStringCreateCopy(alloc, urlString); 1497 } 1498 else { 1499 CFStringRef start = CFStringCreateWithSubstring(alloc, urlString, CFRangeMake(0, maxLength - suffixLength)); 1500 CFStringRef end = CFStringCreateWithSubstring(alloc, urlString, CFRangeMake(len - suffixLength, suffixLength)); 1501 result = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@ ... %@"), start, end); 1502 if ( start ) { 1503 CFRelease(start); 1504 } 1505 if ( (end) ) { 1506 CFRelease(end); 1507 } 1508 } 1509 return ( result ); 1510} 1511 1512static CFStringRef __CFURLCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { 1513 CFStringRef result; 1514 CFURLRef url = (CFURLRef)cf; 1515 __CFGenericValidateType(cf, CFURLGetTypeID()); 1516 CFAllocatorRef alloc = CFGetAllocator(url); 1517 1518 Boolean isDataURL = false; 1519 CFStringRef scheme = CFURLCopyScheme(url); 1520 if ( scheme ) { 1521 isDataURL = CFStringCompare(scheme, kCFURLDataScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo; 1522 CFRelease(scheme); 1523 } 1524 1525 if ( !isDataURL ) { 1526 if (!url->_base) { 1527 { 1528 result = CFStringCreateCopy(alloc, url->_string); 1529 } 1530 } else { 1531 // Do not dereference url->_base; it may be an ObjC object 1532 result = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@ -- %@"), url->_string, url->_base); 1533 } 1534 } 1535 else { 1536 if ( !url->_base ) { 1537 result = CreateTruncatedURLString(alloc, url->_string, 128, 8); 1538 } 1539 else { 1540 CFStringRef urlString = CreateTruncatedURLString(alloc, url->_string, 128, 8); 1541 CFStringRef baseString = CreateTruncatedURLString(alloc, CFURLGetString(url->_base), 128, 8); 1542 // Do not dereference url->_base; it may be an ObjC object 1543 result = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@ -- %@"), urlString, baseString); 1544 if ( urlString ) { 1545 CFRelease(urlString); 1546 } 1547 if ( baseString ) { 1548 CFRelease(baseString); 1549 } 1550 } 1551 } 1552 return ( result ); 1553} 1554 1555 1556static CFStringRef __CFURLCopyDescription(CFTypeRef cf) { 1557 CFURLRef url = (CFURLRef)cf; 1558 CFStringRef result; 1559 CFAllocatorRef alloc = CFGetAllocator(url); 1560 Boolean isDataURL = false; 1561 CFStringRef scheme = CFURLCopyScheme(url); 1562 1563 if ( scheme ) { 1564 isDataURL = CFStringCompare(scheme, kCFURLDataScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo; 1565 CFRelease(scheme); 1566 } 1567 1568 if ( !isDataURL ) { 1569 if ( url->_base) { 1570 CFStringRef baseString = CFCopyDescription(url->_base); 1571 result = CFStringCreateWithFormat(alloc, NULL, CFSTR("<CFURL %p [%p]>{string = %@, encoding = %u\n\tbase = %@}"), cf, alloc, url->_string, (unsigned int)(url->_encoding), baseString); 1572 CFRelease(baseString); 1573 } else { 1574 result = CFStringCreateWithFormat(alloc, NULL, CFSTR("<CFURL %p [%p]>{string = %@, encoding = %u, base = (null)}"), cf, alloc, url->_string, (unsigned int)(url->_encoding)); 1575 } 1576 } 1577 else { 1578 CFStringRef urlString = CreateTruncatedURLString(alloc, url->_string, 128, 8); 1579 if ( url->_base) { 1580 CFStringRef baseString = CFCopyDescription(url->_base); 1581 result = CFStringCreateWithFormat(alloc, NULL, CFSTR("<CFURL %p [%p]>{string = %@, encoding = %u\n\tbase = %@}"), cf, alloc, urlString, (unsigned int)(url->_encoding), baseString); 1582 CFRelease(baseString); 1583 } else { 1584 result = CFStringCreateWithFormat(alloc, NULL, CFSTR("<CFURL %p [%p]>{string = %@, encoding = %u, base = (null)}"), cf, alloc, urlString, (unsigned int)(url->_encoding)); 1585 } 1586 CFRelease(urlString); 1587 } 1588 return result; 1589} 1590 1591#if DEBUG_URL_MEMORY_USAGE 1592extern __attribute((used)) void __CFURLDumpMemRecord(void) { 1593 syslog(LOG_ERR, "%d URLs created; %d destroyed; %d URLs parsed; %d urls had 'extra' data allocated; %d had base urls; %d were not UTF8 encoded", numURLs, numDealloced, numFileURLsParsed, numExtraDataAllocated, numURLsWithBaseURL, numNonUTF8EncodedURLs); 1594} 1595#endif 1596 1597static void __CFURLDeallocate(CFTypeRef cf) { 1598 CFURLRef url = (CFURLRef)cf; 1599 CFAllocatorRef alloc; 1600 __CFGenericValidateType(cf, CFURLGetTypeID()); 1601 alloc = CFGetAllocator(url); 1602#if DEBUG_URL_MEMORY_USAGE 1603 numDealloced ++; 1604#endif 1605 if (url->_string) CFRelease(url->_string); // GC: 3879914 1606 if (url->_base) CFRelease(url->_base); 1607 if (url->_ranges) CFAllocatorDeallocate(alloc, url->_ranges); 1608 CFStringRef sanitizedString = _getSanitizedString(url); 1609 if (sanitizedString) CFRelease(sanitizedString); 1610 if ( url->_extra != NULL ) CFAllocatorDeallocate( alloc, url->_extra ); 1611 if (_getResourceInfo(url)) CFRelease(_getResourceInfo(url)); 1612} 1613 1614static CFTypeID __kCFURLTypeID = _kCFRuntimeNotATypeID; 1615 1616static const CFRuntimeClass __CFURLClass = { 1617 0, // version 1618 "CFURL", // className 1619 NULL, // init 1620 NULL, // copy 1621 __CFURLDeallocate, // finalize 1622 __CFURLEqual, // equal 1623 __CFURLHash, // hash 1624 __CFURLCopyFormattingDescription, // copyFormattingDesc 1625 __CFURLCopyDescription, // copyDebugDesc 1626 NULL, // reclaim 1627 NULL, // refcount 1628}; 1629 1630CF_PRIVATE void __CFURLInitialize(void) { 1631 __kCFURLTypeID = _CFRuntimeRegisterClass(&__CFURLClass); 1632} 1633 1634/* Toll-free bridging support; get the true CFURL from an NSURL */ 1635CF_INLINE CFURLRef _CFURLFromNSURL(CFURLRef url) { 1636 CF_OBJC_FUNCDISPATCHV(__kCFURLTypeID, CFURLRef, (NSURL *)url, _cfurl); 1637 return url; 1638} 1639 1640CFTypeID CFURLGetTypeID(void) { 1641 return __kCFURLTypeID; 1642} 1643 1644CF_PRIVATE void CFShowURL(CFURLRef url) { 1645 if (!url) { 1646 fprintf(stdout, "(null)\n"); 1647 return; 1648 } 1649 fprintf(stdout, "<CFURL %p>{", (const void*)url); 1650 if (CF_IS_OBJC(__kCFURLTypeID, url)) { 1651 fprintf(stdout, "ObjC bridged object}\n"); 1652 return; 1653 } 1654 fprintf(stdout, "\n\tRelative string: "); 1655 CFShow(url->_string); 1656 fprintf(stdout, "\tBase URL: "); 1657 if (url->_base) { 1658 fprintf(stdout, "<%p> ", (const void*)url->_base); 1659 CFShow(url->_base); 1660 } else { 1661 fprintf(stdout, "(null)\n"); 1662 } 1663 fprintf(stdout, "\tFlags: 0x%x\n}\n", (unsigned int)url->_flags); 1664} 1665 1666 1667/***************************************************/ 1668/* URL creation and String/Data creation from URLS */ 1669/***************************************************/ 1670static void constructBuffers(CFAllocatorRef alloc, CFStringRef string, UInt8 *inBuffer, CFIndex inBufferSize, const char **cstring, const UniChar **ustring, Boolean *useCString, Boolean *freeCharacters) { 1671 CFIndex neededLength; 1672 CFIndex length; 1673 CFRange rg; 1674 1675 *cstring = CFStringGetCStringPtr(string, kCFStringEncodingISOLatin1); 1676 if (*cstring) { 1677 *ustring = NULL; 1678 *useCString = true; 1679 *freeCharacters = false; 1680 return; 1681 } 1682 1683 *ustring = CFStringGetCharactersPtr(string); 1684 if (*ustring) { 1685 *useCString = false; 1686 *freeCharacters = false; 1687 return; 1688 } 1689 1690 length = CFStringGetLength(string); 1691 rg = CFRangeMake(0, length); 1692 CFStringGetBytes(string, rg, kCFStringEncodingISOLatin1, 0, false, NULL, INT_MAX, &neededLength); 1693 if (neededLength == length) { 1694 char *buf; 1695 if ( (inBuffer != NULL) && (length <= inBufferSize) ) { 1696 buf = (char *)inBuffer; 1697 *freeCharacters = false; 1698 } 1699 else { 1700 buf = (char *)CFAllocatorAllocate(alloc, length, 0); 1701 *freeCharacters = true; 1702 } 1703 CFStringGetBytes(string, rg, kCFStringEncodingISOLatin1, 0, false, (uint8_t *)buf, length, NULL); 1704 *cstring = buf; 1705 *useCString = true; 1706 } else { 1707 UniChar *buf; 1708 if ( (inBuffer != NULL) && ((length * sizeof(UniChar)) <= inBufferSize) ) { 1709 buf = (UniChar *)inBuffer; 1710 *freeCharacters = false; 1711 } 1712 else { 1713 buf = (UniChar *)CFAllocatorAllocate(alloc, length * sizeof(UniChar), 0); 1714 *freeCharacters = true; 1715 } 1716 CFStringGetCharacters(string, rg, buf); 1717 *ustring = buf; 1718 *useCString = false; 1719 } 1720} 1721 1722static void _parseComponentsCString(CFAllocatorRef alloc, CFURLRef baseURL, UInt32 *theFlags, CFRange **range, CFIndex cfStringLength, const char *characterArray) 1723#define CFURL_INCLUDE_PARSE_COMPONENTS 1724#include "CFURL.inc.h" 1725#undef CFURL_INCLUDE_PARSE_COMPONENTS 1726 1727static void _parseComponentsUString(CFAllocatorRef alloc, CFURLRef baseURL, UInt32 *theFlags, CFRange **range, CFIndex cfStringLength, const UniChar *characterArray) 1728#define CFURL_INCLUDE_PARSE_COMPONENTS 1729#include "CFURL.inc.h" 1730#undef CFURL_INCLUDE_PARSE_COMPONENTS 1731 1732static void _parseComponents(CFAllocatorRef alloc, CFStringRef string, CFURLRef baseURL, UInt32 *theFlags, CFRange **range) { 1733 CFIndex cfStringLength = CFStringGetLength(string); 1734 Boolean useCString, freeCharacters; 1735 const char *cstring = NULL; 1736 const UniChar *ustring = NULL; 1737 CFIndex stackBufferSize = 4096; 1738 STACK_BUFFER_DECL(UInt8, stackBuffer, stackBufferSize); 1739 1740 constructBuffers(alloc, string, stackBuffer, stackBufferSize, &cstring, &ustring, &useCString, &freeCharacters); 1741 1742 if (useCString) { 1743 _parseComponentsCString(alloc, baseURL, theFlags, range, cfStringLength, cstring); 1744 } 1745 else { 1746 _parseComponentsUString(alloc, baseURL, theFlags, range, cfStringLength, ustring); 1747 } 1748 1749 if (freeCharacters) { 1750 CFAllocatorDeallocate(alloc, useCString ? (void *)cstring : (void *)ustring); 1751 } 1752} 1753 1754static Boolean scanCharactersCString(CFAllocatorRef alloc, CFMutableStringRef *escapedString, UInt32 *flags, const char *characterArray, Boolean useCString, CFIndex base, CFIndex end, CFIndex *mark, UInt32 componentFlag, CFStringEncoding encoding) 1755#define CFURL_INCLUDE_SCAN_CHARACTERS 1756#include "CFURL.inc.h" 1757#undef CFURL_INCLUDE_SCAN_CHARACTERS 1758 1759static Boolean scanCharactersUString(CFAllocatorRef alloc, CFMutableStringRef *escapedString, UInt32 *flags, const UniChar *characterArray, Boolean useCString, CFIndex base, CFIndex end, CFIndex *mark, UInt32 componentFlag, CFStringEncoding encoding) 1760#define CFURL_INCLUDE_SCAN_CHARACTERS 1761#include "CFURL.inc.h" 1762#undef CFURL_INCLUDE_SCAN_CHARACTERS 1763 1764static Boolean scanCharacters(CFAllocatorRef alloc, CFMutableStringRef *escapedString, UInt32 *flags, const char *cstring, const UniChar *ustring, Boolean useCString, CFIndex base, CFIndex end, CFIndex *mark, UInt32 componentFlag, CFStringEncoding encoding) { 1765 if ( useCString ) { 1766 return ( scanCharactersCString(alloc, escapedString, flags, cstring, useCString, base, end, mark, componentFlag, encoding)); 1767 } 1768 else { 1769 return ( scanCharactersUString(alloc, escapedString, flags, ustring, useCString, base, end, mark, componentFlag, encoding)); 1770 } 1771} 1772 1773static void computeSanitizedString(CFURLRef url) { 1774 CFAllocatorRef alloc = CFGetAllocator(url); 1775 CFIndex string_length = CFStringGetLength(url->_string); 1776 Boolean useCString, freeCharacters; 1777 const char *cstring = NULL; 1778 const UniChar *ustring = NULL; 1779 CFIndex base; // where to scan from 1780 CFIndex mark; // first character not-yet copied to sanitized string 1781 CFIndex stackBufferSize = 4096; 1782 STACK_BUFFER_DECL(UInt8, stackBuffer, stackBufferSize); 1783 CFMutableStringRef sanitizedString = NULL; 1784 UInt32 additionalDataFlags = 0; 1785 1786 constructBuffers(alloc, url->_string, stackBuffer, stackBufferSize, &cstring, &ustring, &useCString, &freeCharacters); 1787 if (!(url->_flags & IS_DECOMPOSABLE)) { 1788 // Impossible to have a problem character in the scheme 1789 base = _rangeForComponent(url->_flags, url->_ranges, HAS_SCHEME).length + 1; 1790 mark = 0; 1791 if (!scanCharacters(alloc, &sanitizedString, &additionalDataFlags, cstring, ustring, useCString, base, string_length, &mark, 0, url->_encoding)) { 1792 ((struct __CFURL *)url)->_flags |= ORIGINAL_AND_URL_STRINGS_MATCH; 1793 } 1794 if ( sanitizedString ) { 1795 _setAdditionalDataFlags((struct __CFURL*)url, additionalDataFlags); 1796 } 1797 } else { 1798 // Go component by component 1799 CFIndex currentComponent = HAS_USER; 1800 mark = 0; 1801 while (currentComponent < (HAS_FRAGMENT << 1)) { 1802 CFRange componentRange = _rangeForComponent(url->_flags, url->_ranges, currentComponent); 1803 if (componentRange.location != kCFNotFound) { 1804 scanCharacters(alloc, &sanitizedString, &additionalDataFlags, cstring, ustring, useCString, componentRange.location, componentRange.location + componentRange.length, &mark, currentComponent, url->_encoding); 1805 } 1806 currentComponent = currentComponent << 1; 1807 } 1808 if (sanitizedString) { 1809 _setAdditionalDataFlags((struct __CFURL*)url, additionalDataFlags); 1810 } else { 1811 ((struct __CFURL *)url)->_flags |= ORIGINAL_AND_URL_STRINGS_MATCH; 1812 } 1813 } 1814 if (sanitizedString && mark != string_length) { 1815 if (useCString) { 1816 __CFStringAppendBytes(sanitizedString, (char *)&(cstring[mark]), string_length - mark, kCFStringEncodingISOLatin1); 1817 } else { 1818 CFStringAppendCharacters(sanitizedString, &(ustring[mark]), string_length - mark); 1819 } 1820 } 1821 if ( sanitizedString ) { 1822 _setSanitizedString((struct __CFURL*) url, sanitizedString); 1823 CFRelease(sanitizedString); 1824 } 1825 if (freeCharacters) { 1826 CFAllocatorDeallocate(alloc, useCString ? (void *)cstring : (void *)ustring); 1827 } 1828} 1829 1830 1831static CFStringRef correctedComponent(CFStringRef comp, UInt32 compFlag, CFStringEncoding enc) { 1832 CFAllocatorRef alloc = CFGetAllocator(comp); 1833 CFIndex string_length = CFStringGetLength(comp); 1834 Boolean useCString, freeCharacters; 1835 const char *cstring = NULL; 1836 const UniChar *ustring = NULL; 1837 CFIndex mark = 0; // first character not-yet copied to sanitized string 1838 CFMutableStringRef result = NULL; 1839 CFIndex stackBufferSize = 1024; 1840 STACK_BUFFER_DECL(UInt8, stackBuffer, stackBufferSize); 1841 1842 constructBuffers(alloc, comp, stackBuffer, stackBufferSize, &cstring, &ustring, &useCString, &freeCharacters); 1843 scanCharacters(alloc, &result, NULL, cstring, ustring, useCString, 0, string_length, &mark, compFlag, enc); 1844 if (result) { 1845 if (mark < string_length) { 1846 if (useCString) { 1847 __CFStringAppendBytes(result, (char *)&(cstring[mark]), string_length - mark, kCFStringEncodingISOLatin1); 1848 } else { 1849 CFStringAppendCharacters(result, &(ustring[mark]), string_length - mark); 1850 } 1851 } 1852 } else { 1853 // This should nevr happen 1854 CFRetain(comp); 1855 result = (CFMutableStringRef)comp; 1856 } 1857 if (freeCharacters) { 1858 CFAllocatorDeallocate(alloc, useCString ? (void *)cstring : (void *)ustring); 1859 } 1860 return result; 1861} 1862 1863 1864CF_EXPORT CFURLRef _CFURLAlloc(CFAllocatorRef allocator) { 1865 struct __CFURL *url; 1866#if DEBUG_URL_MEMORY_USAGE 1867 numURLs ++; 1868// if ( numURLs % 1000 == 0 ) { 1869// __CFURLDumpMemRecord(); 1870// } 1871#endif 1872 url = (struct __CFURL *)_CFRuntimeCreateInstance(allocator, __kCFURLTypeID, sizeof(struct __CFURL) - sizeof(CFRuntimeBase), NULL); 1873 if (url) { 1874 url->_flags = 0; 1875 url->_encoding = kCFStringEncodingUTF8; 1876 url->_string = NULL; 1877 url->_base = NULL; 1878 url->_ranges = NULL; 1879 url->_extra = NULL; 1880 url->_resourceInfo = NULL; 1881 } 1882 return url; 1883} 1884 1885// It is the caller's responsibility to guarantee that if URLString is absolute, base is NULL. This is necessary to avoid duplicate processing for file system URLs, which had to decide whether to compute the cwd for the base; we don't want to duplicate that work. If parseURL is false, the caller is responsible for parsing the URL. 1886static void _CFURLInit(struct __CFURL *url, CFStringRef URLString, CFURLRef base, Boolean parseURL) { 1887 1888 // Coming in, the url has its allocator flag properly set, and its base initialized, and nothing else. 1889 url->_string = CFStringCreateCopy(CFGetAllocator(url), URLString); 1890 url->_base = base ? CFURLCopyAbsoluteURL(base) : NULL; 1891 1892#if DEBUG_URL_MEMORY_USAGE 1893 if ( url->_base ) { 1894 numURLsWithBaseURL ++; 1895 } 1896#endif 1897 if ( parseURL ) { 1898#if DEBUG_URL_MEMORY_USAGE 1899 numFileURLsParsed++; 1900#endif 1901 _parseComponentsOfURL(url); 1902 } 1903} 1904 1905static Boolean _CFStringIsLegalURLString(CFStringRef string) { 1906 Boolean result = true; 1907 if ( string ) { 1908 CFStringInlineBuffer stringBuffer; 1909 Boolean sawHash = false; 1910 CFIndex idx = 0; 1911 CFIndex checkHexDigit = 0; 1912 CFIndex length; 1913 1914 length = CFStringGetLength(string); 1915 { 1916 CFStringInitInlineBuffer(string, &stringBuffer, CFRangeMake(0, length)); 1917 1918 while ( idx < length ) { 1919 CFIndex rangeLength; 1920 const UniChar *chPtr; 1921 1922 rangeLength = (idx + __kCFStringInlineBufferLength <= length) ? __kCFStringInlineBufferLength : length - idx; 1923 chPtr = CFStringGetCharactersPtrFromInlineBuffer(&stringBuffer, CFRangeMake(idx, rangeLength)); 1924 for ( CFIndex rangeIdx = 0; rangeIdx < rangeLength; ++rangeIdx, ++chPtr ) { 1925 if ( !checkHexDigit ) { 1926 if ( *chPtr == '%' ) { 1927 // percent encoded? make sure there at least 2 characters left to check 1928 if ( (idx + rangeIdx + 2) < length ) { 1929 // the next 2 characters must be HEXDIG 1930 checkHexDigit = 2; 1931 continue; 1932 } 1933 else { 1934 result = false; 1935 break; 1936 } 1937 } 1938 if ( *chPtr == '[' || *chPtr == ']' ) { 1939 continue; // IPV6 support (RFC 2732) DCJ June/10/2002 1940 } 1941 if ( *chPtr == '#' ) { 1942 // only allow one # character 1943 if ( !sawHash ) { 1944 sawHash = true; 1945 continue; 1946 } 1947 else { 1948 result = false; 1949 break; 1950 } 1951 } 1952#if DEPLOYMENT_TARGET_WINDOWS 1953 // <rdar://problem/7134119> CF on Windows: CFURLCreateWithString should work with | in path on Windows 1954 if ( isURLLegalCharacter(*chPtr) || *chPtr == '|' ) { 1955 continue; 1956 } 1957#else 1958 if ( isURLLegalCharacter(*chPtr) ) { 1959 continue; 1960 } 1961#endif 1962 else { 1963 result = false; 1964 break; 1965 } 1966 } 1967 else { 1968 if ( isHexDigit(*chPtr) ) { 1969 --checkHexDigit; 1970 continue; 1971 } 1972 else { 1973 result = false; 1974 break; 1975 } 1976 } 1977 } 1978 1979 if ( !result ) { 1980 break; // out of main while loop 1981 } 1982 1983 idx += rangeLength; 1984 } 1985 } 1986 } 1987 else { 1988 CFAssert(false, __kCFLogAssertion, "Cannot create an CFURL from a NULL string"); 1989 result = false; 1990 } 1991 1992 return ( result ); 1993} 1994 1995/* initializes a URL object with a URL string */ 1996CF_EXPORT Boolean _CFURLInitWithURLString(CFURLRef uninitializedURL, CFStringRef string, Boolean checkForLegalCharacters, CFURLRef baseURL) 1997{ 1998#if DEBUG_URL_INITIALIZER_LOGGING 1999 CFStringRef input_string = string ? CFRetain(string) : NULL; 2000 Boolean input_checkForLegalCharacters = checkForLegalCharacters; 2001 CFURLRef input_baseURL = baseURL ? CFRetain(baseURL) : NULL; 2002#endif 2003 Boolean result = checkForLegalCharacters ? _CFStringIsLegalURLString(string) : true; 2004 2005 if ( result ) { 2006 // determine if URL is absolute (i.e. it has a scheme and the scheme characters are valid 2007 CFStringInlineBuffer stringBuffer; 2008 CFIndex length = CFStringGetLength(string); 2009 CFIndex idx = 0; 2010 Boolean isAbsolute = false; 2011 Boolean schemeCharsValid = true; 2012 2013 CFStringInitInlineBuffer(string, &stringBuffer, CFRangeMake(0, length)); 2014 // the first character of the scheme must be ALPHA 2015 if ( (length > 0) && isALPHA(__CFStringGetCharacterFromInlineBufferQuick(&stringBuffer, 0)) ) { 2016 // stop looking if we hit the end of the string, find a colon (isAbsolute), or find a non-scheme character (schemeCharsValid) 2017 while (idx < length && schemeCharsValid && !isAbsolute) { 2018 CFIndex rangeLength = (idx + __kCFStringInlineBufferLength <= length) ? __kCFStringInlineBufferLength : length - idx; 2019 const UniChar *chPtr = CFStringGetCharactersPtrFromInlineBuffer(&stringBuffer, CFRangeMake(idx, rangeLength)); 2020 for (CFIndex i = 0; i < rangeLength; ++i, ++chPtr) { 2021 if ( *chPtr == ':' ) { 2022 isAbsolute = true; 2023 break; 2024 } 2025 if ( !scheme_valid(*chPtr) ) { 2026 schemeCharsValid = false; 2027 break; 2028 } 2029 } 2030 if ( isAbsolute ) { 2031 break; 2032 } 2033 idx += rangeLength; 2034 } 2035 } 2036 2037 _CFURLInit((struct __CFURL *)uninitializedURL, string, isAbsolute ? NULL : baseURL, TRUE); 2038 } 2039#if DEBUG_URL_INITIALIZER_LOGGING 2040 CFLog(kCFLogLevelError, CFSTR("_CFURLInitWithURLString (in) string '%@', checkForLegalCharacters %@, baseURL %@\n\t_CFURLInitWithURLString (out) result %@, url %@"), input_string, input_checkForLegalCharacters ? CFSTR("YES") : CFSTR("NO"), input_baseURL, result ? CFSTR("YES") : CFSTR("NO"), uninitializedURL); 2041 if ( input_string ) { 2042 CFRelease(input_string); 2043 } 2044 if ( input_baseURL ) { 2045 CFRelease(input_baseURL); 2046 } 2047#endif 2048 return ( result ); 2049} 2050 2051/* initializes a URL object with a file system path */ 2052CF_EXPORT Boolean _CFURLInitWithFileSystemPath(CFURLRef uninitializedURL, CFStringRef fileSystemPath, CFURLPathStyle pathStyle, Boolean isDirectory, CFURLRef baseURL) 2053{ 2054#if DEBUG_URL_INITIALIZER_LOGGING 2055 CFStringRef input_fileSystemPath = fileSystemPath ? CFRetain(fileSystemPath) : NULL; 2056 CFURLPathStyle input_pathStyle = pathStyle; 2057 Boolean input_isDirectory = isDirectory; 2058 CFURLRef input_baseURL = baseURL ? CFRetain(baseURL) : NULL; 2059#endif 2060 Boolean result = false; 2061 CFAssert1(fileSystemPath != NULL, __kCFLogAssertion, "%s(): NULL path string not permitted", __PRETTY_FUNCTION__); 2062#pragma GCC diagnostic push 2063#pragma GCC diagnostic ignored "-Wdeprecated" 2064 CFAssert2(pathStyle == kCFURLPOSIXPathStyle || pathStyle == kCFURLHFSPathStyle || pathStyle == kCFURLWindowsPathStyle, __kCFLogAssertion, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__, pathStyle); 2065#pragma GCC diagnostic pop 2066 2067 struct __CFURL *url = (struct __CFURL *)uninitializedURL; 2068 CFAllocatorRef alloc = CFGetAllocator(uninitializedURL); 2069 CFStringRef urlString = NULL; 2070 Boolean isAbsolute; 2071 Boolean isFileReferencePath = false; 2072 Boolean posixAndUrlPathsMatch = false; 2073 UniChar pathDelim = '\0'; 2074 Boolean releaseBaseURL = false; 2075 CFIndex len = CFStringGetLength(fileSystemPath); 2076 2077 if (len > 0) { 2078 // determine if fileSystemPath is an absolute path and what pathDelim we're using 2079 switch (pathStyle) { 2080 case kCFURLPOSIXPathStyle: 2081 pathDelim = '/'; 2082 isAbsolute = (len > 0 && CFStringGetCharacterAtIndex(fileSystemPath, 0) == pathDelim); 2083 break; 2084 2085 2086 case kCFURLWindowsPathStyle: 2087 { 2088 // this is what _CFURLInitFSPath() did (with a small change to handle absolute paths that begin with a single backslash) 2089 UniChar firstChar = 0 < len ? CFStringGetCharacterAtIndex(fileSystemPath, 0) : 0; 2090 UniChar secondChar = 1 < len ? CFStringGetCharacterAtIndex(fileSystemPath, 1) : 0; 2091 Boolean isDrive = isALPHA(firstChar) && (secondChar == ':' || secondChar == '|'); 2092 2093 if ( isDrive) { 2094 // A disk designator 2095 pathDelim = '\\'; 2096 isAbsolute = true; 2097 } 2098 else if ( firstChar == '\\' ) { 2099 // Either a UNC name of any format (which always start with two backslash characters), or an absolute path which starts with a single backslash. 2100 pathDelim = '\\'; 2101 isAbsolute = true; 2102 } 2103 else if (firstChar == '/') { 2104 // We switch to kCFURLPOSIXPathStyle here because there's probably code that passes POSIX paths in but wrongly specifies kCFURLWindowsPathStyle. 2105 pathStyle = kCFURLPOSIXPathStyle; 2106 pathDelim = '/'; 2107 isAbsolute = true; 2108 } 2109 else { 2110 // We switch to kCFURLPOSIXPathStyle here because this is not an absolute path and we're going to set baseURL to the current working directory below, and so we assume the relative path will have to be combined with the base path at some point. 2111 pathStyle = kCFURLPOSIXPathStyle; 2112 pathDelim = '/'; 2113 isAbsolute = false; 2114 } 2115 } 2116 break; 2117 } 2118 2119 // Convert the fileSystemPath to a urlString and determine if it is a file reference path. 2120 // The urlString returned will have a pathDelim at the end if isDirectory was true and no pathDelim if isDirectory was false (unless the urlPath is "/") 2121 // If isAbsolute, "file://" will be prepended to the urlString. 2122 switch (pathStyle) { 2123 case kCFURLPOSIXPathStyle: 2124 isFileReferencePath = _pathHasFileIDPrefix(fileSystemPath); 2125 urlString = POSIXPathToURLPath(fileSystemPath, alloc, isDirectory, isAbsolute, &posixAndUrlPathsMatch); 2126 break; 2127 2128 2129 case kCFURLWindowsPathStyle: 2130 urlString = WindowsPathToURLPath(fileSystemPath, alloc, isDirectory, isAbsolute); 2131 break; 2132 } 2133 2134 CFAssert2(urlString != NULL, __kCFLogAssertion, "%s(): Encountered malformed file system URL %@", __PRETTY_FUNCTION__, urlString); 2135 2136 if ( urlString ) { 2137 if ( isAbsolute ) { 2138 // if fileSystemPath is an absolute path, ignore baseURL (if provided) 2139 baseURL = NULL; 2140 } 2141 else if ( baseURL == NULL ) { 2142 // if fileSystemPath is a relative path and no baseURL is provided, use the current working directory 2143 baseURL = _CFURLCreateCurrentDirectoryURL(CFGetAllocator(uninitializedURL)); 2144 releaseBaseURL = true; 2145 } 2146 2147 // override isDirectory if the path is to root 2148 if ( !isDirectory && (len == 1) && (CFStringGetCharacterAtIndex(urlString, 0) == pathDelim) ) { 2149 // Override isDirectory 2150 isDirectory = true; 2151 } 2152 2153 _CFURLInit(url, urlString, baseURL, FALSE); 2154 2155 // hard coded parse 2156 if ( isAbsolute ) { 2157 UInt32 flags = IS_DECOMPOSABLE | HAS_SCHEME | HAS_PATH | ORIGINAL_AND_URL_STRINGS_MATCH; 2158 flags |= (isDirectory ? IS_DIRECTORY : 0); 2159 if ( isFileReferencePath ) { 2160 // if the URL is a file reference URL, don't set IS_CANONICAL_FILE_URL or POSIX_AND_URL_PATHS_MATCH 2161 flags |= PATH_HAS_FILE_ID; 2162 } 2163 else { 2164 // only posix style paths can be canonical because POSIXPathToURLPath() converts the string to file system representation 2165 flags |= ((pathStyle == kCFURLPOSIXPathStyle) ? IS_CANONICAL_FILE_URL : 0); 2166 flags |= (posixAndUrlPathsMatch ? POSIX_AND_URL_PATHS_MATCH : 0); 2167 } 2168 _setSchemeTypeInFlags(&flags, kHasFileScheme); 2169 2170 if ( AddAuthorityToFileURL() ) { 2171 url->_flags = flags | HAS_HOST; 2172 url->_ranges = (CFRange *)CFAllocatorAllocate(alloc, sizeof(CFRange) * 3, 0); 2173 url->_ranges[0] = CFRangeMake(0, 4); // scheme "file" 2174 url->_ranges[1] = CFRangeMake(7, 9); // host "localhost" 2175 url->_ranges[2] = CFRangeMake(16, CFStringGetLength(urlString)- 16); 2176 } 2177 else { 2178 url->_flags = flags; 2179 url->_ranges = (CFRange *)CFAllocatorAllocate(alloc, sizeof(CFRange) * 2, 0); 2180 url->_ranges[0] = CFRangeMake(0, 4); // scheme "file" 2181 url->_ranges[1] = CFRangeMake(7, CFStringGetLength(urlString)- 7); 2182 } 2183 } else { 2184 url->_flags = (isDirectory ? IS_DIRECTORY : 0) | IS_DECOMPOSABLE | HAS_PATH | ORIGINAL_AND_URL_STRINGS_MATCH; 2185 url->_ranges = (CFRange *)CFAllocatorAllocate(alloc, sizeof(CFRange), 0); 2186 *(url->_ranges) = CFRangeMake(0, CFStringGetLength(url->_string)); 2187 } 2188 2189 if ( releaseBaseURL && baseURL ) { 2190 CFRelease(baseURL); 2191 } 2192 2193 CFRelease(urlString); 2194 result = true; 2195 } 2196 } 2197#if DEBUG_URL_INITIALIZER_LOGGING 2198#pragma GCC diagnostic push 2199#pragma GCC diagnostic ignored "-Wdeprecated" 2200 CFLog(kCFLogLevelError, CFSTR("_CFURLInitWithFileSystemPath (in) fileSystemPath '%@', pathStyle %@, isDirectory %@, baseURL %@\n\t_CFURLInitWithFileSystemPath (out) result %@, url %@"), input_fileSystemPath, input_pathStyle == kCFURLPOSIXPathStyle ? CFSTR("POSIX") : input_pathStyle == kCFURLHFSPathStyle ? CFSTR("HFS"): CFSTR("Windows"), input_isDirectory ? CFSTR("YES") : CFSTR("NO"), input_baseURL, result ? CFSTR("YES") : CFSTR("NO"), uninitializedURL); 2201#pragma GCC diagnostic pop 2202 if ( input_fileSystemPath ) { 2203 CFRelease(input_fileSystemPath); 2204 } 2205 if ( input_baseURL ) { 2206 CFRelease(input_baseURL); 2207 } 2208#endif 2209 return ( result ); 2210} 2211 2212/* initializes a URL object with the file system representation */ 2213CF_EXPORT Boolean _CFURLInitWithFileSystemRepresentation(CFURLRef uninitializedURL, const UInt8 *buffer, CFIndex bufLen, Boolean isDirectory, CFURLRef baseURL) 2214{ 2215#if DEBUG_URL_INITIALIZER_LOGGING 2216 STACK_BUFFER_DECL(UInt8, input_buffer, bufLen); 2217 CFIndex input_bufLen = bufLen; 2218 Boolean input_isDirectory = isDirectory; 2219 CFURLRef input_baseURL = baseURL ? CFRetain(baseURL) : NULL; 2220 memcpy(input_buffer, buffer, bufLen); 2221#endif 2222 Boolean result = false; 2223 if ( bufLen > 0 ) { 2224 CFAllocatorRef alloc = CFGetAllocator(uninitializedURL); 2225#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX 2226 struct __CFURL *url = (struct __CFURL *)uninitializedURL; 2227 Boolean isAbsolute = bufLen && (*buffer == '/'); 2228 Boolean addedPercentEncoding; 2229 Boolean releaseBaseURL = false; 2230 2231 if ( isAbsolute ) { 2232 // if buffer contains an absolute path, ignore baseURL (if provided) 2233 baseURL = NULL; 2234 } 2235 else if ( baseURL == NULL ) { 2236 // if buffer contains a relative path and no baseURL is provided, use the current working directory 2237 baseURL = _CFURLCreateCurrentDirectoryURL(CFGetAllocator(uninitializedURL)); 2238 releaseBaseURL = true; 2239 } 2240 CFStringRef urlString = CreateStringFromFileSystemRepresentationByAddingPercentEscapes(alloc, buffer, bufLen, isDirectory, isAbsolute, false /*windowsPath*/, &addedPercentEncoding); 2241 if ( urlString ) { 2242 _CFURLInit(url, urlString, baseURL, FALSE); 2243 2244 // hard coded parse 2245 if ( isAbsolute ) { 2246 if ( AddAuthorityToFileURL() ) { 2247 url->_flags = (addedPercentEncoding ? 0 : POSIX_AND_URL_PATHS_MATCH ) | (isDirectory ? IS_DIRECTORY : 0) | IS_DECOMPOSABLE | HAS_SCHEME | HAS_HOST | HAS_PATH | ORIGINAL_AND_URL_STRINGS_MATCH | IS_CANONICAL_FILE_URL; 2248 _setSchemeTypeInFlags(&url->_flags, kHasFileScheme); 2249 url->_ranges = (CFRange *)CFAllocatorAllocate(alloc, sizeof(CFRange) * 3, 0); 2250 url->_ranges[0] = CFRangeMake(0, 4); // scheme "file" 2251 url->_ranges[1] = CFRangeMake(7, 9); // host "localhost" 2252 url->_ranges[2] = CFRangeMake(16, CFStringGetLength(urlString)- 16); 2253 } 2254 else { 2255 url->_flags = (addedPercentEncoding ? 0 : POSIX_AND_URL_PATHS_MATCH ) | (isDirectory ? IS_DIRECTORY : 0) | IS_DECOMPOSABLE | HAS_SCHEME | HAS_PATH | ORIGINAL_AND_URL_STRINGS_MATCH | IS_CANONICAL_FILE_URL; 2256 _setSchemeTypeInFlags(&url->_flags, kHasFileScheme); 2257 url->_ranges = (CFRange *)CFAllocatorAllocate(alloc, sizeof(CFRange) * 2, 0); 2258 url->_ranges[0] = CFRangeMake(0, 4); // scheme "file" 2259 url->_ranges[1] = CFRangeMake(7, CFStringGetLength(urlString)- 7); 2260 } 2261 } else { 2262 url->_flags = (addedPercentEncoding ? 0 : POSIX_AND_URL_PATHS_MATCH ) | (isDirectory ? IS_DIRECTORY : 0) | IS_DECOMPOSABLE | HAS_PATH | ORIGINAL_AND_URL_STRINGS_MATCH; 2263 url->_ranges = (CFRange *)CFAllocatorAllocate(alloc, sizeof(CFRange), 0); 2264 *(url->_ranges) = CFRangeMake(0, CFStringGetLength(url->_string)); 2265 } 2266 if ( releaseBaseURL && baseURL ) { 2267 CFRelease(baseURL); 2268 } 2269 CFRelease(urlString); 2270 result = true; 2271 } 2272#elif DEPLOYMENT_TARGET_WINDOWS 2273 CFStringRef filePath = CFStringCreateWithBytes(alloc, buffer, bufLen, CFStringFileSystemEncoding(), false); 2274 if ( filePath ) { 2275 result = _CFURLInitWithFileSystemPath(uninitializedURL, filePath, kCFURLWindowsPathStyle, isDirectory, baseURL); 2276 CFRelease(filePath); 2277 } 2278#endif 2279 } 2280#if DEBUG_URL_INITIALIZER_LOGGING 2281 CFLog(kCFLogLevelError, CFSTR("_CFURLInitWithFileSystemRepresentation (in) buffer '%*s', isDirectory %@, baseURL %@\n\t_CFURLInitWithFileSystemRepresentation (out) result %@, url %@"), input_bufLen, input_buffer, input_isDirectory ? CFSTR("YES") : CFSTR("NO"), input_baseURL, result ? CFSTR("YES") : CFSTR("NO"), uninitializedURL); 2282 if ( input_baseURL ) { 2283 CFRelease(input_baseURL); 2284 } 2285#endif 2286 return ( result ); 2287} 2288 2289// encoding will be used both to interpret the bytes of URLBytes, and to interpret any percent-escapes within the bytes. 2290CFURLRef CFURLCreateWithBytes(CFAllocatorRef allocator, const uint8_t *URLBytes, CFIndex length, CFStringEncoding encoding, CFURLRef baseURL) { 2291 CFStringRef urlString = CFStringCreateWithBytes(allocator, URLBytes, length, encoding, false); 2292 CFURLRef result = NULL; 2293 if ( urlString ) { 2294 if ( baseURL || CFStringGetLength(urlString) != 0 ) { 2295 result = _CFURLAlloc(allocator); 2296 if (result) { 2297 if ( !_CFURLInitWithURLString(result, urlString, false /* checkForLegalCharacters */, baseURL) ) { 2298 CFRelease(result); 2299 result = NULL; 2300 } 2301 else { 2302 if (encoding != kCFStringEncodingUTF8) { 2303 ((struct __CFURL *)result)->_encoding = encoding; 2304#if DEBUG_URL_MEMORY_USAGE 2305 numNonUTF8EncodedURLs++; 2306#endif 2307 } 2308 } 2309 } 2310 } 2311 CFRelease(urlString); // it's retained by result, now. 2312 } 2313 return ( result ); 2314} 2315 2316CFDataRef CFURLCreateData(CFAllocatorRef allocator, CFURLRef url, CFStringEncoding encoding, Boolean escapeWhitespace) { 2317 CFDataRef result = NULL; 2318 if ( url ) { 2319 CFStringRef myStr = CFURLGetString(url); 2320 if ( myStr ) { 2321 result = CFStringCreateExternalRepresentation(allocator, myStr, encoding, 0); 2322 } 2323 } 2324 return result; 2325} 2326 2327// Any escape sequences in URLString will be interpreted via UTF-8. 2328CFURLRef CFURLCreateWithString(CFAllocatorRef allocator, CFStringRef URLString, CFURLRef baseURL) { 2329 CFURLRef url = NULL; 2330 if ( URLString && (baseURL || CFStringGetLength(URLString) != 0) ) { 2331 url = _CFURLAlloc(allocator); 2332 if (url) { 2333 if ( !_CFURLInitWithURLString(url, URLString, true /* checkForLegalCharacters */, baseURL) ) { 2334 CFRelease(url); 2335 url = NULL; 2336 } 2337 } 2338 } 2339 return ( url ); 2340} 2341 2342static CFURLRef _CFURLCreateWithArbitraryString(CFAllocatorRef allocator, CFStringRef URLString, CFURLRef baseURL) { 2343 CFURLRef url = NULL; 2344 if ( URLString && (baseURL || CFStringGetLength(URLString) != 0) ) { 2345 url = _CFURLAlloc(allocator); 2346 if (url) { 2347 if ( !_CFURLInitWithURLString(url, URLString, false /* checkForLegalCharacters */, baseURL) ) { 2348 CFRelease(url); 2349 url = NULL; 2350 } 2351 } 2352 } 2353 return ( url ); 2354} 2355 2356CFURLRef CFURLCreateAbsoluteURLWithBytes(CFAllocatorRef alloc, const UInt8 *relativeURLBytes, CFIndex length, CFStringEncoding encoding, CFURLRef baseURL, Boolean useCompatibilityMode) { 2357 CFURLRef result = NULL; 2358 2359 /* 2360 CFURLCreateAbsoluteURLWithBytes() and useCompatibilityMode is for: 2361 <rdar://problem/2711611> Problem with '|' in url and calling CFURLCreateWithString() 2362 <rdar://problem/3085893> CFURL resolves "../" URLs at top level in a way that is not the same as web browsers 2363 <rdar://problem/3085920> CFURL API should be more helpful for accepting URLs with technically-bad characters 2364 <rdar://problem/3205656> Safari needs CFURL to deal with google.com URLs that end in "%" 2365 <rdar://problem/3219233> Safari needs CFURL to not remove path when relative URL is just a query string 2366 <rdar://problem/3219240> Safari needs CFURL to support "compatibility" resolution of partial URLs with schemes 2367 2368 If useCompatibilityMode is true, the rules historically used on the web are used to resolve relativeString against baseURL - these rules are generally listed in the RFC as optional or alternate interpretations. Otherwise, the strict rules from the RFC are used. 2369 2370 The major differences are that in compatibility mode, we are lenient of the scheme appearing in relative portion, leading "../" components are removed from the final URL's path, and if the relative portion contains only resource specifier pieces (query, parameters, and fragment), then the last path component of the base URL will not be deleted 2371 */ 2372 2373 // if not useCompatibilityMode, use CFURLCreateWithBytes and then CFURLCopyAbsoluteURL if there's a baseURL 2374 if ( !useCompatibilityMode ) { 2375 CFURLRef url = CFURLCreateWithBytes(alloc, relativeURLBytes, length, encoding, baseURL); 2376 if ( url != NULL ) { 2377 if ( baseURL != NULL ) { 2378 result = CFURLCopyAbsoluteURL(url); 2379 CFRelease(url); 2380 } else { 2381 result = url; 2382 } 2383 } 2384 } else { 2385 UInt32 absFlags = 0; 2386 CFRange *absRanges; 2387 CFStringRef absString = NULL; 2388 Boolean absStringIsMutable = false; 2389 CFURLRef absURL; 2390 CFStringRef relativeString; 2391 2392 relativeString = CFStringCreateWithBytes(alloc, relativeURLBytes, length, encoding, false); 2393 if ( relativeString != NULL ) { 2394 if (!baseURL) { 2395 absString = relativeString; 2396 } else { 2397 UniChar ch = 0; 2398 if ( CFStringGetLength(relativeString) > 0 ) { 2399 ch = CFStringGetCharacterAtIndex(relativeString, 0); 2400 } 2401 if (ch == '?' || ch == ';' || ch == '#') { 2402 // Nothing but parameter + query + fragment; append to the baseURL string 2403 CFStringRef baseString; 2404 if (CF_IS_OBJC(__kCFURLTypeID, baseURL)) { 2405 baseString = CFURLGetString(baseURL); 2406 } else { 2407 baseString = baseURL->_string; 2408 } 2409 absString = CFStringCreateMutable(alloc, CFStringGetLength(baseString) + CFStringGetLength(relativeString)); 2410 CFStringAppend((CFMutableStringRef)absString, baseString); 2411 CFStringAppend((CFMutableStringRef)absString, relativeString); 2412 absStringIsMutable = true; 2413 } else { 2414 UInt32 relFlags = 0; 2415 CFRange *relRanges; 2416 CFStringRef relString = NULL; 2417 _parseComponents(alloc, relativeString, baseURL, &relFlags, &relRanges); 2418 if (relFlags & HAS_SCHEME) { 2419 CFStringRef baseScheme = CFURLCopyScheme(baseURL); 2420 CFRange relSchemeRange = _rangeForComponent(relFlags, relRanges, HAS_SCHEME); 2421 if (baseScheme && CFStringGetLength(baseScheme) == relSchemeRange.length && CFStringHasPrefix(relativeString, baseScheme)) { 2422 relString = CFStringCreateWithSubstring(alloc, relativeString, CFRangeMake(relSchemeRange.length+1, CFStringGetLength(relativeString) - relSchemeRange.length - 1)); 2423 CFAllocatorDeallocate(alloc, relRanges); 2424 relFlags = 0; 2425 _parseComponents(alloc, relString, baseURL, &relFlags, &relRanges); 2426 } else { 2427 // Discard the base string; the relative string is absolute and we're not in the funky edge case where the schemes match 2428 CFRetain(relativeString); 2429 absString = relativeString; 2430 } 2431 if (baseScheme) CFRelease(baseScheme); 2432 } else { 2433 CFRetain(relativeString); 2434 relString = relativeString; 2435 } 2436 if (!absString) { 2437 if (!CF_IS_OBJC(__kCFURLTypeID, baseURL)) { 2438 absString = resolveAbsoluteURLString(alloc, relString, relFlags, relRanges, baseURL->_string, baseURL->_flags, baseURL->_ranges); 2439 } else { 2440 CFStringRef baseString; 2441 UInt32 baseFlags = 0; 2442 CFRange *baseRanges; 2443 if (CF_IS_OBJC(__kCFURLTypeID, baseURL)) { 2444 baseString = CFURLGetString(baseURL); 2445 } else { 2446 baseString = baseURL->_string; 2447 } 2448 _parseComponents(alloc, baseString, NULL, &baseFlags, &baseRanges); 2449 absString = resolveAbsoluteURLString(alloc, relString, relFlags, relRanges, baseString, baseFlags, baseRanges); 2450 CFAllocatorDeallocate(alloc, baseRanges); 2451 } 2452 absStringIsMutable = true; 2453 } 2454 if (relString) CFRelease(relString); 2455 CFAllocatorDeallocate(alloc, relRanges); 2456 } 2457 CFRelease(relativeString); 2458 } 2459 } 2460 if ( absString ) { 2461 _parseComponents(alloc, absString, NULL, &absFlags, &absRanges); 2462 if (absFlags & HAS_PATH) { 2463 CFRange pathRg = _rangeForComponent(absFlags, absRanges, HAS_PATH); 2464 // This is expensive, but it allows us to reuse _resolvedPath. It should be cleaned up to get this allocation removed at some point. - REW 2465 UniChar *buf = (UniChar *)CFAllocatorAllocate(alloc, sizeof(UniChar) * (pathRg.length + 1), 0); 2466 CFStringRef newPath; 2467 CFStringGetCharacters(absString, pathRg, buf); 2468 buf[pathRg.length] = '\0'; 2469 newPath = _resolvedPath(buf, buf + pathRg.length, '/', true, false, alloc); 2470 if (CFStringGetLength(newPath) != pathRg.length) { 2471 if (!absStringIsMutable) { 2472 CFStringRef tmp = CFStringCreateMutableCopy(alloc, CFStringGetLength(absString), absString); 2473 CFRelease(absString); 2474 absString = tmp; 2475 } 2476 CFStringReplace((CFMutableStringRef)absString, pathRg, newPath); 2477 } 2478 CFRelease(newPath); 2479 // Do not deallocate buf; newPath took ownership of it. 2480 } 2481 CFAllocatorDeallocate(alloc, absRanges); 2482 absURL = _CFURLCreateWithArbitraryString(alloc, absString, NULL); 2483 CFRelease(absString); 2484 if (absURL) { 2485 ((struct __CFURL *)absURL)->_encoding = encoding; 2486#if DEBUG_URL_MEMORY_USAGE 2487 if ( encoding != kCFStringEncodingUTF8 ) { 2488 numNonUTF8EncodedURLs++; 2489 } 2490#endif 2491 } 2492 result = absURL; 2493 } 2494 } 2495 2496 return ( result ); 2497} 2498 2499/* This function is this way because I pulled it out of _resolvedURLPath (so that _resolvedFileSystemPath could use it), and I didn't want to spend a bunch of energy reworking the code. So instead of being a bit more intelligent about inputs, it just demands a slightly perverse set of parameters, to match the old _resolvedURLPath code. -- REW, 6/14/99 */ 2500static CFStringRef _resolvedPath(UniChar *pathStr, UniChar *end, UniChar pathDelimiter, Boolean stripLeadingDotDots, Boolean stripTrailingDelimiter, CFAllocatorRef alloc) { 2501 UniChar *idx = pathStr; 2502 while (idx < end) { 2503 if (*idx == '.') { 2504 if (idx+1 == end) { 2505 if (idx != pathStr) { 2506 *idx = '\0'; 2507 end = idx; 2508 } 2509 break; 2510 } else if (*(idx+1) == pathDelimiter) { 2511 if (idx + 2 != end || idx != pathStr) { 2512 memmove(idx, idx+2, (end-(idx+2)+1) * sizeof(UniChar)); 2513 end -= 2; 2514 continue; 2515 } else { 2516 // Do not delete the sole path component 2517 break; 2518 } 2519 } else if (( end-idx >= 2 ) && *(idx+1) == '.' && (idx+2 == end || (( end-idx > 2 ) && *(idx+2) == pathDelimiter))) { 2520 if (idx - pathStr >= 2) { 2521 // Need at least 2 characters between index and pathStr, because we know if index != newPath, then *(index-1) == pathDelimiter, and we need something before that to compact out. 2522 UniChar *lastDelim = idx-2; 2523 while (lastDelim >= pathStr && *lastDelim != pathDelimiter) lastDelim --; 2524 lastDelim ++; 2525 if (lastDelim != idx && (idx-lastDelim != 3 || *lastDelim != '.' || *(lastDelim +1) != '.')) { 2526 // We have a genuine component to compact out 2527 if (idx+2 != end) { 2528 unsigned numCharsToMove = end - (idx+3) + 1; // +1 to move the '\0' as well 2529 memmove(lastDelim, idx+3, numCharsToMove * sizeof(UniChar)); 2530 end -= (idx + 3 - lastDelim); 2531 idx = lastDelim; 2532 continue; 2533 } else if (lastDelim != pathStr) { 2534 *lastDelim = '\0'; 2535 end = lastDelim; 2536 break; 2537 } else { 2538 // Don't allow the path string to devolve to the empty string. Fall back to "." instead. - REW 2539 pathStr[0] = '.'; 2540 pathStr[1] = '/'; 2541 pathStr[2] = '\0'; 2542 end = &pathStr[3]; 2543 break; 2544 } 2545 } 2546 } else if (stripLeadingDotDots) { 2547 if (idx + 3 != end) { 2548 unsigned numCharsToMove = end - (idx + 3) + 1; 2549 memmove(idx, idx+3, numCharsToMove * sizeof(UniChar)); 2550 end -= 3; 2551 continue; 2552 } else { 2553 // Do not devolve the last path component 2554 break; 2555 } 2556 } 2557 } 2558 } 2559 while (idx < end && *idx != pathDelimiter) idx ++; 2560 idx ++; 2561 } 2562 if (stripTrailingDelimiter && end > pathStr && end-1 != pathStr && *(end-1) == pathDelimiter) { 2563 end --; 2564 } 2565 // return an zero-length string if end < pathStr 2566 return CFStringCreateWithCharactersNoCopy(alloc, pathStr, end >= pathStr ? end - pathStr : 0, alloc); 2567} 2568 2569static CFMutableStringRef resolveAbsoluteURLStringBuffer(CFAllocatorRef alloc, CFStringRef relString, UInt32 relFlags, CFRange *relRanges, CFStringRef baseString, UInt32 baseFlags, CFRange *baseRanges, UniChar *buf) 2570{ 2571 CFStringAppendBuffer appendBuffer; 2572 UniChar chars[2]; 2573 CFStringInitAppendBuffer(alloc, &appendBuffer); 2574 CFRange rg; 2575 2576 rg = _rangeForComponent(baseFlags, baseRanges, HAS_SCHEME); 2577 if (rg.location != kCFNotFound) { 2578 CFStringGetCharacters(baseString, rg, buf); 2579 CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length); 2580 chars[0] = ':'; 2581 CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 1); 2582 } 2583 2584 if (relFlags & NET_LOCATION_MASK) { 2585 CFStringAppendStringToAppendBuffer(&appendBuffer, relString); 2586 } else { 2587 chars[0] = '/'; 2588 chars[1] = '/'; 2589 CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 2); 2590 rg = _netLocationRange(baseFlags, baseRanges); 2591 if (rg.location != kCFNotFound) { 2592 CFStringGetCharacters(baseString, rg, buf); 2593 CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length); 2594 } 2595 2596 if (relFlags & HAS_PATH) { 2597 CFRange relPathRg = _rangeForComponent(relFlags, relRanges, HAS_PATH); 2598 CFRange basePathRg = _rangeForComponent(baseFlags, baseRanges, HAS_PATH); 2599 CFStringRef newPath; 2600 Boolean useRelPath = false; 2601 Boolean useBasePath = false; 2602 if (basePathRg.location == kCFNotFound) { 2603 useRelPath = true; 2604 } else if (relPathRg.length == 0) { 2605 useBasePath = true; 2606 } else if (CFStringGetCharacterAtIndex(relString, relPathRg.location) == '/') { 2607 useRelPath = true; 2608 } else if (basePathRg.location == kCFNotFound || basePathRg.length == 0) { 2609 useRelPath = true; 2610 } 2611 if (useRelPath) { 2612 newPath = CFStringCreateWithSubstring(alloc, relString, relPathRg); 2613 } else if (useBasePath) { 2614 newPath = CFStringCreateWithSubstring(alloc, baseString, basePathRg); 2615 } else { 2616 // FIXME: Get rid of this allocation 2617 UniChar *newPathBuf = (UniChar *)CFAllocatorAllocate(alloc, sizeof(UniChar) * (relPathRg.length + basePathRg.length + 1), 0); 2618 UniChar *idx, *end; 2619 CFStringGetCharacters(baseString, basePathRg, newPathBuf); 2620 idx = newPathBuf + basePathRg.length - 1; 2621 while (idx != newPathBuf && *idx != '/') idx --; 2622 if (*idx == '/') idx ++; 2623 CFStringGetCharacters(relString, relPathRg, idx); 2624 end = idx + relPathRg.length; 2625 *end = 0; 2626 newPath = _resolvedPath(newPathBuf, end, '/', false, false, alloc); 2627 } 2628 /* Under Win32 absolute path can begin with letter 2629 * so we have to add one '/' to the newString 2630 * (Sergey Zubarev) 2631 */ 2632 // No - the input strings here are URL path strings, not Win32 paths. 2633 // Absolute paths should have had a '/' prepended before this point. 2634 // I have removed Sergey Zubarev's change and left his comment (and 2635 // this one) as a record. - REW, 1/5/2004 2636 2637 // if the relative URL does not begin with a slash and 2638 // the base does not end with a slash, add a slash 2639 if ((basePathRg.location == kCFNotFound || basePathRg.length == 0) && CFStringGetCharacterAtIndex(newPath, 0) != '/') { 2640 chars[0] = '/'; 2641 CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 1); 2642 } 2643 2644 CFStringAppendStringToAppendBuffer(&appendBuffer, newPath); 2645 CFRelease(newPath); 2646 rg.location = relPathRg.location + relPathRg.length; 2647 rg.length = CFStringGetLength(relString); 2648 if (rg.length > rg.location) { 2649 rg.length -= rg.location; 2650 CFStringGetCharacters(relString, rg, buf); 2651 CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length); 2652 } 2653 } else { 2654 rg = _rangeForComponent(baseFlags, baseRanges, HAS_PATH); 2655 if (rg.location != kCFNotFound) { 2656 CFStringGetCharacters(baseString, rg, buf); 2657 CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length); 2658 } 2659 2660 if (!(relFlags & RESOURCE_SPECIFIER_MASK)) { 2661 // ??? Can this ever happen? 2662 UInt32 rsrcFlag = _firstResourceSpecifierFlag(baseFlags); 2663 if (rsrcFlag) { 2664 rg.location = _rangeForComponent(baseFlags, baseRanges, rsrcFlag).location; 2665 rg.length = CFStringGetLength(baseString) - rg.location; 2666 rg.location --; // To pick up the separator 2667 rg.length ++; 2668 CFStringGetCharacters(baseString, rg, buf); 2669 CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length); 2670 } 2671 } else if (relFlags & HAS_PARAMETERS) { 2672 rg = _rangeForComponent(relFlags, relRanges, HAS_PARAMETERS); 2673 rg.location --; // To get the semicolon that starts the parameters 2674 rg.length = CFStringGetLength(relString) - rg.location; 2675 CFStringGetCharacters(relString, rg, buf); 2676 CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length); 2677 } else { 2678 // Sigh; we have to resolve these against one another 2679 rg = _rangeForComponent(baseFlags, baseRanges, HAS_PARAMETERS); 2680 if (rg.location != kCFNotFound) { 2681 chars[0] = ';'; 2682 CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 1); 2683 CFStringGetCharacters(baseString, rg, buf); 2684 CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length); 2685 } 2686 rg = _rangeForComponent(relFlags, relRanges, HAS_QUERY); 2687 if (rg.location != kCFNotFound) { 2688 chars[0] = '?'; 2689 CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 1); 2690 CFStringGetCharacters(relString, rg, buf); 2691 CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length); 2692 } else { 2693 rg = _rangeForComponent(baseFlags, baseRanges, HAS_QUERY); 2694 if (rg.location != kCFNotFound) { 2695 chars[0] = '?'; 2696 CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 1); 2697 CFStringGetCharacters(baseString, rg, buf); 2698 CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length); 2699 } 2700 } 2701 // Only the relative portion of the URL can supply the fragment; otherwise, what would be in the relativeURL? 2702 rg = _rangeForComponent(relFlags, relRanges, HAS_FRAGMENT); 2703 if (rg.location != kCFNotFound) { 2704 chars[0] = '#'; 2705 CFStringAppendCharactersToAppendBuffer(&appendBuffer, chars, 1); 2706 CFStringGetCharacters(relString, rg, buf); 2707 CFStringAppendCharactersToAppendBuffer(&appendBuffer, buf, rg.length); 2708 } 2709 } 2710 } 2711 } 2712 return CFStringCreateMutableWithAppendBuffer(&appendBuffer); 2713} 2714 2715static CFMutableStringRef resolveAbsoluteURLString(CFAllocatorRef alloc, CFStringRef relString, UInt32 relFlags, CFRange *relRanges, CFStringRef baseString, UInt32 baseFlags, CFRange *baseRanges) { 2716 CFMutableStringRef result; 2717 CFIndex bufLen = CFStringGetLength(baseString) + CFStringGetLength(relString); // Overkill, but guarantees we never allocate again 2718 if ( bufLen <= 1024 ) { 2719 STACK_BUFFER_DECL(UniChar, buf, bufLen); 2720 result = resolveAbsoluteURLStringBuffer(alloc, relString, relFlags, relRanges, baseString, baseFlags, baseRanges, buf); 2721 return ( result ); 2722 } 2723 else { 2724 UniChar *buf = (UniChar *)CFAllocatorAllocate(alloc, bufLen * sizeof(UniChar), 0); 2725 result = resolveAbsoluteURLStringBuffer(alloc, relString, relFlags, relRanges, baseString, baseFlags, baseRanges, buf); 2726 CFAllocatorDeallocate(alloc, buf); 2727 return ( result ); 2728 } 2729} 2730 2731CFURLRef CFURLCopyAbsoluteURL(CFURLRef relativeURL) { 2732 CFURLRef anURL, base; 2733 CFAllocatorRef alloc = CFGetAllocator(relativeURL); 2734 CFStringRef baseString, newString; 2735 UInt32 baseFlags; 2736 CFRange *baseRanges; 2737 Boolean baseIsObjC; 2738 2739 CFAssert1(relativeURL != NULL, __kCFLogAssertion, "%s(): Cannot create an absolute URL from a NULL relative URL", __PRETTY_FUNCTION__); 2740 if (CF_IS_OBJC(__kCFURLTypeID, relativeURL)) { 2741 anURL = (CFURLRef) CF_OBJC_CALLV((NSURL *)relativeURL, absoluteURL); 2742 if (anURL) CFRetain(anURL); 2743 return anURL; 2744 } 2745 2746 __CFGenericValidateType(relativeURL, __kCFURLTypeID); 2747 2748 base = relativeURL->_base; 2749 if (!base) { 2750 return (CFURLRef)CFRetain(relativeURL); 2751 } 2752 baseIsObjC = CF_IS_OBJC(__kCFURLTypeID, base); 2753 2754 if (!baseIsObjC) { 2755 baseString = base->_string; 2756 baseFlags = base->_flags; 2757 baseRanges = base->_ranges; 2758 } else { 2759 baseString = CFURLGetString(base); 2760 baseFlags = 0; 2761 baseRanges = NULL; 2762 _parseComponents(alloc, baseString, NULL, &baseFlags, &baseRanges); 2763 } 2764 2765 newString = resolveAbsoluteURLString(alloc, relativeURL->_string, relativeURL->_flags, relativeURL->_ranges, baseString, baseFlags, baseRanges); 2766 if (baseIsObjC) { 2767 CFAllocatorDeallocate(alloc, baseRanges); 2768 } 2769 anURL = _CFURLCreateWithArbitraryString(alloc, newString, NULL); 2770 CFRelease(newString); 2771 ((struct __CFURL *)anURL)->_encoding = relativeURL->_encoding; 2772#if DEBUG_URL_MEMORY_USAGE 2773 if ( relativeURL->_encoding != kCFStringEncodingUTF8 ) { 2774 numNonUTF8EncodedURLs++; 2775 } 2776#endif 2777 return anURL; 2778} 2779 2780 2781/*******************/ 2782/* Basic accessors */ 2783/*******************/ 2784CFStringEncoding _CFURLGetEncoding(CFURLRef url) { 2785 return url->_encoding; 2786} 2787 2788Boolean CFURLCanBeDecomposed(CFURLRef anURL) { 2789 anURL = _CFURLFromNSURL(anURL); 2790 return ((anURL->_flags & IS_DECOMPOSABLE) != 0); 2791} 2792 2793CFStringRef CFURLGetString(CFURLRef url) { 2794 CF_OBJC_FUNCDISPATCHV(__kCFURLTypeID, CFStringRef, (NSURL *)url, relativeString); 2795 if (!_haveTestedOriginalString(url)) { 2796 computeSanitizedString(url); 2797 } 2798 if (url->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) { 2799 return url->_string; 2800 } else { 2801 return _getSanitizedString( url ); 2802 } 2803} 2804 2805CFIndex CFURLGetBytes(CFURLRef url, UInt8 *buffer, CFIndex bufferLength) { 2806 CFIndex length, charsConverted, usedLength; 2807 CFStringRef string; 2808 CFStringEncoding enc; 2809 if (CF_IS_OBJC(__kCFURLTypeID, url)) { 2810 string = CFURLGetString(url); 2811 enc = kCFStringEncodingUTF8; 2812 } else { 2813 string = url->_string; 2814 enc = url->_encoding; 2815 } 2816 length = CFStringGetLength(string); 2817 charsConverted = CFStringGetBytes(string, CFRangeMake(0, length), enc, 0, false, buffer, bufferLength, &usedLength); 2818 if (charsConverted != length) { 2819 return -1; 2820 } else { 2821 return usedLength; 2822 } 2823} 2824 2825CFURLRef CFURLGetBaseURL(CFURLRef anURL) { 2826 CF_OBJC_FUNCDISPATCHV(__kCFURLTypeID, CFURLRef, (NSURL *)anURL, baseURL); 2827 return anURL->_base; 2828} 2829 2830// Assumes the URL is already parsed 2831static CFRange _rangeForComponent(UInt32 flags, CFRange *ranges, UInt32 compFlag) { 2832 UInt32 idx = 0; 2833 if (!(flags & compFlag)) return CFRangeMake(kCFNotFound, 0); 2834 while (!(compFlag & 1)) { 2835 compFlag = compFlag >> 1; 2836 if (flags & 1) { 2837 idx ++; 2838 } 2839 flags = flags >> 1; 2840 } 2841 return ranges[idx]; 2842} 2843 2844static CFStringRef _retainedComponentString(CFURLRef url, UInt32 compFlag, Boolean fromOriginalString, Boolean removePercentEscapes) { 2845 CFRange rg; 2846 CFStringRef comp; 2847 CFAllocatorRef alloc = CFGetAllocator(url); 2848 if (removePercentEscapes) { 2849 fromOriginalString = true; 2850 } 2851 rg = _rangeForComponent(url->_flags, url->_ranges, compFlag); 2852 if (rg.location == kCFNotFound) { 2853 comp = NULL; 2854 } 2855 else { 2856 if ( compFlag & HAS_SCHEME ) { 2857 switch ( _getSchemeTypeFromFlags(url->_flags) ) { 2858 case kHasHttpScheme: 2859 comp = (CFStringRef)CFRetain(kCFURLHTTPScheme); 2860 break; 2861 2862 case kHasHttpsScheme: 2863 comp = (CFStringRef)CFRetain(kCFURLHTTPSScheme); 2864 break; 2865 2866 case kHasFileScheme: 2867 comp = (CFStringRef)CFRetain(kCFURLFileScheme); 2868 break; 2869 2870 case kHasDataScheme: 2871 comp = (CFStringRef)CFRetain(kCFURLDataScheme); 2872 break; 2873 2874 case kHasFtpScheme: 2875 comp = (CFStringRef)CFRetain(kCFURLFTPScheme); 2876 break; 2877 2878 default: 2879 comp = CFStringCreateWithSubstring(alloc, url->_string, rg); 2880 break; 2881 } 2882 } 2883 else { 2884 comp = CFStringCreateWithSubstring(alloc, url->_string, rg); 2885 } 2886 2887 if (comp && !fromOriginalString) { 2888 if (!_haveTestedOriginalString(url)) { 2889 computeSanitizedString(url); 2890 } 2891 if (!(url->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) && (_getAdditionalDataFlags(url) & compFlag)) { 2892 CFStringRef newComp = correctedComponent(comp, compFlag, url->_encoding); 2893 CFRelease(comp); 2894 comp = newComp; 2895 } 2896 } 2897 if (comp && removePercentEscapes) { 2898 CFStringRef tmp; 2899 if (url->_encoding == kCFStringEncodingUTF8) { 2900 tmp = CFURLCreateStringByReplacingPercentEscapes(alloc, comp, CFSTR("")); 2901 } else { 2902 tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(alloc, comp, CFSTR(""), url->_encoding); 2903 } 2904 CFRelease(comp); 2905 comp = tmp; 2906 } 2907 2908 } 2909 return comp; 2910} 2911 2912CFStringRef CFURLCopyScheme(CFURLRef anURL) { 2913 CFStringRef scheme; 2914 if (CF_IS_OBJC(__kCFURLTypeID, anURL)) { 2915 scheme = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, scheme); 2916 if ( scheme ) { 2917 CFRetain(scheme); 2918 } 2919 } 2920 else { 2921 switch ( _getSchemeTypeFromFlags(anURL->_flags) ) { 2922 case kHasHttpScheme: 2923 scheme = (CFStringRef)CFRetain(kCFURLHTTPScheme); 2924 break; 2925 2926 case kHasHttpsScheme: 2927 scheme = (CFStringRef)CFRetain(kCFURLHTTPSScheme); 2928 break; 2929 2930 case kHasFileScheme: 2931 scheme = (CFStringRef)CFRetain(kCFURLFileScheme); 2932 break; 2933 2934 case kHasDataScheme: 2935 scheme = (CFStringRef)CFRetain(kCFURLDataScheme); 2936 break; 2937 2938 case kHasFtpScheme: 2939 scheme = (CFStringRef)CFRetain(kCFURLFTPScheme); 2940 break; 2941 2942 default: 2943 scheme = _retainedComponentString(anURL, HAS_SCHEME, true, false); 2944 if ( !scheme ) { 2945 if (anURL->_base) { 2946 scheme = CFURLCopyScheme(anURL->_base); 2947 } else { 2948 scheme = NULL; 2949 } 2950 } 2951 break; 2952 } 2953 } 2954 return ( scheme ); 2955} 2956 2957static CFRange _netLocationRange(UInt32 flags, CFRange *ranges) { 2958 CFRange netRgs[4]; 2959 CFRange netRg = {kCFNotFound, 0}; 2960 CFIndex i, c = 4; 2961 2962 if ((flags & NET_LOCATION_MASK) == 0) return CFRangeMake(kCFNotFound, 0); 2963 2964 netRgs[0] = _rangeForComponent(flags, ranges, HAS_USER); 2965 netRgs[1] = _rangeForComponent(flags, ranges, HAS_PASSWORD); 2966 netRgs[2] = _rangeForComponent(flags, ranges, HAS_HOST); 2967 netRgs[3] = _rangeForComponent(flags, ranges, HAS_PORT); 2968 for (i = 0; i < c; i ++) { 2969 if (netRgs[i].location == kCFNotFound) continue; 2970 if (netRg.location == kCFNotFound) { 2971 netRg = netRgs[i]; 2972 } else { 2973 netRg.length = netRgs[i].location + netRgs[i].length - netRg.location; 2974 } 2975 } 2976 return netRg; 2977} 2978 2979CFStringRef CFURLCopyNetLocation(CFURLRef anURL) { 2980 anURL = _CFURLFromNSURL(anURL); 2981 if (anURL->_flags & NET_LOCATION_MASK) { 2982 // We provide the net location 2983 CFRange netRg = _netLocationRange(anURL->_flags, anURL->_ranges); 2984 CFStringRef netLoc; 2985 if (!_haveTestedOriginalString(anURL)) { 2986 computeSanitizedString(anURL); 2987 } 2988 if (!(anURL->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) && (_getAdditionalDataFlags(anURL) & (USER_DIFFERS | PASSWORD_DIFFERS | HOST_DIFFERS | PORT_DIFFERS))) { 2989 // Only thing that can come before the net location is the scheme. It's impossible for the scheme to contain percent escapes. Therefore, we can use the location of netRg in _sanatizedString, just not the length. 2990 CFRange netLocEnd; 2991 CFStringRef sanitizedString = _getSanitizedString(anURL); 2992 netRg.length = CFStringGetLength(sanitizedString) - netRg.location; 2993 if (CFStringFindWithOptions(sanitizedString, CFSTR("/"), netRg, 0, &netLocEnd)) { 2994 netRg.length = netLocEnd.location - netRg.location; 2995 } 2996 netLoc = CFStringCreateWithSubstring(CFGetAllocator(anURL), sanitizedString, netRg); 2997 } else { 2998 netLoc = CFStringCreateWithSubstring(CFGetAllocator(anURL), anURL->_string, netRg); 2999 } 3000 return netLoc; 3001 } else if (anURL->_base) { 3002 return CFURLCopyNetLocation(anURL->_base); 3003 } else { 3004 return NULL; 3005 } 3006} 3007 3008// NOTE - if you want an absolute path, you must first get the absolute URL. If you want a file system path, use the file system methods above. 3009CFStringRef CFURLCopyPath(CFURLRef anURL) { 3010 anURL = _CFURLFromNSURL(anURL); 3011 return _retainedComponentString(anURL, HAS_PATH, false, false); 3012} 3013 3014/* NULL if CFURLCanBeDecomposed(anURL) is false; also does not resolve the URL against its base. See also CFCreateAbsoluteURL(). Note that, strictly speaking, any leading '/' is not considered part of the URL's path, although its presence or absence determines whether the path is absolute. CFURLCopyPath()'s return value includes any leading slash (giving the path the normal POSIX appearance); CFURLCopyStrictPath()'s return value omits any leading slash, and uses isAbsolute to report whether the URL's path is absolute. 3015 3016 CFURLCopyFileSystemPath() returns the URL's path as a file system path for the given path style. All percent escape sequences are replaced. The URL is not resolved against its base before computing the path. 3017*/ 3018CFStringRef CFURLCopyStrictPath(CFURLRef anURL, Boolean *isAbsolute) { 3019 CFStringRef path = CFURLCopyPath(anURL); 3020 if (!path || CFStringGetLength(path) == 0) { 3021 if (path) CFRelease(path); 3022 if (isAbsolute) *isAbsolute = false; 3023 return NULL; 3024 } 3025 if (CFStringGetCharacterAtIndex(path, 0) == '/') { 3026 CFStringRef tmp; 3027 if (isAbsolute) *isAbsolute = true; 3028 tmp = CFStringCreateWithSubstring(CFGetAllocator(path), path, CFRangeMake(1, CFStringGetLength(path)-1)); 3029 CFRelease(path); 3030 path = tmp; 3031 } else { 3032 if (isAbsolute) *isAbsolute = false; 3033 } 3034 return path; 3035} 3036 3037Boolean CFURLHasDirectoryPath(CFURLRef anURL) { 3038 __CFGenericValidateType(anURL, __kCFURLTypeID); 3039 if (!anURL->_base || (anURL->_flags & (HAS_PATH | NET_LOCATION_MASK))) { 3040 return ((anURL->_flags & IS_DIRECTORY) != 0); 3041 } 3042 else { 3043 return CFURLHasDirectoryPath(anURL->_base); 3044 } 3045} 3046 3047static UInt32 _firstResourceSpecifierFlag(UInt32 flags) { 3048 UInt32 firstRsrcSpecFlag = 0; 3049 UInt32 flag = HAS_FRAGMENT; 3050 while (flag != HAS_PATH) { 3051 if (flags & flag) { 3052 firstRsrcSpecFlag = flag; 3053 } 3054 flag = flag >> 1; 3055 } 3056 return firstRsrcSpecFlag; 3057} 3058 3059CFStringRef CFURLCopyResourceSpecifier(CFURLRef anURL) { 3060 anURL = _CFURLFromNSURL(anURL); 3061 __CFGenericValidateType(anURL, __kCFURLTypeID); 3062 if (!(anURL->_flags & IS_DECOMPOSABLE)) { 3063 CFRange schemeRg = _rangeForComponent(anURL->_flags, anURL->_ranges, HAS_SCHEME); 3064 CFIndex base = schemeRg.location + schemeRg.length + 1; 3065 if (!_haveTestedOriginalString(anURL)) { 3066 computeSanitizedString(anURL); 3067 } 3068 3069 CFStringRef sanitizedString = _getSanitizedString(anURL); 3070 3071 if (sanitizedString) { 3072 // It is impossible to have a percent escape in the scheme (if there were one, we would have considered the URL a relativeURL with a colon in the path instead), so this range computation is always safe. 3073 return CFStringCreateWithSubstring(CFGetAllocator(anURL), sanitizedString, CFRangeMake(base, CFStringGetLength(sanitizedString)-base)); 3074 } else { 3075 return CFStringCreateWithSubstring(CFGetAllocator(anURL), anURL->_string, CFRangeMake(base, CFStringGetLength(anURL->_string)-base)); 3076 } 3077 } else { 3078 UInt32 firstRsrcSpecFlag = _firstResourceSpecifierFlag(anURL->_flags); 3079 UInt32 flag; 3080 if (firstRsrcSpecFlag) { 3081 Boolean canUseOriginalString = true; 3082 Boolean canUseSanitizedString = true; 3083 CFAllocatorRef alloc = CFGetAllocator(anURL); 3084 if (!_haveTestedOriginalString(anURL)) { 3085 computeSanitizedString(anURL); 3086 } 3087 3088 UInt32 additionalDataFlags = _getAdditionalDataFlags(anURL); 3089 CFStringRef sanitizedString = _getSanitizedString(anURL); 3090 3091 if (!(anURL->_flags & ORIGINAL_AND_URL_STRINGS_MATCH)) { 3092 // See if any pieces in the resource specifier differ between sanitized string and original string 3093 for (flag = firstRsrcSpecFlag; flag != (HAS_FRAGMENT << 1); flag = flag << 1) { 3094 if (additionalDataFlags & flag) { 3095 canUseOriginalString = false; 3096 break; 3097 } 3098 } 3099 } 3100 if (!canUseOriginalString) { 3101 // If none of the pieces prior to the first resource specifier flag differ, then we can use the offset from the original string as the offset in the sanitized string. 3102 for (flag = firstRsrcSpecFlag >> 1; flag != 0; flag = flag >> 1) { 3103 if (additionalDataFlags & flag) { 3104 canUseSanitizedString = false; 3105 break; 3106 } 3107 } 3108 } 3109 if (canUseOriginalString) { 3110 CFRange rg = _rangeForComponent(anURL->_flags, anURL->_ranges, firstRsrcSpecFlag); 3111 rg.location --; // Include the character that demarcates the component 3112 rg.length = CFStringGetLength(anURL->_string) - rg.location; 3113 return CFStringCreateWithSubstring(alloc, anURL->_string, rg); 3114 } else if (canUseSanitizedString) { 3115 CFRange rg = _rangeForComponent(anURL->_flags, anURL->_ranges, firstRsrcSpecFlag); 3116 rg.location --; // Include the character that demarcates the component 3117 rg.length = CFStringGetLength(sanitizedString) - rg.location; 3118 return CFStringCreateWithSubstring(alloc, sanitizedString, rg); 3119 } else { 3120 // Must compute the correct string to return; just reparse.... 3121 UInt32 sanFlags = 0; 3122 CFRange *sanRanges = NULL; 3123 CFRange rg; 3124 _parseComponents(alloc, sanitizedString, anURL->_base, &sanFlags, &sanRanges); 3125 rg = _rangeForComponent(sanFlags, sanRanges, firstRsrcSpecFlag); 3126 CFAllocatorDeallocate(alloc, sanRanges); 3127 rg.location --; // Include the character that demarcates the component 3128 rg.length = CFStringGetLength(sanitizedString) - rg.location; 3129 return CFStringCreateWithSubstring(CFGetAllocator(anURL), sanitizedString, rg); 3130 } 3131 } else { 3132 // The resource specifier cannot possibly come from the base. 3133 return NULL; 3134 } 3135 } 3136} 3137 3138/*************************************/ 3139/* Accessors that create new objects */ 3140/*************************************/ 3141 3142// For the next four methods, it is important to realize that, if a URL supplies any part of the net location (host, user, port, or password), it must supply all of the net location (i.e. none of it comes from its base URL). Also, it is impossible for a URL to be relative, supply none of the net location, and still have its (empty) net location take precedence over its base URL (because there's nothing that precedes the net location except the scheme, and if the URL supplied the scheme, it would be absolute, and there would be no base). 3143CFStringRef CFURLCopyHostName(CFURLRef anURL) { 3144 CFStringRef tmp; 3145 if (CF_IS_OBJC(__kCFURLTypeID, anURL)) { 3146 tmp = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, host); 3147 if (tmp) CFRetain(tmp); 3148 return tmp; 3149 } 3150 __CFGenericValidateType(anURL, __kCFURLTypeID); 3151 tmp = _retainedComponentString(anURL, HAS_HOST, true, true); 3152 if (tmp) { 3153 if (anURL->_flags & IS_IPV6_ENCODED) { 3154 // Have to strip off the brackets to get the true hostname. 3155 // Assume that to be legal the first and last characters are brackets! 3156 CFStringRef strippedHost = CFStringCreateWithSubstring(CFGetAllocator(anURL), tmp, CFRangeMake(1, CFStringGetLength(tmp) - 2)); 3157 CFRelease(tmp); 3158 tmp = strippedHost; 3159 } 3160 return tmp; 3161 } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) { 3162 return CFURLCopyHostName(anURL->_base); 3163 } else { 3164 return NULL; 3165 } 3166} 3167 3168// Return -1 to indicate no port is specified 3169SInt32 CFURLGetPortNumber(CFURLRef anURL) { 3170 CFStringRef port; 3171 if (CF_IS_OBJC(__kCFURLTypeID, anURL)) { 3172 CFNumberRef cfPort = (CFNumberRef) CF_OBJC_CALLV((NSURL *)anURL, port); 3173 SInt32 num; 3174 if (cfPort && CFNumberGetValue(cfPort, kCFNumberSInt32Type, &num)) return num; 3175 return -1; 3176 } 3177 __CFGenericValidateType(anURL, __kCFURLTypeID); 3178 port = _retainedComponentString(anURL, HAS_PORT, true, false); 3179 if (port) { 3180 SInt32 portNum, idx, length = CFStringGetLength(port); 3181 CFStringInlineBuffer buf; 3182 CFStringInitInlineBuffer(port, &buf, CFRangeMake(0, length)); 3183 idx = 0; 3184 if (!__CFStringScanInteger(&buf, NULL, &idx, false, &portNum) || (idx != length)) { 3185 portNum = -1; 3186 } 3187 CFRelease(port); 3188 return portNum; 3189 } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) { 3190 return CFURLGetPortNumber(anURL->_base); 3191 } else { 3192 return -1; 3193 } 3194} 3195 3196CFStringRef CFURLCopyUserName(CFURLRef anURL) { 3197 CFStringRef user; 3198 if (CF_IS_OBJC(__kCFURLTypeID, anURL)) { 3199 user = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, user); 3200 if (user) CFRetain(user); 3201 return user; 3202 } 3203 __CFGenericValidateType(anURL, __kCFURLTypeID); 3204 user = _retainedComponentString(anURL, HAS_USER, true, true); 3205 if (user) { 3206 return user; 3207 } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) { 3208 return CFURLCopyUserName(anURL->_base); 3209 } else { 3210 return NULL; 3211 } 3212} 3213 3214CFStringRef CFURLCopyPassword(CFURLRef anURL) { 3215 CFStringRef passwd; 3216 if (CF_IS_OBJC(__kCFURLTypeID, anURL)) { 3217 passwd = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, password); 3218 if (passwd) CFRetain(passwd); 3219 return passwd; 3220 } 3221 __CFGenericValidateType(anURL, __kCFURLTypeID); 3222 passwd = _retainedComponentString(anURL, HAS_PASSWORD, true, true); 3223 if (passwd) { 3224 return passwd; 3225 } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) { 3226 return CFURLCopyPassword(anURL->_base); 3227 } else { 3228 return NULL; 3229 } 3230} 3231 3232// The NSURL methods do not deal with escaping escape characters at all; therefore, in order to properly bridge NSURL methods, and still provide the escaping behavior that we want, we need to create functions that match the ObjC behavior exactly, and have the public CFURL... functions call these. -- REW, 10/29/98 3233 3234static CFStringRef _unescapedParameterString(CFURLRef anURL) { 3235 CFStringRef str; 3236 if (CF_IS_OBJC(__kCFURLTypeID, anURL)) { 3237 str = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, parameterString); 3238 if (str) CFRetain(str); 3239 return str; 3240 } 3241 __CFGenericValidateType(anURL, __kCFURLTypeID); 3242 str = _retainedComponentString(anURL, HAS_PARAMETERS, false, false); 3243 if (str) return str; 3244 if (!(anURL->_flags & IS_DECOMPOSABLE)) return NULL; 3245 if (!anURL->_base || (anURL->_flags & (NET_LOCATION_MASK | HAS_PATH | HAS_SCHEME))) { 3246 return NULL; 3247 // Parameter string definitely coming from the relative portion of the URL 3248 } 3249 return _unescapedParameterString( anURL->_base); 3250} 3251 3252CFStringRef CFURLCopyParameterString(CFURLRef anURL, CFStringRef charactersToLeaveEscaped) { 3253 CFStringRef param = _unescapedParameterString(anURL); 3254 if (param) { 3255 CFStringRef result; 3256 if (anURL->_encoding == kCFStringEncodingUTF8) { 3257 result = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL), param, charactersToLeaveEscaped); 3258 } else { 3259 result = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL), param, charactersToLeaveEscaped, anURL->_encoding); 3260 } 3261 CFRelease(param); 3262 return result; 3263 } 3264 return NULL; 3265} 3266 3267static CFStringRef _unescapedQueryString(CFURLRef anURL) { 3268 CFStringRef str; 3269 if (CF_IS_OBJC(__kCFURLTypeID, anURL)) { 3270 str = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, query); 3271 if (str) CFRetain(str); 3272 return str; 3273 } 3274 __CFGenericValidateType(anURL, __kCFURLTypeID); 3275 str = _retainedComponentString(anURL, HAS_QUERY, false, false); 3276 if (str) return str; 3277 if (!(anURL->_flags & IS_DECOMPOSABLE)) return NULL; 3278 if (!anURL->_base || (anURL->_flags & (HAS_SCHEME | NET_LOCATION_MASK | HAS_PATH | HAS_PARAMETERS))) { 3279 return NULL; 3280 } 3281 return _unescapedQueryString(anURL->_base); 3282} 3283 3284CFStringRef CFURLCopyQueryString(CFURLRef anURL, CFStringRef charactersToLeaveEscaped) { 3285 CFStringRef query = _unescapedQueryString(anURL); 3286 if (query) { 3287 CFStringRef tmp; 3288 if (anURL->_encoding == kCFStringEncodingUTF8) { 3289 tmp = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL), query, charactersToLeaveEscaped); 3290 } else { 3291 tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL), query, charactersToLeaveEscaped, anURL->_encoding); 3292 } 3293 CFRelease(query); 3294 return tmp; 3295 } 3296 return NULL; 3297} 3298 3299// Fragments are NEVER taken from a base URL 3300static CFStringRef _unescapedFragment(CFURLRef anURL) { 3301 CFStringRef str; 3302 if (CF_IS_OBJC(__kCFURLTypeID, anURL)) { 3303 str = (CFStringRef) CF_OBJC_CALLV((NSURL *)anURL, fragment); 3304 if (str) CFRetain(str); 3305 return str; 3306 } 3307 __CFGenericValidateType(anURL, __kCFURLTypeID); 3308 str = _retainedComponentString(anURL, HAS_FRAGMENT, false, false); 3309 return str; 3310} 3311 3312CFStringRef CFURLCopyFragment(CFURLRef anURL, CFStringRef charactersToLeaveEscaped) { 3313 CFStringRef fragment = _unescapedFragment(anURL); 3314 if (fragment) { 3315 CFStringRef tmp; 3316 if (anURL->_encoding == kCFStringEncodingUTF8) { 3317 tmp = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL), fragment, charactersToLeaveEscaped); 3318 } else { 3319 tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL), fragment, charactersToLeaveEscaped, anURL->_encoding); 3320 } 3321 CFRelease(fragment); 3322 return tmp; 3323 } 3324 return NULL; 3325} 3326 3327static CFIndex insertionLocationForMask(CFURLRef url, CFOptionFlags mask) { 3328 CFIndex firstMaskFlag = 1; 3329 CFIndex lastComponentBeforeMask = 0; 3330 while (firstMaskFlag <= HAS_FRAGMENT) { 3331 if (firstMaskFlag & mask) break; 3332 if (url->_flags & firstMaskFlag) lastComponentBeforeMask = firstMaskFlag; 3333 firstMaskFlag = firstMaskFlag << 1; 3334 } 3335 if (lastComponentBeforeMask == 0) { 3336 // mask includes HAS_SCHEME 3337 return 0; 3338 } else if (lastComponentBeforeMask == HAS_SCHEME) { 3339 // Do not have to worry about the non-decomposable case here. However, we must be prepared for the degenerate 3340 // case file:/path/immediately/without/host 3341 CFRange schemeRg = _rangeForComponent(url->_flags, url->_ranges, HAS_SCHEME); 3342 CFRange pathRg = _rangeForComponent(url->_flags, url->_ranges, HAS_PATH); 3343 if (schemeRg.length + 1 == pathRg.location) { 3344 return schemeRg.length + 1; 3345 } else { 3346 return schemeRg.length + 3; 3347 } 3348 } else { 3349 // For all other components, the separator precedes the component, so there's no need 3350 // to add extra chars to get to the next insertion point 3351 CFRange rg = _rangeForComponent(url->_flags, url->_ranges, lastComponentBeforeMask); 3352 return rg.location + rg.length; 3353 } 3354} 3355 3356static CFRange _CFURLGetCharRangeForMask(CFURLRef url, CFOptionFlags mask, CFRange *charRangeWithSeparators) { 3357 CFOptionFlags currentOption; 3358 CFOptionFlags firstMaskFlag = HAS_SCHEME; 3359 Boolean haveReachedMask = false; 3360 CFIndex beforeMask = 0; 3361 CFIndex afterMask = kCFNotFound; 3362 CFRange *currRange = url->_ranges; 3363 CFRange maskRange = {kCFNotFound, 0}; 3364 for (currentOption = 1; currentOption <= HAS_FRAGMENT; currentOption = currentOption << 1) { 3365 if (!haveReachedMask && (currentOption & mask) != 0) { 3366 firstMaskFlag = currentOption; 3367 haveReachedMask = true; 3368 } 3369 if (!(url->_flags & currentOption)) continue; 3370 if (!haveReachedMask) { 3371 beforeMask = currRange->location + currRange->length; 3372 } else if (currentOption <= mask) { 3373 if (maskRange.location == kCFNotFound) { 3374 maskRange = *currRange; 3375 } else { 3376 maskRange.length = currRange->location + currRange->length - maskRange.location; 3377 } 3378 } else { 3379 afterMask = currRange->location; 3380 break; 3381 } 3382 currRange ++; 3383 } 3384 if (afterMask == kCFNotFound) { 3385 afterMask = maskRange.location + maskRange.length; 3386 } 3387 charRangeWithSeparators->location = beforeMask; 3388 charRangeWithSeparators->length = afterMask - beforeMask; 3389 return maskRange; 3390} 3391 3392static CFRange _getCharRangeInDecomposableURL(CFURLRef url, CFURLComponentType component, CFRange *rangeIncludingSeparators) { 3393 CFOptionFlags mask; 3394 switch (component) { 3395 case kCFURLComponentScheme: 3396 mask = HAS_SCHEME; 3397 break; 3398 case kCFURLComponentNetLocation: 3399 mask = NET_LOCATION_MASK; 3400 break; 3401 case kCFURLComponentPath: 3402 mask = HAS_PATH; 3403 break; 3404 case kCFURLComponentResourceSpecifier: 3405 mask = RESOURCE_SPECIFIER_MASK; 3406 break; 3407 case kCFURLComponentUser: 3408 mask = HAS_USER; 3409 break; 3410 case kCFURLComponentPassword: 3411 mask = HAS_PASSWORD; 3412 break; 3413 case kCFURLComponentUserInfo: 3414 mask = HAS_USER | HAS_PASSWORD; 3415 break; 3416 case kCFURLComponentHost: 3417 mask = HAS_HOST; 3418 break; 3419 case kCFURLComponentPort: 3420 mask = HAS_PORT; 3421 break; 3422 case kCFURLComponentParameterString: 3423 mask = HAS_PARAMETERS; 3424 break; 3425 case kCFURLComponentQuery: 3426 mask = HAS_QUERY; 3427 break; 3428 case kCFURLComponentFragment: 3429 mask = HAS_FRAGMENT; 3430 break; 3431 default: 3432 rangeIncludingSeparators->location = kCFNotFound; 3433 rangeIncludingSeparators->length = 0; 3434 return CFRangeMake(kCFNotFound, 0); 3435 } 3436 3437 if ((url->_flags & mask) == 0) { 3438 rangeIncludingSeparators->location = insertionLocationForMask(url, mask); 3439 rangeIncludingSeparators->length = 0; 3440 return CFRangeMake(kCFNotFound, 0); 3441 } else { 3442 return _CFURLGetCharRangeForMask(url, mask, rangeIncludingSeparators); 3443 } 3444} 3445 3446static CFRange _getCharRangeInNonDecomposableURL(CFURLRef url, CFURLComponentType component, CFRange *rangeIncludingSeparators) { 3447 if (component == kCFURLComponentScheme) { 3448 CFRange schemeRg = _rangeForComponent(url->_flags, url->_ranges, HAS_SCHEME); 3449 rangeIncludingSeparators->location = 0; 3450 rangeIncludingSeparators->length = schemeRg.length + 1; 3451 return schemeRg; 3452 } else if (component == kCFURLComponentResourceSpecifier) { 3453 CFRange schemeRg = _rangeForComponent(url->_flags, url->_ranges, HAS_SCHEME); 3454 CFIndex stringLength = CFStringGetLength(url->_string); 3455 if (schemeRg.length + 1 == stringLength) { 3456 rangeIncludingSeparators->location = schemeRg.length + 1; 3457 rangeIncludingSeparators->length = 0; 3458 return CFRangeMake(kCFNotFound, 0); 3459 } else { 3460 rangeIncludingSeparators->location = schemeRg.length; 3461 rangeIncludingSeparators->length = stringLength - schemeRg.length; 3462 return CFRangeMake(schemeRg.length + 1, rangeIncludingSeparators->length - 1); 3463 } 3464 } else { 3465 rangeIncludingSeparators->location = kCFNotFound; 3466 rangeIncludingSeparators->length = 0; 3467 return CFRangeMake(kCFNotFound, 0); 3468 } 3469 3470} 3471 3472CFRange CFURLGetByteRangeForComponent(CFURLRef url, CFURLComponentType component, CFRange *rangeIncludingSeparators) { 3473 CFRange charRange, charRangeWithSeparators; 3474 CFRange byteRange; 3475 CFAssert2(component > 0 && component < 13, __kCFLogAssertion, "%s(): passed invalid component %d", __PRETTY_FUNCTION__, component); 3476 url = _CFURLFromNSURL(url); 3477 3478 if (!(url->_flags & IS_DECOMPOSABLE)) { 3479 // Special-case this because non-decomposable URLs have a slightly strange flags setup 3480 charRange = _getCharRangeInNonDecomposableURL(url, component, &charRangeWithSeparators); 3481 } else { 3482 charRange = _getCharRangeInDecomposableURL(url, component, &charRangeWithSeparators); 3483 } 3484 3485 if (charRangeWithSeparators.location == kCFNotFound) { 3486 if (rangeIncludingSeparators) { 3487 rangeIncludingSeparators->location = kCFNotFound; 3488 rangeIncludingSeparators->length = 0; 3489 } 3490 return CFRangeMake(kCFNotFound, 0); 3491 } else if (rangeIncludingSeparators) { 3492 CFStringGetBytes(url->_string, CFRangeMake(0, charRangeWithSeparators.location), url->_encoding, 0, false, NULL, 0, &(rangeIncludingSeparators->location)); 3493 3494 if (charRange.location == kCFNotFound) { 3495 byteRange = charRange; 3496 CFStringGetBytes(url->_string, charRangeWithSeparators, url->_encoding, 0, false, NULL, 0, &(rangeIncludingSeparators->length)); 3497 } else { 3498 CFIndex maxCharRange = charRange.location + charRange.length; 3499 CFIndex maxCharRangeWithSeparators = charRangeWithSeparators.location + charRangeWithSeparators.length; 3500 3501 if (charRangeWithSeparators.location == charRange.location) { 3502 byteRange.location = rangeIncludingSeparators->location; 3503 } else { 3504 CFIndex numBytes; 3505 CFStringGetBytes(url->_string, CFRangeMake(charRangeWithSeparators.location, charRange.location - charRangeWithSeparators.location), url->_encoding, 0, false, NULL, 0, &numBytes); 3506 byteRange.location = charRangeWithSeparators.location + numBytes; 3507 } 3508 CFStringGetBytes(url->_string, charRange, url->_encoding, 0, false, NULL, 0, &(byteRange.length)); 3509 if (maxCharRangeWithSeparators == maxCharRange) { 3510 rangeIncludingSeparators->length = byteRange.location + byteRange.length - rangeIncludingSeparators->location; 3511 } else { 3512 CFIndex numBytes; 3513 CFRange rg; 3514 rg.location = maxCharRange; 3515 rg.length = maxCharRangeWithSeparators - rg.location; 3516 CFStringGetBytes(url->_string, rg, url->_encoding, 0, false, NULL, 0, &numBytes); 3517 rangeIncludingSeparators->length = byteRange.location + byteRange.length + numBytes - rangeIncludingSeparators->location; 3518 } 3519 } 3520 } else if (charRange.location == kCFNotFound) { 3521 byteRange = charRange; 3522 } else { 3523 CFStringGetBytes(url->_string, CFRangeMake(0, charRange.location), url->_encoding, 0, false, NULL, 0, &(byteRange.location)); 3524 CFStringGetBytes(url->_string, charRange, url->_encoding, 0, false, NULL, 0, &(byteRange.length)); 3525 } 3526 return byteRange; 3527} 3528 3529/* Component support */ 3530 3531static Boolean decomposeToNonHierarchical(CFURLRef url, CFURLComponentsNonHierarchical *components) { 3532 if ( CFURLGetBaseURL(url) != NULL) { 3533 components->scheme = NULL; 3534 } else { 3535 components->scheme = CFURLCopyScheme(url); 3536 } 3537 components->schemeSpecific = CFURLCopyResourceSpecifier(url); 3538 return true; 3539} 3540 3541static CFURLRef composeFromNonHierarchical(CFAllocatorRef alloc, const CFURLComponentsNonHierarchical *components) { 3542 CFStringRef str; 3543 if (components->scheme) { 3544 UniChar ch = ':'; 3545 str = CFStringCreateMutableCopy(alloc, CFStringGetLength(components->scheme) + 1 + (components->schemeSpecific ? CFStringGetLength(components->schemeSpecific): 0), components->scheme); 3546 CFStringAppendCharacters((CFMutableStringRef)str, &ch, 1); 3547 if (components->schemeSpecific) CFStringAppend((CFMutableStringRef)str, components->schemeSpecific); 3548 } else if (components->schemeSpecific) { 3549 str = components->schemeSpecific; 3550 CFRetain(str); 3551 } else { 3552 str = NULL; 3553 } 3554 if (str) { 3555 CFURLRef url = CFURLCreateWithString(alloc, str, NULL); 3556 CFRelease(str); 3557 return url; 3558 } else { 3559 return NULL; 3560 } 3561} 3562 3563static Boolean decomposeToRFC1808(CFURLRef url, CFURLComponentsRFC1808 *components) { 3564 CFAllocatorRef alloc = CFGetAllocator(url); 3565 static CFStringRef emptyStr = NULL; 3566 if (!emptyStr) { 3567 emptyStr = CFSTR(""); 3568 } 3569 3570 if (!CFURLCanBeDecomposed(url)) { 3571 return false; 3572 } 3573 3574 CFStringRef path = CFURLCopyPath(url); 3575 if (path) { 3576 components->pathComponents = CFStringCreateArrayBySeparatingStrings(alloc, path, CFSTR("/")); 3577 CFRelease(path); 3578 } else { 3579 components->pathComponents = NULL; 3580 } 3581 components->baseURL = CFURLGetBaseURL(url); 3582 if (components->baseURL) { 3583 CFRetain(components->baseURL); 3584 components->scheme = NULL; 3585 } else { 3586 components->scheme = _retainedComponentString(url, HAS_SCHEME, true, false); 3587 } 3588 components->user = _retainedComponentString(url, HAS_USER, false, false); 3589 components->password = _retainedComponentString(url, HAS_PASSWORD, false, false); 3590 components->host = _retainedComponentString(url, HAS_HOST, false, false); 3591 if (url->_flags & HAS_PORT) { 3592 components->port = CFURLGetPortNumber(url); 3593 } else { 3594 components->port = kCFNotFound; 3595 } 3596 components->parameterString = _retainedComponentString(url, HAS_PARAMETERS, false, false); 3597 components->query = _retainedComponentString(url, HAS_QUERY, false, false); 3598 components->fragment = _retainedComponentString(url, HAS_FRAGMENT, false, false); 3599 return true; 3600} 3601 3602static CFURLRef composeFromRFC1808(CFAllocatorRef alloc, const CFURLComponentsRFC1808 *comp) { 3603 CFMutableStringRef urlString = CFStringCreateMutable(alloc, 0); 3604 CFURLRef base = comp->baseURL; 3605 CFURLRef url; 3606 Boolean hadPrePathComponent = false; 3607 if (comp->scheme) { 3608 base = NULL; 3609 CFStringAppend(urlString, comp->scheme); 3610 CFStringAppend(urlString, CFSTR("://")); 3611 hadPrePathComponent = true; 3612 } 3613 if (comp->user || comp->password) { 3614 if (comp->user) { 3615 CFStringAppend(urlString, comp->user); 3616 } 3617 if (comp->password) { 3618 CFStringAppend(urlString, CFSTR(":")); 3619 CFStringAppend(urlString, comp->password); 3620 } 3621 CFStringAppend(urlString, CFSTR("@")); 3622 hadPrePathComponent = true; 3623 } 3624 if (comp->host) { 3625 CFStringAppend(urlString, comp->host); 3626 hadPrePathComponent = true; 3627 } 3628 if (comp->port != kCFNotFound) { 3629 CFStringAppendFormat(urlString, NULL, CFSTR(":%ld"), (long)comp->port); 3630 hadPrePathComponent = true; 3631 } 3632 3633 if (hadPrePathComponent && (comp->pathComponents == NULL || CFArrayGetCount( comp->pathComponents ) == 0 || CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(comp->pathComponents, 0)) != 0)) { 3634 CFStringAppend(urlString, CFSTR("/")); 3635 } 3636 if (comp->pathComponents) { 3637 CFStringRef pathStr = CFStringCreateByCombiningStrings(alloc, comp->pathComponents, CFSTR("/")); 3638 CFStringAppend(urlString, pathStr); 3639 CFRelease(pathStr); 3640 } 3641 if (comp->parameterString) { 3642 CFStringAppend(urlString, CFSTR(";")); 3643 CFStringAppend(urlString, comp->parameterString); 3644 } 3645 if (comp->query) { 3646 CFStringAppend(urlString, CFSTR("?")); 3647 CFStringAppend(urlString, comp->query); 3648 } 3649 if (comp->fragment) { 3650 CFStringAppend(urlString, CFSTR("#")); 3651 CFStringAppend(urlString, comp->fragment); 3652 } 3653 url = CFURLCreateWithString(alloc, urlString, base); 3654 CFRelease(urlString); 3655 return url; 3656} 3657 3658static Boolean decomposeToRFC2396(CFURLRef url, CFURLComponentsRFC2396 *comp) { 3659 CFAllocatorRef alloc = CFGetAllocator(url); 3660 CFURLComponentsRFC1808 oldComp; 3661 CFStringRef tmpStr; 3662 if (!decomposeToRFC1808(url, &oldComp)) { 3663 return false; 3664 } 3665 comp->scheme = oldComp.scheme; 3666 if (oldComp.user) { 3667 if (oldComp.password) { 3668 comp->userinfo = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@:%@"), oldComp.user, oldComp.password); 3669 CFRelease(oldComp.password); 3670 CFRelease(oldComp.user); 3671 } else { 3672 comp->userinfo = oldComp.user; 3673 } 3674 } else { 3675 comp->userinfo = NULL; 3676 } 3677 comp->host = oldComp.host; 3678 comp->port = oldComp.port; 3679 if (!oldComp.parameterString) { 3680 comp->pathComponents = oldComp.pathComponents; 3681 } else { 3682 int length = CFArrayGetCount(oldComp.pathComponents); 3683 comp->pathComponents = CFArrayCreateMutableCopy(alloc, length, oldComp.pathComponents); 3684 tmpStr = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@;%@"), CFArrayGetValueAtIndex(comp->pathComponents, length - 1), oldComp.parameterString); 3685 CFArraySetValueAtIndex((CFMutableArrayRef)comp->pathComponents, length - 1, tmpStr); 3686 CFRelease(tmpStr); 3687 CFRelease(oldComp.pathComponents); 3688 CFRelease(oldComp.parameterString); 3689 } 3690 comp->query = oldComp.query; 3691 comp->fragment = oldComp.fragment; 3692 comp->baseURL = oldComp.baseURL; 3693 return true; 3694} 3695 3696static CFURLRef composeFromRFC2396(CFAllocatorRef alloc, const CFURLComponentsRFC2396 *comp) { 3697 CFMutableStringRef urlString = CFStringCreateMutable(alloc, 0); 3698 CFURLRef base = comp->baseURL; 3699 CFURLRef url; 3700 Boolean hadPrePathComponent = false; 3701 if (comp->scheme) { 3702 base = NULL; 3703 CFStringAppend(urlString, comp->scheme); 3704 CFStringAppend(urlString, CFSTR("://")); 3705 hadPrePathComponent = true; 3706 } 3707 if (comp->userinfo) { 3708 CFStringAppend(urlString, comp->userinfo); 3709 CFStringAppend(urlString, CFSTR("@")); 3710 hadPrePathComponent = true; 3711 } 3712 if (comp->host) { 3713 CFStringAppend(urlString, comp->host); 3714 if (comp->port != kCFNotFound) { 3715 CFStringAppendFormat(urlString, NULL, CFSTR(":%ld"), (long)comp->port); 3716 } 3717 hadPrePathComponent = true; 3718 } 3719 if (hadPrePathComponent && (comp->pathComponents == NULL || CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(comp->pathComponents, 0)) != 0)) { 3720 CFStringAppend(urlString, CFSTR("/")); 3721 } 3722 if (comp->pathComponents) { 3723 CFStringRef pathStr = CFStringCreateByCombiningStrings(alloc, comp->pathComponents, CFSTR("/")); 3724 CFStringAppend(urlString, pathStr); 3725 CFRelease(pathStr); 3726 } 3727 if (comp->query) { 3728 CFStringAppend(urlString, CFSTR("?")); 3729 CFStringAppend(urlString, comp->query); 3730 } 3731 if (comp->fragment) { 3732 CFStringAppend(urlString, CFSTR("#")); 3733 CFStringAppend(urlString, comp->fragment); 3734 } 3735 url = CFURLCreateWithString(alloc, urlString, base); 3736 CFRelease(urlString); 3737 return url; 3738} 3739 3740#undef CFURLCopyComponents 3741#undef CFURLCreateFromComponents 3742 3743CF_EXPORT 3744Boolean _CFURLCopyComponents(CFURLRef url, CFURLComponentDecomposition decompositionType, void *components) { 3745 url = _CFURLFromNSURL(url); 3746 switch (decompositionType) { 3747 case kCFURLComponentDecompositionNonHierarchical: 3748 return decomposeToNonHierarchical(url, (CFURLComponentsNonHierarchical *)components); 3749 case kCFURLComponentDecompositionRFC1808: 3750 return decomposeToRFC1808(url, (CFURLComponentsRFC1808 *)components); 3751 case kCFURLComponentDecompositionRFC2396: 3752 return decomposeToRFC2396(url, (CFURLComponentsRFC2396 *)components); 3753 default: 3754 return false; 3755 } 3756} 3757 3758CF_EXPORT 3759CFURLRef _CFURLCreateFromComponents(CFAllocatorRef alloc, CFURLComponentDecomposition decompositionType, const void *components) { 3760 switch (decompositionType) { 3761 case kCFURLComponentDecompositionNonHierarchical: 3762 return composeFromNonHierarchical(alloc, (const CFURLComponentsNonHierarchical *)components); 3763 case kCFURLComponentDecompositionRFC1808: 3764 return composeFromRFC1808(alloc, (const CFURLComponentsRFC1808 *)components); 3765 case kCFURLComponentDecompositionRFC2396: 3766 return composeFromRFC2396(alloc, (const CFURLComponentsRFC2396 *)components); 3767 default: 3768 return NULL; 3769 } 3770} 3771 3772CF_EXPORT void *__CFURLReservedPtr(CFURLRef url) { 3773 return _getReserved(url); 3774} 3775 3776CF_EXPORT void __CFURLSetReservedPtr(CFURLRef url, void *ptr) { 3777 _setReserved ( (struct __CFURL*) url, ptr ); 3778} 3779 3780CF_EXPORT void *__CFURLResourceInfoPtr(CFURLRef url) { 3781 return _getResourceInfo(url); 3782} 3783 3784CF_EXPORT void __CFURLSetResourceInfoPtr(CFURLRef url, void *ptr) { 3785 _setResourceInfo ( (struct __CFURL*) url, ptr ); 3786} 3787 3788/* File system stuff */ 3789 3790/* HFSPath<->URLPath functions at the bottom of the file */ 3791static CFArrayRef WindowsPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir, Boolean isAbsolute) { 3792 CFArrayRef tmp; 3793 CFMutableArrayRef urlComponents = NULL; 3794 CFIndex i=0; 3795 3796 tmp = CFStringCreateArrayBySeparatingStrings(alloc, path, CFSTR("\\")); 3797 urlComponents = CFArrayCreateMutableCopy(alloc, 0, tmp); 3798 CFRelease(tmp); 3799 3800 CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(urlComponents, 0); 3801 if (isAbsolute && CFStringGetLength(str) == 2 && CFStringGetCharacterAtIndex(str, 1) == ':') { 3802 i = 1; // Skip over the drive letter 3803 } 3804 CFIndex c; 3805 for (c = CFArrayGetCount(urlComponents); i < c; i ++) { 3806 CFStringRef fileComp = (CFStringRef)CFArrayGetValueAtIndex(urlComponents,i); 3807 CFStringRef urlComp = _replacePathIllegalCharacters(fileComp, alloc, false); 3808 if (!urlComp) { 3809 // Couldn't decode fileComp 3810 CFRelease(urlComponents); 3811 return NULL; 3812 } 3813 if (urlComp != fileComp) { 3814 CFArraySetValueAtIndex(urlComponents, i, urlComp); 3815 } 3816 CFRelease(urlComp); 3817 } 3818 3819 if (isDir) { 3820 if (CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(urlComponents, CFArrayGetCount(urlComponents) - 1)) != 0) 3821 CFArrayAppendValue(urlComponents, CFSTR("")); 3822 } 3823 if (isAbsolute) { 3824 if ( AddAuthorityToFileURL() ) { 3825 CFArrayInsertValueAtIndex(urlComponents, 0, CFSTR(FILE_PREFIX_WITH_AUTHORITY)); 3826 } 3827 else { 3828 CFArrayInsertValueAtIndex(urlComponents, 0, CFSTR(FILE_PREFIX)); 3829 } 3830 } 3831 return urlComponents; 3832} 3833 3834static CFStringRef WindowsPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDir, Boolean isAbsolute) { 3835 CFArrayRef urlComponents; 3836 CFStringRef str; 3837 3838 if (CFStringGetLength(path) == 0) return CFStringCreateWithCString(alloc, "", kCFStringEncodingASCII); 3839 urlComponents = WindowsPathToURLComponents(path, alloc, isDir, isAbsolute); 3840 if (!urlComponents) return CFStringCreateWithCString(alloc, "", kCFStringEncodingASCII); 3841 3842 // WindowsPathToURLComponents already added percent escapes for us; no need to add them again here. 3843 str = CFStringCreateByCombiningStrings(alloc, urlComponents, CFSTR("/")); 3844 CFRelease(urlComponents); 3845 return str; 3846} 3847 3848static CFStringRef POSIXPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDirectory, Boolean isAbsolute, Boolean *posixAndUrlPathsMatch) { 3849 Boolean addedPercentEncoding; 3850 CFStringRef pathString = NULL; 3851 STACK_BUFFER_DECL(char, buffer, PATH_MAX); 3852 if ( CFStringGetFileSystemRepresentation(path, buffer, PATH_MAX) ) { 3853 pathString = CreateStringFromFileSystemRepresentationByAddingPercentEscapes(kCFAllocatorDefault, (const UInt8 *)buffer, strlen(buffer), isDirectory, isAbsolute, false /* windowsPath */, &addedPercentEncoding); 3854 } 3855 3856 if ( posixAndUrlPathsMatch ) { 3857 *posixAndUrlPathsMatch = !addedPercentEncoding; 3858 } 3859 return pathString; 3860} 3861 3862static CFStringRef URLPathToPOSIXPath(CFStringRef path, CFAllocatorRef allocator, CFStringEncoding encoding) { 3863 // This is the easiest case; just remove the percent escape codes and we're done 3864 CFStringRef result = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator, path, CFSTR(""), encoding); 3865 if (result) { 3866 CFIndex length = CFStringGetLength(result); 3867 if (length > 1 && CFStringGetCharacterAtIndex(result, length-1) == '/') { 3868 CFStringRef tmp = CFStringCreateWithSubstring(allocator, result, CFRangeMake(0, length-1)); 3869 CFRelease(result); 3870 result = tmp; 3871 } 3872 } 3873 return result; 3874} 3875 3876#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX 3877static Boolean CanonicalFileURLStringToFileSystemRepresentation(CFStringRef str, UInt8 *inBuffer, CFIndex inBufferLen) 3878{ 3879 size_t fileURLPrefixLength; 3880 if ( AddAuthorityToFileURL() ) { 3881 fileURLPrefixLength = sizeof(fileURLPrefixWithAuthority); 3882 } 3883 else { 3884 fileURLPrefixLength = sizeof(fileURLPrefix); 3885 } 3886 Boolean result; 3887 if ( inBuffer && inBufferLen ) { 3888 STACK_BUFFER_DECL(UInt8, stackEscapedBuf, PATH_MAX * 3); // worst case size is every unicode code point could be a 3-byte UTF8 sequence 3889 UInt8 *escapedBuf; 3890 CFIndex strLength = CFStringGetLength(str) - (fileURLPrefixLength - 1); 3891 if ( strLength != 0 ) { 3892 CFIndex maxBufLength = strLength * 3; 3893 CFIndex usedBufLen; 3894 CFIndex charsConverted; 3895 if ( strLength <= PATH_MAX ) { 3896 escapedBuf = &stackEscapedBuf[0]; 3897 } 3898 else { 3899 // worst case size is every unicode code point could be a 3-byte UTF8 sequence 3900 escapedBuf = (UInt8 *)malloc(maxBufLength); 3901 } 3902 if ( escapedBuf != NULL ) { 3903 charsConverted = CFStringGetBytes(str, CFRangeMake(fileURLPrefixLength - 1, strLength), kCFStringEncodingUTF8, 0, false, escapedBuf, maxBufLength, &usedBufLen); 3904 if ( charsConverted ) { 3905 static const UInt8 hexvalues[] = { 3906 /* 00 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3907 /* 08 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3908 /* 10 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3909 /* 18 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3910 /* 20 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3911 /* 28 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3912 /* 30 */ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 3913 /* 38 */ 0x8, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3914 /* 40 */ 0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0, 3915 /* 48 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3916 /* 50 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3917 /* 58 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3918 /* 60 */ 0x0, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0, 3919 /* 68 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3920 /* 70 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3921 /* 78 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3922 3923 /* 80 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3924 /* 88 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3925 /* 90 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3926 /* 98 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3927 /* A0 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3928 /* A8 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3929 /* B0 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3930 /* B8 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3931 /* C0 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3932 /* C8 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3933 /* D0 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3934 /* D8 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3935 /* E0 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3936 /* E8 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3937 /* F0 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3938 /* F8 */ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 3939 }; 3940 UInt8 *bufStartPtr; 3941 UInt8 *bufEndPtr; 3942 UInt8 *bufPtr; 3943 const UInt8 *bytePtr = escapedBuf; 3944 CFIndex idx; 3945 Boolean trailingSlash = false; 3946 3947 bufPtr = bufStartPtr = inBuffer; 3948 bufEndPtr = inBuffer + inBufferLen; 3949 result = TRUE; 3950 3951 for ( idx = 0; (idx < usedBufLen) && result; ++idx ) { 3952 if ( bufPtr == bufEndPtr ) { 3953 // ooops, ran out of inBuffer 3954 *bufStartPtr = '\0'; 3955 result = FALSE; 3956 } 3957 else { 3958 switch ( *bytePtr ) { 3959 case '%': 3960 idx += 2; 3961 if ( idx < usedBufLen ) { 3962 // skip over % 3963 bytePtr++; 3964 // convert hex digits 3965 *bufPtr = hexvalues[*bytePtr++] << 4; 3966 *bufPtr += hexvalues[*bytePtr++]; 3967 trailingSlash = (*bufPtr == '/'); 3968 } 3969 break; 3970 default: 3971 // copy everything else 3972 *bufPtr = *bytePtr++; 3973 trailingSlash = (*bufPtr == '/'); 3974 break; 3975 } 3976 ++bufPtr; 3977 } 3978 } 3979 if ( result ) { 3980 // remove trailing slash (if any) 3981 if ( (bufPtr > (bufStartPtr + 1)) && trailingSlash ) { 3982 --bufPtr; 3983 } 3984 if ( bufPtr < bufEndPtr ) { 3985 *bufPtr = '\0'; 3986 } 3987 } 3988 } 3989 else { 3990 // CFStringGetBytes failed 3991 result = FALSE; 3992 } 3993 3994 // free the buffer if we malloc'd it 3995 if ( escapedBuf != &stackEscapedBuf[0] ) { 3996 free(escapedBuf); 3997 } 3998 } 3999 else { 4000 // could not allocate escapedBuf 4001 result = FALSE; 4002 } 4003 } 4004 else { 4005 // str was zero characters 4006 *inBuffer = '\0'; 4007 result = TRUE; 4008 } 4009 } 4010 else { 4011 // no inBuffer or inBufferLen is zero 4012 result = FALSE; 4013 } 4014 4015 return ( result ); 4016} 4017#endif 4018 4019#if DEPLOYMENT_TARGET_WINDOWS 4020// From CFPlatform.c 4021extern CFStringRef CFCreateWindowsDrivePathFromVolumeName(CFStringRef volNameStr); 4022#endif 4023 4024static CFStringRef URLPathToWindowsPath(CFStringRef path, CFAllocatorRef allocator, CFStringEncoding encoding) { 4025 // Check for a drive letter, then flip all the slashes 4026 CFStringRef result; 4027 CFArrayRef tmp = CFStringCreateArrayBySeparatingStrings(allocator, path, CFSTR("/")); 4028 SInt32 count = CFArrayGetCount(tmp); 4029 CFMutableArrayRef components = CFArrayCreateMutableCopy(allocator, count, tmp); 4030 CFStringRef newPath; 4031 4032 4033 4034 CFRelease(tmp); 4035 if (CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(components,count-1)) == 0) { 4036 CFArrayRemoveValueAtIndex(components, count-1); 4037 count --; 4038 } 4039 4040 if (count > 1 && CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(components, 0)) == 0) { 4041 // Absolute path; we need to check for a drive letter in the second component, and if so, remove the first component 4042 CFStringRef firstComponent = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator, (CFStringRef)CFArrayGetValueAtIndex(components, 1), CFSTR(""), encoding); 4043 UniChar ch; 4044 4045 { 4046 if (firstComponent) { 4047 if (CFStringGetLength(firstComponent) == 2 && ((ch = CFStringGetCharacterAtIndex(firstComponent, 1)) == '|' || ch == ':')) { 4048 // Drive letter 4049 CFArrayRemoveValueAtIndex(components, 0); 4050 if (ch == '|') { 4051 CFStringRef driveStr = CFStringCreateWithFormat(allocator, NULL, CFSTR("%c:"), CFStringGetCharacterAtIndex(firstComponent, 0)); 4052 CFArraySetValueAtIndex(components, 0, driveStr); 4053 CFRelease(driveStr); 4054 } 4055 } 4056#if DEPLOYMENT_TARGET_WINDOWS 4057 else { 4058 // From <rdar://problem/5623405> [DEFECT] CFURL returns a Windows path that contains volume name instead of a drive letter 4059 // we need to replace the volume name (it is not valid on Windows) with the drive mounting point path 4060 // remove the first component and set the component with the drive letter to be the first component 4061 CFStringRef driveRootPath = CFCreateWindowsDrivePathFromVolumeName(firstComponent); 4062 4063 if (driveRootPath) { 4064 // remove trailing slash 4065 if (CFStringHasSuffix(driveRootPath, CFSTR("\\"))) { 4066 CFStringRef newDriveRootPath = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, driveRootPath, CFRangeMake(0, CFStringGetLength(driveRootPath) - 1)); 4067 CFRelease(driveRootPath); 4068 driveRootPath = newDriveRootPath; 4069 } 4070 4071 // replace the first component of the path with the drive path 4072 CFArrayRemoveValueAtIndex(components, 0); 4073 CFArraySetValueAtIndex(components, 0, driveRootPath); 4074 4075 CFRelease(driveRootPath); 4076 } 4077 } 4078#endif 4079 } 4080 } 4081 if ( firstComponent ) { 4082 CFRelease(firstComponent); 4083 } 4084 } 4085 newPath = CFStringCreateByCombiningStrings(allocator, components, CFSTR("\\")); 4086 CFRelease(components); 4087 result = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator, newPath, CFSTR(""), encoding); 4088 CFRelease(newPath); 4089 return result; 4090} 4091 4092 4093 4094// Caller must release the returned string 4095static CFStringRef _resolveFileSystemPaths(CFStringRef relativePath, CFStringRef basePath, Boolean baseIsDir, CFURLPathStyle fsType, CFAllocatorRef alloc) { 4096 CFIndex baseLen = CFStringGetLength(basePath); 4097 CFIndex relLen = CFStringGetLength(relativePath); 4098 UniChar pathDelimiter = '/'; 4099 UniChar *buf = (UniChar *)CFAllocatorAllocate(alloc, sizeof(UniChar)*(relLen + baseLen + 2), 0); 4100 CFStringGetCharacters(basePath, CFRangeMake(0, baseLen), buf); 4101 if (baseIsDir) { 4102 if (buf[baseLen-1] != pathDelimiter) { 4103 buf[baseLen] = pathDelimiter; 4104 baseLen ++; 4105 } 4106 } else { 4107 UniChar *ptr = buf + baseLen - 1; 4108 while (ptr > buf && *ptr != pathDelimiter) { 4109 ptr --; 4110 } 4111 baseLen = ptr - buf + 1; 4112 } 4113#pragma GCC diagnostic push 4114#pragma GCC diagnostic ignored "-Wdeprecated" 4115 if (fsType == kCFURLHFSPathStyle) { 4116#pragma GCC diagnostic pop 4117 // HFS relative paths will begin with a colon, so we must remove the trailing colon from the base path first. 4118 baseLen --; 4119 } 4120 CFStringGetCharacters(relativePath, CFRangeMake(0, relLen), buf + baseLen); 4121 *(buf + baseLen + relLen) = '\0'; 4122 return _resolvedPath(buf, buf + baseLen + relLen, pathDelimiter, false, true, alloc); 4123} 4124 4125CFURLRef _CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator) { 4126 CFURLRef url = NULL; 4127 uint8_t buf[CFMaxPathSize + 1]; 4128 if (_CFGetCurrentDirectory((char *)buf, CFMaxPathLength)) { 4129 url = CFURLCreateFromFileSystemRepresentation(allocator, buf, strlen((char *)buf), true); 4130 } 4131 return url; 4132} 4133 4134CFURLRef CFURLCreateWithFileSystemPath(CFAllocatorRef allocator, CFStringRef filePath, CFURLPathStyle fsType, Boolean isDirectory) { 4135 CFURLRef result; 4136 4137 result = _CFURLAlloc(allocator); 4138 if ( result ) { 4139 if ( !_CFURLInitWithFileSystemPath(result, filePath, fsType, isDirectory, NULL) ) { 4140 CFRelease(result); 4141 result = NULL; 4142 } 4143 } 4144 4145 return ( result ); 4146} 4147 4148CF_EXPORT CFURLRef CFURLCreateWithFileSystemPathRelativeToBase(CFAllocatorRef allocator, CFStringRef filePath, CFURLPathStyle fsType, Boolean isDirectory, CFURLRef baseURL) { 4149 CFURLRef result; 4150 4151 result = _CFURLAlloc(allocator); 4152 if ( result ) { 4153 if ( !_CFURLInitWithFileSystemPath(result, filePath, fsType, isDirectory, baseURL) ) { 4154 CFRelease(result); 4155 result = NULL; 4156 } 4157 } 4158 4159 return ( result ); 4160} 4161 4162static Boolean _pathHasFileIDPrefix( CFStringRef path ) 4163{ 4164 // path is not NULL, path has prefix "/.file/" and has at least one character following the prefix. 4165#ifdef __CONSTANT_STRINGS__ 4166 static const 4167#endif 4168 CFStringRef fileIDPrefix = CFSTR( "/" FILE_ID_PREFIX "/" ); 4169 return path && CFStringHasPrefix( path, fileIDPrefix ) && CFStringGetLength( path ) > CFStringGetLength( fileIDPrefix ); 4170} 4171 4172#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 4173static Boolean _pathHasFileIDOnly( CFStringRef path ) 4174{ 4175 // Is file ID rooted and contains no additonal path segments 4176 CFRange slashRange; 4177 return _pathHasFileIDPrefix( path ) && ( !CFStringFindWithOptions( path, CFSTR("/"), CFRangeMake( sizeof(FILE_ID_PREFIX) + 1, CFStringGetLength( path ) - sizeof(FILE_ID_PREFIX) - 1), 0, &slashRange ) || slashRange.location == CFStringGetLength( path ) - 1 ); 4178} 4179#endif 4180 4181CF_EXPORT CFStringRef CFURLCopyFileSystemPath(CFURLRef anURL, CFURLPathStyle pathStyle) { 4182#pragma GCC diagnostic push 4183#pragma GCC diagnostic ignored "-Wdeprecated" 4184 CFAssert2(pathStyle == kCFURLPOSIXPathStyle || pathStyle == kCFURLHFSPathStyle || pathStyle == kCFURLWindowsPathStyle, __kCFLogAssertion, "%s(): Encountered unknown path style %d", __PRETTY_FUNCTION__, pathStyle); 4185#pragma GCC diagnostic pop 4186 4187 CFStringRef result = NULL; 4188 CFAllocatorRef alloc = CFGetAllocator(anURL); 4189#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX 4190 Boolean isCanonicalFileURL = false; 4191 4192 if ( (pathStyle == kCFURLPOSIXPathStyle) && (CFURLGetBaseURL(anURL) == NULL) ) { 4193 if ( !CF_IS_OBJC(__kCFURLTypeID, anURL) ) { 4194 // We can grope the ivars 4195 isCanonicalFileURL = ((anURL->_flags & IS_CANONICAL_FILE_URL) != 0); 4196 if ( isCanonicalFileURL ) { 4197 STACK_BUFFER_DECL(UInt8, buffer, PATH_MAX + 1); 4198 if ( CanonicalFileURLStringToFileSystemRepresentation(anURL->_string, buffer, PATH_MAX + 1) ) { 4199 result = CFStringCreateWithBytes(alloc, buffer, strlen((char *)buffer), kCFStringEncodingUTF8, false); 4200 } 4201 } 4202 } 4203 } 4204 if ( ! result ) { 4205 // fall back to slower way. 4206 result = CFURLCreateStringWithFileSystemPath(alloc, anURL, pathStyle, false); 4207 } 4208#else // !DEPLOYMENT_TARGET_MACOSX 4209 result = CFURLCreateStringWithFileSystemPath(alloc, anURL, pathStyle, false); 4210#endif // !DEPLOYMENT_TARGET_MACOSX 4211 4212 return ( result ); 4213} 4214 4215 4216// There is no matching ObjC method for this functionality; because this function sits on top of the CFURL primitives, it's o.k. not to check for the need to dispatch an ObjC method instead, but this means care must be taken that this function never call anything that will result in dereferencing anURL without first checking for an ObjC dispatch. -- REW, 10/29/98 4217CFStringRef CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator, CFURLRef anURL, CFURLPathStyle fsType, Boolean resolveAgainstBase) { 4218 CFURLRef base = resolveAgainstBase ? CFURLGetBaseURL(anURL) : NULL; 4219 CFStringRef basePath = base ? CFURLCreateStringWithFileSystemPath(allocator, base, fsType, false) : NULL; 4220 CFStringRef relPath = NULL; 4221 4222 if (!CF_IS_OBJC(__kCFURLTypeID, anURL)) { 4223 // We can grope the ivars 4224 if (fsType == kCFURLPOSIXPathStyle) { 4225 if (anURL->_flags & POSIX_AND_URL_PATHS_MATCH) { 4226 relPath = _retainedComponentString(anURL, HAS_PATH, true, true); 4227 } 4228 } 4229 } 4230 4231 if (relPath == NULL) { 4232 CFStringRef urlPath = CFURLCopyPath(anURL); 4233 CFStringEncoding enc = anURL->_encoding; 4234 if (urlPath) { 4235 switch (fsType) { 4236 case kCFURLPOSIXPathStyle: 4237 relPath = URLPathToPOSIXPath(urlPath, allocator, enc); 4238 break; 4239#pragma GCC diagnostic push 4240#pragma GCC diagnostic ignored "-Wdeprecated" 4241 case kCFURLHFSPathStyle: 4242#pragma GCC diagnostic pop 4243 relPath = NULL; 4244 break; 4245 case kCFURLWindowsPathStyle: 4246 relPath = URLPathToWindowsPath(urlPath, allocator, enc); 4247 break; 4248 default: 4249 CFAssert2(true, __kCFLogAssertion, "%s(): Received unknown path type %d", __PRETTY_FUNCTION__, fsType); 4250 } 4251 CFRelease(urlPath); 4252 } 4253 } 4254 4255 // For Tiger, leave this behavior in for all path types. For Leopard, it would be nice to remove this entirely 4256 // and do a linked-on-or-later check so we don't break third parties. 4257 // See <rdar://problem/4003028> Converting volume name from POSIX to HFS form fails and 4258 // <rdar://problem/4018895> CF needs to back out 4003028 for icky details. 4259 if ( relPath && CFURLHasDirectoryPath(anURL) && CFStringGetLength(relPath) > 1 && CFStringGetCharacterAtIndex(relPath, CFStringGetLength(relPath)-1) == '/') { 4260 CFStringRef tmp = CFStringCreateWithSubstring(allocator, relPath, CFRangeMake(0, CFStringGetLength(relPath)-1)); 4261 CFRelease(relPath); 4262 relPath = tmp; 4263 } 4264 4265 if ( relPath ) { 4266 if ( basePath && (CFStringGetCharacterAtIndex(relPath, 0) != '/') ) { 4267 // we have both basePath and relPath, and relPath is not absolute -- resolve them 4268 CFStringRef result = _resolveFileSystemPaths(relPath, basePath, CFURLHasDirectoryPath(base), fsType, allocator); 4269 CFRelease(basePath); 4270 CFRelease(relPath); 4271 return result; 4272 } 4273 else { 4274 // we only have the relPath or relpath is absolute -- return it 4275 if ( basePath ) { 4276 CFRelease(basePath); 4277 } 4278 return relPath; 4279 } 4280 } 4281 else if ( basePath ) { 4282 // we only have the basePath --- return it 4283 return basePath; 4284 } 4285 else { 4286 // we have nothing to return 4287 return NULL; 4288 } 4289} 4290 4291Boolean CFURLGetFileSystemRepresentation(CFURLRef url, Boolean resolveAgainstBase, uint8_t *buffer, CFIndex bufLen) { 4292#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_WINDOWS 4293 CFAllocatorRef alloc = CFGetAllocator(url); 4294 CFStringRef path; 4295 4296 if (!url) return false; 4297#endif 4298#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX 4299 if ( !resolveAgainstBase || (CFURLGetBaseURL(url) == NULL) ) { 4300 if (!CF_IS_OBJC(__kCFURLTypeID, url)) { 4301 // We can grope the ivars 4302 if ( url->_flags & IS_CANONICAL_FILE_URL ) { 4303 return CanonicalFileURLStringToFileSystemRepresentation(url->_string, buffer, bufLen); 4304 } 4305 } 4306 } 4307 // else fall back to slower way. 4308 path = CFURLCreateStringWithFileSystemPath(alloc, url, kCFURLPOSIXPathStyle, resolveAgainstBase); 4309 if (path) { 4310 Boolean convResult = _CFStringGetFileSystemRepresentation(path, buffer, bufLen); 4311 CFRelease(path); 4312 return convResult; 4313 } 4314#elif DEPLOYMENT_TARGET_WINDOWS 4315 path = CFURLCreateStringWithFileSystemPath(alloc, url, kCFURLWindowsPathStyle, resolveAgainstBase); 4316 if (path) { 4317 CFIndex usedLen; 4318 CFIndex pathLen = CFStringGetLength(path); 4319 CFIndex numConverted = CFStringGetBytes(path, CFRangeMake(0, pathLen), CFStringFileSystemEncoding(), 0, true, buffer, bufLen-1, &usedLen); // -1 because we need one byte to zero-terminate. 4320 CFRelease(path); 4321 if (numConverted == pathLen) { 4322 buffer[usedLen] = '\0'; 4323 return true; 4324 } 4325 } 4326#endif 4327 return false; 4328} 4329 4330#if DEPLOYMENT_TARGET_WINDOWS 4331CF_EXPORT Boolean _CFURLGetWideFileSystemRepresentation(CFURLRef url, Boolean resolveAgainstBase, wchar_t *buffer, CFIndex bufferLength) { 4332 CFStringRef path = CFURLCreateStringWithFileSystemPath(CFGetAllocator(url), url, kCFURLWindowsPathStyle, resolveAgainstBase); 4333 CFIndex pathLength, charsConverted, usedBufLen; 4334 if (!path) return false; 4335 pathLength = CFStringGetLength(path); 4336 if (pathLength+1 > bufferLength) { 4337 CFRelease(path); 4338 return false; 4339 } 4340 charsConverted = CFStringGetBytes(path, CFRangeMake(0, pathLength), kCFStringEncodingUTF16, 0, false, (UInt8 *)buffer, bufferLength*sizeof(wchar_t), &usedBufLen); 4341// CFStringGetCharacters(path, CFRangeMake(0, pathLength), (UniChar *)buffer); 4342 CFRelease(path); 4343 if (charsConverted != pathLength || usedBufLen%sizeof(wchar_t) != 0) { 4344 return false; 4345 } else { 4346 buffer[usedBufLen/sizeof(wchar_t)] = 0; 4347// buffer[pathLength] = 0; 4348 return true; 4349 } 4350} 4351#endif 4352 4353CFURLRef CFURLCreateFromFileSystemRepresentation(CFAllocatorRef allocator, const uint8_t *buffer, CFIndex bufLen, Boolean isDirectory) { 4354 CFURLRef result; 4355 4356 result = _CFURLAlloc(allocator); 4357 if ( result ) { 4358 if ( !_CFURLInitWithFileSystemRepresentation(result, buffer, bufLen, isDirectory, NULL) ) { 4359 CFRelease(result); 4360 result = NULL; 4361 } 4362 } 4363 4364 return ( result ); 4365} 4366 4367CF_EXPORT CFURLRef CFURLCreateFromFileSystemRepresentationRelativeToBase(CFAllocatorRef allocator, const uint8_t *buffer, CFIndex bufLen, Boolean isDirectory, CFURLRef baseURL) { 4368 CFURLRef result; 4369 4370 result = _CFURLAlloc(allocator); 4371 if ( result ) { 4372 if ( !_CFURLInitWithFileSystemRepresentation(result, buffer, bufLen, isDirectory, baseURL) ) { 4373 CFRelease(result); 4374 result = NULL; 4375 } 4376 } 4377 4378 return ( result ); 4379} 4380 4381 4382/******************************/ 4383/* Support for path utilities */ 4384/******************************/ 4385 4386// Assumes url is a CFURL (not an Obj-C NSURL) 4387static CFRange _rangeOfLastPathComponent(CFURLRef url) { 4388 CFRange pathRg, componentRg; 4389 4390 pathRg = _rangeForComponent(url->_flags, url->_ranges, HAS_PATH); 4391 4392 if (pathRg.location == kCFNotFound || pathRg.length == 0) { 4393 // No path 4394 return pathRg; 4395 } 4396 if (CFStringGetCharacterAtIndex(url->_string, pathRg.location + pathRg.length - 1) == '/') { 4397 pathRg.length --; 4398 if (pathRg.length == 0) { 4399 pathRg.length ++; 4400 return pathRg; 4401 } 4402 } 4403 if (CFStringFindWithOptions(url->_string, CFSTR("/"), pathRg, kCFCompareBackwards, &componentRg)) { 4404 componentRg.location ++; 4405 componentRg.length = pathRg.location + pathRg.length - componentRg.location; 4406 } else { 4407 componentRg = pathRg; 4408 } 4409 return componentRg; 4410} 4411 4412CFStringRef CFURLCopyLastPathComponent(CFURLRef url) { 4413 CFStringRef result; 4414 4415 if (CF_IS_OBJC(__kCFURLTypeID, url)) { 4416 CFStringRef path = CFURLCreateStringWithFileSystemPath(CFGetAllocator(url), url, kCFURLPOSIXPathStyle, false); 4417 CFIndex length; 4418 CFRange rg, compRg; 4419 if (!path) return NULL; 4420 rg = CFRangeMake(0, CFStringGetLength(path)); 4421 if ( rg.length == 0 ) return path; 4422 length = rg.length; // Remember this for comparison later 4423 if (CFStringGetCharacterAtIndex(path, rg.length - 1) == '/' ) { 4424 rg.length --; 4425 } 4426 if ( rg.length == 0 ) 4427 { 4428 // If we have reduced the string to empty, then it's "/", and that's what we return as 4429 // the last path component. 4430 return path; 4431 } 4432 else if (CFStringFindWithOptions(path, CFSTR("/"), rg, kCFCompareBackwards, &compRg)) { 4433 rg.length = rg.location + rg.length - (compRg.location+1); 4434 rg.location = compRg.location + 1; 4435 } 4436 if (rg.location == 0 && rg.length == length) { 4437 result = path; 4438 } else { 4439 result = CFStringCreateWithSubstring(CFGetAllocator(url), path, rg); 4440 CFRelease(path); 4441 } 4442 } else { 4443 Boolean filePathURLCreated = false; 4444#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 4445 if ( CFURLIsFileReferenceURL(url) ) { 4446 // use a file path URL or fail 4447 CFURLRef filePathURL = CFURLCreateFilePathURL(CFGetAllocator(url), url, NULL); 4448 if ( filePathURL ) { 4449 filePathURLCreated = TRUE; 4450 url = filePathURL; 4451 } 4452 else { 4453 return NULL; 4454 } 4455 } 4456#endif 4457 4458 CFRange rg = _rangeOfLastPathComponent(url); 4459 if (rg.location == kCFNotFound || rg.length == 0) { 4460 // No path 4461 if ( filePathURLCreated ) { 4462 CFRelease(url); 4463 } 4464 return (CFStringRef)CFRetain(CFSTR("")); 4465 } 4466 if (rg.length == 1 && CFStringGetCharacterAtIndex(url->_string, rg.location) == '/') { 4467 if ( filePathURLCreated ) { 4468 CFRelease(url); 4469 } 4470 return (CFStringRef)CFRetain(CFSTR("/")); 4471 } 4472 result = CFStringCreateWithSubstring(CFGetAllocator(url), url->_string, rg); 4473 if (!(url->_flags & POSIX_AND_URL_PATHS_MATCH)) { 4474 CFStringRef tmp; 4475 if (url->_encoding == kCFStringEncodingUTF8) { 4476 tmp = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(url), result, CFSTR("")); 4477 } else { 4478 tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(url), result, CFSTR(""), url->_encoding); 4479 } 4480 CFRelease(result); 4481 result = tmp; 4482 } 4483 if ( filePathURLCreated ) { 4484 CFRelease(url); 4485 } 4486 } 4487 return result; 4488} 4489 4490CFStringRef CFURLCopyPathExtension(CFURLRef url) { 4491 CFStringRef lastPathComp = CFURLCopyLastPathComponent(url); 4492 CFStringRef ext = NULL; 4493 4494 if (lastPathComp) { 4495 CFRange rg = CFStringFind(lastPathComp, CFSTR("."), kCFCompareBackwards); 4496 if (rg.location != kCFNotFound) { 4497 rg.location ++; 4498 rg.length = CFStringGetLength(lastPathComp) - rg.location; 4499 if (rg.length > 0) { 4500 ext = CFStringCreateWithSubstring(CFGetAllocator(url), lastPathComp, rg); 4501 } else { 4502 ext = (CFStringRef)CFRetain(CFSTR("")); 4503 } 4504 } 4505 CFRelease(lastPathComp); 4506 } 4507 return ext; 4508} 4509 4510CFURLRef CFURLCreateCopyAppendingPathComponent(CFAllocatorRef allocator, CFURLRef url, CFStringRef pathComponent, Boolean isDirectory) { 4511 CFURLRef result; 4512 url = _CFURLFromNSURL(url); 4513 __CFGenericValidateType(url, __kCFURLTypeID); 4514 CFAssert1(pathComponent != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL component to append", __PRETTY_FUNCTION__); 4515 4516 Boolean filePathURLCreated = false; 4517#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 4518 if ( CFURLIsFileReferenceURL(url) ) { 4519 // use a file path URL if possible (only because this is appending a path component) 4520 CFURLRef filePathURL = CFURLCreateFilePathURL(allocator, url, NULL); 4521 if ( filePathURL ) { 4522 filePathURLCreated = TRUE; 4523 url = filePathURL; 4524 } 4525 } 4526#endif 4527 4528 CFMutableStringRef newString; 4529 CFStringRef newComp; 4530 CFRange pathRg; 4531 if (!(url->_flags & HAS_PATH)) { 4532 result = NULL; 4533 } 4534 else { 4535 newString = CFStringCreateMutableCopy(allocator, 0, url->_string); 4536 newComp = CFURLCreateStringByAddingPercentEscapes(allocator, pathComponent, NULL, CFSTR(";?"), url->_encoding); 4537 pathRg = _rangeForComponent(url->_flags, url->_ranges, HAS_PATH); 4538 if ( (!pathRg.length || CFStringGetCharacterAtIndex(url->_string, pathRg.location + pathRg.length - 1) != '/') && (CFStringGetCharacterAtIndex(newComp, 0) != '/') ) { 4539 CFStringInsert(newString, pathRg.location + pathRg.length, CFSTR("/")); 4540 pathRg.length ++; 4541 } 4542 CFStringInsert(newString, pathRg.location + pathRg.length, newComp); 4543 if (isDirectory) { 4544 CFStringInsert(newString, pathRg.location + pathRg.length + CFStringGetLength(newComp), CFSTR("/")); 4545 } 4546 CFRelease(newComp); 4547 result = _CFURLCreateWithArbitraryString(allocator, newString, url->_base); 4548 CFRelease(newString); 4549 } 4550 if ( filePathURLCreated ) { 4551 CFRelease(url); 4552 } 4553 return result; 4554} 4555 4556CFURLRef CFURLCreateCopyDeletingLastPathComponent(CFAllocatorRef allocator, CFURLRef url) { 4557 CFURLRef result; 4558 CFMutableStringRef newString; 4559 CFRange lastCompRg, pathRg; 4560 Boolean appendDotDot = false; 4561 4562 url = _CFURLFromNSURL(url); 4563 CFAssert1(url != NULL, __kCFLogAssertion, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__); 4564 __CFGenericValidateType(url, __kCFURLTypeID); 4565 4566 Boolean filePathURLCreated = false; 4567#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 4568 if ( CFURLIsFileReferenceURL(url) ) { 4569 // use a file path URL or fail 4570 CFURLRef filePathURL = CFURLCreateFilePathURL(allocator, url, NULL); 4571 if ( filePathURL ) { 4572 filePathURLCreated = TRUE; 4573 url = filePathURL; 4574 } 4575 else { 4576 return NULL; 4577 } 4578 } 4579#endif 4580 4581 if (!(url->_flags & HAS_PATH)) { 4582 if ( filePathURLCreated ) { 4583 CFRelease(url); 4584 } 4585 return NULL; 4586 } 4587 pathRg = _rangeForComponent(url->_flags, url->_ranges, HAS_PATH); 4588 lastCompRg = _rangeOfLastPathComponent(url); 4589 if (lastCompRg.length == 0) { 4590 appendDotDot = true; 4591 } else if (lastCompRg.length == 1) { 4592 UniChar ch = CFStringGetCharacterAtIndex(url->_string, lastCompRg.location); 4593 if (ch == '.' || ch == '/') { 4594 appendDotDot = true; 4595 } 4596 } else if (lastCompRg.length == 2 && CFStringGetCharacterAtIndex(url->_string, lastCompRg.location) == '.' && CFStringGetCharacterAtIndex(url->_string, lastCompRg.location+1) == '.') { 4597 appendDotDot = true; 4598 } 4599 4600 newString = CFStringCreateMutableCopy(allocator, 0, url->_string); 4601 if (appendDotDot) { 4602 CFIndex delta = 0; 4603 if (pathRg.length > 0 && CFStringGetCharacterAtIndex(url->_string, pathRg.location + pathRg.length - 1) != '/') { 4604 CFStringInsert(newString, pathRg.location + pathRg.length, CFSTR("/")); 4605 delta ++; 4606 } 4607 CFStringInsert(newString, pathRg.location + pathRg.length + delta, CFSTR("..")); 4608 delta += 2; 4609 CFStringInsert(newString, pathRg.location + pathRg.length + delta, CFSTR("/")); 4610 delta ++; 4611 // We know we have "/../" at the end of the path; we wish to know if that's immediately preceded by "/." (but that "/." doesn't start the string), in which case we want to delete the "/.". 4612 if (pathRg.length + delta > 4 && CFStringGetCharacterAtIndex(newString, pathRg.location + pathRg.length + delta - 5) == '.') { 4613 if (pathRg.length+delta > 7 && CFStringGetCharacterAtIndex(newString, pathRg.location + pathRg.length + delta - 6) == '/') { 4614 CFStringDelete(newString, CFRangeMake(pathRg.location + pathRg.length + delta - 6, 2)); 4615 } else if (pathRg.length+delta == 5) { 4616 CFStringDelete(newString, CFRangeMake(pathRg.location + pathRg.length + delta - 5, 2)); 4617 } 4618 } 4619 } else if (lastCompRg.location == pathRg.location) { 4620 CFStringReplace(newString, pathRg, CFSTR(".")); 4621 CFStringInsert(newString, 1, CFSTR("/")); 4622 } else { 4623 CFStringDelete(newString, CFRangeMake(lastCompRg.location, pathRg.location + pathRg.length - lastCompRg.location)); 4624 } 4625 result = _CFURLCreateWithArbitraryString(allocator, newString, url->_base); 4626 CFRelease(newString); 4627 if ( filePathURLCreated ) { 4628 CFRelease(url); 4629 } 4630 return result; 4631} 4632 4633CFURLRef CFURLCreateCopyAppendingPathExtension(CFAllocatorRef allocator, CFURLRef url, CFStringRef extension) { 4634 CFMutableStringRef newString; 4635 CFURLRef result; 4636 CFRange rg; 4637 4638 CFAssert1(url != NULL && extension != NULL, __kCFLogAssertion, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__); 4639 url = _CFURLFromNSURL(url); 4640 __CFGenericValidateType(url, __kCFURLTypeID); 4641 __CFGenericValidateType(extension, CFStringGetTypeID()); 4642 4643 Boolean filePathURLCreated = false; 4644#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 4645 if ( CFURLIsFileReferenceURL(url) ) { 4646 // use a file path URL or fail 4647 CFURLRef filePathURL = CFURLCreateFilePathURL(allocator, url, NULL); 4648 if ( filePathURL ) { 4649 filePathURLCreated = TRUE; 4650 url = filePathURL; 4651 } 4652 else { 4653 return NULL; 4654 } 4655 } 4656#endif 4657 4658 rg = _rangeOfLastPathComponent(url); 4659 if (rg.location < 0) { 4660 if ( filePathURLCreated ) { 4661 CFRelease(url); 4662 } 4663 return NULL; // No path 4664 } 4665 4666 newString = CFStringCreateMutableCopy(allocator, 0, url->_string); 4667 CFStringInsert(newString, rg.location + rg.length, CFSTR(".")); 4668 CFStringRef newExt = CFURLCreateStringByAddingPercentEscapes(allocator, extension, NULL, CFSTR(";?/"), url->_encoding); 4669 CFStringInsert(newString, rg.location + rg.length + 1, newExt); 4670 CFRelease(newExt); 4671 result = _CFURLCreateWithArbitraryString(allocator, newString, url->_base); 4672 CFRelease(newString); 4673 if ( filePathURLCreated ) { 4674 CFRelease(url); 4675 } 4676 return result; 4677} 4678 4679CFURLRef CFURLCreateCopyDeletingPathExtension(CFAllocatorRef allocator, CFURLRef url) { 4680 CFRange rg, dotRg; 4681 CFURLRef result; 4682 4683 CFAssert1(url != NULL, __kCFLogAssertion, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__); 4684 url = _CFURLFromNSURL(url); 4685 __CFGenericValidateType(url, __kCFURLTypeID); 4686 4687 Boolean filePathURLCreated = false; 4688#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 4689 if ( CFURLIsFileReferenceURL(url) ) { 4690 // use a file path URL or fail 4691 CFURLRef filePathURL = CFURLCreateFilePathURL(allocator, url, NULL); 4692 if ( filePathURL ) { 4693 filePathURLCreated = TRUE; 4694 url = filePathURL; 4695 } 4696 else { 4697 return NULL; 4698 } 4699 } 4700#endif 4701 4702 rg = _rangeOfLastPathComponent(url); 4703 if (rg.location < 0) { 4704 result = NULL; 4705 } else if (rg.length && CFStringFindWithOptions(url->_string, CFSTR("."), rg, kCFCompareBackwards, &dotRg)) { 4706 CFMutableStringRef newString = CFStringCreateMutableCopy(allocator, 0, url->_string); 4707 dotRg.length = rg.location + rg.length - dotRg.location; 4708 CFStringDelete(newString, dotRg); 4709 result = _CFURLCreateWithArbitraryString(allocator, newString, url->_base); 4710 CFRelease(newString); 4711 } else { 4712 result = (CFURLRef)CFRetain(url); 4713 } 4714 if ( filePathURLCreated ) { 4715 CFRelease(url); 4716 } 4717 return result; 4718} 4719 4720 4721#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 4722static CFArrayRef HFSPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir) { 4723 CFArrayRef components = CFStringCreateArrayBySeparatingStrings(alloc, path, CFSTR(":")); 4724 CFMutableArrayRef newComponents = CFArrayCreateMutableCopy(alloc, 0, components); 4725 Boolean doSpecialLeadingColon = false; 4726 UniChar firstChar = CFStringGetCharacterAtIndex(path, 0); 4727 UInt32 i, cnt; 4728 CFRelease(components); 4729 4730 4731 if (!doSpecialLeadingColon && firstChar != ':') { 4732 CFArrayInsertValueAtIndex(newComponents, 0, CFSTR("")); 4733 } else if (firstChar != ':') { 4734 // see what we need to add at the beginning. Under MacOS, if the 4735 // first character isn't a ':', then the first component is the 4736 // volume name, and we need to find the mount point. Bleah. If we 4737 // don't find a mount point, we're going to have to lie, and make something up. 4738 CFStringRef firstComp = (CFStringRef)CFArrayGetValueAtIndex(newComponents, 0); 4739 if (CFStringGetLength(firstComp) == 1 && CFStringGetCharacterAtIndex(firstComp, 0) == '/') { 4740 // "/" is the "magic" path for a UFS root directory 4741 CFArrayRemoveValueAtIndex(newComponents, 0); 4742 CFArrayInsertValueAtIndex(newComponents, 0, CFSTR("")); 4743 } else { 4744 // See if we can get a mount point. 4745 Boolean foundMountPoint = false; 4746 if (!foundMountPoint) { 4747 // Fall back to treating the volume name as the top level directory 4748 CFArrayInsertValueAtIndex(newComponents, 0, CFSTR("")); 4749 } 4750 } 4751 } else { 4752 CFArrayRemoveValueAtIndex(newComponents, 0); 4753 } 4754 4755 cnt = CFArrayGetCount(newComponents); 4756 for (i = 0; i < cnt; i ++) { 4757 CFStringRef comp = (CFStringRef)CFArrayGetValueAtIndex(newComponents, i); 4758 CFStringRef newComp = NULL; 4759 CFRange searchRg, slashRg; 4760 searchRg.location = 0; 4761 searchRg.length = CFStringGetLength(comp); 4762 while (CFStringFindWithOptions(comp, CFSTR("/"), searchRg, 0, &slashRg)) { 4763 if (!newComp) { 4764 newComp = CFStringCreateMutableCopy(alloc, searchRg.location + searchRg.length, comp); 4765 } 4766 CFStringReplace((CFMutableStringRef)newComp, slashRg, CFSTR(":")); 4767 searchRg.length = searchRg.location + searchRg.length - slashRg.location - 1; 4768 searchRg.location = slashRg.location + 1; 4769 } 4770 if (newComp) { 4771 CFArraySetValueAtIndex(newComponents, i, newComp); 4772 CFRelease(newComp); 4773 } 4774 } 4775 if (isDir && CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(newComponents, cnt-1)) != 0) { 4776 CFArrayAppendValue(newComponents, CFSTR("")); 4777 } 4778 return newComponents; 4779} 4780 4781typedef CFStringRef (*StringTransformation)(CFAllocatorRef, CFStringRef, CFIndex); 4782static CFArrayRef copyStringArrayWithTransformation(CFArrayRef array, StringTransformation transformation) { 4783 CFAllocatorRef alloc = CFGetAllocator(array); 4784 CFMutableArrayRef mArray = NULL; 4785 CFIndex i, c = CFArrayGetCount(array); 4786 for (i = 0; i < c; i ++) { 4787 CFStringRef origComp = (CFStringRef)CFArrayGetValueAtIndex(array, i); 4788 CFStringRef unescapedComp = transformation(alloc, origComp, i); 4789 if (!unescapedComp) { 4790 break; 4791 } 4792 if (unescapedComp != origComp) { 4793 if (!mArray) { 4794 mArray = CFArrayCreateMutableCopy(alloc, c, array); 4795 } 4796 CFArraySetValueAtIndex(mArray, i, unescapedComp); 4797 } 4798 CFRelease(unescapedComp); 4799 } 4800 if (i != c) { 4801 if (mArray) CFRelease(mArray); 4802 return NULL; 4803 } else if (mArray) { 4804 return mArray; 4805 } else { 4806 CFRetain(array); 4807 return array; 4808 } 4809} 4810 4811static CFStringRef escapePathComponent(CFAllocatorRef alloc, CFStringRef origComponent, CFIndex componentIndex) { 4812 return CFURLCreateStringByAddingPercentEscapes(alloc, origComponent, NULL, CFSTR(";?/"), kCFStringEncodingUTF8); 4813} 4814 4815static CFStringRef HFSPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDir, Boolean isAbsolute) { 4816 CFArrayRef components = HFSPathToURLComponents(path, alloc, isDir); 4817 CFArrayRef newComponents = components ? copyStringArrayWithTransformation(components, escapePathComponent) : NULL; 4818 CFIndex cnt; 4819 CFStringRef result; 4820 if (components) CFRelease(components); 4821 if (!newComponents) return NULL; 4822 4823 cnt = CFArrayGetCount(newComponents); 4824 if (cnt == 1 && CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(newComponents, 0)) == 0) { 4825 result = (CFStringRef)CFRetain(CFSTR("/")); 4826 } else { 4827 result = CFStringCreateByCombiningStrings(alloc, newComponents, CFSTR("/")); 4828 } 4829 if ( isAbsolute && result ) { 4830 CFStringRef temp = result; 4831 if ( AddAuthorityToFileURL() ) { 4832 result = CFStringCreateWithFormat(alloc, 0, CFSTR(FILE_PREFIX_WITH_AUTHORITY "%@"), temp); 4833 } 4834 else { 4835 result = CFStringCreateWithFormat(alloc, 0, CFSTR(FILE_PREFIX "%@"), temp); 4836 } 4837 CFRelease(temp); 4838 } 4839 CFRelease(newComponents); 4840 return result; 4841} 4842#endif 4843 4844 4845 4846// keys and vals must have space for at least 4 key/value pairs. No argument can be NULL. 4847// Caller must release values, but not keys 4848static void __CFURLCopyPropertyListKeysAndValues(CFURLRef url, CFTypeRef *keys, CFTypeRef *vals, CFIndex *count) { 4849 CFAllocatorRef alloc = CFGetAllocator(url); 4850 CFURLRef base = CFURLGetBaseURL(url); 4851 keys[0] = CFSTR("_CFURLStringType"); 4852 keys[1] = CFSTR("_CFURLString"); 4853 keys[2] = CFSTR("_CFURLBaseStringType"); 4854 keys[3] = CFSTR("_CFURLBaseURLString"); 4855 if (CF_IS_OBJC(__kCFURLTypeID, url)) { 4856 SInt32 urlType = FULL_URL_REPRESENTATION; 4857 vals[0] = CFNumberCreate(alloc, kCFNumberSInt32Type, &urlType); 4858 vals[1] = CFURLGetString(url); 4859 } else { 4860 SInt32 urlType = FULL_URL_REPRESENTATION; 4861 vals[0] = CFNumberCreate(alloc, kCFNumberSInt32Type, &urlType); 4862 if (url->_flags & IS_DIRECTORY) { 4863 if (CFStringGetCharacterAtIndex(url->_string, CFStringGetLength(url->_string) - 1) == '/') { 4864 vals[1] = CFRetain(url->_string); 4865 } else { 4866 vals[1] = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@%c"), url->_string, '/'); 4867 } 4868 } else { 4869 if (CFStringGetCharacterAtIndex(url->_string, CFStringGetLength(url->_string) - 1) != '/') { 4870 vals[1] = CFRetain(url->_string); 4871 } else { 4872 vals[1] = CFStringCreateWithSubstring(alloc, url->_string, CFRangeMake(0, CFStringGetLength(url->_string) - 1)); 4873 } 4874 } 4875 } 4876 if (base != NULL) { 4877 if (CF_IS_OBJC(__kCFURLTypeID, base)) { 4878 SInt32 urlType = FULL_URL_REPRESENTATION; 4879 vals[2] = CFNumberCreate(alloc, kCFNumberSInt32Type, &urlType); 4880 vals[3] = CFURLGetString(base); 4881 } else { 4882 SInt32 urlType = FULL_URL_REPRESENTATION; 4883 vals[2] = CFNumberCreate(alloc, kCFNumberSInt32Type, &urlType); 4884 if (base->_flags & IS_DIRECTORY) { 4885 if (CFStringGetCharacterAtIndex(base->_string, CFStringGetLength(base->_string) - 1) == '/') { 4886 vals[3] = CFRetain(base->_string); 4887 } else { 4888 vals[3] = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@%c"), base->_string, '/'); 4889 } 4890 } else { 4891 if (CFStringGetCharacterAtIndex(base->_string, CFStringGetLength(base->_string) - 1) != '/') { 4892 vals[3] = CFRetain(base->_string); 4893 } else { 4894 vals[3] = CFStringCreateWithSubstring(alloc, base->_string, CFRangeMake(0, CFStringGetLength(base->_string) - 1)); 4895 } 4896 } 4897 } 4898 *count = 4; 4899 } else { 4900 *count = 2; 4901 } 4902} 4903 4904// Private API for Finder to use 4905CFPropertyListRef _CFURLCopyPropertyListRepresentation(CFURLRef url) { 4906 CFTypeRef keys[4], vals[4]; 4907 CFDictionaryRef dict; 4908 CFIndex count, idx; 4909 __CFURLCopyPropertyListKeysAndValues(url, keys, vals, &count); 4910 dict = CFDictionaryCreate(CFGetAllocator(url), keys, vals, count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 4911 for (idx = 0; idx < count; idx ++) { 4912 CFRelease(vals[idx]); 4913 } 4914 return dict; 4915} 4916 4917CFURLRef _CFURLCreateFromPropertyListRepresentation(CFAllocatorRef alloc, CFPropertyListRef pListRepresentation) { 4918 CFStringRef baseString, string; 4919 CFNumberRef baseTypeNum, urlTypeNum; 4920 SInt32 baseType, urlType; 4921 CFURLRef baseURL = NULL, url; 4922 CFDictionaryRef dict = (CFDictionaryRef)pListRepresentation; 4923 4924 // Start by getting all the pieces and verifying they're of the correct type. 4925 if (CFGetTypeID(pListRepresentation) != CFDictionaryGetTypeID()) { 4926 return NULL; 4927 } 4928 string = (CFStringRef)CFDictionaryGetValue(dict, CFSTR("_CFURLString")); 4929 if (!string || CFGetTypeID(string) != CFStringGetTypeID()) { 4930 return NULL; 4931 } 4932 urlTypeNum = (CFNumberRef)CFDictionaryGetValue(dict, CFSTR("_CFURLStringType")); 4933#pragma GCC diagnostic push 4934#pragma GCC diagnostic ignored "-Wdeprecated" 4935 if (!urlTypeNum || CFGetTypeID(urlTypeNum) != CFNumberGetTypeID() || !CFNumberGetValue(urlTypeNum, kCFNumberSInt32Type, &urlType) || (urlType != FULL_URL_REPRESENTATION && urlType != kCFURLPOSIXPathStyle && urlType != kCFURLHFSPathStyle && urlType != kCFURLWindowsPathStyle)) { 4936#pragma GCC diagnostic pop 4937 return NULL; 4938 } 4939 baseString = (CFStringRef)CFDictionaryGetValue(dict, CFSTR("_CFURLBaseURLString")); 4940 if (baseString) { 4941 if (CFGetTypeID(baseString) != CFStringGetTypeID()) { 4942 return NULL; 4943 } 4944 baseTypeNum = (CFNumberRef)CFDictionaryGetValue(dict, CFSTR("_CFURLBaseStringType")); 4945#pragma GCC diagnostic push 4946#pragma GCC diagnostic ignored "-Wdeprecated" 4947 if (!baseTypeNum || CFGetTypeID(baseTypeNum) != CFNumberGetTypeID() || !CFNumberGetValue(baseTypeNum, kCFNumberSInt32Type, &baseType) || 4948 (baseType != FULL_URL_REPRESENTATION && baseType != kCFURLPOSIXPathStyle && baseType != kCFURLHFSPathStyle && baseType != kCFURLWindowsPathStyle)) { 4949#pragma GCC diagnostic pop 4950 return NULL; 4951 } 4952 if (baseType == FULL_URL_REPRESENTATION) { 4953 baseURL = _CFURLCreateWithArbitraryString(alloc, baseString, NULL); 4954 } else { 4955 baseURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, baseString, (CFURLPathStyle)baseType, CFStringGetCharacterAtIndex(baseString, CFStringGetLength(baseString)-1) == '/', NULL); 4956 } 4957 } 4958 if (urlType == FULL_URL_REPRESENTATION) { 4959 url = _CFURLCreateWithArbitraryString(alloc, string, baseURL); 4960 } else { 4961 url = CFURLCreateWithFileSystemPathRelativeToBase(alloc, string, (CFURLPathStyle)urlType, CFStringGetCharacterAtIndex(string, CFStringGetLength(string)-1) == '/', baseURL); 4962 } 4963 if (baseURL) CFRelease(baseURL); 4964 return url; 4965} 4966 4967#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 4968Boolean _CFURLIsFileReferenceURL(CFURLRef url) 4969{ 4970 return ( CFURLIsFileReferenceURL(url) ); 4971} 4972 4973Boolean CFURLIsFileReferenceURL(CFURLRef url) 4974{ 4975 // returns TRUE if url is is a file URL whose path starts with a file ID reference 4976 Boolean result = false; 4977 CFURLRef baseURL = CFURLGetBaseURL(url); 4978 if ( baseURL ) { 4979 result = CFURLIsFileReferenceURL(baseURL); 4980 } 4981 else { 4982 if ( CF_IS_OBJC(__kCFURLTypeID, url) ) { 4983 result = (Boolean) CF_OBJC_CALLV((NSURL *)url, isFileReferenceURL); 4984 } 4985 else { 4986 result = ((_getSchemeTypeFromFlags(url->_flags) == kHasFileScheme) && ((url->_flags & PATH_HAS_FILE_ID) != 0)); 4987 } 4988 } 4989 return ( result ); 4990} 4991 4992static Boolean _CFURLHasFileURLScheme(CFURLRef url, Boolean *hasScheme) 4993{ 4994 Boolean result; 4995 CFURLRef baseURL = CFURLGetBaseURL(url); 4996 4997 if ( baseURL ) { 4998 result = _CFURLHasFileURLScheme(baseURL, hasScheme); 4999 } 5000 else { 5001 if ( CF_IS_OBJC(__kCFURLTypeID, url) ) { 5002 CFStringRef scheme = CFURLCopyScheme(url); 5003 if ( scheme ) { 5004 if ( scheme == kCFURLFileScheme ) { 5005 result = true; 5006 } 5007 else { 5008 result = CFStringCompare(scheme, kCFURLFileScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo; 5009 } 5010 if ( hasScheme ) { 5011 *hasScheme = true; 5012 } 5013 CFRelease(scheme); 5014 } 5015 else { 5016 if ( hasScheme ) { 5017 *hasScheme = false; 5018 } 5019 result = false; 5020 } 5021 } 5022 else { 5023 if ( hasScheme ) { 5024 *hasScheme = (url->_flags & HAS_SCHEME) != 0; 5025 } 5026 result = (_getSchemeTypeFromFlags(url->_flags) == kHasFileScheme); 5027 } 5028 } 5029 return ( result ); 5030} 5031 5032Boolean _CFURLIsFileURL(CFURLRef url) 5033{ 5034 Boolean result = _CFURLHasFileURLScheme(url, NULL); 5035 return ( result ); 5036} 5037 5038CFURLRef CFURLCreateFilePathURL(CFAllocatorRef alloc, CFURLRef url, CFErrorRef *error) 5039{ 5040 CFURLRef result = NULL; 5041 Boolean hasScheme; 5042 if (!_CFURLHasFileURLScheme(url, &hasScheme)) { 5043 if ( !hasScheme ) { 5044 CFLog(kCFLogLevelWarning, CFSTR("CFURLCreateFilePathURL failed because it was passed this URL which has no scheme: %@"), url); 5045 } 5046 if ( error ) { 5047 *error = CFErrorCreate( kCFAllocatorDefault, kCFErrorDomainCocoa, kCFURLReadUnsupportedSchemeError, NULL ); 5048 } 5049 result = NULL; 5050 } else { 5051 // File URL. Form of the path is unknown. Make a new URL. 5052 CFStringRef newURLString; 5053 CFStringRef netLoc; 5054 CFStringRef fsPath; 5055 CFStringRef rSpec; 5056 5057 if ( CFURLGetBaseURL( url )) { 5058 CFURLRef absURL = CFURLCopyAbsoluteURL( url ); 5059 fsPath = CFURLCreateStringWithFileSystemPath(CFGetAllocator(absURL), absURL, kCFURLPOSIXPathStyle, false); 5060 netLoc = CFURLCopyNetLocation( absURL ); 5061 rSpec = CFURLCopyResourceSpecifier( absURL ); 5062 CFRelease( absURL ); 5063 } else { 5064 fsPath = CFURLCreateStringWithFileSystemPath(CFGetAllocator(url), url, kCFURLPOSIXPathStyle, false); 5065 netLoc = CFURLCopyNetLocation( url ); 5066 rSpec = CFURLCopyResourceSpecifier( url ); 5067 } 5068 if ( fsPath ) { 5069 CFStringRef urlPath = _replacePathIllegalCharacters( fsPath, alloc, true ); 5070 newURLString = CFStringCreateWithFormat( alloc, NULL, CFSTR(FILE_PREFIX "%@%@%@%@"), (netLoc ? netLoc : CFSTR("")), urlPath, ((CFStringCompare(urlPath, CFSTR("/"), 0) != kCFCompareEqualTo) ? (CFURLHasDirectoryPath( url ) ? CFSTR("/") : CFSTR("")) : CFSTR("")), (rSpec ? rSpec : CFSTR(""))); 5071 result = CFURLCreateWithString( alloc, newURLString, NULL ); 5072 CFRelease( newURLString ); 5073 CFRelease( urlPath ); 5074 CFRelease( fsPath ); 5075 } else { 5076 if ( error ) { 5077 // Would be better here to get an underlying error back from CFURLCreateStringWithFileSystemPath 5078 *error = CFErrorCreate( kCFAllocatorDefault, kCFErrorDomainCocoa, kCFURLNoSuchResourceError, NULL ); 5079 } 5080 result = NULL; 5081 } 5082 if ( netLoc ) { 5083 CFRelease( netLoc ); 5084 } 5085 if ( rSpec ) { 5086 CFRelease( rSpec ); 5087 } 5088 } 5089 return result; 5090} 5091 5092#endif 5093 5094 5095CFURLRef CFURLCreateFileReferenceURL(CFAllocatorRef alloc, CFURLRef url, CFErrorRef *error) { return NULL; } 5096 5097