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
26package sun.net.www;
27import java.net.URL;
28import java.io.*;
29import java.util.StringJoiner;
30import java.util.StringTokenizer;
31
32public 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 do 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}
324