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