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