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