/*==========================================================================*\
| $Id: Browser.java,v 1.4 2010/05/27 21:41:07 stedwar2 Exp $
|*-------------------------------------------------------------------------*|
| Copyright (C) 2007-2010 Virginia Tech
|
| This file is part of the Student-Library.
|
| The Student-Library is free software; you can redistribute it and/or
| modify it under the terms of the GNU Lesser General Public License as
| published by the Free Software Foundation; either version 3 of the
| License, or (at your option) any later version.
|
| The Student-Library is distributed in the hope that it will be useful,
| but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
| GNU Lesser General Public License for more details.
|
| You should have received a copy of the GNU Lesser General Public License
| along with the Student-Library; if not, see <http://www.gnu.org/licenses/>.
\*==========================================================================*/
package student;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.text.MessageFormat;
import java.util.StringTokenizer;
import java.util.Vector;
// -------------------------------------------------------------------------
/**
* This class provides a simple interface to your web browser. The
* primary capability it currently supports is opening a URL in a
* browser window.
*
* @version 2003.09.25
* @author Last changed by $Author: stedwar2 $
* @version $Revision: 1.4 $, $Date: 2010/05/27 21:41:07 $
*/
public class Browser
{
//~ Instance/static variables .............................................
// These fields are used for overridable world startup
private static String[] exec = null;
private static boolean runningInBatch = false;
//~ Constructors ..........................................................
// ----------------------------------------------------------
/**
* This class should not be instantiated.
*/
private Browser()
{
// Nothing to initialize
}
//~ Methods ...............................................................
// ----------------------------------------------------------
/**
* Display a file in the system browser.
* @param pathname a relative or absolute path designating the file to
* display
*/
public static void openFile( String pathname )
{
openFile( IOHelper.getFile( pathname ) );
}
// ----------------------------------------------------------
/**
* Display a file in the system browser.
* @param file the file to display
*/
public static void openFile( File file )
{
try
{
openURL( "file:///" + file.getCanonicalPath() );
}
catch ( Exception e )
{
throw new RuntimeException( e.getMessage() );
}
}
// ----------------------------------------------------------
/**
* Display a URL in the system browser.
* @param url the url to display
*/
public static void openURL( String url )
{
if ( runningInBatch) return;
if ( exec == null || exec.length == 0 )
{
if ( System.getProperty( "os.name" ).startsWith( "Mac" ) )
{
boolean success = false;
try
{
Class<?> macClass;
if ( System.getProperty( "java.vm.version" )
.startsWith( "1.3" ) )
{
macClass = Class.forName( "MRJFileUtils" );
}
else
{
macClass =
Class.forName( "com.apple.eio.FileManager" );
}
Method m = macClass.getMethod(
"openURL",
new Class[] { String.class }
);
m.invoke( null, (Object[])new String[] { url } );
success = true;
}
catch(Exception e)
{
// ignore it
}
if ( !success )
{
try
{
Class<?> nSWorkspace;
if ( new File( "/System/Library/Java/com/apple/cocoa/application/NSWorkspace.class" ).exists() )
{
// Mac OS X has NSWorkspace, but it is not in the
// classpath, so add it.
ClassLoader classLoader = new URLClassLoader(
new URL[]{ new File( "/System/Library/Java" )
.toURI().toURL() } );
nSWorkspace = Class.forName(
"com.apple.cocoa.application.NSWorkspace",
true,
classLoader );
}
else
{
nSWorkspace = Class.forName(
"com.apple.cocoa.application.NSWorkspace" );
}
Method sharedWorkspace = nSWorkspace.getMethod(
"sharedWorkspace", new Class[] {} );
Object workspace =
sharedWorkspace.invoke(
null, new Object[] {} );
Method openURL = nSWorkspace.getMethod(
"openURL",
new Class[] { java.net.URL.class } );
success = ( (Boolean)openURL.invoke(
workspace,
new Object[] { new java.net.URL( url ) } )
).booleanValue();
// success = com.apple.cocoa.application.NSWorkspace
// .sharedWorkspace().openURL(new java.net.URL(url));
}
catch ( Exception x )
{
// swallow exception
}
}
if ( !success )
{
try
{
Class<?> mrjFileUtils =
Class.forName( "com.apple.mrj.MRJFileUtils" );
Method openURL = mrjFileUtils.getMethod(
"openURL",
new Class[] { String.class }
);
openURL.invoke( null, new Object[] { url } );
// com.apple.mrj.MRJFileUtils.openURL( url );
}
catch ( Exception x )
{
throw new RuntimeException( "Browser launch failed:"
+ x.getMessage() );
}
}
}
else
{
throw new RuntimeException(
"Browser execute command cannot be found" );
}
}
else
{
// for security, see if the url is valid.
// this is primarily to catch an attack in which the url
// starts with a - to fool the command line flags, bu
// it could catch other stuff as well, and will throw a
// MalformedURLException which will give the caller of this
// function useful information.
try
{
new URL( url );
}
catch ( Exception e )
{
throw new RuntimeException(e);
}
// escape any weird characters in the url. This is primarily
// to prevent an attacker from putting in spaces
// that might fool exec into allowing
// the attacker to execute arbitrary code.
StringBuffer sb = new StringBuffer( url.length() );
for ( int i = 0; i < url.length(); i++ )
{
char c = url.charAt( i );
if ( (c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9')
|| c == '.' || c == ':' || c == '&' || c == '@'
|| c == '/' || c == '?' || c == '%' || c =='+'
|| c == '=' || c == '#' || c == '-' || c == '\\' )
{
// characters that are necessary for URLs and should be
// safe to pass to exec. Exec uses a default string
// tokenizer with the default arguments (whitespace) to
// separate command line arguments, so there should be no
// problem with anything but whitespace.
sb.append( c );
}
else
{
// get the lowest 8 bits (URLEncoding)
c = (char)( c & 0xFF );
if ( c < 0x10 )
{
sb.append( "%0" + Integer.toHexString( c ) );
}
else
{
sb.append( "%" + Integer.toHexString( c ) );
}
}
}
String[] messageArray = new String[1];
messageArray[0] = sb.toString();
String command = null;
boolean found = false;
// try each of the exec commands until something works
try
{
for ( int i = 0; i < exec.length && !found; i++ )
{
try
{
// stick the url into the command
command =
MessageFormat.format( exec[i],
(Object[])messageArray );
// parse the command line.
Vector<String> argsVector = new Vector<String>();
StringTokenizer lex = new StringTokenizer( command );
while ( lex.hasMoreTokens() )
{
argsVector.add( lex.nextToken() );
}
String[] args = new String[ argsVector.size() ];
args = argsVector.toArray( args );
// the windows url protocol handler doesn't work well
// with file URLs. Correct those problems here before
// continuing Java File.toURL() gives only one /
// following file: but we need two. If there are
// escaped characters in the url, we will have
// to create an Internet shortcut and open that, as
// the command line version of the rundll doesn't like
// them.
boolean useShortCut = false;
if ( args[0].equals( "rundll32" ) &&
args[1].equals( "url.dll,FileProtocolHandler" ) )
{
if ( args[2].startsWith( "file:/" ) )
{
if ( args[2].charAt( 6 ) != '/' )
{
args[2] =
"file://" + args[2].substring( 6 );
}
if ( args[2].charAt( 7 ) != '/' )
{
args[2] =
"file:///" + args[2].substring( 7 );
}
useShortCut = true;
}
else if ( args[2].toLowerCase().endsWith( "html" )
|| args[2].toLowerCase().endsWith("htm"))
{
useShortCut = true;
}
}
if ( useShortCut )
{
try
{
File shortcut = File.createTempFile(
"OpenInBrowser", ".url" );
shortcut = shortcut.getCanonicalFile();
shortcut.deleteOnExit();
PrintWriter out = new PrintWriter(
new FileWriter( shortcut ) );
out.println( "[InternetShortcut]" );
out.println( "URL=" + args[2] );
out.close();
args[2] = shortcut.getCanonicalPath();
}
catch ( Exception e )
{
throw new RuntimeException(
"Failure to create shortcut to open "
+ "file URL",
e );
}
}
// start the browser
Process p = Runtime.getRuntime().exec( args );
// give the browser a bit of time to fail.
// I have found that sometimes sleep doesn't work
// the first time, so do it twice. My tests
// seem to show that 1000 milliseconds is enough
// time for the browsers I'm using.
for ( int j = 0; j < 2; j++ )
{
try
{
Thread.sleep( 1000 );
}
catch ( InterruptedException inte )
{
// ignore it
}
}
if ( p.exitValue() == 0 )
{
// this is a weird case. The browser exited after
// a couple seconds saying that it successfully
// displayed the url. Either the browser is lying
// or the user closed it *really* quickly. Oh well.
found = true;
}
}
catch ( IOException x )
{
// the command was not a valid command.
System.err.println("Warning: " + x.getMessage() );
}
}
if ( !found )
{
// we never found a command that didn't terminate with an
// error.
throw new RuntimeException( "Browser launch failed" );
}
}
catch ( IllegalThreadStateException e )
{
// the browser is still running. This is a good sign.
// lets just say that it is displaying the url right now!
}
}
}
//~ Static Initialization .................................................
/*
* Retrieve the default commands to open a browser for this system.
*/
static
{
runningInBatch = false;
try
{
Browser.class.getClassLoader().loadClass( "student.RunInBatch" );
runningInBatch = true;
}
catch ( Exception e )
{
try
{
Browser.class.getClassLoader().loadClass( "cs1705.RunInBatch" );
runningInBatch = true;
}
catch ( Exception ee )
{
// Leave default value in place
}
}
if ( !runningInBatch )
{
if ( System.getProperty( "os.name" ).startsWith( "Windows" ) )
{
exec = new String[]{
"rundll32 url.dll,FileProtocolHandler {0}"
};
}
else if ( System.getProperty( "os.name" ).startsWith( "Mac" ) )
{
Vector<String> browsers = new Vector<String>();
try
{
Process p = Runtime.getRuntime().exec( "which open" );
if ( p.waitFor() == 0 )
{
browsers.add( "open {0}" );
}
}
catch ( IOException e )
{
// ignore it
}
catch ( InterruptedException e )
{
// ignore it
}
if ( browsers.size() == 0 )
{
exec = null;
}
else
{
exec = browsers.toArray( new String[0] );
}
}
else
{
Vector<String> browsers = new Vector<String>();
try
{
Process p = Runtime.getRuntime().exec( "which firebird" );
if ( p.waitFor() == 0 )
{
browsers.add( "firebird -remote openURL({0})" );
browsers.add( "firebird {0}" );
}
}
catch ( IOException e )
{
// ignore it
}
catch ( InterruptedException e )
{
// ignore it
}
try
{
Process p = Runtime.getRuntime().exec( "which mozilla" );
if ( p.waitFor() == 0 )
{
browsers.add( "mozilla -remote openURL({0})" );
browsers.add( "mozilla {0}" );
}
}
catch ( IOException e )
{
// ignore it
}
catch ( InterruptedException e )
{
// ignore it
}
try
{
Process p = Runtime.getRuntime().exec( "which opera" );
if ( p.waitFor() == 0 )
{
browsers.add( "opera -remote openURL({0})" );
browsers.add( "opera {0}" );
}
}
catch ( IOException e )
{
// ignore it
}
catch ( InterruptedException e )
{
// ignore it
}
try
{
Process p = Runtime.getRuntime().exec( "which galeon" );
if ( p.waitFor() == 0 )
{
browsers.add( "galeon {0}" );
}
}
catch ( IOException e )
{
// ignore it
}
catch ( InterruptedException e )
{
// ignore it
}
try
{
Process p = Runtime.getRuntime().exec( "which konqueror" );
if ( p.waitFor() == 0 )
{
browsers.add( "konqueror {0}" );
}
}
catch ( IOException e )
{
// ignore it
}
catch ( InterruptedException e )
{
// ignore it
}
try
{
Process p = Runtime.getRuntime().exec( "which netscape" );
if ( p.waitFor() == 0 )
{
browsers.add( "netscape -remote openURL({0})" );
browsers.add( "netscape {0}" );
}
}
catch ( IOException e )
{
// ignore it
}
catch ( InterruptedException e )
{
// ignore it
}
try
{
Process p = Runtime.getRuntime().exec( "which xterm" );
if ( p.waitFor() == 0 )
{
p = Runtime.getRuntime().exec( "which lynx" );
if ( p.waitFor() == 0 )
{
browsers.add( "xterm -e lynx {0}" );
}
}
}
catch ( IOException e )
{
// ignore it
}
catch ( InterruptedException e )
{
// ignore it
}
if ( browsers.size() == 0 )
{
exec = null;
}
else
{
exec = browsers.toArray(new String[0]);
}
}
}
}
}