1/*
2 * Copyright (c) 1999, 2016, 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.applet;
27
28import java.io.BufferedInputStream;
29import java.io.File;
30import java.io.FileInputStream;
31import java.io.FileOutputStream;
32import java.io.IOException;
33import java.net.URL;
34import java.net.MalformedURLException;
35import java.util.Enumeration;
36import java.util.Properties;
37import java.util.Vector;
38import sun.net.www.ParseUtil;
39
40/**
41 * The main entry point into AppletViewer.
42 *
43 * @deprecated The Applet API is deprecated. See the
44 * <a href="../../java/applet/package-summary.html"> java.applet package
45 * documentation</a> for further information.
46 */
47@Deprecated(since = "9")
48public class Main {
49    /**
50     * The file which contains all of the AppletViewer specific properties.
51     */
52    static File theUserPropertiesFile;
53
54    /**
55     * The default key/value pairs for the required user-specific properties.
56     */
57    static final String [][] avDefaultUserProps = {
58        // There's a bootstrapping problem here.  If we don't have a proxyHost,
59        // then we will not be able to connect to a URL outside the firewall;
60        // however, there's no way for us to set the proxyHost without starting
61        // AppletViewer.  This problem existed before the re-write.
62        {"http.proxyHost", ""},
63        {"http.proxyPort", "80"},
64        {"package.restrict.access.sun", "true"}
65    };
66
67    static {
68        File userHome = new File(System.getProperty("user.home"));
69        // make sure we can write to this location
70        userHome.canWrite();
71
72        theUserPropertiesFile = new File(userHome, ".appletviewer");
73    }
74
75    // i18n
76    private static AppletMessageHandler amh = new AppletMessageHandler("appletviewer");
77
78    /**
79     * Member variables set according to options passed in to AppletViewer.
80     */
81    private boolean helpFlag  = false;
82    private String  encoding  = null;
83    private boolean noSecurityFlag  = false;
84    private static boolean cmdLineTestFlag = false;
85
86    /**
87     * The list of valid URLs passed in to AppletViewer.
88     */
89    private static Vector<URL> urlList = new Vector<>(1);
90
91    // This is used in init().  Getting rid of this is desirable but depends
92    // on whether the property that uses it is necessary/standard.
93    public static final String theVersion = System.getProperty("java.version");
94
95    /**
96     * The main entry point into AppletViewer.
97     */
98    public static void main(String [] args) {
99        Main m = new Main();
100        int ret = m.run(args);
101
102        // Exit immediately if we got some sort of error along the way.
103        // For debugging purposes, if we have passed in "-XcmdLineTest" we
104        // force a premature exit.
105        if ((ret != 0) || (cmdLineTestFlag))
106            System.exit(ret);
107    }
108
109    private int run(String [] args) {
110        // DECODE ARGS
111        try {
112            System.err.println(lookup("deprecated"));
113            System.err.flush();
114            if (args.length == 0) {
115                usage();
116                return 0;
117            }
118            for (int i = 0; i < args.length; ) {
119                int j = decodeArg(args, i);
120                if (j == 0) {
121                    throw new ParseException(lookup("main.err.unrecognizedarg",
122                                                    args[i]));
123                }
124                i += j;
125            }
126        } catch (ParseException e) {
127            System.err.println(e.getMessage());
128            return 1;
129        }
130
131        // CHECK ARGUMENTS
132        if (helpFlag) {
133            usage();
134            return 0;
135        }
136
137        if (urlList.size() == 0) {
138            System.err.println(lookup("main.err.inputfile"));
139            return 1;
140        }
141
142        // INSTALL THE SECURITY MANAGER (if necessary)
143        if (!noSecurityFlag && (System.getSecurityManager() == null))
144            init();
145
146        // LAUNCH APPLETVIEWER FOR EACH URL
147        for (int i = 0; i < urlList.size(); i++) {
148            try {
149                // XXX 5/17 this parsing method should be changed/fixed so that
150                // it doesn't do both parsing of the html file and launching of
151                // the AppletPanel
152                AppletViewer.parse(urlList.elementAt(i), encoding);
153            } catch (IOException e) {
154                System.err.println(lookup("main.err.io", e.getMessage()));
155                return 1;
156            }
157        }
158        return 0;
159    }
160
161    private static void usage() {
162        System.out.println(lookup("usage"));
163    }
164
165    /**
166     * Decode a single argument in an array and return the number of elements
167     * used.
168     *
169     * @param args The array of arguments.
170     * @param i    The argument to decode.
171     * @return     The number of array elements used when the argument was
172     *             decoded.
173     * @exception ParseException
174     *             Thrown when there is a problem with something in the
175     *             argument array.
176     */
177    private int decodeArg(String [] args, int i) throws ParseException {
178        String arg = args[i];
179        int argc = args.length;
180
181        if ("-help".equalsIgnoreCase(arg) || "-?".equals(arg)) {
182            helpFlag = true;
183            return 1;
184        } else if ("-encoding".equals(arg) && (i < argc - 1)) {
185            if (encoding != null)
186                throw new ParseException(lookup("main.err.dupoption", arg));
187            encoding = args[++i];
188            return 2;
189        } else if ("-Xnosecurity".equals(arg)) {
190            // This is an undocumented (and, in the future, unsupported)
191            // flag which prevents AppletViewer from installing its own
192            // SecurityManager.
193
194            System.err.println();
195            System.err.println(lookup("main.warn.nosecmgr"));
196            System.err.println();
197
198            noSecurityFlag = true;
199            return 1;
200        } else if ("-XcmdLineTest".equals(arg)) {
201            // This is an internal flag which should be used for command-line
202            // testing.  It instructs AppletViewer to force a premature exit
203            // immediately after the applet has been launched.
204            cmdLineTestFlag = true;
205            return 1;
206        } else if (arg.startsWith("-")) {
207            throw new ParseException(lookup("main.err.unsupportedopt", arg));
208        } else {
209            // we found what we hope is a url
210            URL url = parseURL(arg);
211            if (url != null) {
212                urlList.addElement(url);
213                return 1;
214            }
215        }
216        return 0;
217    }
218
219    /**
220     * Following the relevant RFC, construct a valid URL based on the passed in
221     * string.
222     *
223     * @param url  a string which represents either a relative or absolute URL.
224     * @return     a URL when the passed in string can be interpreted according
225     *             to the RFC, {@code null} otherwise.
226     * @exception  ParseException
227     *             Thrown when we are unable to construct a proper URL from the
228     *             passed in string.
229     */
230    private URL parseURL(String url) throws ParseException {
231        URL u = null;
232        // prefix of the urls with 'file' scheme
233        String prefix = "file:";
234
235        try {
236            if (url.indexOf(':') <= 1)
237            {
238                // appletviewer accepts only unencoded filesystem paths
239                u = ParseUtil.fileToEncodedURL(new File(url));
240            } else if (url.startsWith(prefix) &&
241                       url.length() != prefix.length() &&
242                       !(new File(url.substring(prefix.length())).isAbsolute()))
243            {
244                // relative file URL, like this "file:index.html"
245                // ensure that this file URL is absolute
246                // ParseUtil.fileToEncodedURL should be done last (see 6329251)
247                String path = ParseUtil.fileToEncodedURL(new File(System.getProperty("user.dir"))).getPath() +
248                    url.substring(prefix.length());
249                u = new URL("file", "", path);
250            } else {
251                // appletviewer accepts only encoded urls
252                u = new URL(url);
253            }
254        } catch (MalformedURLException e) {
255            throw new ParseException(lookup("main.err.badurl",
256                                            url, e.getMessage()));
257        }
258
259        return u;
260    }
261
262    private void init() {
263        // GET APPLETVIEWER USER-SPECIFIC PROPERTIES
264        Properties avProps = getAVProps();
265
266        // ADD OTHER RANDOM PROPERTIES
267        // XXX 5/18 need to revisit why these are here, is there some
268        // standard for what is available?
269
270        // Standard browser properties
271        avProps.put("browser", "sun.applet.AppletViewer");
272        avProps.put("browser.version", "1.06");
273        avProps.put("browser.vendor", "Oracle Corporation");
274        avProps.put("http.agent", "Java(tm) 2 SDK, Standard Edition v" + theVersion);
275
276        // Define which packages can be extended by applets
277        // XXX 5/19 probably not needed, not checked in AppletSecurity
278        avProps.put("package.restrict.definition.java", "true");
279        avProps.put("package.restrict.definition.sun", "true");
280
281        // Define which properties can be read by applets.
282        // A property named by "key" can be read only when its twin
283        // property "key.applet" is true.  The following ten properties
284        // are open by default.  Any other property can be explicitly
285        // opened up by the browser user by calling appletviewer with
286        // -J-Dkey.applet=true
287        avProps.put("java.version.applet", "true");
288        avProps.put("java.vendor.applet", "true");
289        avProps.put("java.vendor.url.applet", "true");
290        avProps.put("java.class.version.applet", "true");
291        avProps.put("os.name.applet", "true");
292        avProps.put("os.version.applet", "true");
293        avProps.put("os.arch.applet", "true");
294        avProps.put("file.separator.applet", "true");
295        avProps.put("path.separator.applet", "true");
296        avProps.put("line.separator.applet", "true");
297
298        // Read in the System properties.  If something is going to be
299        // over-written, warn about it.
300        Properties sysProps = System.getProperties();
301        for (Enumeration<?> e = sysProps.propertyNames(); e.hasMoreElements(); ) {
302            String key = (String) e.nextElement();
303            String val = sysProps.getProperty(key);
304            String oldVal;
305            if ((oldVal = (String) avProps.setProperty(key, val)) != null)
306                System.err.println(lookup("main.warn.prop.overwrite", key,
307                                          oldVal, val));
308        }
309
310        // INSTALL THE PROPERTY LIST
311        System.setProperties(avProps);
312
313        // Create and install the security manager
314        if (!noSecurityFlag) {
315            System.setSecurityManager(new AppletSecurity());
316        } else {
317            System.err.println(lookup("main.nosecmgr"));
318        }
319
320        // REMIND: Create and install a socket factory!
321    }
322
323    /**
324     * Read the AppletViewer user-specific properties.  Typically, these
325     * properties should reside in the file $USER/.appletviewer.  If this file
326     * does not exist, one will be created.  Information for this file will
327     * be gleaned from $USER/.hotjava/properties.  If that file does not exist,
328     * then default values will be used.
329     *
330     * @return     A Properties object containing all of the AppletViewer
331     *             user-specific properties.
332     */
333    private Properties getAVProps() {
334        Properties avProps = new Properties();
335
336        File dotAV = theUserPropertiesFile;
337        if (dotAV.exists()) {
338            // we must have already done the conversion
339            if (dotAV.canRead()) {
340                // just read the file
341                avProps = getAVProps(dotAV);
342            } else {
343                // send out warning and use defaults
344                System.err.println(lookup("main.warn.cantreadprops",
345                                          dotAV.toString()));
346                avProps = setDefaultAVProps();
347            }
348        } else {
349            // create the $USER/.appletviewer file
350
351            // see if $USER/.hotjava/properties exists
352            File userHome = new File(System.getProperty("user.home"));
353            File dotHJ = new File(userHome, ".hotjava");
354            dotHJ = new File(dotHJ, "properties");
355            if (dotHJ.exists()) {
356                // just read the file
357                avProps = getAVProps(dotHJ);
358            } else {
359                // send out warning and use defaults
360                System.err.println(lookup("main.warn.cantreadprops",
361                                          dotHJ.toString()));
362                avProps = setDefaultAVProps();
363            }
364
365            // SAVE THE FILE
366            try (FileOutputStream out = new FileOutputStream(dotAV)) {
367                avProps.store(out, lookup("main.prop.store"));
368            } catch (IOException e) {
369                System.err.println(lookup("main.err.prop.cantsave",
370                                          dotAV.toString()));
371            }
372        }
373        return avProps;
374    }
375
376    /**
377     * Set the AppletViewer user-specific properties to be the default values.
378     *
379     * @return     A Properties object containing all of the AppletViewer
380     *             user-specific properties, set to the default values.
381     */
382    private Properties setDefaultAVProps() {
383        Properties avProps = new Properties();
384        for (int i = 0; i < avDefaultUserProps.length; i++) {
385            avProps.setProperty(avDefaultUserProps[i][0],
386                                avDefaultUserProps[i][1]);
387        }
388        return avProps;
389    }
390
391    /**
392     * Given a file, find only the properties that are setable by AppletViewer.
393     *
394     * @param inFile A Properties file from which we select the properties of
395     *             interest.
396     * @return     A Properties object containing all of the AppletViewer
397     *             user-specific properties.
398     */
399    private Properties getAVProps(File inFile) {
400        Properties avProps  = new Properties();
401
402        // read the file
403        Properties tmpProps = new Properties();
404        try (FileInputStream in = new FileInputStream(inFile)) {
405            tmpProps.load(new BufferedInputStream(in));
406        } catch (IOException e) {
407            System.err.println(lookup("main.err.prop.cantread", inFile.toString()));
408        }
409
410        // pick off the properties we care about
411        for (int i = 0; i < avDefaultUserProps.length; i++) {
412            String value = tmpProps.getProperty(avDefaultUserProps[i][0]);
413            if (value != null) {
414                // the property exists in the file, so replace the default
415                avProps.setProperty(avDefaultUserProps[i][0], value);
416            } else {
417                // just use the default
418                avProps.setProperty(avDefaultUserProps[i][0],
419                                    avDefaultUserProps[i][1]);
420            }
421        }
422        return avProps;
423    }
424
425    /**
426     * Methods for easier i18n handling.
427     */
428
429    private static String lookup(String key) {
430        return amh.getMessage(key);
431    }
432
433    private static String lookup(String key, String arg0) {
434        return amh.getMessage(key, arg0);
435    }
436
437    private static String lookup(String key, String arg0, String arg1) {
438        return amh.getMessage(key, arg0, arg1);
439    }
440
441    private static String lookup(String key, String arg0, String arg1,
442                                 String arg2) {
443        return amh.getMessage(key, arg0, arg1, arg2);
444    }
445
446    @SuppressWarnings("serial") // JDK implementation class
447    class ParseException extends RuntimeException
448    {
449        public ParseException(String msg) {
450            super(msg);
451        }
452
453        public ParseException(Throwable t) {
454            super(t.getMessage());
455            this.t = t;
456        }
457
458        Throwable t = null;
459    }
460}
461