1 /*
   2  * Copyright (c) 1994, 2014, 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 sun.net.www;
  27 import java.net.URL;
  28 import java.io.*;
  29 import java.util.StringJoiner;
  30 import java.util.StringTokenizer;
  31 
  32 public class MimeEntry implements Cloneable {
  33     private String typeName;    // of the form: "type/subtype"
  34     private String tempFileNameTemplate;
  35 
  36     private int action;
  37     private String command;
  38     private String description;
  39     private String imageFileName;
  40     private String fileExtensions[];
  41 
  42     boolean starred;
  43 
  44     // Actions
  45     public static final int             UNKNOWN                 = 0;
  46     public static final int             LOAD_INTO_BROWSER       = 1;
  47     public static final int             SAVE_TO_FILE            = 2;
  48     public static final int             LAUNCH_APPLICATION      = 3;
  49 
  50     static final String[] actionKeywords = {
  51         "unknown",
  52         "browser",
  53         "save",
  54         "application",
  55     };
  56 
  57     /**
  58      * Construct an empty entry of the given type and subtype.
  59      */
  60     public MimeEntry(String type) {
  61         // Default action is UNKNOWN so clients can decide what the default
  62         // should be, typically save to file or ask user.
  63         this(type, UNKNOWN, null, null, null);
  64     }
  65 
  66     //
  67     // The next two constructors are used only by the deprecated
  68     // PlatformMimeTable classes or, in last case, is called by the public
  69     // constructor.  They are kept here anticipating putting support for
  70     // mailcap formatted config files back in (so BOTH the properties format
  71     // and the mailcap formats are supported).
  72     //
  73     MimeEntry(String type, String imageFileName, String extensionString) {
  74         typeName = type.toLowerCase();
  75         action = UNKNOWN;
  76         command = null;
  77         this.imageFileName = imageFileName;
  78         setExtensions(extensionString);
  79         starred = isStarred(typeName);
  80     }
  81 
  82     // For use with MimeTable::parseMailCap
  83     MimeEntry(String typeName, int action, String command,
  84               String tempFileNameTemplate) {
  85         this.typeName = typeName.toLowerCase();
  86         this.action = action;
  87         this.command = command;
  88         this.imageFileName = null;
  89         this.fileExtensions = null;
  90 
  91         this.tempFileNameTemplate = tempFileNameTemplate;
  92     }
  93 
  94     // This is the one called by the public constructor.
  95     MimeEntry(String typeName, int action, String command,
  96               String imageFileName, String fileExtensions[]) {
  97 
  98         this.typeName = typeName.toLowerCase();
  99         this.action = action;
 100         this.command = command;
 101         this.imageFileName = imageFileName;
 102         this.fileExtensions = fileExtensions;
 103 
 104         starred = isStarred(typeName);
 105 
 106     }
 107 
 108     public synchronized String getType() {
 109         return typeName;
 110     }
 111 
 112     public synchronized void setType(String type) {
 113         typeName = type.toLowerCase();
 114     }
 115 
 116     public synchronized int getAction() {
 117         return action;
 118     }
 119 
 120     public synchronized void setAction(int action, String command) {
 121         this.action = action;
 122         this.command = command;
 123     }
 124 
 125     public synchronized void setAction(int action) {
 126         this.action = action;
 127     }
 128 
 129     public synchronized String getLaunchString() {
 130         return command;
 131     }
 132 
 133     public synchronized void setCommand(String command) {
 134         this.command = command;
 135     }
 136 
 137     public synchronized String getDescription() {
 138         return (description != null ? description : typeName);
 139     }
 140 
 141     public synchronized void setDescription(String description) {
 142         this.description = description;
 143     }
 144 
 145     // ??? what to return for the image -- the file name or should this return
 146     // something more advanced like an image source or something?
 147     // returning the name has the least policy associated with it.
 148     // pro tempore, we'll use the name
 149     public String getImageFileName() {
 150         return imageFileName;
 151     }
 152 
 153     public synchronized void setImageFileName(String filename) {
 154         File file = new File(filename);
 155         if (file.getParent() == null) {
 156             imageFileName = System.getProperty(
 157                                      "java.net.ftp.imagepath."+filename);
 158         }
 159         else {
 160             imageFileName = filename;
 161         }
 162 
 163         if (filename.lastIndexOf('.') < 0) {
 164             imageFileName = imageFileName + ".gif";
 165         }
 166     }
 167 
 168     public String getTempFileTemplate() {
 169         return tempFileNameTemplate;
 170     }
 171 
 172     public synchronized String[] getExtensions() {
 173         return fileExtensions;
 174     }
 175 
 176     public synchronized String getExtensionsAsList() {
 177         String extensionsAsString = "";
 178         if (fileExtensions != null) {
 179             for (int i = 0; i < fileExtensions.length; i++) {
 180                 extensionsAsString += fileExtensions[i];
 181                 if (i < (fileExtensions.length - 1)) {
 182                     extensionsAsString += ",";
 183                 }
 184             }
 185         }
 186 
 187         return extensionsAsString;
 188     }
 189 
 190     public synchronized void setExtensions(String extensionString) {
 191         StringTokenizer extTokens = new StringTokenizer(extensionString, ",");
 192         int numExts = extTokens.countTokens();
 193         String extensionStrings[] = new String[numExts];
 194 
 195         for (int i = 0; i < numExts; i++) {
 196             String ext = (String)extTokens.nextElement();
 197             extensionStrings[i] = ext.trim();
 198         }
 199 
 200         fileExtensions = extensionStrings;
 201     }
 202 
 203     private boolean isStarred(String typeName) {
 204         return (typeName != null)
 205             && (typeName.length() > 0)
 206             && (typeName.endsWith("/*"));
 207     }
 208 
 209     /**
 210      * Invoke the MIME type specific behavior for this MIME type.
 211      * Returned value can be one of several types:
 212      * <ol>
 213      * <li>A thread -- the caller can choose when to launch this thread.
 214      * <li>A string -- the string is loaded into the browser directly.
 215      * <li>An input stream -- the caller can read from this byte stream and
 216      *     will typically store the results in a file.
 217      * <li>A document (?) --
 218      * </ol>
 219      */
 220     public Object launch(java.net.URLConnection urlc, InputStream is, MimeTable mt) throws ApplicationLaunchException {
 221         switch (action) {
 222         case SAVE_TO_FILE:
 223             // REMIND: is this really the right thing to do?
 224             try {
 225                 return is;
 226             } catch(Exception e) {
 227                 // I18N
 228                 return "Load to file failed:\n" + e;
 229             }
 230 
 231         case LOAD_INTO_BROWSER:
 232             // REMIND: invoke the content handler?
 233             // may be the right thing to do, may not be -- short term
 234             // where docs are not loaded asynch, loading and returning
 235             // the content is the right thing to do.
 236             try {
 237                 return urlc.getContent();
 238             } catch (Exception e) {
 239                 return null;
 240             }
 241 
 242         case LAUNCH_APPLICATION:
 243             {
 244                 String threadName = command;
 245                 int fst = threadName.indexOf(' ');
 246                 if (fst > 0) {
 247                     threadName = threadName.substring(0, fst);
 248                 }
 249 
 250                 return new MimeLauncher(this, urlc, is,
 251                                         mt.getTempFileTemplate(), threadName);
 252             }
 253 
 254         case UNKNOWN:
 255             // REMIND: What to do here?
 256             return null;
 257         }
 258 
 259         return null;
 260     }
 261 
 262     public boolean matches(String type) {
 263         if (starred) {
 264           // REMIND: is this the right thing or not?
 265           return type.startsWith(typeName);
 266         } else {
 267             return type.equals(typeName);
 268         }
 269     }
 270 
 271     public Object clone() {
 272         // return a shallow copy of this.
 273         MimeEntry theClone = new MimeEntry(typeName);
 274         theClone.action = action;
 275         theClone.command = command;
 276         theClone.description = description;
 277         theClone.imageFileName = imageFileName;
 278         theClone.tempFileNameTemplate = tempFileNameTemplate;
 279         theClone.fileExtensions = fileExtensions;
 280 
 281         return theClone;
 282     }
 283 
 284     public synchronized String toProperty() {
 285         StringJoiner sj = new StringJoiner("; ");
 286 
 287         int action = getAction();
 288         if (action != MimeEntry.UNKNOWN) {
 289             sj.add("action=" + actionKeywords[action]);
 290         }
 291 
 292         String command = getLaunchString();
 293         if (command != null && command.length() > 0) {
 294             sj.add("application=" + command);
 295         }
 296 
 297         String image = getImageFileName();
 298         if (image != null) {
 299             sj.add("icon=" + image);
 300         }
 301 
 302         String extensions = getExtensionsAsList();
 303         if (extensions.length() > 0) {
 304             sj.add("file_extensions=" + extensions);
 305         }
 306 
 307         String description = getDescription();
 308         if (description != null && !description.equals(getType())) {
 309             sj.add("description=" + description);
 310         }
 311 
 312         return sj.toString();
 313     }
 314 
 315     public String toString() {
 316         return "MimeEntry[contentType=" + typeName
 317             + ", image=" + imageFileName
 318             + ", action=" + action
 319             + ", command=" + command
 320             + ", extensions=" + getExtensionsAsList()
 321             + "]";
 322     }
 323 }