1 /* 2 * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.activation; 27 28 import java.io.*; 29 import java.net.*; 30 import java.util.*; 31 import java.security.AccessController; 32 import java.security.PrivilegedAction; 33 import com.sun.activation.registries.MimeTypeFile; 34 import com.sun.activation.registries.LogSupport; 35 36 /** 37 * This class extends FileTypeMap and provides data typing of files 38 * via their file extension. It uses the {@code .mime.types} format. <p> 39 * 40 * <b>MIME types file search order:</b><p> 41 * The MimetypesFileTypeMap looks in various places in the user's 42 * system for MIME types file entries. When requests are made 43 * to search for MIME types in the MimetypesFileTypeMap, it searches 44 * MIME types files in the following order: 45 * <ol> 46 * <li> Programmatically added entries to the MimetypesFileTypeMap instance. 47 * <li> The file {@code .mime.types} in the user's home directory. 48 * <li> The file {@code mime.types} in the Java runtime. 49 * <li> The file or resources named {@code META-INF/mime.types}. 50 * <li> The file or resource named {@code META-INF/mimetypes.default} 51 * (usually found only in the {@code activation.jar} file). 52 * </ol> 53 * <p> 54 * (The current implementation looks for the {@code mime.types} file 55 * in the Java runtime in the directory <i>java.home</i>{@code /conf} 56 * if it exists, and otherwise in the directory 57 * <i>java.home</i>{@code /lib}, where <i>java.home</i> is the value 58 * of the "java.home" System property. Note that the "conf" directory was 59 * introduced in JDK 9.) 60 * <p> 61 * <b>MIME types file format:</b> 62 * 63 * <pre>{@code 64 * # comments begin with a '#' 65 * # the format is <mime type> <space separated file extensions> 66 * # for example: 67 * text/plain txt text TXT 68 * # this would map file.txt, file.text, and file.TXT to 69 * # the mime type "text/plain" 70 * }</pre> 71 * 72 * @author Bart Calder 73 * @author Bill Shannon 74 * 75 * @since 1.6 76 */ 77 public class MimetypesFileTypeMap extends FileTypeMap { 78 /* 79 * We manage a collection of databases, searched in order. 80 */ 81 private MimeTypeFile[] DB; 82 private static final int PROG = 0; // programmatically added entries 83 84 private static final String defaultType = "application/octet-stream"; 85 86 private static final String confDir; 87 88 static { 89 String dir = null; 90 try { 91 dir = (String)AccessController.doPrivileged( 92 new PrivilegedAction() { 93 public Object run() { 94 String home = System.getProperty("java.home"); 95 String newdir = home + File.separator + "conf"; 96 File conf = new File(newdir); 97 if (conf.exists()) 98 return newdir + File.separator; 99 else 100 return home + File.separator + "lib" + File.separator; 101 } 102 }); 103 } catch (Exception ex) { 104 // ignore any exceptions 105 } 106 confDir = dir; 107 } 108 109 /** 110 * The default constructor. 111 */ 112 public MimetypesFileTypeMap() { 113 Vector dbv = new Vector(5); // usually 5 or less databases 114 MimeTypeFile mf = null; 115 dbv.addElement(null); // place holder for PROG entry 116 117 LogSupport.log("MimetypesFileTypeMap: load HOME"); 118 try { 119 String user_home = System.getProperty("user.home"); 120 121 if (user_home != null) { 122 String path = user_home + File.separator + ".mime.types"; 123 mf = loadFile(path); 124 if (mf != null) 125 dbv.addElement(mf); 126 } 127 } catch (SecurityException ex) {} 128 129 LogSupport.log("MimetypesFileTypeMap: load SYS"); 130 try { 131 // check system's home 132 if (confDir != null) { 133 mf = loadFile(confDir + "mime.types"); 134 if (mf != null) 135 dbv.addElement(mf); 136 } 137 } catch (SecurityException ex) {} 138 139 LogSupport.log("MimetypesFileTypeMap: load JAR"); 140 // load from the app's jar file 141 loadAllResources(dbv, "META-INF/mime.types"); 142 143 LogSupport.log("MimetypesFileTypeMap: load DEF"); 144 mf = loadResource("/META-INF/mimetypes.default"); 145 146 if (mf != null) 147 dbv.addElement(mf); 148 149 DB = new MimeTypeFile[dbv.size()]; 150 dbv.copyInto(DB); 151 } 152 153 /** 154 * Load from the named resource. 155 */ 156 private MimeTypeFile loadResource(String name) { 157 InputStream clis = null; 158 try { 159 clis = SecuritySupport.getResourceAsStream(this.getClass(), name); 160 if (clis != null) { 161 MimeTypeFile mf = new MimeTypeFile(clis); 162 if (LogSupport.isLoggable()) 163 LogSupport.log("MimetypesFileTypeMap: successfully " + 164 "loaded mime types file: " + name); 165 return mf; 166 } else { 167 if (LogSupport.isLoggable()) 168 LogSupport.log("MimetypesFileTypeMap: not loading " + 169 "mime types file: " + name); 170 } 171 } catch (IOException e) { 172 if (LogSupport.isLoggable()) 173 LogSupport.log("MimetypesFileTypeMap: can't load " + name, e); 174 } catch (SecurityException sex) { 175 if (LogSupport.isLoggable()) 176 LogSupport.log("MimetypesFileTypeMap: can't load " + name, sex); 177 } finally { 178 try { 179 if (clis != null) 180 clis.close(); 181 } catch (IOException ex) { } // ignore it 182 } 183 return null; 184 } 185 186 /** 187 * Load all of the named resource. 188 */ 189 private void loadAllResources(Vector v, String name) { 190 boolean anyLoaded = false; 191 try { 192 URL[] urls; 193 ClassLoader cld = null; 194 // First try the "application's" class loader. 195 cld = SecuritySupport.getContextClassLoader(); 196 if (cld == null) 197 cld = this.getClass().getClassLoader(); 198 if (cld != null) 199 urls = SecuritySupport.getResources(cld, name); 200 else 201 urls = SecuritySupport.getSystemResources(name); 202 if (urls != null) { 203 if (LogSupport.isLoggable()) 204 LogSupport.log("MimetypesFileTypeMap: getResources"); 205 for (int i = 0; i < urls.length; i++) { 206 URL url = urls[i]; 207 InputStream clis = null; 208 if (LogSupport.isLoggable()) 209 LogSupport.log("MimetypesFileTypeMap: URL " + url); 210 try { 211 clis = SecuritySupport.openStream(url); 212 if (clis != null) { 213 v.addElement(new MimeTypeFile(clis)); 214 anyLoaded = true; 215 if (LogSupport.isLoggable()) 216 LogSupport.log("MimetypesFileTypeMap: " + 217 "successfully loaded " + 218 "mime types from URL: " + url); 219 } else { 220 if (LogSupport.isLoggable()) 221 LogSupport.log("MimetypesFileTypeMap: " + 222 "not loading " + 223 "mime types from URL: " + url); 224 } 225 } catch (IOException ioex) { 226 if (LogSupport.isLoggable()) 227 LogSupport.log("MimetypesFileTypeMap: can't load " + 228 url, ioex); 229 } catch (SecurityException sex) { 230 if (LogSupport.isLoggable()) 231 LogSupport.log("MimetypesFileTypeMap: can't load " + 232 url, sex); 233 } finally { 234 try { 235 if (clis != null) 236 clis.close(); 237 } catch (IOException cex) { } 238 } 239 } 240 } 241 } catch (Exception ex) { 242 if (LogSupport.isLoggable()) 243 LogSupport.log("MimetypesFileTypeMap: can't load " + name, ex); 244 } 245 246 // if failed to load anything, fall back to old technique, just in case 247 if (!anyLoaded) { 248 LogSupport.log("MimetypesFileTypeMap: !anyLoaded"); 249 MimeTypeFile mf = loadResource("/" + name); 250 if (mf != null) 251 v.addElement(mf); 252 } 253 } 254 255 /** 256 * Load the named file. 257 */ 258 private MimeTypeFile loadFile(String name) { 259 MimeTypeFile mtf = null; 260 261 try { 262 mtf = new MimeTypeFile(name); 263 } catch (IOException e) { 264 // e.printStackTrace(); 265 } 266 return mtf; 267 } 268 269 /** 270 * Construct a MimetypesFileTypeMap with programmatic entries 271 * added from the named file. 272 * 273 * @param mimeTypeFileName the file name 274 * @exception IOException for errors reading the file 275 */ 276 public MimetypesFileTypeMap(String mimeTypeFileName) throws IOException { 277 this(); 278 DB[PROG] = new MimeTypeFile(mimeTypeFileName); 279 } 280 281 /** 282 * Construct a MimetypesFileTypeMap with programmatic entries 283 * added from the InputStream. 284 * 285 * @param is the input stream to read from 286 */ 287 public MimetypesFileTypeMap(InputStream is) { 288 this(); 289 try { 290 DB[PROG] = new MimeTypeFile(is); 291 } catch (IOException ex) { 292 // XXX - really should throw it 293 } 294 } 295 296 /** 297 * Prepend the MIME type values to the registry. 298 * 299 * @param mime_types A .mime.types formatted string of entries. 300 */ 301 public synchronized void addMimeTypes(String mime_types) { 302 // check to see if we have created the registry 303 if (DB[PROG] == null) 304 DB[PROG] = new MimeTypeFile(); // make one 305 306 DB[PROG].appendToRegistry(mime_types); 307 } 308 309 /** 310 * Return the MIME type of the file object. 311 * The implementation in this class calls 312 * {@code getContentType(f.getName())}. 313 * 314 * @param f the file 315 * @return the file's MIME type 316 */ 317 public String getContentType(File f) { 318 return this.getContentType(f.getName()); 319 } 320 321 /** 322 * Return the MIME type based on the specified file name. 323 * The MIME type entries are searched as described above under 324 * <i>MIME types file search order</i>. 325 * If no entry is found, the type "application/octet-stream" is returned. 326 * 327 * @param filename the file name 328 * @return the file's MIME type 329 */ 330 public synchronized String getContentType(String filename) { 331 int dot_pos = filename.lastIndexOf("."); // period index 332 333 if (dot_pos < 0) 334 return defaultType; 335 336 String file_ext = filename.substring(dot_pos + 1); 337 if (file_ext.length() == 0) 338 return defaultType; 339 340 for (int i = 0; i < DB.length; i++) { 341 if (DB[i] == null) 342 continue; 343 String result = DB[i].getMIMETypeString(file_ext); 344 if (result != null) 345 return result; 346 } 347 return defaultType; 348 } 349 350 /** 351 * for debugging... 352 * 353 public static void main(String[] argv) throws Exception { 354 MimetypesFileTypeMap map = new MimetypesFileTypeMap(); 355 System.out.println("File " + argv[0] + " has MIME type " + 356 map.getContentType(argv[0])); 357 System.exit(0); 358 } 359 */ 360 }