WebCoreURLResponse.mm [plain text]
/*
* Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import "WebCoreURLResponse.h"
#import "FoundationExtras.h"
#import "MIMETypeRegistry.h"
#import <objc/objc-class.h>
#import <wtf/Assertions.h>
#import <wtf/RetainPtr.h>
#ifndef BUILDING_ON_TIGER
// <rdar://problem/5321972> Plain text document from HTTP server detected as application/octet-stream
// When we sniff a resource as application/octet-stream but the http response headers had "text/plain",
// we have a hard decision to make about which of the two generic MIME types to go with.
// When the URL's extension is a known binary type, we'll go with application/octet-stream.
// Otherwise, we'll trust the server.
static NSSet *createBinaryExtensionsSet()
{
return [[NSSet alloc] initWithObjects:
@"3g2",
@"3gp",
@"ai",
@"aif",
@"aifc",
@"aiff",
@"au",
@"avi",
@"bcpio",
@"bin",
@"bmp",
@"boz",
@"bpk",
@"bz",
@"bz2",
@"chm",
@"class",
@"com",
@"cpio",
@"dcr",
@"dir",
@"dist",
@"distz",
@"dll",
@"dmg",
@"dms",
@"doc",
@"dot",
@"dump",
@"dv",
@"dvi",
@"dxr",
@"elc",
@"eot",
@"eps",
@"exe",
@"fgd",
@"gif",
@"gtar",
@"h261",
@"h263",
@"h264",
@"ico",
@"ims",
@"indd",
@"iso",
@"jp2",
@"jpe",
@"jpeg",
@"jpg",
@"jpgm",
@"jpgv",
@"jpm",
@"kar",
@"kmz",
@"lha",
@"lrm",
@"lzh",
@"m1v",
@"m2a",
@"m2v",
@"m3a",
@"m3u",
@"m4a",
@"m4p",
@"m4v",
@"mdb",
@"mid",
@"midi",
@"mj2",
@"mjp2",
@"mov",
@"movie",
@"mp2",
@"mp2a",
@"mp3",
@"mp4",
@"mp4a",
@"mp4s",
@"mp4v",
@"mpe",
@"mpeg",
@"mpg",
@"mpg4",
@"mpga",
@"mpp",
@"mpt",
@"msi",
@"ogg",
@"otf",
@"pct",
@"pdf",
@"pfa",
@"pfb",
@"pic",
@"pict",
@"pkg",
@"png",
@"pot",
@"pps",
@"ppt",
@"ps",
@"psd",
@"qt",
@"qti",
@"qtif",
@"qwd",
@"qwt",
@"qxb",
@"qxd",
@"qxl",
@"qxp",
@"qxt",
@"ra",
@"ram",
@"rm",
@"rmi",
@"rmp",
@"scpt",
@"sit",
@"sitx",
@"snd",
@"so",
@"swf",
@"tar",
@"tif",
@"tiff",
@"ttf",
@"wav",
@"wcm",
@"wdb",
@"wks",
@"wm",
@"wma",
@"wmd",
@"wmf",
@"wmv",
@"wmx",
@"wmz",
@"wpd",
@"wpl",
@"wps",
@"wvx",
@"xla",
@"xlc",
@"xlm",
@"xls",
@"xlt",
@"xlw",
@"xps",
@"zip",
nil
];
}
#endif
// <rdar://problem/7007389> CoreTypes UTI map is missing 100+ file extensions that GateKeeper knew about
// When we disabled content sniffing for file URLs we caused problems with these 100+ extensions that CoreTypes
// doesn't know about.
// If CoreTypes is ever brought up to speed we can remove this table and associated code.
static NSDictionary *createExtensionToMIMETypeMap()
{
return [[NSDictionary alloc] initWithObjectsAndKeys:
@"application/postscript", @"ai",
@"text/plain", @"asc",
@"application/x-bcpio", @"bcpio",
@"image/bmp", @"bmp",
@"application/x-netcdf", @"cdf",
@"application/octet-stream", @"class",
@"application/x-gzip", @"cpgz",
@"application/x-cpio", @"cpio",
@"application/mac-compactpro", @"cpt",
@"application/x-csh", @"csh",
@"text/css", @"css",
@"application/x-director", @"dcr",
@"application/x-director", @"dir",
@"application/x-diskcopy", @"dmg",
@"application/octet-stream", @"dms",
@"application/x-dvi", @"dvi",
@"application/x-director", @"dxr",
@"application/postscript", @"eps",
@"text/x-setext", @"etx",
@"application/andrew-inset", @"ez",
@"application/vnd.fdf", @"fdf",
@"application/octet-stream", @"fla",
@"application/x-filemaker", @"fp",
@"application/x-filemaker", @"fp2",
@"application/x-filemaker", @"fp3",
@"application/x-filemaker", @"fp4",
@"application/x-filemaker", @"fp5",
@"application/x-filemaker", @"fp6",
@"application/x-hdf", @"hdf",
@"x-conference/x-cooltalk", @"ice",
@"image/x-icon", @"ico",
@"text/calendar", @"ics",
@"image/ief", @"ief",
@"model/iges", @"iges",
@"model/iges", @"igs",
@"application/octet-stream", @"iso",
@"text/html", @"jhtml",
@"application/x-latex", @"latex",
@"application/octet-stream", @"lha",
@"application/octet-stream", @"lzh",
@"audio/x-mpegurl", @"m3u",
@"audio/x-m4p", @"m4p",
@"image/x-macpaint", @"mac",
@"application/x-troff-man", @"man",
@"application/x-troff-me", @"me",
@"model/mesh", @"mesh",
@"application/vnd.mif", @"mif",
@"video/x-sgi-movie", @"movie",
@"audio/mpeg", @"mp2",
@"audio/mpeg", @"mpga",
@"application/x-troff-ms", @"ms",
@"model/mesh", @"msh",
@"video/vnd.mpegurl", @"mxu",
@"application/x-netcdf", @"nc",
@"application/oda", @"oda",
@"image/x-portable-bitmap", @"pbm",
@"image/x-pcx", @"pcx",
@"chemical/x-pdb", @"pdb",
@"image/x-portable-graymap", @"pgm",
@"application/x-chess-pgn", @"pgn",
@"audio/scpls", @"pls",
@"image/x-portable-anymap", @"pnm",
@"image/x-macpaint", @"pnt",
@"image/x-macpaint", @"pntg",
@"image/x-portable-pixmap", @"ppm",
@"image/x-cmu-raster", @"ras",
@"image/x-rgb", @"rgb",
@"application/x-troff", @"roff",
@"audio/x-pn-realaudio-plugin", @"rpm",
@"text/richtext", @"rtx",
@"text/sgml", @"sgm",
@"text/sgml", @"sgml",
@"application/x-sh", @"sh",
@"application/x-shar", @"shar",
@"model/mesh", @"silo",
@"application/x-koan", @"skd",
@"application/x-koan", @"skm",
@"application/x-koan", @"skp",
@"application/x-koan", @"skt",
@"application/x-diskcopy", @"smi",
@"application/octet-stream", @"so",
@"application/x-futuresplash", @"spl",
@"application/x-wais-source", @"src",
@"application/x-sv4cpio", @"sv4cpio",
@"application/x-sv4crc", @"sv4crc",
@"application/x-shockwave-flash", @"swf",
@"application/x-troff", @"t",
@"image/x-targa", @"targa",
@"application/x-tcl", @"tcl",
@"application/x-tex", @"tex",
@"application/x-texinfo", @"texi",
@"application/x-texinfo", @"texinfo",
@"application/x-gzip", @"tgz",
@"application/x-bittorrent", @"torrent",
@"application/x-troff", @"tr",
@"text/tab-separated-values", @"tsv",
@"application/x-ustar", @"ustar",
@"application/x-cdlink", @"vcd",
@"model/vrml", @"vrml",
@"image/vnd.wap.wbmp", @"wbmp",
@"application/vnd.wap.wbxml", @"wbxml",
@"application/x-webarchive", @"webarchive",
@"application/x-ms-wmd", @"wmd",
@"text/vnd.wap.wml", @"wml",
@"application/vnd.wap.wmlc", @"wmlc",
@"text/vnd.wap.wmlscript", @"wmls",
@"application/vnd.wap.wmlscriptc", @"wmlsc",
@"model/vrml", @"wrl",
@"application/vnd.adobe.xdp+xml", @"xdp",
@"application/vnd.adobe.xfd+xml", @"xfd",
@"application/vnd.adobe.xfdf", @"xfdf",
@"image/x-xpixmap", @"xpm",
@"text/xml", @"xsl",
@"image/x-xwindowdump", @"xwd",
@"chemical/x-xyz", @"xyz",
@"application/x-compress", @"z",
nil
];
}
static NSString *mimeTypeFromUTITree(CFStringRef uti)
{
// Check if this UTI has a MIME type.
RetainPtr<CFStringRef> mimeType(AdoptCF, UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType));
if (mimeType)
return (NSString *)HardAutorelease(mimeType.releaseRef());
// If not, walk the ancestory of this UTI via its "ConformsTo" tags and return the first MIME type we find.
RetainPtr<CFDictionaryRef> decl(AdoptCF, UTTypeCopyDeclaration(uti));
if (!decl)
return nil;
CFTypeRef value = CFDictionaryGetValue(decl.get(), kUTTypeConformsToKey);
if (!value)
return nil;
CFTypeID typeID = CFGetTypeID(value);
if (typeID == CFStringGetTypeID())
return mimeTypeFromUTITree((CFStringRef)value);
if (typeID == CFArrayGetTypeID()) {
CFArrayRef newTypes = (CFArrayRef)value;
CFIndex count = CFArrayGetCount(newTypes);
for (CFIndex i = 0; i < count; ++i) {
CFTypeRef object = CFArrayGetValueAtIndex(newTypes, i);
if (CFGetTypeID(object) != CFStringGetTypeID())
continue;
if (NSString *mimeType = mimeTypeFromUTITree((CFStringRef)object))
return mimeType;
}
}
return nil;
}
@implementation NSURLResponse (WebCoreURLResponse)
-(void)adjustMIMETypeIfNecessary
{
NSString *result = [self MIMEType];
NSString *originalResult = result;
#ifdef BUILDING_ON_TIGER
// When content sniffing is disabled, Tiger's CFNetwork automatically returns application/octet-stream for certain
// extensions even when scouring the UTI maps would end up with a better result, so we'll give a chance for that to happen.
if ([[self URL] isFileURL] && [result caseInsensitiveCompare:@"application/octet-stream"] == NSOrderedSame)
result = nil;
#endif
if (!result) {
NSURL *url = [self URL];
if ([url isFileURL]) {
if (NSString *extension = [[url path] pathExtension]) {
// <rdar://problem/7007389> CoreTypes UTI map is missing 100+ file extensions that GateKeeper knew about
// When this radar is resolved, we can remove this file:// url specific code.
static NSDictionary *extensionMap = createExtensionToMIMETypeMap();
result = [extensionMap objectForKey:[extension lowercaseString]];
if (!result) {
// If the Gatekeeper-based map doesn't have a MIME type, we'll try to figure out what it should be by
// looking up the file extension in the UTI maps.
RetainPtr<CFStringRef> uti(AdoptCF, UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (CFStringRef)extension, 0));
result = mimeTypeFromUTITree(uti.get());
}
}
}
}
if (!result) {
static NSString *defaultMIMETypeString = [(NSString *)WebCore::defaultMIMEType() retain];
result = defaultMIMETypeString;
}
#ifndef BUILDING_ON_TIGER
// <rdar://problem/5321972> Plain text document from HTTP server detected as application/octet-stream
// Make the best guess when deciding between "generic binary" and "generic text" using a table of known binary MIME types.
if ([result isEqualToString:@"application/octet-stream"] && [self respondsToSelector:@selector(allHeaderFields)] && [[[self performSelector:@selector(allHeaderFields)] objectForKey:@"Content-Type"] hasPrefix:@"text/plain"]) {
static NSSet *binaryExtensions = createBinaryExtensionsSet();
if (![binaryExtensions containsObject:[[[self suggestedFilename] pathExtension] lowercaseString]])
result = @"text/plain";
}
#endif
#ifdef BUILDING_ON_LEOPARD
// Workaround for <rdar://problem/5539824>
if ([result isEqualToString:@"text/xml"])
result = @"application/xml";
#endif
if (result != originalResult)
[self _setMIMEType:result];
}
@end