/*
* File : WebPlugin.java
* Created : 23-Jan-2004
* By : parg
*
* Azureus - a Java Bittorrent client
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* This program 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 General Public License for more details ( see the LICENSE file ).
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.gudy.azureus2.ui.webplugin;
/**
* @author parg
*
*/
import java.io.*;
import java.util.*;
import java.net.*;
import org.gudy.azureus2.core3.util.*;
import org.gudy.azureus2.plugins.*;
import org.gudy.azureus2.plugins.logging.*;
import org.gudy.azureus2.plugins.ipfilter.*;
import org.gudy.azureus2.plugins.tracker.*;
import org.gudy.azureus2.plugins.tracker.web.*;
import org.gudy.azureus2.plugins.ui.*;
import org.gudy.azureus2.plugins.ui.config.*;
import org.gudy.azureus2.plugins.ui.model.*;
import com.aelitis.azureus.plugins.upnp.UPnPPlugin;
public class
WebPlugin
implements Plugin, TrackerWebPageGenerator
{
public static final String PR_PORT = "Port"; // Integer
public static final String PR_BIND_IP = "Bind IP"; // String
public static final String PR_ROOT_RESOURCE = "Root Resource"; // String
public static final String PR_LOG = "DefaultLoggerChannel"; // LoggerChannel
public static final String PR_CONFIG_MODEL = "DefaultConfigModel"; // BasicPluginConfigModel
public static final String PR_VIEW_MODEL = "DefaultViewModel"; // BasicPluginViewModel
public static final String PR_HIDE_RESOURCE_CONFIG = "DefaultHideResourceConfig"; // Boolean
public static final String PROPERTIES_MIGRATED = "Properties Migrated";
public static final String CONFIG_MIGRATED = "Config Migrated";
public static final String CONFIG_PASSWORD_ENABLE = "Password Enable";
public final boolean CONFIG_PASSWORD_ENABLE_DEFAULT = false;
public static final String CONFIG_USER = "User";
public final String CONFIG_USER_DEFAULT = "";
public static final String CONFIG_PASSWORD = "Password";
public final byte[] CONFIG_PASSWORD_DEFAULT = {};
public static final String CONFIG_PORT = PR_PORT;
public int CONFIG_PORT_DEFAULT = 8089;
public static final String CONFIG_BIND_IP = PR_BIND_IP;
public String CONFIG_BIND_IP_DEFAULT = "";
public static final String CONFIG_PROTOCOL = "Protocol";
public final String CONFIG_PROTOCOL_DEFAULT = "HTTP";
public static final String CONFIG_UPNP_ENABLE = "UPnP Enable";
public final boolean CONFIG_UPNP_ENABLE_DEFAULT = true;
public static final String CONFIG_HOME_PAGE = "Home Page";
public final String CONFIG_HOME_PAGE_DEFAULT = "index.html";
public static final String CONFIG_ROOT_DIR = "Root Dir";
public final String CONFIG_ROOT_DIR_DEFAULT = "";
public static final String CONFIG_ROOT_RESOURCE = PR_ROOT_RESOURCE;
public String CONFIG_ROOT_RESOURCE_DEFAULT = "";
public static final String CONFIG_MODE = "Mode";
public static final String CONFIG_MODE_FULL = "full";
public final String CONFIG_MODE_DEFAULT = CONFIG_MODE_FULL;
public static final String CONFIG_ACCESS = "Access";
public final String CONFIG_ACCESS_DEFAULT = "all";
protected static final String NL = "\r\n";
protected static final String[] welcome_pages = {"index.html", "index.htm", "index.php", "index.tmpl" };
protected static File[] welcome_files;
protected PluginInterface plugin_interface; // unfortunately this is accessed by webui - fix sometime
private LoggerChannel log;
private Tracker tracker;
private BasicPluginViewModel view_model;
private BasicPluginConfigModel config_model;
private String home_page;
private String file_root;
private String resource_root;
private boolean ip_range_all = false;
private IPRange ip_range;
private Properties properties;
public
WebPlugin()
{
properties = new Properties();
}
public
WebPlugin(
Properties defaults )
{
properties = defaults;
}
public void
initialize(
PluginInterface _plugin_interface )
throws PluginException
{
plugin_interface = _plugin_interface;
Integer pr_port = (Integer)properties.get(PR_PORT);
if ( pr_port != null ){
CONFIG_PORT_DEFAULT = pr_port.intValue();
}
String pr_bind_ip = (String)properties.get(PR_BIND_IP);
if ( pr_bind_ip != null ){
CONFIG_BIND_IP_DEFAULT = pr_bind_ip.trim();
}
String pr_root_resource = (String)properties.get( PR_ROOT_RESOURCE );
if( pr_root_resource != null ){
CONFIG_ROOT_RESOURCE_DEFAULT = pr_root_resource;
}
Boolean pr_hide_resource_config = (Boolean)properties.get( PR_HIDE_RESOURCE_CONFIG );
log = (LoggerChannel)properties.get( PR_LOG );
if ( log == null ){
log = plugin_interface.getLogger().getChannel("WebPlugin");
}
UIManager ui_manager = plugin_interface.getUIManager();
view_model = (BasicPluginViewModel)properties.get( PR_VIEW_MODEL );
if ( view_model == null ){
view_model = ui_manager.createBasicPluginViewModel( plugin_interface.getPluginName());
}
String sConfigSectionID = "plugins." + plugin_interface.getPluginID();
view_model.setConfigSectionID(sConfigSectionID);
view_model.getStatus().setText( "Running" );
view_model.getActivity().setVisible( false );
view_model.getProgress().setVisible( false );
log.addListener(
new LoggerChannelListener()
{
public void
messageLogged(
int type,
String message )
{
view_model.getLogArea().appendText( message+"\n");
}
public void
messageLogged(
String str,
Throwable error )
{
view_model.getLogArea().appendText( str + "\n" );
view_model.getLogArea().appendText( error.toString() + "\n" );
}
});
PluginConfig plugin_config = plugin_interface.getPluginconfig();
config_model = (BasicPluginConfigModel)properties.get( PR_CONFIG_MODEL );
if ( config_model == null ){
config_model = ui_manager.createBasicPluginConfigModel(ConfigSection.SECTION_PLUGINS, sConfigSectionID);
}
boolean save_needed = false;
if ( !plugin_config.getPluginBooleanParameter( CONFIG_MIGRATED, false )){
plugin_config.setPluginParameter( CONFIG_MIGRATED, true );
save_needed = true;
plugin_config.setPluginParameter(
CONFIG_PASSWORD_ENABLE,
plugin_config.getBooleanParameter(
"Tracker Password Enable Web", CONFIG_PASSWORD_ENABLE_DEFAULT ));
plugin_config.setPluginParameter(
CONFIG_USER,
plugin_config.getStringParameter(
"Tracker Username", CONFIG_USER_DEFAULT ));
plugin_config.setPluginParameter(
CONFIG_PASSWORD,
plugin_config.getByteParameter(
"Tracker Password", CONFIG_PASSWORD_DEFAULT ));
}
if ( !plugin_config.getPluginBooleanParameter( PROPERTIES_MIGRATED, false )){
plugin_config.setPluginParameter( PROPERTIES_MIGRATED, true );
Properties props = plugin_interface.getPluginProperties();
// make sure we've got an old properties file too
if ( props.getProperty( "port", "" ).length() > 0 ){
save_needed = true;
String prop_port = props.getProperty( "port", ""+CONFIG_PORT_DEFAULT );
String prop_protocol = props.getProperty( "protocol", CONFIG_PROTOCOL_DEFAULT );
String prop_home = props.getProperty( "homepage", CONFIG_HOME_PAGE_DEFAULT );
String prop_rootdir = props.getProperty( "rootdir", CONFIG_ROOT_DIR_DEFAULT );
String prop_rootres = props.getProperty( "rootresource", CONFIG_ROOT_RESOURCE_DEFAULT );
String prop_mode = props.getProperty( "mode", CONFIG_MODE_DEFAULT );
String prop_access = props.getProperty( "access", CONFIG_ACCESS_DEFAULT );
int prop_port_int = CONFIG_PORT_DEFAULT;
try{
prop_port_int = Integer.parseInt( prop_port );
}catch( Throwable e ){
}
plugin_config.setPluginParameter(CONFIG_PORT, prop_port_int );
plugin_config.setPluginParameter(CONFIG_PROTOCOL, prop_protocol );
plugin_config.setPluginParameter(CONFIG_HOME_PAGE, prop_home );
plugin_config.setPluginParameter(CONFIG_ROOT_DIR, prop_rootdir );
plugin_config.setPluginParameter(CONFIG_ROOT_RESOURCE, prop_rootres );
plugin_config.setPluginParameter(CONFIG_MODE, prop_mode );
plugin_config.setPluginParameter(CONFIG_ACCESS, prop_access );
File props_file = new File( plugin_interface.getPluginDirectoryName(), "plugin.properties" );
PrintWriter pw = null;
try{
File backup = new File( plugin_interface.getPluginDirectoryName(), "plugin.properties.bak" );
props_file.renameTo( backup );
pw = new PrintWriter( new FileWriter( props_file ));
pw.println( "plugin.class=" + props.getProperty( "plugin.class" ));
pw.println( "plugin.name=" + props.getProperty( "plugin.name" ));
pw.println( "plugin.version=" + props.getProperty( "plugin.version" ));
pw.println( "plugin.id=" + props.getProperty( "plugin.id" ));
pw.println( "" );
pw.println( "# configuration has been migrated to plugin config - see view->config->plugins" );
pw.println( "# in the SWT user interface" );
log.logAlert( LoggerChannel.LT_INFORMATION,
plugin_interface.getPluginName() + " - plugin.properties settings migrated to plugin configuration." );
}catch( Throwable e ){
Debug.printStackTrace( e );
log.logAlert( LoggerChannel.LT_ERROR,
plugin_interface.getPluginName() + " - plugin.properties settings migration failed." );
}finally{
if ( pw != null ){
pw.close();
}
}
}
}
if ( save_needed ){
plugin_config.save();
}
config_model.addLabelParameter2( "webui.restart.info" );
IntParameter param_port = config_model.addIntParameter2( CONFIG_PORT, "webui.port", CONFIG_PORT_DEFAULT );
StringParameter param_bind = config_model.addStringParameter2( CONFIG_BIND_IP, "webui.bindip", CONFIG_BIND_IP_DEFAULT );
StringListParameter param_protocol =
config_model.addStringListParameter2(
CONFIG_PROTOCOL, "webui.protocol", new String[]{ "http", "https" }, CONFIG_PROTOCOL_DEFAULT );
final BooleanParameter upnp_enable =
config_model.addBooleanParameter2(
CONFIG_UPNP_ENABLE,
"webui.upnpenable",
CONFIG_UPNP_ENABLE_DEFAULT );
StringParameter param_home = config_model.addStringParameter2( CONFIG_HOME_PAGE, "webui.homepage", CONFIG_HOME_PAGE_DEFAULT );
StringParameter param_rootdir = config_model.addStringParameter2( CONFIG_ROOT_DIR, "webui.rootdir", CONFIG_ROOT_DIR_DEFAULT );
StringParameter param_rootres = config_model.addStringParameter2( CONFIG_ROOT_RESOURCE, "webui.rootres", CONFIG_ROOT_RESOURCE_DEFAULT );
if ( pr_hide_resource_config != null && pr_hide_resource_config.booleanValue()){
param_home.setVisible( false );
param_rootdir.setVisible( false );
param_rootres.setVisible( false );
}
config_model.addLabelParameter2( "webui.mode.info" );
config_model.addStringListParameter2(
CONFIG_MODE, "webui.mode", new String[]{ "full", "view" }, CONFIG_MODE_DEFAULT );
config_model.addLabelParameter2( "webui.access.info" );
StringParameter param_access = config_model.addStringParameter2( CONFIG_ACCESS, "webui.access", CONFIG_ACCESS_DEFAULT );
final BooleanParameter pw_enable =
config_model.addBooleanParameter2(
CONFIG_PASSWORD_ENABLE,
"webui.passwordenable",
CONFIG_PASSWORD_ENABLE_DEFAULT );
final StringParameter user_name =
config_model.addStringParameter2(
CONFIG_USER,
"webui.user",
CONFIG_USER_DEFAULT );
final PasswordParameter password =
config_model.addPasswordParameter2(
CONFIG_PASSWORD,
"webui.password",
PasswordParameter.ET_SHA1,
CONFIG_PASSWORD_DEFAULT );
pw_enable.addEnabledOnSelection( user_name );
pw_enable.addEnabledOnSelection( password );
tracker = plugin_interface.getTracker();
home_page = param_home.getValue().trim();
if ( home_page.length() == 0 ){
home_page = null;
}else if (!home_page.startsWith("/" )){
home_page = "/" + home_page;
}
resource_root = param_rootres.getValue().trim();
if ( resource_root.length() == 0 ){
resource_root = null;
}else if ( resource_root.startsWith("/" )){
resource_root = resource_root.substring(1);
}
String root_dir = param_rootdir.getValue().trim();
if ( root_dir.length() == 0 ){
file_root = plugin_interface.getPluginDirectoryName();
if ( file_root == null ){
file_root = SystemProperties.getUserPath() + "web";
}
}else{
// absolute or relative
if ( root_dir.startsWith(File.separator) || root_dir.indexOf(":") != -1 ){
file_root = root_dir;
}else{
file_root = SystemProperties.getUserPath() + "web" + File.separator + root_dir;
}
}
File f_root = new File( file_root );
if ( !f_root.exists()){
String error = "WebPlugin: root dir '" + file_root + "' doesn't exist";
log.log( LoggerChannel.LT_ERROR, error );
throw( new PluginException( error ));
}
if ( !f_root.isDirectory()){
String error = "WebPlugin: root dir '" + file_root + "' isn't a directory";
log.log( LoggerChannel.LT_ERROR, error );
throw( new PluginException( error ));
}
welcome_files = new File[welcome_pages.length];
for (int i=0;i<welcome_pages.length;i++){
welcome_files[i] = new File( file_root + File.separator + welcome_pages[i] );
}
final int port = param_port.getValue();
String protocol_str = param_protocol.getValue().trim();
String bind_ip_str = param_bind.getValue().trim();
InetAddress bind_ip = null;
if ( bind_ip_str.length() > 0 ){
try{
bind_ip = InetAddress.getByName( bind_ip_str );
}catch( Throwable e ){
log.log( LoggerChannel.LT_ERROR, "Bind IP parameter '" + bind_ip_str + "' is invalid" );
}
}
int protocol = protocol_str.equalsIgnoreCase( "HTTP")?Tracker.PR_HTTP:Tracker.PR_HTTPS;
log.log( LoggerChannel.LT_INFORMATION,
"Initialisation: port = " + port +
(bind_ip == null?"":(", bind = " + bind_ip_str + ")")) +
", protocol = " + protocol_str + (root_dir.length()==0?"":(", root = " + root_dir )));
String access_str = param_access.getValue().trim();
if ( access_str.length() > 7 && Character.isDigit(access_str.charAt(0))){
ip_range = plugin_interface.getIPFilter().createRange(true);
int sep = access_str.indexOf("-");
if ( sep == -1 ){
ip_range.setStartIP( access_str );
ip_range.setEndIP( access_str );
}else{
ip_range.setStartIP( access_str.substring(0,sep).trim());
ip_range.setEndIP( access_str.substring( sep+1 ).trim());
}
ip_range.checkValid();
if (!ip_range.isValid()){
log.log( LoggerChannel.LT_ERROR, "Access parameter '" + access_str + "' is invalid" );
ip_range = null;
}
}else{
if ( access_str.equalsIgnoreCase( "all" )){
ip_range_all = true;
}
}
log.log( LoggerChannel.LT_INFORMATION,
"acceptable IP range = " +
( ip_range==null?
(ip_range_all?"all":"local"):
(ip_range.getStartIP() + " - " + ip_range.getEndIP())));
try{
TrackerWebContext context =
tracker.createWebContext(
plugin_interface.getAzureusName() + " - " + plugin_interface.getPluginName(),
port, protocol, bind_ip );
context.addPageGenerator( this );
context.addAuthenticationListener(
new TrackerAuthenticationAdapter()
{
String last_pw = "";
byte[] last_hash = {};
AEMonitor this_mon = new AEMonitor( "WebPlugin:auth" );
public boolean
authenticate(
URL resource,
String user,
String pw )
{
try{
this_mon.enter();
if ( !pw_enable.getValue()){
return( true );
}
if ( !user.equals(user_name.getValue())){
return( false );
}
byte[] hash = last_hash;
if ( !last_pw.equals( pw )){
hash = plugin_interface.getUtilities().getSecurityManager().calculateSHA1( pw.getBytes());
last_pw = pw;
last_hash = hash;
}
return( Arrays.equals( hash, password.getValue()));
}finally{
this_mon.exit();
}
}
});
}catch( TrackerException e ){
log.log( "Plugin Initialisation Fails", e );
}
plugin_interface.addListener(
new PluginListener()
{
public void
initializationComplete()
{
PluginInterface pi_upnp = plugin_interface.getPluginManager().getPluginInterfaceByClass( UPnPPlugin.class );
if ( pi_upnp == null ){
log.log( "No UPnP plugin available, not attempting port mapping");
}else{
if ( upnp_enable.getValue()){
((UPnPPlugin)pi_upnp.getPlugin()).addMapping( plugin_interface.getPluginName(), true, port, true );
}else{
log.log( "UPnP disabled for the plugin, not attempting port mapping");
}
}
}
public void
closedownInitiated()
{
}
public void
closedownComplete()
{
}
});
}
public boolean
generateSupport(
TrackerWebPageRequest request,
TrackerWebPageResponse response )
throws IOException
{
return( false );
}
public boolean
generate(
TrackerWebPageRequest request,
TrackerWebPageResponse response )
throws IOException
{
if ( !ip_range_all ){
String client = request.getClientAddress();
// System.out.println( "client = " + client );
try{
InetAddress ia = InetAddress.getByName( client );
if ( ip_range == null ){
if ( !ia.isLoopbackAddress()){
log.log( LoggerChannel.LT_ERROR, "Client '" + client + "' is not local, rejecting" );
return( false );
}
}else{
if ( !ip_range.isInRange( ia.getHostAddress())){
log.log( LoggerChannel.LT_ERROR, "Client '" + client + "' (" + ia.getHostAddress() + ") is not in range, rejecting" );
return( false );
}
}
}catch( Throwable e ){
Debug.printStackTrace( e );
return( false );
}
}
if ( request.getURL().toString().endsWith(".class")){
System.out.println( "WebPlugin::generate:" + request.getURL());
}
if ( generateSupport( request, response )){
return(true);
}
OutputStream os = response.getOutputStream();
String url = request.getURL();
if (url.equals("/")){
if (home_page != null ){
url = home_page;
}else{
for (int i=0;i<welcome_files.length;i++){
if ( welcome_files[i].exists()){
url = "/" + welcome_pages[i];
break;
}
}
}
}
// first try file system for data
if ( response.useFile( file_root, url )){
return( true );
}
// now try jars
String resource_name = url;
if (resource_name.startsWith("/")){
resource_name = resource_name.substring(1);
}
int pos = resource_name.lastIndexOf(".");
if ( pos != -1 ){
String type = resource_name.substring( pos+1 );
ClassLoader cl = plugin_interface.getPluginClassLoader();
InputStream is = cl.getResourceAsStream( resource_name );
if ( is == null ){
// failed absolute load, try relative
if ( resource_root != null ){
resource_name = resource_root + "/" + resource_name;
is = cl.getResourceAsStream( resource_name );
}
}
// System.out.println( resource_name + "->" + is + ", url = " + url );
if (is != null ){
try{
response.useStream( type, is );
}finally{
is.close();
}
return( true );
}
}
return( false );
}
protected BasicPluginConfigModel
getConfigModel()
{
return( config_model );
}
protected BasicPluginViewModel getViewModel() {
return this.view_model;
}
}