1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package eu.medsea.mimeutil.detector;
17
18 import java.io.BufferedInputStream;
19 import java.io.BufferedReader;
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
26 import java.io.Reader;
27 import java.net.URL;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.Enumeration;
33 import java.util.Iterator;
34 import java.util.LinkedHashSet;
35 import java.util.LinkedList;
36 import java.util.List;
37 import java.util.regex.Pattern;
38
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 import eu.medsea.mimeutil.MimeException;
43 import eu.medsea.mimeutil.MimeUtil;
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143 public class MagicMimeMimeDetector extends MimeDetector {
144
145 private static Logger log = LoggerFactory.getLogger(MagicMimeMimeDetector.class);
146
147
148
149 protected static String[] defaultLocations = { "/usr/share/mimelnk/magic",
150 "/usr/share/file/magic.mime", "/etc/magic.mime" };
151 private static List magicMimeFileLocations = Arrays
152 .asList(defaultLocations);
153
154 private static ArrayList mMagicMimeEntries = new ArrayList();
155
156 public MagicMimeMimeDetector() {
157 MagicMimeMimeDetector.initMagicRules();
158 }
159
160 public String getDescription() {
161 return "Get the mime types of files or streams using the Unix file(5) magic.mime files";
162 }
163
164
165
166
167
168
169
170
171 public Collection getMimeTypesByteArray(final byte[] data)
172 throws UnsupportedOperationException {
173 Collection mimeTypes = new LinkedHashSet();
174 int len = mMagicMimeEntries.size();
175 try {
176 for (int i = 0; i < len; i++) {
177 MagicMimeEntry me = (MagicMimeEntry) mMagicMimeEntries.get(i);
178 MagicMimeEntry matchingMagicMimeEntry = me.getMatch(data);
179 if (matchingMagicMimeEntry != null) {
180 mimeTypes.add(matchingMagicMimeEntry.getMimeType());
181 }
182 }
183 } catch (Exception e) {
184 log.error(e.getMessage(), e);
185 }
186 return mimeTypes;
187 }
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203 public Collection getMimeTypesInputStream(final InputStream in)
204 throws UnsupportedOperationException {
205 Collection mimeTypes = new LinkedHashSet();
206 int len = mMagicMimeEntries.size();
207 try {
208 for (int i = 0; i < len; i++) {
209 MagicMimeEntry me = (MagicMimeEntry) mMagicMimeEntries.get(i);
210 MagicMimeEntry matchingMagicMimeEntry = me.getMatch(in);
211 if (matchingMagicMimeEntry != null) {
212 mimeTypes.add(matchingMagicMimeEntry.getMimeType());
213 }
214 }
215 } catch (Exception e) {
216 log.error(e.getMessage(), e);
217 }
218 return mimeTypes;
219 }
220
221
222
223
224 public Collection getMimeTypesFileName(final String fileName) throws UnsupportedOperationException {
225 return getMimeTypesFile(new File(fileName));
226 }
227
228
229
230
231
232 public Collection getMimeTypesURL(final URL url) throws UnsupportedOperationException {
233 InputStream in = null;
234 try {
235 return getMimeTypesInputStream(in = new BufferedInputStream(MimeUtil.getInputStreamForURL(url)));
236 }catch(Exception e) {
237 throw new MimeException(e);
238 }finally {
239 closeStream(in);
240 }
241 }
242
243
244
245
246 public Collection getMimeTypesFile(final File file) throws UnsupportedOperationException {
247 InputStream in = null;
248 try {
249 return getMimeTypesInputStream(in = new BufferedInputStream(new FileInputStream(file)));
250 }catch(FileNotFoundException e) {
251 throw new UnsupportedOperationException(e.getLocalizedMessage());
252 }catch(Exception e) {
253 throw new MimeException(e);
254 }finally {
255 closeStream(in);
256 }
257 }
258
259
260
261
262
263
264
265
266
267
268
269
270 private static void initMagicRules() {
271 InputStream in = null;
272
273
274 try {
275 String fname = System.getProperty("magic-mime");
276 if (fname != null && fname.length() != 0) {
277 in = new FileInputStream(fname);
278 if (in != null) {
279 parse("-Dmagic-mime=" + fname, new InputStreamReader(in));
280 }
281 }
282 } catch (Exception e) {
283 log.error("Failed to parse custom magic mime file defined by system property -Dmagic-mime ["
284 + System.getProperty("magic-mime")
285 + "]. File will be ignored.", e);
286 } finally {
287 in = closeStream(in);
288 }
289
290
291
292
293 try {
294 Enumeration en = MimeUtil.class.getClassLoader().getResources("magic.mime");
295 while(en.hasMoreElements()) {
296 URL url = (URL)en.nextElement();
297 in = url.openStream();
298 if(in != null) {
299 try {
300 parse("classpath:[" + url + "]", new InputStreamReader(in));
301 } catch(Exception ex) {
302 log.error("Failed to parse magic.mime rule file [" + url + "] on the classpath. File will be ignored.",
303 ex);
304 }
305 }
306
307 }
308 }catch(Exception e) {
309 log.error("Problem while processing magic.mime files from classpath. Files will be ignored.", e);
310 } finally {
311 in = closeStream(in);
312 }
313
314
315
316 try {
317 File f = new File(System.getProperty("user.home") + File.separator
318 + ".magic.mime");
319 if (f.exists()) {
320 in = new FileInputStream(f);
321 if (in != null) {
322 try {
323 parse(f.getAbsolutePath(), new InputStreamReader(in));
324 } catch(Exception ex) {
325 log.error("Failed to parse .magic.mime file from the users home directory. File will be ignored.", ex);
326 }
327 }
328 }
329 }catch(Exception e) {
330 log.error("Problem while processing .magic.mime file from the users home directory. File will be ignored.", e);
331 } finally {
332 in = closeStream(in);
333 }
334
335
336
337
338
339 try {
340 String name = System.getProperty("MAGIC");
341 if (name != null && name.length() != 0) {
342
343
344 if (name.indexOf('.') < 0) {
345 name = name + ".mime";
346 } else {
347
348 name = name.substring(0, name.indexOf('.') - 1) + "mime";
349 }
350 File f = new File(name);
351 if (f.exists()) {
352 in = new FileInputStream(f);
353 if (in != null) {
354 try {
355 parse(f.getAbsolutePath(),
356 new InputStreamReader(in));
357 }catch(Exception ex) {
358 log.error("Failed to parse magic.mime file from directory located by environment variable MAGIC. File will be ignored.", ex);
359 }
360 }
361 }
362 }
363 } catch (Exception e) {
364 log.error("Problem while processing magic.mime file from directory located by environment variable MAGIC. File will be ignored.", e);
365 } finally {
366 in = closeStream(in);
367 }
368
369
370
371
372
373
374
375 int mMagicMimeEntriesSizeBeforeReadingOS = mMagicMimeEntries.size();
376 Iterator it = magicMimeFileLocations.iterator();
377 while (it.hasNext()) {
378 parseMagicMimeFileLocation((String) it.next());
379 }
380
381 if (mMagicMimeEntriesSizeBeforeReadingOS == mMagicMimeEntries.size()) {
382
383 try {
384 String resource = "eu/medsea/mimeutil/magic.mime";
385 in = MimeUtil.class.getClassLoader().getResourceAsStream(
386 resource);
387 if(in != null) {
388 try {
389 parse("resource:" + resource, new InputStreamReader(in));
390 }catch(Exception ex) {
391 log.error("Failed to parse internal magic.mime file.", ex);
392 }
393 }
394 } catch (Exception e) {
395 log.error("Problem while processing internal magic.mime file.", e);
396 } finally {
397 in = closeStream(in);
398 }
399 }
400 }
401
402 private static void parseMagicMimeFileLocation(final String location) {
403 InputStream is = null;
404
405 List magicMimeFiles = getMagicFilesFromMagicMimeFileLocation(location);
406
407 for (Iterator itFile = magicMimeFiles.iterator(); itFile.hasNext();) {
408 File f = (File) itFile.next();
409 try {
410 if (f.exists()) {
411 is = new FileInputStream(f);
412 try {
413 parse(f.getAbsolutePath(), new InputStreamReader(is));
414 }catch(Exception e) {
415 log.error("Failed to parse " + f.getName() + ". File will be ignored.");
416 }
417 }
418 } catch (Exception e) {
419 log.error(e.getMessage(), e);
420 } finally {
421 is = closeStream(is);
422 }
423 }
424 }
425
426 private static List getMagicFilesFromMagicMimeFileLocation(
427 final String magicMimeFileLocation) {
428 List magicMimeFiles = new LinkedList();
429 if (magicMimeFileLocation.indexOf('*') < 0) {
430 magicMimeFiles.add(new File(magicMimeFileLocation));
431 } else {
432 int lastSlashPos = magicMimeFileLocation.lastIndexOf('/');
433 File dir;
434 String fileNameSimplePattern;
435 if (lastSlashPos < 0) {
436 dir = new File("someProbablyNotExistingFile").getAbsoluteFile()
437 .getParentFile();
438 fileNameSimplePattern = magicMimeFileLocation;
439 } else {
440 String dirName = magicMimeFileLocation.substring(0,
441 lastSlashPos);
442 if (dirName.indexOf('*') >= 0)
443 throw new UnsupportedOperationException(
444 "The wildcard '*' is not allowed in directory part of the location! Do you want to implement expressions like /path/**/*.mime for recursive search? Please do!");
445
446 dir = new File(dirName);
447 fileNameSimplePattern = magicMimeFileLocation
448 .substring(lastSlashPos + 1);
449 }
450
451 if (!dir.isDirectory())
452 return Collections.EMPTY_LIST;
453
454 String s = fileNameSimplePattern.replaceAll("\\.", "\\\\.");
455 s = s.replaceAll("\\*", ".*");
456 Pattern fileNamePattern = Pattern.compile(s);
457
458 File[] files = dir.listFiles();
459 for (int i = 0; i < files.length; i++) {
460 File file = files[i];
461
462 if (fileNamePattern.matcher(file.getName()).matches())
463 magicMimeFiles.add(file);
464 }
465 }
466 return magicMimeFiles;
467 }
468
469
470 private static void parse(final String magicFile, final Reader r)
471 throws IOException {
472 long start = System.currentTimeMillis();
473
474 BufferedReader br = new BufferedReader(r);
475 String line;
476 ArrayList sequence = new ArrayList();
477
478 long lineNumber = 0;
479 line = br.readLine();
480 if (line != null)
481 ++lineNumber;
482 while (true) {
483 if (line == null) {
484 break;
485 }
486 line = line.trim();
487 if (line.length() == 0 || line.charAt(0) == '#') {
488 line = br.readLine();
489 if (line != null)
490 ++lineNumber;
491 continue;
492 }
493 sequence.add(line);
494
495
496
497 while (true) {
498 line = br.readLine();
499 if (line != null)
500 ++lineNumber;
501 if (line == null) {
502 addEntry(magicFile, lineNumber, sequence);
503 sequence.clear();
504 break;
505 }
506 line = line.trim();
507 if (line.length() == 0 || line.charAt(0) == '#') {
508 continue;
509 }
510 if (line.charAt(0) != '>') {
511 addEntry(magicFile, lineNumber, sequence);
512 sequence.clear();
513 break;
514 }
515 sequence.add(line);
516 }
517
518 }
519 if (!sequence.isEmpty()) {
520 addEntry(magicFile, lineNumber, sequence);
521 }
522
523 if (log.isDebugEnabled())
524 log.debug("Parsing \"" + magicFile + "\" took "
525 + (System.currentTimeMillis() - start) + " msec.");
526 }
527
528 private static void addEntry(final String magicFile, final long lineNumber,
529 final ArrayList aStringArray) {
530 try {
531 MagicMimeEntry magicEntry = new MagicMimeEntry(aStringArray);
532 mMagicMimeEntries.add(magicEntry);
533
534 if (magicEntry.getMimeType() != null) {
535 MimeUtil.addKnownMimeType(magicEntry.getMimeType());
536 }
537 } catch (InvalidMagicMimeEntryException e) {
538
539
540 log.warn(e.getClass().getName() + ": " + e.getMessage()
541 + ": file \"" + magicFile + "\": before or at line "
542 + lineNumber, e);
543 }
544 }
545 }