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