1 /*
   2  * Copyright (c) 1997, 2017, 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 javax.activation;
  27 
  28 import java.io.*;
  29 import java.net.*;
  30 import java.util.*;
  31 import java.security.AccessController;
  32 import java.security.PrivilegedAction;
  33 import com.sun.activation.registries.MimeTypeFile;
  34 import com.sun.activation.registries.LogSupport;
  35 
  36 /**
  37  * This class extends FileTypeMap and provides data typing of files
  38  * via their file extension. It uses the {@code .mime.types} format. <p>
  39  *
  40  * <b>MIME types file search order:</b><p>
  41  * The MimetypesFileTypeMap looks in various places in the user's
  42  * system for MIME types file entries. When requests are made
  43  * to search for MIME types in the MimetypesFileTypeMap, it searches
  44  * MIME types files in the following order:
  45  * <ol>
  46  * <li> Programmatically added entries to the MimetypesFileTypeMap instance.
  47  * <li> The file {@code .mime.types} in the user's home directory.
  48  * <li> The file <i>java.home</i>{@code /}<i>conf</i>{@code /mime.types}.
  49  * <li> The file or resources named {@code META-INF/mime.types}.
  50  * <li> The file or resource named {@code META-INF/mimetypes.default}
  51  * (usually found only in the {@code activation.jar} file).
  52  * </ol>
  53  * <p>
  54  * (Where <i>java.home</i> is the value of the "java.home" System property
  55  * and <i>conf</i> is the directory named "conf" if it exists,
  56  * otherwise the directory named "lib"; the "conf" directory was
  57  * introduced in JDK 1.9.)
  58  * <p>
  59  * <b>MIME types file format:</b>
  60  *
  61  * <pre>{@code
  62  * # comments begin with a '#'
  63  * # the format is <mime type> <space separated file extensions>
  64  * # for example:
  65  * text/plain    txt text TXT
  66  * # this would map file.txt, file.text, and file.TXT to
  67  * # the mime type "text/plain"
  68  * }</pre>
  69  *
  70  * @author Bart Calder
  71  * @author Bill Shannon
  72  *
  73  * @since 1.6
  74  */
  75 public class MimetypesFileTypeMap extends FileTypeMap {
  76     /*
  77      * We manage a collection of databases, searched in order.
  78      */
  79     private MimeTypeFile[] DB;
  80     private static final int PROG = 0;  // programmatically added entries
  81 
  82     private static final String defaultType = "application/octet-stream";
  83 
  84     private static final String confDir;
  85 
  86     static {
  87         String dir = null;
  88         try {
  89             dir = (String)AccessController.doPrivileged(
  90                 new PrivilegedAction() {
  91                     public Object run() {
  92                         String home = System.getProperty("java.home");
  93                         String newdir = home + File.separator + "conf";
  94                         File conf = new File(newdir);
  95                         if (conf.exists())
  96                             return newdir + File.separator;
  97                         else
  98                             return home + File.separator + "lib" + File.separator;
  99                     }
 100                 });
 101         } catch (Exception ex) {
 102             // ignore any exceptions
 103         }
 104         confDir = dir;
 105     }
 106 
 107     /**
 108      * The default constructor.
 109      */
 110     public MimetypesFileTypeMap() {
 111         Vector dbv = new Vector(5);     // usually 5 or less databases
 112         MimeTypeFile mf = null;
 113         dbv.addElement(null);           // place holder for PROG entry
 114 
 115         LogSupport.log("MimetypesFileTypeMap: load HOME");
 116         try {
 117             String user_home = System.getProperty("user.home");
 118 
 119             if (user_home != null) {
 120                 String path = user_home + File.separator + ".mime.types";
 121                 mf = loadFile(path);
 122                 if (mf != null)
 123                     dbv.addElement(mf);
 124             }
 125         } catch (SecurityException ex) {}
 126 
 127         LogSupport.log("MimetypesFileTypeMap: load SYS");
 128         try {
 129             // check system's home
 130             if (confDir != null) {
 131                 mf = loadFile(confDir + "mime.types");
 132                 if (mf != null)
 133                     dbv.addElement(mf);
 134             }
 135         } catch (SecurityException ex) {}
 136 
 137         LogSupport.log("MimetypesFileTypeMap: load JAR");
 138         // load from the app's jar file
 139         loadAllResources(dbv, "META-INF/mime.types");
 140 
 141         LogSupport.log("MimetypesFileTypeMap: load DEF");
 142         mf = loadResource("/META-INF/mimetypes.default");
 143 
 144         if (mf != null)
 145             dbv.addElement(mf);
 146 
 147         DB = new MimeTypeFile[dbv.size()];
 148         dbv.copyInto(DB);
 149     }
 150 
 151     /**
 152      * Load from the named resource.
 153      */
 154     private MimeTypeFile loadResource(String name) {
 155         InputStream clis = null;
 156         try {
 157             clis = SecuritySupport.getResourceAsStream(this.getClass(), name);
 158             if (clis != null) {
 159                 MimeTypeFile mf = new MimeTypeFile(clis);
 160                 if (LogSupport.isLoggable())
 161                     LogSupport.log("MimetypesFileTypeMap: successfully " +
 162                         "loaded mime types file: " + name);
 163                 return mf;
 164             } else {
 165                 if (LogSupport.isLoggable())
 166                     LogSupport.log("MimetypesFileTypeMap: not loading " +
 167                         "mime types file: " + name);
 168             }
 169         } catch (IOException e) {
 170             if (LogSupport.isLoggable())
 171                 LogSupport.log("MimetypesFileTypeMap: can't load " + name, e);
 172         } catch (SecurityException sex) {
 173             if (LogSupport.isLoggable())
 174                 LogSupport.log("MimetypesFileTypeMap: can't load " + name, sex);
 175         } finally {
 176             try {
 177                 if (clis != null)
 178                     clis.close();
 179             } catch (IOException ex) { }        // ignore it
 180         }
 181         return null;
 182     }
 183 
 184     /**
 185      * Load all of the named resource.
 186      */
 187     private void loadAllResources(Vector v, String name) {
 188         boolean anyLoaded = false;
 189         try {
 190             URL[] urls;
 191             ClassLoader cld = null;
 192             // First try the "application's" class loader.
 193             cld = SecuritySupport.getContextClassLoader();
 194             if (cld == null)
 195                 cld = this.getClass().getClassLoader();
 196             if (cld != null)
 197                 urls = SecuritySupport.getResources(cld, name);
 198             else
 199                 urls = SecuritySupport.getSystemResources(name);
 200             if (urls != null) {
 201                 if (LogSupport.isLoggable())
 202                     LogSupport.log("MimetypesFileTypeMap: getResources");
 203                 for (int i = 0; i < urls.length; i++) {
 204                     URL url = urls[i];
 205                     InputStream clis = null;
 206                     if (LogSupport.isLoggable())
 207                         LogSupport.log("MimetypesFileTypeMap: URL " + url);
 208                     try {
 209                         clis = SecuritySupport.openStream(url);
 210                         if (clis != null) {
 211                             v.addElement(new MimeTypeFile(clis));
 212                             anyLoaded = true;
 213                             if (LogSupport.isLoggable())
 214                                 LogSupport.log("MimetypesFileTypeMap: " +
 215                                     "successfully loaded " +
 216                                     "mime types from URL: " + url);
 217                         } else {
 218                             if (LogSupport.isLoggable())
 219                                 LogSupport.log("MimetypesFileTypeMap: " +
 220                                     "not loading " +
 221                                     "mime types from URL: " + url);
 222                         }
 223                     } catch (IOException ioex) {
 224                         if (LogSupport.isLoggable())
 225                             LogSupport.log("MimetypesFileTypeMap: can't load " +
 226                                                 url, ioex);
 227                     } catch (SecurityException sex) {
 228                         if (LogSupport.isLoggable())
 229                             LogSupport.log("MimetypesFileTypeMap: can't load " +
 230                                                 url, sex);
 231                     } finally {
 232                         try {
 233                             if (clis != null)
 234                                 clis.close();
 235                         } catch (IOException cex) { }
 236                     }
 237                 }
 238             }
 239         } catch (Exception ex) {
 240             if (LogSupport.isLoggable())
 241                 LogSupport.log("MimetypesFileTypeMap: can't load " + name, ex);
 242         }
 243 
 244         // if failed to load anything, fall back to old technique, just in case
 245         if (!anyLoaded) {
 246             LogSupport.log("MimetypesFileTypeMap: !anyLoaded");
 247             MimeTypeFile mf = loadResource("/" + name);
 248             if (mf != null)
 249                 v.addElement(mf);
 250         }
 251     }
 252 
 253     /**
 254      * Load the named file.
 255      */
 256     private MimeTypeFile loadFile(String name) {
 257         MimeTypeFile mtf = null;
 258 
 259         try {
 260             mtf = new MimeTypeFile(name);
 261         } catch (IOException e) {
 262             //  e.printStackTrace();
 263         }
 264         return mtf;
 265     }
 266 
 267     /**
 268      * Construct a MimetypesFileTypeMap with programmatic entries
 269      * added from the named file.
 270      *
 271      * @param mimeTypeFileName  the file name
 272      * @exception       IOException     for errors reading the file
 273      */
 274     public MimetypesFileTypeMap(String mimeTypeFileName) throws IOException {
 275         this();
 276         DB[PROG] = new MimeTypeFile(mimeTypeFileName);
 277     }
 278 
 279     /**
 280      * Construct a MimetypesFileTypeMap with programmatic entries
 281      * added from the InputStream.
 282      *
 283      * @param is        the input stream to read from
 284      */
 285     public MimetypesFileTypeMap(InputStream is) {
 286         this();
 287         try {
 288             DB[PROG] = new MimeTypeFile(is);
 289         } catch (IOException ex) {
 290             // XXX - really should throw it
 291         }
 292     }
 293 
 294     /**
 295      * Prepend the MIME type values to the registry.
 296      *
 297      * @param mime_types A .mime.types formatted string of entries.
 298      */
 299     public synchronized void addMimeTypes(String mime_types) {
 300         // check to see if we have created the registry
 301         if (DB[PROG] == null)
 302             DB[PROG] = new MimeTypeFile(); // make one
 303 
 304         DB[PROG].appendToRegistry(mime_types);
 305     }
 306 
 307     /**
 308      * Return the MIME type of the file object.
 309      * The implementation in this class calls
 310      * {@code getContentType(f.getName())}.
 311      *
 312      * @param f the file
 313      * @return  the file's MIME type
 314      */
 315     public String getContentType(File f) {
 316         return this.getContentType(f.getName());
 317     }
 318 
 319     /**
 320      * Return the MIME type based on the specified file name.
 321      * The MIME type entries are searched as described above under
 322      * <i>MIME types file search order</i>.
 323      * If no entry is found, the type "application/octet-stream" is returned.
 324      *
 325      * @param filename  the file name
 326      * @return          the file's MIME type
 327      */
 328     public synchronized String getContentType(String filename) {
 329         int dot_pos = filename.lastIndexOf("."); // period index
 330 
 331         if (dot_pos < 0)
 332             return defaultType;
 333 
 334         String file_ext = filename.substring(dot_pos + 1);
 335         if (file_ext.length() == 0)
 336             return defaultType;
 337 
 338         for (int i = 0; i < DB.length; i++) {
 339             if (DB[i] == null)
 340                 continue;
 341             String result = DB[i].getMIMETypeString(file_ext);
 342             if (result != null)
 343                 return result;
 344         }
 345         return defaultType;
 346     }
 347 
 348     /**
 349      * for debugging...
 350      *
 351     public static void main(String[] argv) throws Exception {
 352         MimetypesFileTypeMap map = new MimetypesFileTypeMap();
 353         System.out.println("File " + argv[0] + " has MIME type " +
 354                                                 map.getContentType(argv[0]));
 355         System.exit(0);
 356     }
 357     */
 358 }