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