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 }