1 /*
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3  *
4  * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved.
5  *
6  * The contents of this file are subject to the terms of either the GNU
7  * General Public License Version 2 only ("GPL") or the Common Development
8  * and Distribution License("CDDL") (collectively, the "License").  You
9  * may not use this file except in compliance with the License.  You can
10  * obtain a copy of the License at
11  * https://oss.oracle.com/licenses/CDDL+GPL-1.1
12  * or LICENSE.txt.  See the License for the specific
13  * language governing permissions and limitations under the License.
14  *
15  * When distributing the software, include this License Header Notice in each
16  * file and include the License file at LICENSE.txt.
17  *
18  * GPL Classpath Exception:
19  * Oracle designates this particular file as subject to the "Classpath"
20  * exception as provided by Oracle in the GPL Version 2 section of the License
21  * file that accompanied this code.
22  *
23  * Modifications:
24  * If applicable, add the following below the License Header, with the fields
25  * enclosed by brackets [] replaced by your own identifying information:
26  * "Portions Copyright [year] [name of copyright owner]"
27  *
28  * Contributor(s):
29  * If you wish your version of this file to be governed by only the CDDL or
30  * only the GPL Version 2, indicate your decision by adding "[Contributor]
31  * elects to include this software in this distribution under the [CDDL or GPL
32  * Version 2] license."  If you don't indicate a single choice of license, a
33  * recipient has the option to distribute your version of this file under
34  * either the CDDL, the GPL Version 2 or to extend the choice of license to
35  * its licensees as provided above.  However, if you add GPL Version 2 code
36  * and therefore, elected the GPL Version 2 license, then the option applies
37  * only if the new code is made subject to such option by the copyright
38  * holder.
39  */
40 
41 package com.sun.activation.registries;
42 
43 import java.io.*;
44 import java.util.*;
45 
46 public class MimeTypeFile {
47     private String fname = null;
48     private Hashtable type_hash = new Hashtable();
49 
50     /**
51      * The construtor that takes a filename as an argument.
52      *
53      * @param new_fname The file name of the mime types file.
54      */
MimeTypeFile(String new_fname)55     public MimeTypeFile(String new_fname) throws IOException {
56 	File mime_file = null;
57 	FileReader fr = null;
58 
59 	fname = new_fname; // remember the file name
60 
61 	mime_file = new File(fname); // get a file object
62 
63 	fr = new FileReader(mime_file);
64 
65 	try {
66 	    parse(new BufferedReader(fr));
67 	} finally {
68 	    try {
69 		fr.close(); // close it
70 	    } catch (IOException e) {
71 		// ignore it
72 	    }
73 	}
74     }
75 
MimeTypeFile(InputStream is)76     public MimeTypeFile(InputStream is) throws IOException {
77 	parse(new BufferedReader(new InputStreamReader(is, "iso-8859-1")));
78     }
79 
80     /**
81      * Creates an empty DB.
82      */
MimeTypeFile()83     public MimeTypeFile() {
84     }
85 
86     /**
87      * get the MimeTypeEntry based on the file extension
88      */
getMimeTypeEntry(String file_ext)89     public MimeTypeEntry getMimeTypeEntry(String file_ext) {
90 	return (MimeTypeEntry)type_hash.get((Object)file_ext);
91     }
92 
93     /**
94      * Get the MIME type string corresponding to the file extension.
95      */
getMIMETypeString(String file_ext)96     public String getMIMETypeString(String file_ext) {
97 	MimeTypeEntry entry = this.getMimeTypeEntry(file_ext);
98 
99 	if (entry != null)
100 	    return entry.getMIMEType();
101 	else
102 	    return null;
103     }
104 
105     /**
106      * Appends string of entries to the types registry, must be valid
107      * .mime.types format.
108      * A mime.types entry is one of two forms:
109      *
110      *	type/subtype	ext1 ext2 ...
111      * or
112      *	type=type/subtype desc="description of type" exts=ext1,ext2,...
113      *
114      * Example:
115      * # this is a test
116      * audio/basic            au
117      * text/plain             txt text
118      * type=application/postscript exts=ps,eps
119      */
appendToRegistry(String mime_types)120     public void appendToRegistry(String mime_types) {
121 	try {
122 	    parse(new BufferedReader(new StringReader(mime_types)));
123 	} catch (IOException ex) {
124 	    // can't happen
125 	}
126     }
127 
128     /**
129      * Parse a stream of mime.types entries.
130      */
parse(BufferedReader buf_reader)131     private void parse(BufferedReader buf_reader) throws IOException {
132 	String line = null, prev = null;
133 
134 	while ((line = buf_reader.readLine()) != null) {
135 	    if (prev == null)
136 		prev = line;
137 	    else
138 		prev += line;
139 	    int end = prev.length();
140 	    if (prev.length() > 0 && prev.charAt(end - 1) == '\\') {
141 		prev = prev.substring(0, end - 1);
142 		continue;
143 	    }
144 	    this.parseEntry(prev);
145 	    prev = null;
146 	}
147 	if (prev != null)
148 	    this.parseEntry(prev);
149     }
150 
151     /**
152      * Parse single mime.types entry.
153      */
parseEntry(String line)154     private void parseEntry(String line) {
155 	String mime_type = null;
156 	String file_ext = null;
157 	line = line.trim();
158 
159 	if (line.length() == 0) // empty line...
160 	    return; // BAIL!
161 
162 	// check to see if this is a comment line?
163 	if (line.charAt(0) == '#')
164 	    return; // then we are done!
165 
166 	// is it a new format line or old format?
167 	if (line.indexOf('=') > 0) {
168 	    // new format
169 	    LineTokenizer lt = new LineTokenizer(line);
170 	    while (lt.hasMoreTokens()) {
171 		String name = lt.nextToken();
172 		String value = null;
173 		if (lt.hasMoreTokens() && lt.nextToken().equals("=") &&
174 							lt.hasMoreTokens())
175 		    value = lt.nextToken();
176 		if (value == null) {
177 		    if (LogSupport.isLoggable())
178 			LogSupport.log("Bad .mime.types entry: " + line);
179 		    return;
180 		}
181 		if (name.equals("type"))
182 		    mime_type = value;
183 		else if (name.equals("exts")) {
184 		    StringTokenizer st = new StringTokenizer(value, ",");
185 		    while (st.hasMoreTokens()) {
186 			file_ext = st.nextToken();
187 			MimeTypeEntry entry =
188 				new MimeTypeEntry(mime_type, file_ext);
189 			type_hash.put(file_ext, entry);
190 			if (LogSupport.isLoggable())
191 			    LogSupport.log("Added: " + entry.toString());
192 		    }
193 		}
194 	    }
195 	} else {
196 	    // old format
197 	    // count the tokens
198 	    StringTokenizer strtok = new StringTokenizer(line);
199 	    int num_tok = strtok.countTokens();
200 
201 	    if (num_tok == 0) // empty line
202 		return;
203 
204 	    mime_type = strtok.nextToken(); // get the MIME type
205 
206 	    while (strtok.hasMoreTokens()) {
207 		MimeTypeEntry entry = null;
208 
209 		file_ext = strtok.nextToken();
210 		entry = new MimeTypeEntry(mime_type, file_ext);
211 		type_hash.put(file_ext, entry);
212 		if (LogSupport.isLoggable())
213 		    LogSupport.log("Added: " + entry.toString());
214 	    }
215 	}
216     }
217 
218     // for debugging
219     /*
220     public static void main(String[] argv) throws Exception {
221 	MimeTypeFile mf = new MimeTypeFile(argv[0]);
222 	System.out.println("ext " + argv[1] + " type " +
223 						mf.getMIMETypeString(argv[1]));
224 	System.exit(0);
225     }
226     */
227 }
228 
229 class LineTokenizer {
230     private int currentPosition;
231     private int maxPosition;
232     private String str;
233     private Vector stack = new Vector();
234     private static final String singles = "=";	// single character tokens
235 
236     /**
237      * Constructs a tokenizer for the specified string.
238      * <p>
239      *
240      * @param   str            a string to be parsed.
241      */
LineTokenizer(String str)242     public LineTokenizer(String str) {
243 	currentPosition = 0;
244 	this.str = str;
245 	maxPosition = str.length();
246     }
247 
248     /**
249      * Skips white space.
250      */
skipWhiteSpace()251     private void skipWhiteSpace() {
252 	while ((currentPosition < maxPosition) &&
253 	       Character.isWhitespace(str.charAt(currentPosition))) {
254 	    currentPosition++;
255 	}
256     }
257 
258     /**
259      * Tests if there are more tokens available from this tokenizer's string.
260      *
261      * @return  <code>true</code> if there are more tokens available from this
262      *          tokenizer's string; <code>false</code> otherwise.
263      */
hasMoreTokens()264     public boolean hasMoreTokens() {
265 	if (stack.size() > 0)
266 	    return true;
267 	skipWhiteSpace();
268 	return (currentPosition < maxPosition);
269     }
270 
271     /**
272      * Returns the next token from this tokenizer.
273      *
274      * @return     the next token from this tokenizer.
275      * @exception  NoSuchElementException  if there are no more tokens in this
276      *               tokenizer's string.
277      */
nextToken()278     public String nextToken() {
279 	int size = stack.size();
280 	if (size > 0) {
281 	    String t = (String)stack.elementAt(size - 1);
282 	    stack.removeElementAt(size - 1);
283 	    return t;
284 	}
285 	skipWhiteSpace();
286 
287 	if (currentPosition >= maxPosition) {
288 	    throw new NoSuchElementException();
289 	}
290 
291 	int start = currentPosition;
292 	char c = str.charAt(start);
293 	if (c == '"') {
294 	    currentPosition++;
295 	    boolean filter = false;
296 	    while (currentPosition < maxPosition) {
297 		c = str.charAt(currentPosition++);
298 		if (c == '\\') {
299 		    currentPosition++;
300 		    filter = true;
301 		} else if (c == '"') {
302 		    String s;
303 
304 		    if (filter) {
305 			StringBuffer sb = new StringBuffer();
306 			for (int i = start + 1; i < currentPosition - 1; i++) {
307 			    c = str.charAt(i);
308 			    if (c != '\\')
309 				sb.append(c);
310 			}
311 			s = sb.toString();
312 		    } else
313 			s = str.substring(start + 1, currentPosition - 1);
314 		    return s;
315 		}
316 	    }
317 	} else if (singles.indexOf(c) >= 0) {
318 	    currentPosition++;
319 	} else {
320 	    while ((currentPosition < maxPosition) &&
321 		   singles.indexOf(str.charAt(currentPosition)) < 0 &&
322 		   !Character.isWhitespace(str.charAt(currentPosition))) {
323 		currentPosition++;
324 	    }
325 	}
326 	return str.substring(start, currentPosition);
327     }
328 
pushToken(String token)329     public void pushToken(String token) {
330 	stack.addElement(token);
331     }
332 }
333