Path: blob/master/src/java.base/share/classes/sun/net/www/MimeTable.java
41159 views
/*1* Copyright (c) 1994, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.net.www;26import jdk.internal.util.StaticProperty;2728import java.io.*;29import java.net.FileNameMap;30import java.util.Hashtable;31import java.util.Enumeration;32import java.util.Properties;33import java.util.StringTokenizer;3435@SuppressWarnings("removal")36public class MimeTable implements FileNameMap {37/** Keyed by content type, returns MimeEntries */38private Hashtable<String, MimeEntry> entries39= new Hashtable<String, MimeEntry>();4041/** Keyed by file extension (with the .), returns MimeEntries */42private Hashtable<String, MimeEntry> extensionMap43= new Hashtable<String, MimeEntry>();4445// Will be reset if in the platform-specific data file46private static String tempFileTemplate;4748static {49java.security.AccessController.doPrivileged(50new java.security.PrivilegedAction<Void>() {51public Void run() {52tempFileTemplate =53System.getProperty("content.types.temp.file.template",54"/tmp/%s");5556mailcapLocations = new String[] {57System.getProperty("user.mailcap"),58StaticProperty.userHome() + "/.mailcap",59"/etc/mailcap",60"/usr/etc/mailcap",61"/usr/local/etc/mailcap",62};63return null;64}65});66}676869private static final String filePreamble = "sun.net.www MIME content-types table";70private static final String fileMagic = "#" + filePreamble;7172MimeTable() {73load();74}7576private static class DefaultInstanceHolder {77static final MimeTable defaultInstance = getDefaultInstance();7879static MimeTable getDefaultInstance() {80return java.security.AccessController.doPrivileged(81new java.security.PrivilegedAction<MimeTable>() {82public MimeTable run() {83MimeTable instance = new MimeTable();84URLConnection.setFileNameMap(instance);85return instance;86}87});88}89}9091/**92* Get the single instance of this class. First use will load the93* table from a data file.94*/95public static MimeTable getDefaultTable() {96return DefaultInstanceHolder.defaultInstance;97}9899/**100*101*/102public static FileNameMap loadTable() {103MimeTable mt = getDefaultTable();104return (FileNameMap)mt;105}106107public synchronized int getSize() {108return entries.size();109}110111public synchronized String getContentTypeFor(String fileName) {112MimeEntry entry = findByFileName(fileName);113if (entry != null) {114return entry.getType();115} else {116return null;117}118}119120public synchronized void add(MimeEntry m) {121entries.put(m.getType(), m);122123String exts[] = m.getExtensions();124if (exts == null) {125return;126}127128for (int i = 0; i < exts.length; i++) {129extensionMap.put(exts[i], m);130}131}132133public synchronized MimeEntry remove(String type) {134MimeEntry entry = entries.get(type);135return remove(entry);136}137138public synchronized MimeEntry remove(MimeEntry entry) {139String[] extensionKeys = entry.getExtensions();140if (extensionKeys != null) {141for (int i = 0; i < extensionKeys.length; i++) {142extensionMap.remove(extensionKeys[i]);143}144}145146return entries.remove(entry.getType());147}148149public synchronized MimeEntry find(String type) {150MimeEntry entry = entries.get(type);151if (entry == null) {152// try a wildcard lookup153Enumeration<MimeEntry> e = entries.elements();154while (e.hasMoreElements()) {155MimeEntry wild = e.nextElement();156if (wild.matches(type)) {157return wild;158}159}160}161162return entry;163}164165/**166* Locate a MimeEntry by the file extension that has been associated167* with it. Parses general file names, and URLs.168*/169public MimeEntry findByFileName(String fname) {170String ext = "";171172int i = fname.lastIndexOf('#');173174if (i > 0) {175fname = fname.substring(0, i - 1);176}177178i = fname.lastIndexOf('.');179// REMIND: OS specific delimters appear here180i = Math.max(i, fname.lastIndexOf('/'));181i = Math.max(i, fname.lastIndexOf('?'));182183if (i != -1 && fname.charAt(i) == '.') {184ext = fname.substring(i).toLowerCase();185}186187return findByExt(ext);188}189190/**191* Locate a MimeEntry by the file extension that has been associated192* with it.193*/194public synchronized MimeEntry findByExt(String fileExtension) {195return extensionMap.get(fileExtension);196}197198public synchronized MimeEntry findByDescription(String description) {199Enumeration<MimeEntry> e = elements();200while (e.hasMoreElements()) {201MimeEntry entry = e.nextElement();202if (description.equals(entry.getDescription())) {203return entry;204}205}206207// We failed, now try treating description as type208return find(description);209}210211String getTempFileTemplate() {212return tempFileTemplate;213}214215public synchronized Enumeration<MimeEntry> elements() {216return entries.elements();217}218219// For backward compatibility -- mailcap format files220// This is not currently used, but may in the future when we add ability221// to read BOTH the properties format and the mailcap format.222protected static String[] mailcapLocations;223224public synchronized void load() {225Properties entries = new Properties();226File file = null;227InputStream in;228229// First try to load the user-specific table, if it exists230String userTablePath = System.getProperty("content.types.user.table");231if (userTablePath != null && (file = new File(userTablePath)).exists()) {232try {233in = new FileInputStream(file);234} catch (FileNotFoundException e) {235System.err.println("Warning: " + file.getPath()236+ " mime table not found.");237return;238}239} else {240in = MimeTable.class.getResourceAsStream("content-types.properties");241if (in == null)242throw new InternalError("default mime table not found");243}244245try (BufferedInputStream bin = new BufferedInputStream(in)) {246entries.load(bin);247} catch (IOException e) {248System.err.println("Warning: " + e.getMessage());249}250parse(entries);251}252253void parse(Properties entries) {254// first, strip out the platform-specific temp file template255String tempFileTemplate = (String)entries.get("temp.file.template");256if (tempFileTemplate != null) {257entries.remove("temp.file.template");258MimeTable.tempFileTemplate = tempFileTemplate;259}260261// now, parse the mime-type spec's262Enumeration<?> types = entries.propertyNames();263while (types.hasMoreElements()) {264String type = (String)types.nextElement();265String attrs = entries.getProperty(type);266parse(type, attrs);267}268}269270//271// Table format:272//273// <entry> ::= <table_tag> | <type_entry>274//275// <table_tag> ::= <table_format_version> | <temp_file_template>276//277// <type_entry> ::= <type_subtype_pair> '=' <type_attrs_list>278//279// <type_subtype_pair> ::= <type> '/' <subtype>280//281// <type_attrs_list> ::= <attr_value_pair> [ ';' <attr_value_pair> ]*282// | [ <attr_value_pair> ]+283//284// <attr_value_pair> ::= <attr_name> '=' <attr_value>285//286// <attr_name> ::= 'description' | 'action' | 'application'287// | 'file_extensions' | 'icon'288//289// <attr_value> ::= <legal_char>*290//291// Embedded ';' in an <attr_value> are quoted with leading '\' .292//293// Interpretation of <attr_value> depends on the <attr_name> it is294// associated with.295//296297void parse(String type, String attrs) {298MimeEntry newEntry = new MimeEntry(type);299300// REMIND handle embedded ';' and '|' and literal '"'301StringTokenizer tokenizer = new StringTokenizer(attrs, ";");302while (tokenizer.hasMoreTokens()) {303String pair = tokenizer.nextToken();304parse(pair, newEntry);305}306307add(newEntry);308}309310void parse(String pair, MimeEntry entry) {311// REMIND add exception handling...312String name = null;313String value = null;314315boolean gotName = false;316StringTokenizer tokenizer = new StringTokenizer(pair, "=");317while (tokenizer.hasMoreTokens()) {318if (gotName) {319value = tokenizer.nextToken().trim();320}321else {322name = tokenizer.nextToken().trim();323gotName = true;324}325}326327fill(entry, name, value);328}329330void fill(MimeEntry entry, String name, String value) {331if ("description".equalsIgnoreCase(name)) {332entry.setDescription(value);333}334else if ("action".equalsIgnoreCase(name)) {335entry.setAction(getActionCode(value));336}337else if ("application".equalsIgnoreCase(name)) {338entry.setCommand(value);339}340else if ("icon".equalsIgnoreCase(name)) {341entry.setImageFileName(value);342}343else if ("file_extensions".equalsIgnoreCase(name)) {344entry.setExtensions(value);345}346347// else illegal name exception348}349350String[] getExtensions(String list) {351StringTokenizer tokenizer = new StringTokenizer(list, ",");352int n = tokenizer.countTokens();353String[] extensions = new String[n];354for (int i = 0; i < n; i++) {355extensions[i] = tokenizer.nextToken();356}357358return extensions;359}360361int getActionCode(String action) {362for (int i = 0; i < MimeEntry.actionKeywords.length; i++) {363if (action.equalsIgnoreCase(MimeEntry.actionKeywords[i])) {364return i;365}366}367368return MimeEntry.UNKNOWN;369}370371public Properties getAsProperties() {372Properties properties = new Properties();373Enumeration<MimeEntry> e = elements();374while (e.hasMoreElements()) {375MimeEntry entry = e.nextElement();376properties.put(entry.getType(), entry.toProperty());377}378379return properties;380}381382protected boolean saveAsProperties(File file) {383FileOutputStream os = null;384try {385os = new FileOutputStream(file);386Properties properties = getAsProperties();387properties.put("temp.file.template", tempFileTemplate);388String tag;389// Perform the property security check for user.name390SecurityManager sm = System.getSecurityManager();391if (sm != null) {392sm.checkPropertyAccess("user.name");393}394String user = StaticProperty.userName();395if (user != null) {396tag = "; customized for " + user;397properties.store(os, filePreamble + tag);398}399else {400properties.store(os, filePreamble);401}402}403catch (IOException e) {404e.printStackTrace();405return false;406}407finally {408if (os != null) {409try { os.close(); } catch (IOException e) {}410}411}412413return true;414}415/*416* Debugging utilities417*418public void list(PrintStream out) {419Enumeration keys = entries.keys();420while (keys.hasMoreElements()) {421String key = (String)keys.nextElement();422MimeEntry entry = (MimeEntry)entries.get(key);423out.println(key + ": " + entry);424}425}426427public static void main(String[] args) {428MimeTable testTable = MimeTable.getDefaultTable();429430Enumeration e = testTable.elements();431while (e.hasMoreElements()) {432MimeEntry entry = (MimeEntry)e.nextElement();433System.out.println(entry);434}435436testTable.save(File.separator + "tmp" +437File.separator + "mime_table.save");438}439*/440}441442443