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 <i>java.home</i>{@code /}<i>conf</i>{@code /mime.types}. 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 * (Where <i>java.home</i> is the value of the "java.home" System property 55 * and <i>conf</i> is the directory named "conf" if it exists, 56 * otherwise the directory named "lib"; the "conf" directory was 57 * introduced in JDK 1.9.) 58 * <p> 59 * <b>MIME types file format:</b> 60 * 61 * <pre>{@code 62 * # comments begin with a '#' 63 * # the format is <mime type> <space separated file extensions> 64 * # for example: 65 * text/plain txt text TXT 66 * # this would map file.txt, file.text, and file.TXT to 67 * # the mime type "text/plain" 68 * }</pre> 69 * 70 * @author Bart Calder 71 * @author Bill Shannon 72 * 73 * @since 1.6 74 */ 75 public class MimetypesFileTypeMap extends FileTypeMap { 76 /* 77 * We manage a collection of databases, searched in order. 78 */ 79 private MimeTypeFile[] DB; 80 private static final int PROG = 0; // programmatically added entries 81 82 private static final String defaultType = "application/octet-stream"; 83 84 private static final String confDir; 85 86 static { 87 String dir = null; 88 try { 89 dir = (String)AccessController.doPrivileged( 90 new PrivilegedAction() { 91 public Object run() { 92 String home = System.getProperty("java.home"); 93 String newdir = home + File.separator + "conf"; 94 File conf = new File(newdir); 95 if (conf.exists()) 96 return newdir + File.separator; 97 else 98 return home + File.separator + "lib" + File.separator; 99 } 100 }); 101 } catch (Exception ex) { 102 // ignore any exceptions 103 } 104 confDir = dir; 105 } 106 107 /** 108 * The default constructor. 109 */ 110 public MimetypesFileTypeMap() { 111 Vector dbv = new Vector(5); // usually 5 or less databases 112 MimeTypeFile mf = null; 113 dbv.addElement(null); // place holder for PROG entry 114 115 LogSupport.log("MimetypesFileTypeMap: load HOME"); 116 try { 117 String user_home = System.getProperty("user.home"); 118 119 if (user_home != null) { 120 String path = user_home + File.separator + ".mime.types"; 121 mf = loadFile(path); 122 if (mf != null) 123 dbv.addElement(mf); 124 } 125 } catch (SecurityException ex) {} 126 127 LogSupport.log("MimetypesFileTypeMap: load SYS"); 128 try { 129 // check system's home 130 if (confDir != null) { 131 mf = loadFile(confDir + "mime.types"); 132 if (mf != null) 133 dbv.addElement(mf); 134 } 135 } catch (SecurityException ex) {} 136 137 LogSupport.log("MimetypesFileTypeMap: load JAR"); 138 // load from the app's jar file 139 loadAllResources(dbv, "META-INF/mime.types"); 140 141 LogSupport.log("MimetypesFileTypeMap: load DEF"); 142 mf = loadResource("/META-INF/mimetypes.default"); 143 144 if (mf != null) 145 dbv.addElement(mf); 146 147 DB = new MimeTypeFile[dbv.size()]; 148 dbv.copyInto(DB); 149 } 150 151 /** 152 * Load from the named resource. 153 */ 154 private MimeTypeFile loadResource(String name) { 155 InputStream clis = null; 156 try { 157 clis = SecuritySupport.getResourceAsStream(this.getClass(), name); 158 if (clis != null) { 159 MimeTypeFile mf = new MimeTypeFile(clis); 160 if (LogSupport.isLoggable()) 161 LogSupport.log("MimetypesFileTypeMap: successfully " + 162 "loaded mime types file: " + name); 163 return mf; 164 } else { 165 if (LogSupport.isLoggable()) 166 LogSupport.log("MimetypesFileTypeMap: not loading " + 167 "mime types file: " + name); 168 } 169 } catch (IOException e) { 170 if (LogSupport.isLoggable()) 171 LogSupport.log("MimetypesFileTypeMap: can't load " + name, e); 172 } catch (SecurityException sex) { 173 if (LogSupport.isLoggable()) 174 LogSupport.log("MimetypesFileTypeMap: can't load " + name, sex); 175 } finally { 176 try { 177 if (clis != null) 178 clis.close(); 179 } catch (IOException ex) { } // ignore it 180 } 181 return null; 182 } 183 184 /** 185 * Load all of the named resource. 186 */ 187 private void loadAllResources(Vector v, String name) { 188 boolean anyLoaded = false; 189 try { 190 URL[] urls; 191 ClassLoader cld = null; 192 // First try the "application's" class loader. 193 cld = SecuritySupport.getContextClassLoader(); 194 if (cld == null) 195 cld = this.getClass().getClassLoader(); 196 if (cld != null) 197 urls = SecuritySupport.getResources(cld, name); 198 else 199 urls = SecuritySupport.getSystemResources(name); 200 if (urls != null) { 201 if (LogSupport.isLoggable()) 202 LogSupport.log("MimetypesFileTypeMap: getResources"); 203 for (int i = 0; i < urls.length; i++) { 204 URL url = urls[i]; 205 InputStream clis = null; 206 if (LogSupport.isLoggable()) 207 LogSupport.log("MimetypesFileTypeMap: URL " + url); 208 try { 209 clis = SecuritySupport.openStream(url); 210 if (clis != null) { 211 v.addElement(new MimeTypeFile(clis)); 212 anyLoaded = true; 213 if (LogSupport.isLoggable()) 214 LogSupport.log("MimetypesFileTypeMap: " + 215 "successfully loaded " + 216 "mime types from URL: " + url); 217 } else { 218 if (LogSupport.isLoggable()) 219 LogSupport.log("MimetypesFileTypeMap: " + 220 "not loading " + 221 "mime types from URL: " + url); 222 } 223 } catch (IOException ioex) { 224 if (LogSupport.isLoggable()) 225 LogSupport.log("MimetypesFileTypeMap: can't load " + 226 url, ioex); 227 } catch (SecurityException sex) { 228 if (LogSupport.isLoggable()) 229 LogSupport.log("MimetypesFileTypeMap: can't load " + 230 url, sex); 231 } finally { 232 try { 233 if (clis != null) 234 clis.close(); 235 } catch (IOException cex) { } 236 } 237 } 238 } 239 } catch (Exception ex) { 240 if (LogSupport.isLoggable()) 241 LogSupport.log("MimetypesFileTypeMap: can't load " + name, ex); 242 } 243 244 // if failed to load anything, fall back to old technique, just in case 245 if (!anyLoaded) { 246 LogSupport.log("MimetypesFileTypeMap: !anyLoaded"); 247 MimeTypeFile mf = loadResource("/" + name); 248 if (mf != null) 249 v.addElement(mf); 250 } 251 } 252 253 /** 254 * Load the named file. 255 */ 256 private MimeTypeFile loadFile(String name) { 257 MimeTypeFile mtf = null; 258 259 try { 260 mtf = new MimeTypeFile(name); 261 } catch (IOException e) { 262 // e.printStackTrace(); 263 } 264 return mtf; 265 } 266 267 /** 268 * Construct a MimetypesFileTypeMap with programmatic entries 269 * added from the named file. 270 * 271 * @param mimeTypeFileName the file name 272 * @exception IOException for errors reading the file 273 */ 274 public MimetypesFileTypeMap(String mimeTypeFileName) throws IOException { 275 this(); 276 DB[PROG] = new MimeTypeFile(mimeTypeFileName); 277 } 278 279 /** 280 * Construct a MimetypesFileTypeMap with programmatic entries 281 * added from the InputStream. 282 * 283 * @param is the input stream to read from 284 */ 285 public MimetypesFileTypeMap(InputStream is) { 286 this(); 287 try { 288 DB[PROG] = new MimeTypeFile(is); 289 } catch (IOException ex) { 290 // XXX - really should throw it 291 } 292 } 293 294 /** 295 * Prepend the MIME type values to the registry. 296 * 297 * @param mime_types A .mime.types formatted string of entries. 298 */ 299 public synchronized void addMimeTypes(String mime_types) { 300 // check to see if we have created the registry 301 if (DB[PROG] == null) 302 DB[PROG] = new MimeTypeFile(); // make one 303 304 DB[PROG].appendToRegistry(mime_types); 305 } 306 307 /** 308 * Return the MIME type of the file object. 309 * The implementation in this class calls 310 * {@code getContentType(f.getName())}. 311 * 312 * @param f the file 313 * @return the file's MIME type 314 */ 315 public String getContentType(File f) { 316 return this.getContentType(f.getName()); 317 } 318 319 /** 320 * Return the MIME type based on the specified file name. 321 * The MIME type entries are searched as described above under 322 * <i>MIME types file search order</i>. 323 * If no entry is found, the type "application/octet-stream" is returned. 324 * 325 * @param filename the file name 326 * @return the file's MIME type 327 */ 328 public synchronized String getContentType(String filename) { 329 int dot_pos = filename.lastIndexOf("."); // period index 330 331 if (dot_pos < 0) 332 return defaultType; 333 334 String file_ext = filename.substring(dot_pos + 1); 335 if (file_ext.length() == 0) 336 return defaultType; 337 338 for (int i = 0; i < DB.length; i++) { 339 if (DB[i] == null) 340 continue; 341 String result = DB[i].getMIMETypeString(file_ext); 342 if (result != null) 343 return result; 344 } 345 return defaultType; 346 } 347 348 /** 349 * for debugging... 350 * 351 public static void main(String[] argv) throws Exception { 352 MimetypesFileTypeMap map = new MimetypesFileTypeMap(); 353 System.out.println("File " + argv[0] + " has MIME type " + 354 map.getContentType(argv[0])); 355 System.exit(0); 356 } 357 */ 358 }