1 /* 2 * Copyright (c) 2012, 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.nio.fs; 27 28 import java.io.IOException; 29 import java.nio.charset.Charset; 30 import java.nio.file.Files; 31 import java.nio.file.Path; 32 import java.util.Map; 33 import java.util.HashMap; 34 import java.util.regex.Pattern; 35 import java.util.regex.Matcher; 36 import java.util.List; 37 38 /** 39 * File type detector that uses a file extension to look up its MIME type 40 * by checking the mappings recorded in a mime.types file. 41 */ 42 43 class MimeTypesFileTypeDetector extends AbstractFileTypeDetector { 44 45 private Map<String, String> mimeTypeMap; 46 47 private final Path mimeTypesFile; 48 49 public MimeTypesFileTypeDetector(final Path filePath) { 50 mimeTypesFile = filePath; 51 } 52 53 @Override 54 protected String implProbeContentType(final Path file) { 55 if (file == null || file.getFileName() == null) { 56 return null; 57 } 58 59 final String extension = getExtension(file.getFileName().toString()); 60 if (extension.isEmpty()) { 61 return null; 62 } 63 64 loadMimeTypes(); 65 if (mimeTypeMap == null || mimeTypeMap.isEmpty()) { 66 return null; 67 } 68 69 String mimeType = null; 70 // Case-sensitive search 71 String ext = extension; 72 do { 73 mimeType = mimeTypeMap.get(ext); 74 if (mimeType == null) { 75 ext = getExtension(ext); 76 } 77 } while (mimeType == null && !ext.isEmpty()); 78 79 // Case-insensitive search if no mime type is found 80 if (mimeType == null) { 81 ext = extension; 82 do { 83 for (String key : mimeTypeMap.keySet()) { 84 if (ext.equalsIgnoreCase(key)) { 85 mimeType = mimeTypeMap.get(key); 86 break; 87 } 88 } 89 if (mimeType == null) { 90 ext = getExtension(ext); 91 } 92 } while (mimeType == null && !ext.isEmpty()); 93 } 94 95 return mimeType; 96 } 97 98 // Get the extension of a file name. 99 private static String getExtension(final String name) { 100 String ext = ""; 101 if (name != null && !name.isEmpty()) { 102 int dot = name.indexOf('.'); 103 if ((dot >= 0) && (dot < name.length() - 1)) { 104 ext = name.substring(dot + 1); 105 } 106 } 107 return ext; 108 } 109 110 // Parse the mime types file, and store the type-extension 111 // mappings into mimeTypeMap. 112 private void loadMimeTypes() { 113 if (mimeTypeMap != null || mimeTypesFile == null || 114 !mimeTypesFile.toFile().isFile()) 115 { 116 return; 117 } 118 119 List<String> lines = null; 120 try { 121 lines = Files.readAllLines(mimeTypesFile, Charset.defaultCharset()); 122 } catch (IOException|SecurityException ex) { 123 } 124 125 if (lines != null && !lines.isEmpty()) { 126 mimeTypeMap = new HashMap<>(lines.size()); 127 String command = ""; 128 for (String line : lines) { 129 command += line; 130 if (command.endsWith("\\")) { 131 command = command.substring(0, command.length() - 1); 132 continue; 133 } 134 parseMimeCommand(command); 135 command = ""; 136 } 137 if (!command.isEmpty()) { 138 parseMimeCommand(command); 139 } 140 } 141 } 142 143 /* 144 * Parse a mime-types command, which can have the following formats: 145 * 1) Simple space-delimited format 146 * image/jpeg jpeg jpg jpe JPG 147 * 148 * 2) Netscape key-value format 149 * type=application/x-java-jnlp-file desc="Java Web Start" exts="jnlp" 150 * or 151 * type=text/html exts=htm,html 152 */ 153 private void parseMimeCommand(String command) { 154 command = command.trim(); 155 if (command.isEmpty() || command.charAt(0) == '#') { 156 return; 157 } 158 159 command = command.replaceAll("\\s*#.*", ""); 160 int equalIdx = command.indexOf('='); 161 if (equalIdx > 0) { 162 // Parse a mime-types command having the key-value format 163 final String TypeSign = "type="; 164 String typeRegex = "\\b" + TypeSign + 165 "(\"\\p{Graph}+?/\\p{Graph}+?\"|\\p{Graph}+/\\p{Graph}+\\b)"; 166 Pattern typePattern = Pattern.compile(typeRegex); 167 Matcher typeMatcher = typePattern.matcher(command); 168 169 if (typeMatcher.find()) { 170 String type = typeMatcher.group().substring(TypeSign.length()); 171 if (type.charAt(0) == '"') { 172 type = type.substring(1, type.length() - 1); 173 } 174 175 final String ExtSign = "exts="; 176 String extRegex = "\\b" + ExtSign + 177 "(\"[\\p{Graph}|\\p{Blank}]+?\"|\\p{Graph}+\\b)"; 178 Pattern extPattern = Pattern.compile(extRegex); 179 Matcher extMatcher = extPattern.matcher(command); 180 181 if (extMatcher.find()) { 182 String exts = extMatcher.group().substring(ExtSign.length()); 183 if (exts.charAt(0) == '"') { 184 exts = exts.substring(1, exts.length() - 1); 185 } 186 String[] extList = exts.split("[\\p{Blank}|\\p{Punct}]+"); 187 for (String ext : extList) { 188 putIfAbsent(ext, type); 189 } 190 } 191 } 192 } else { 193 // Parse a mime-types command having the space-delimited format 194 String[] elements = command.split("\\s+"); 195 int i = 1; 196 while (i < elements.length) { 197 putIfAbsent(elements[i++], elements[0]); 198 } 199 } 200 } 201 202 private void putIfAbsent(String key, String value) { 203 if (key != null && !key.isEmpty() && 204 value != null && !value.isEmpty() && 205 !mimeTypeMap.containsKey(key)) 206 { 207 mimeTypeMap.put(key, value); 208 } 209 } 210 }