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