• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2008 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#import "config.h"
30#import "WebCoreURLResponse.h"
31
32#import "FoundationExtras.h"
33#import "MIMETypeRegistry.h"
34#import <objc/objc-class.h>
35#import <wtf/Assertions.h>
36#import <wtf/RetainPtr.h>
37
38#ifndef BUILDING_ON_TIGER
39// <rdar://problem/5321972> Plain text document from HTTP server detected as application/octet-stream
40// When we sniff a resource as application/octet-stream but the http response headers had "text/plain",
41// we have a hard decision to make about which of the two generic MIME types to go with.
42// When the URL's extension is a known binary type, we'll go with application/octet-stream.
43// Otherwise, we'll trust the server.
44static NSSet *createBinaryExtensionsSet()
45{
46    return [[NSSet alloc] initWithObjects:
47        @"3g2",
48        @"3gp",
49        @"ai",
50        @"aif",
51        @"aifc",
52        @"aiff",
53        @"au",
54        @"avi",
55        @"bcpio",
56        @"bin",
57        @"bmp",
58        @"boz",
59        @"bpk",
60        @"bz",
61        @"bz2",
62        @"chm",
63        @"class",
64        @"com",
65        @"cpio",
66        @"dcr",
67        @"dir",
68        @"dist",
69        @"distz",
70        @"dll",
71        @"dmg",
72        @"dms",
73        @"doc",
74        @"dot",
75        @"dump",
76        @"dv",
77        @"dvi",
78        @"dxr",
79        @"elc",
80        @"eot",
81        @"eps",
82        @"exe",
83        @"fgd",
84        @"gif",
85        @"gtar",
86        @"h261",
87        @"h263",
88        @"h264",
89        @"ico",
90        @"ims",
91        @"indd",
92        @"iso",
93        @"jp2",
94        @"jpe",
95        @"jpeg",
96        @"jpg",
97        @"jpgm",
98        @"jpgv",
99        @"jpm",
100        @"kar",
101        @"kmz",
102        @"lha",
103        @"lrm",
104        @"lzh",
105        @"m1v",
106        @"m2a",
107        @"m2v",
108        @"m3a",
109        @"m3u",
110        @"m4a",
111        @"m4p",
112        @"m4v",
113        @"mdb",
114        @"mid",
115        @"midi",
116        @"mj2",
117        @"mjp2",
118        @"mov",
119        @"movie",
120        @"mp2",
121        @"mp2a",
122        @"mp3",
123        @"mp4",
124        @"mp4a",
125        @"mp4s",
126        @"mp4v",
127        @"mpe",
128        @"mpeg",
129        @"mpg",
130        @"mpg4",
131        @"mpga",
132        @"mpp",
133        @"mpt",
134        @"msi",
135        @"ogg",
136        @"otf",
137        @"pct",
138        @"pdf",
139        @"pfa",
140        @"pfb",
141        @"pic",
142        @"pict",
143        @"pkg",
144        @"png",
145        @"pot",
146        @"pps",
147        @"ppt",
148        @"ps",
149        @"psd",
150        @"qt",
151        @"qti",
152        @"qtif",
153        @"qwd",
154        @"qwt",
155        @"qxb",
156        @"qxd",
157        @"qxl",
158        @"qxp",
159        @"qxt",
160        @"ra",
161        @"ram",
162        @"rm",
163        @"rmi",
164        @"rmp",
165        @"scpt",
166        @"sit",
167        @"sitx",
168        @"snd",
169        @"so",
170        @"swf",
171        @"tar",
172        @"tif",
173        @"tiff",
174        @"ttf",
175        @"wav",
176        @"wcm",
177        @"wdb",
178        @"wks",
179        @"wm",
180        @"wma",
181        @"wmd",
182        @"wmf",
183        @"wmv",
184        @"wmx",
185        @"wmz",
186        @"wpd",
187        @"wpl",
188        @"wps",
189        @"wvx",
190        @"xla",
191        @"xlc",
192        @"xlm",
193        @"xls",
194        @"xlt",
195        @"xlw",
196        @"xps",
197        @"zip",
198        nil
199    ];
200}
201#endif
202
203// <rdar://problem/7007389> CoreTypes UTI map is missing 100+ file extensions that GateKeeper knew about
204// When we disabled content sniffing for file URLs we caused problems with these 100+ extensions that CoreTypes
205// doesn't know about.
206// If CoreTypes is ever brought up to speed we can remove this table and associated code.
207static NSDictionary *createExtensionToMIMETypeMap()
208{
209    return [[NSDictionary alloc] initWithObjectsAndKeys:
210        @"application/postscript", @"ai",
211        @"text/plain", @"asc",
212        @"application/x-bcpio", @"bcpio",
213        @"image/bmp", @"bmp",
214        @"application/x-netcdf", @"cdf",
215        @"application/octet-stream", @"class",
216        @"application/x-gzip", @"cpgz",
217        @"application/x-cpio", @"cpio",
218        @"application/mac-compactpro", @"cpt",
219        @"application/x-csh", @"csh",
220        @"text/css", @"css",
221        @"application/x-director", @"dcr",
222        @"application/x-director", @"dir",
223        @"application/x-diskcopy", @"dmg",
224        @"application/octet-stream", @"dms",
225        @"application/x-dvi", @"dvi",
226        @"application/x-director", @"dxr",
227        @"application/postscript", @"eps",
228        @"text/x-setext", @"etx",
229        @"application/andrew-inset", @"ez",
230        @"application/vnd.fdf", @"fdf",
231        @"application/octet-stream", @"fla",
232        @"application/x-filemaker", @"fp",
233        @"application/x-filemaker", @"fp2",
234        @"application/x-filemaker", @"fp3",
235        @"application/x-filemaker", @"fp4",
236        @"application/x-filemaker", @"fp5",
237        @"application/x-filemaker", @"fp6",
238        @"application/x-hdf", @"hdf",
239        @"x-conference/x-cooltalk", @"ice",
240        @"image/x-icon", @"ico",
241        @"text/calendar", @"ics",
242        @"image/ief", @"ief",
243        @"model/iges", @"iges",
244        @"model/iges", @"igs",
245        @"application/octet-stream", @"iso",
246        @"text/html", @"jhtml",
247        @"application/x-latex", @"latex",
248        @"application/octet-stream", @"lha",
249        @"application/octet-stream", @"lzh",
250        @"audio/x-mpegurl", @"m3u",
251        @"audio/x-m4p", @"m4p",
252        @"image/x-macpaint", @"mac",
253        @"application/x-troff-man", @"man",
254        @"application/x-troff-me", @"me",
255        @"model/mesh", @"mesh",
256        @"application/vnd.mif", @"mif",
257        @"video/x-sgi-movie", @"movie",
258        @"audio/mpeg", @"mp2",
259        @"audio/mpeg", @"mpga",
260        @"application/x-troff-ms", @"ms",
261        @"model/mesh", @"msh",
262        @"video/vnd.mpegurl", @"mxu",
263        @"application/x-netcdf", @"nc",
264        @"application/oda", @"oda",
265        @"image/x-portable-bitmap", @"pbm",
266        @"image/x-pcx", @"pcx",
267        @"chemical/x-pdb", @"pdb",
268        @"image/x-portable-graymap", @"pgm",
269        @"application/x-chess-pgn", @"pgn",
270        @"audio/scpls", @"pls",
271        @"image/x-portable-anymap", @"pnm",
272        @"image/x-macpaint", @"pnt",
273        @"image/x-macpaint", @"pntg",
274        @"image/x-portable-pixmap", @"ppm",
275        @"image/x-cmu-raster", @"ras",
276        @"image/x-rgb", @"rgb",
277        @"application/x-troff", @"roff",
278        @"audio/x-pn-realaudio-plugin", @"rpm",
279        @"text/richtext", @"rtx",
280        @"text/sgml", @"sgm",
281        @"text/sgml", @"sgml",
282        @"application/x-sh", @"sh",
283        @"application/x-shar", @"shar",
284        @"model/mesh", @"silo",
285        @"application/x-koan", @"skd",
286        @"application/x-koan", @"skm",
287        @"application/x-koan", @"skp",
288        @"application/x-koan", @"skt",
289        @"application/x-diskcopy", @"smi",
290        @"application/octet-stream", @"so",
291        @"application/x-futuresplash", @"spl",
292        @"application/x-wais-source", @"src",
293        @"application/x-sv4cpio", @"sv4cpio",
294        @"application/x-sv4crc", @"sv4crc",
295        @"application/x-shockwave-flash", @"swf",
296        @"application/x-troff", @"t",
297        @"image/x-targa", @"targa",
298        @"application/x-tcl", @"tcl",
299        @"application/x-tex", @"tex",
300        @"application/x-texinfo", @"texi",
301        @"application/x-texinfo", @"texinfo",
302        @"application/x-gzip", @"tgz",
303        @"application/x-bittorrent", @"torrent",
304        @"application/x-troff", @"tr",
305        @"text/tab-separated-values", @"tsv",
306        @"application/x-ustar", @"ustar",
307        @"application/x-cdlink", @"vcd",
308        @"model/vrml", @"vrml",
309        @"image/vnd.wap.wbmp", @"wbmp",
310        @"application/vnd.wap.wbxml", @"wbxml",
311        @"application/x-webarchive", @"webarchive",
312        @"application/x-ms-wmd", @"wmd",
313        @"text/vnd.wap.wml", @"wml",
314        @"application/vnd.wap.wmlc", @"wmlc",
315        @"text/vnd.wap.wmlscript", @"wmls",
316        @"application/vnd.wap.wmlscriptc", @"wmlsc",
317        @"model/vrml", @"wrl",
318        @"application/vnd.adobe.xdp+xml", @"xdp",
319        @"application/vnd.adobe.xfd+xml", @"xfd",
320        @"application/vnd.adobe.xfdf", @"xfdf",
321        @"image/x-xpixmap", @"xpm",
322        @"text/xml", @"xsl",
323        @"image/x-xwindowdump", @"xwd",
324        @"chemical/x-xyz", @"xyz",
325        @"application/x-compress", @"z",
326        nil
327    ];
328}
329
330static IMP oldNSURLResponseMIMETypeIMP = 0;
331static NSString *webNSURLResponseMIMEType(id, SEL);
332
333void swizzleMIMETypeMethodIfNecessary()
334{
335    if (!oldNSURLResponseMIMETypeIMP) {
336        Method nsURLResponseMIMETypeMethod = class_getInstanceMethod([NSURLResponse class], @selector(MIMEType));
337        ASSERT(nsURLResponseMIMETypeMethod);
338#ifdef BUILDING_ON_TIGER
339        oldNSURLResponseMIMETypeIMP = nsURLResponseMIMETypeMethod->method_imp;
340        nsURLResponseMIMETypeMethod->method_imp = (IMP)webNSURLResponseMIMEType;
341#else
342        oldNSURLResponseMIMETypeIMP = method_setImplementation(nsURLResponseMIMETypeMethod, (IMP)webNSURLResponseMIMEType);
343#endif
344    }
345}
346
347static NSString *mimeTypeFromUTITree(CFStringRef uti)
348{
349    // Check if this UTI has a MIME type.
350    RetainPtr<CFStringRef> mimeType(AdoptCF, UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType));
351    if (mimeType)
352        return (NSString *)HardAutorelease(mimeType.releaseRef());
353
354    // If not, walk the ancestory of this UTI via its "ConformsTo" tags and return the first MIME type we find.
355    RetainPtr<CFDictionaryRef> decl(AdoptCF, UTTypeCopyDeclaration(uti));
356    if (!decl)
357        return nil;
358    CFTypeRef value = CFDictionaryGetValue(decl.get(), kUTTypeConformsToKey);
359    if (!value)
360        return nil;
361    CFTypeID typeID = CFGetTypeID(value);
362
363    if (typeID == CFStringGetTypeID())
364        return mimeTypeFromUTITree((CFStringRef)value);
365
366    if (typeID == CFArrayGetTypeID()) {
367        CFArrayRef newTypes = (CFArrayRef)value;
368        CFIndex count = CFArrayGetCount(newTypes);
369        for (CFIndex i = 0; i < count; ++i) {
370            CFTypeRef object = CFArrayGetValueAtIndex(newTypes, i);
371            if (CFGetTypeID(object) != CFStringGetTypeID())
372                continue;
373
374            if (NSString *mimeType = mimeTypeFromUTITree((CFStringRef)object))
375                return mimeType;
376        }
377    }
378
379    return nil;
380}
381
382static NSString *webNSURLResponseMIMEType(id self, SEL _cmd)
383{
384    ASSERT(oldNSURLResponseMIMETypeIMP);
385    NSString *result = oldNSURLResponseMIMETypeIMP(self, _cmd);
386
387#ifdef BUILDING_ON_TIGER
388    // When content sniffing is disabled, Tiger's CFNetwork automatically returns application/octet-stream for certain
389    // extensions even when scouring the UTI maps would end up with a better result, so we'll give a chance for that to happen.
390    if ([[self URL] isFileURL] && [result caseInsensitiveCompare:@"application/octet-stream"] == NSOrderedSame)
391        result = nil;
392#endif
393
394    if (!result) {
395        NSURL *url = [self URL];
396        if ([url isFileURL]) {
397            if (NSString *extension = [[url path] pathExtension]) {
398                // <rdar://problem/7007389> CoreTypes UTI map is missing 100+ file extensions that GateKeeper knew about
399                // When this radar is resolved, we can remove this file:// url specific code.
400                static NSDictionary *extensionMap = createExtensionToMIMETypeMap();
401                result = [extensionMap objectForKey:[extension lowercaseString]];
402
403                if (!result) {
404                    // If the Gatekeeper-based map doesn't have a MIME type, we'll try to figure out what it should be by
405                    // looking up the file extension in the UTI maps.
406                    RetainPtr<CFStringRef> uti(AdoptCF, UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (CFStringRef)extension, 0));
407                    result = mimeTypeFromUTITree(uti.get());
408                }
409            }
410        }
411    }
412
413    if (!result) {
414        static NSString *defaultMIMETypeString = [(NSString *)WebCore::defaultMIMEType() retain];
415        result = defaultMIMETypeString;
416    }
417
418#ifndef BUILDING_ON_TIGER
419    // <rdar://problem/5321972> Plain text document from HTTP server detected as application/octet-stream
420    // Make the best guess when deciding between "generic binary" and "generic text" using a table of known binary MIME types.
421    if ([result isEqualToString:@"application/octet-stream"] && [self respondsToSelector:@selector(allHeaderFields)] && [[[self allHeaderFields] objectForKey:@"Content-Type"] hasPrefix:@"text/plain"]) {
422        static NSSet *binaryExtensions = createBinaryExtensionsSet();
423        if (![binaryExtensions containsObject:[[[self suggestedFilename] pathExtension] lowercaseString]])
424            result = @"text/plain";
425    }
426
427#endif
428
429#ifdef BUILDING_ON_LEOPARD
430    // Workaround for <rdar://problem/5539824>
431    if ([result isEqualToString:@"text/xml"])
432        result = @"application/xml";
433#endif
434
435    return result;
436}
437
438@implementation NSURLResponse (WebCoreURLResponse)
439
440-(NSString *)_webcore_reportedMIMEType
441{
442    swizzleMIMETypeMethodIfNecessary();
443    return oldNSURLResponseMIMETypeIMP(self, @selector(_webcore_realMIMEType));
444}
445
446@end
447