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  * @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")
  48 public 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 }