1 /* 2 * Copyright (c) 1994, 1999, 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 sun.net.www; 27 import java.io.*; 28 import java.util.Calendar; 29 import java.util.Date; 30 import java.text.SimpleDateFormat; 31 import java.net.URL; 32 import java.net.FileNameMap; 33 import java.util.Hashtable; 34 import java.util.Enumeration; 35 import java.util.Properties; 36 import java.util.StringTokenizer; 37 38 public class MimeTable implements FileNameMap { 39 /** Keyed by content type, returns MimeEntries */ 40 private Hashtable<String, MimeEntry> entries 41 = new Hashtable<String, MimeEntry>(); 42 43 /** Keyed by file extension (with the .), returns MimeEntries */ 44 private Hashtable<String, MimeEntry> extensionMap 45 = new Hashtable<String, MimeEntry>(); 46 47 // Will be reset if in the platform-specific data file 48 private static String tempFileTemplate; 49 50 static { 51 java.security.AccessController.doPrivileged( 52 new java.security.PrivilegedAction<Void>() { 53 public Void run() { 54 tempFileTemplate = 55 System.getProperty("content.types.temp.file.template", 56 "/tmp/%s"); 57 58 mailcapLocations = new String[] { 59 System.getProperty("user.mailcap"), 60 System.getProperty("user.home") + "/.mailcap", 61 "/etc/mailcap", 62 "/usr/etc/mailcap", 63 "/usr/local/etc/mailcap", 64 System.getProperty("hotjava.home", 65 "/usr/local/hotjava") 66 + "/lib/mailcap", 67 }; 68 return null; 69 } 70 }); 71 } 72 73 74 private static final String filePreamble = "sun.net.www MIME content-types table"; 75 private static final String fileMagic = "#" + filePreamble; 76 private static MimeTable defaultInstance = null; 77 78 MimeTable() { 79 load(); 80 } 81 82 /** 83 * Get the single instance of this class. First use will load the 84 * table from a data file. 85 */ 86 public static MimeTable getDefaultTable() { 87 if (defaultInstance == null) { 88 java.security.AccessController.doPrivileged( 89 new java.security.PrivilegedAction<Void>() { 90 public Void run() { 91 defaultInstance = new MimeTable(); 92 URLConnection.setFileNameMap(defaultInstance); 93 return null; 94 } 95 }); 96 } 97 98 return defaultInstance; 99 } 100 101 /** 102 * 103 */ 104 public static FileNameMap loadTable() { 105 MimeTable mt = getDefaultTable(); 106 return (FileNameMap)mt; 107 } 108 109 public synchronized int getSize() { 110 return entries.size(); 111 } 112 113 public synchronized String getContentTypeFor(String fileName) { 114 MimeEntry entry = findByFileName(fileName); 115 if (entry != null) { 116 return entry.getType(); 117 } else { 118 return null; 119 } 120 } 121 122 public synchronized void add(MimeEntry m) { 123 entries.put(m.getType(), m); 124 125 String exts[] = m.getExtensions(); 126 if (exts == null) { 127 return; 128 } 129 130 for (int i = 0; i < exts.length; i++) { 131 extensionMap.put(exts[i], m); 132 } 133 } 134 135 public synchronized MimeEntry remove(String type) { 136 MimeEntry entry = entries.get(type); 137 return remove(entry); 138 } 139 140 public synchronized MimeEntry remove(MimeEntry entry) { 141 String[] extensionKeys = entry.getExtensions(); 142 if (extensionKeys != null) { 143 for (int i = 0; i < extensionKeys.length; i++) { 144 extensionMap.remove(extensionKeys[i]); 145 } 146 } 147 148 return entries.remove(entry.getType()); 149 } 150 151 public synchronized MimeEntry find(String type) { 152 MimeEntry entry = entries.get(type); 153 if (entry == null) { 154 // try a wildcard lookup 155 Enumeration<MimeEntry> e = entries.elements(); 156 while (e.hasMoreElements()) { 157 MimeEntry wild = e.nextElement(); 158 if (wild.matches(type)) { 159 return wild; 160 } 161 } 162 } 163 164 return entry; 165 } 166 167 /** 168 * Locate a MimeEntry by the file extension that has been associated 169 * with it. Parses general file names, and URLs. 170 */ 171 public MimeEntry findByFileName(String fname) { 172 String ext = ""; 173 174 int i = fname.lastIndexOf('#'); 175 176 if (i > 0) { 177 fname = fname.substring(0, i - 1); 178 } 179 180 i = fname.lastIndexOf('.'); 181 // REMIND: OS specific delimters appear here 182 i = Math.max(i, fname.lastIndexOf('/')); 183 i = Math.max(i, fname.lastIndexOf('?')); 184 185 if (i != -1 && fname.charAt(i) == '.') { 186 ext = fname.substring(i).toLowerCase(); 187 } 188 189 return findByExt(ext); 190 } 191 192 /** 193 * Locate a MimeEntry by the file extension that has been associated 194 * with it. 195 */ 196 public synchronized MimeEntry findByExt(String fileExtension) { 197 return extensionMap.get(fileExtension); 198 } 199 200 public synchronized MimeEntry findByDescription(String description) { 201 Enumeration<MimeEntry> e = elements(); 202 while (e.hasMoreElements()) { 203 MimeEntry entry = e.nextElement(); 204 if (description.equals(entry.getDescription())) { 205 return entry; 206 } 207 } 208 209 // We failed, now try treating description as type 210 return find(description); 211 } 212 213 String getTempFileTemplate() { 214 return tempFileTemplate; 215 } 216 217 public synchronized Enumeration<MimeEntry> elements() { 218 return entries.elements(); 219 } 220 221 // For backward compatibility -- mailcap format files 222 // This is not currently used, but may in the future when we add ability 223 // to read BOTH the properties format and the mailcap format. 224 protected static String[] mailcapLocations; 225 226 public synchronized void load() { 227 Properties entries = new Properties(); 228 File file = null; 229 try { 230 InputStream is; 231 // First try to load the user-specific table, if it exists 232 String userTablePath = 233 System.getProperty("content.types.user.table"); 234 if (userTablePath != null) { 235 file = new File(userTablePath); 236 if (!file.exists()) { 237 // No user-table, try to load the default built-in table. 238 file = new File(System.getProperty("java.home") + 239 File.separator + 240 "lib" + 241 File.separator + 242 "content-types.properties"); 243 } 244 } 245 else { 246 // No user table, try to load the default built-in table. 247 file = new File(System.getProperty("java.home") + 248 File.separator + 249 "lib" + 250 File.separator + 251 "content-types.properties"); 252 } 253 254 is = new BufferedInputStream(new FileInputStream(file)); 255 entries.load(is); 256 is.close(); 257 } 258 catch (IOException e) { 259 System.err.println("Warning: default mime table not found: " + 260 file.getPath()); 261 return; 262 } 263 parse(entries); 264 } 265 266 void parse(Properties entries) { 267 // first, strip out the platform-specific temp file template 268 String tempFileTemplate = (String)entries.get("temp.file.template"); 269 if (tempFileTemplate != null) { 270 entries.remove("temp.file.template"); 271 this.tempFileTemplate = tempFileTemplate; 272 } 273 274 // now, parse the mime-type spec's 275 Enumeration<?> types = entries.propertyNames(); 276 while (types.hasMoreElements()) { 277 String type = (String)types.nextElement(); 278 String attrs = entries.getProperty(type); 279 parse(type, attrs); 280 } 281 } 282 283 // 284 // Table format: 285 // 286 // <entry> ::= <table_tag> | <type_entry> 287 // 288 // <table_tag> ::= <table_format_version> | <temp_file_template> 289 // 290 // <type_entry> ::= <type_subtype_pair> '=' <type_attrs_list> 291 // 292 // <type_subtype_pair> ::= <type> '/' <subtype> 293 // 294 // <type_attrs_list> ::= <attr_value_pair> [ ';' <attr_value_pair> ]* 295 // | [ <attr_value_pair> ]+ 296 // 297 // <attr_value_pair> ::= <attr_name> '=' <attr_value> 298 // 299 // <attr_name> ::= 'description' | 'action' | 'application' 300 // | 'file_extensions' | 'icon' 301 // 302 // <attr_value> ::= <legal_char>* 303 // 304 // Embedded ';' in an <attr_value> are quoted with leading '\' . 305 // 306 // Interpretation of <attr_value> depends on the <attr_name> it is 307 // associated with. 308 // 309 310 void parse(String type, String attrs) { 311 MimeEntry newEntry = new MimeEntry(type); 312 313 // REMIND handle embedded ';' and '|' and literal '"' 314 StringTokenizer tokenizer = new StringTokenizer(attrs, ";"); 315 while (tokenizer.hasMoreTokens()) { 316 String pair = tokenizer.nextToken(); 317 parse(pair, newEntry); 318 } 319 320 add(newEntry); 321 } 322 323 void parse(String pair, MimeEntry entry) { 324 // REMIND add exception handling... 325 String name = null; 326 String value = null; 327 328 boolean gotName = false; 329 StringTokenizer tokenizer = new StringTokenizer(pair, "="); 330 while (tokenizer.hasMoreTokens()) { 331 if (gotName) { 332 value = tokenizer.nextToken().trim(); 333 } 334 else { 335 name = tokenizer.nextToken().trim(); 336 gotName = true; 337 } 338 } 339 340 fill(entry, name, value); 341 } 342 343 void fill(MimeEntry entry, String name, String value) { 344 if ("description".equalsIgnoreCase(name)) { 345 entry.setDescription(value); 346 } 347 else if ("action".equalsIgnoreCase(name)) { 348 entry.setAction(getActionCode(value)); 349 } 350 else if ("application".equalsIgnoreCase(name)) { 351 entry.setCommand(value); 352 } 353 else if ("icon".equalsIgnoreCase(name)) { 354 entry.setImageFileName(value); 355 } 356 else if ("file_extensions".equalsIgnoreCase(name)) { 357 entry.setExtensions(value); 358 } 359 360 // else illegal name exception 361 } 362 363 String[] getExtensions(String list) { 364 StringTokenizer tokenizer = new StringTokenizer(list, ","); 365 int n = tokenizer.countTokens(); 366 String[] extensions = new String[n]; 367 for (int i = 0; i < n; i++) { 368 extensions[i] = tokenizer.nextToken(); 369 } 370 371 return extensions; 372 } 373 374 int getActionCode(String action) { 375 for (int i = 0; i < MimeEntry.actionKeywords.length; i++) { 376 if (action.equalsIgnoreCase(MimeEntry.actionKeywords[i])) { 377 return i; 378 } 379 } 380 381 return MimeEntry.UNKNOWN; 382 } 383 384 public synchronized boolean save(String filename) { 385 if (filename == null) { 386 filename = System.getProperty("user.home" + 387 File.separator + 388 "lib" + 389 File.separator + 390 "content-types.properties"); 391 } 392 393 return saveAsProperties(new File(filename)); 394 } 395 396 public Properties getAsProperties() { 397 Properties properties = new Properties(); 398 Enumeration<MimeEntry> e = elements(); 399 while (e.hasMoreElements()) { 400 MimeEntry entry = e.nextElement(); 401 properties.put(entry.getType(), entry.toProperty()); 402 } 403 404 return properties; 405 } 406 407 protected boolean saveAsProperties(File file) { 408 FileOutputStream os = null; 409 try { 410 os = new FileOutputStream(file); 411 Properties properties = getAsProperties(); 412 properties.put("temp.file.template", tempFileTemplate); 413 String tag; 414 String user = System.getProperty("user.name"); 415 if (user != null) { 416 tag = "; customized for " + user; 417 properties.save(os, filePreamble + tag); 418 } 419 else { 420 properties.save(os, filePreamble); 421 } 422 } 423 catch (IOException e) { 424 e.printStackTrace(); 425 return false; 426 } 427 finally { 428 if (os != null) { 429 try { os.close(); } catch (IOException e) {} 430 } 431 } 432 433 return true; 434 } 435 /* 436 * Debugging utilities 437 * 438 public void list(PrintStream out) { 439 Enumeration keys = entries.keys(); 440 while (keys.hasMoreElements()) { 441 String key = (String)keys.nextElement(); 442 MimeEntry entry = (MimeEntry)entries.get(key); 443 out.println(key + ": " + entry); 444 } 445 } 446 447 public static void main(String[] args) { 448 MimeTable testTable = MimeTable.getDefaultTable(); 449 450 Enumeration e = testTable.elements(); 451 while (e.hasMoreElements()) { 452 MimeEntry entry = (MimeEntry)e.nextElement(); 453 System.out.println(entry); 454 } 455 456 testTable.save(File.separator + "tmp" + 457 File.separator + "mime_table.save"); 458 } 459 */ 460 }