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