/* * 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.util.zip.GZIPOutputStream; import java.net.*; import org.gudy.azureus2.core3.config.COConfigurationManager; 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 org.json.simple.JSONObject; import com.aelitis.azureus.core.pairing.PairedService; import com.aelitis.azureus.core.pairing.PairedServiceRequestHandler; import com.aelitis.azureus.core.pairing.PairingConnectionData; import com.aelitis.azureus.core.pairing.PairingManager; import com.aelitis.azureus.core.pairing.PairingManagerFactory; import com.aelitis.azureus.core.pairing.PairingManagerListener; import com.aelitis.azureus.plugins.upnp.UPnPMapping; import com.aelitis.azureus.plugins.upnp.UPnPPlugin; import com.aelitis.azureus.util.JSONUtils; public class WebPlugin implements Plugin, TrackerWebPageGenerator { public static final String PR_ENABLE = "Enable"; // Boolean public static final String PR_DISABLABLE = "Disablable"; // Boolean 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_ROOT_DIR = "Root Dir"; // String public static final String PR_ACCESS = "Access"; // String public static final String PR_LOG = "DefaultLoggerChannel"; // LoggerChannel public static final String PR_CONFIG_MODEL_PARAMS = "DefaultConfigModelParams"; // String[] params to use when creating config model 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 PR_ENABLE_KEEP_ALIVE = "DefaultEnableKeepAlive"; // Boolean public static final String PR_PAIRING_SID = "PairingSID"; // String public static final String PROPERTIES_MIGRATED = "Properties Migrated"; public static final String CONFIG_MIGRATED = "Config Migrated"; public static final String PAIRING_MIGRATED = "Pairing Migrated"; public static final String PAIRING_SESSION_KEY = "Pairing Session Key"; public static final String CONFIG_PASSWORD_ENABLE = "Password Enable"; public static final boolean CONFIG_PASSWORD_ENABLE_DEFAULT = false; public static final String CONFIG_PAIRING_ENABLE = "Pairing Enable"; public static final boolean CONFIG_PAIRING_ENABLE_DEFAULT = true; public static final String CONFIG_PORT_OVERRIDE = "Port Override"; public static final String CONFIG_PAIRING_AUTO_AUTH = "Pairing Auto Auth"; public static final boolean CONFIG_PAIRING_AUTO_AUTH_DEFAULT = true; public static final String CONFIG_ENABLE = PR_ENABLE; public boolean CONFIG_ENABLE_DEFAULT = true; public static final String CONFIG_USER = "User"; public static final String CONFIG_USER_DEFAULT = ""; public static final String CONFIG_PASSWORD = "Password"; public static 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 static final String CONFIG_PROTOCOL_DEFAULT = "HTTP"; public static final String CONFIG_UPNP_ENABLE = "UPnP Enable"; public static final boolean CONFIG_UPNP_ENABLE_DEFAULT = true; public static final String CONFIG_HOME_PAGE = "Home Page"; public static final String CONFIG_HOME_PAGE_DEFAULT = "index.html"; public static final String CONFIG_ROOT_DIR = PR_ROOT_DIR; public 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 static final String CONFIG_MODE_DEFAULT = CONFIG_MODE_FULL; public static final String CONFIG_ACCESS = PR_ACCESS; public 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 PluginConfig plugin_config; private BasicPluginViewModel view_model; private BasicPluginConfigModel config_model; private StringParameter param_home; private StringParameter param_rootdir; private StringParameter param_rootres; private IntParameter param_port; private StringListParameter param_protocol; private StringParameter param_bind; private StringParameter param_access; private BooleanParameter p_upnp_enable; private BooleanParameter pw_enable; private StringParameter p_user_name; private PasswordParameter p_password; private BooleanParameter param_auto_auth; private IntParameter param_port_or; private boolean setting_auto_auth; private String pairing_access_code; private String pairing_session_code; private boolean plugin_enabled; private String home_page; private String file_root; private String resource_root; private String root_dir; private boolean ip_range_all = false; private List<IPRange> ip_ranges; private TrackerWebContext tracker_context; private UPnPMapping upnp_mapping; private PairingManagerListener pairing_listener; private Properties properties; private static ThreadLocal<String> tls = new ThreadLocal<String>() { public String initialValue() { return( null ); } }; private static final int LOGOUT_GRACE_MILLIS = 5*1000; private static final String GRACE_PERIOD_MARKER = "<grace_period>"; private Map<String,Long> logout_timer = new HashMap<String, Long>(); public WebPlugin() { properties = new Properties(); } public WebPlugin( Properties defaults ) { properties = defaults; } public void initialize( PluginInterface _plugin_interface ) throws PluginException { plugin_interface = _plugin_interface; plugin_config = plugin_interface.getPluginconfig(); Properties plugin_properties = plugin_interface.getPluginProperties(); if ( plugin_properties != null ){ Object o = plugin_properties.get( "plugin." + PR_ROOT_DIR.replaceAll( " ", "_" )); if ( o instanceof String ){ properties.put( PR_ROOT_DIR, o ); } } Boolean pr_enable = (Boolean)properties.get(PR_ENABLE); if ( pr_enable != null ){ CONFIG_ENABLE_DEFAULT = pr_enable.booleanValue(); } 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; } String pr_root_dir = (String)properties.get( PR_ROOT_DIR ); if( pr_root_dir != null ){ CONFIG_ROOT_DIR_DEFAULT = pr_root_dir; } String pr_access = (String)properties.get( PR_ACCESS ); if( pr_access != null ){ CONFIG_ACCESS_DEFAULT = pr_access; } 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 plugin_id = plugin_interface.getPluginID(); String sConfigSectionID = "plugins." + plugin_id; 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" ); } }); config_model = (BasicPluginConfigModel)properties.get( PR_CONFIG_MODEL ); if ( config_model == null ){ String[] cm_params = (String[])properties.get( PR_CONFIG_MODEL_PARAMS ); if ( cm_params == null || cm_params.length == 0 ){ config_model = ui_manager.createBasicPluginConfigModel(ConfigSection.SECTION_PLUGINS, sConfigSectionID); }else if ( cm_params.length == 1 ){ config_model = ui_manager.createBasicPluginConfigModel( cm_params[0] ); }else{ config_model = ui_manager.createBasicPluginConfigModel( cm_params[0], cm_params[1] ); } } 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(); } Boolean disablable = (Boolean)properties.get( PR_DISABLABLE ); final BooleanParameter param_enable; if ( disablable != null && disablable ){ param_enable = config_model.addBooleanParameter2( CONFIG_ENABLE, "webui.enable", CONFIG_ENABLE_DEFAULT ); plugin_enabled = param_enable.getValue(); }else{ param_enable = null; plugin_enabled = true; } initStage(1); // connection group param_port = config_model.addIntParameter2( CONFIG_PORT, "webui.port", CONFIG_PORT_DEFAULT ); param_bind = config_model.addStringParameter2( CONFIG_BIND_IP, "webui.bindip", CONFIG_BIND_IP_DEFAULT ); param_protocol = config_model.addStringListParameter2( CONFIG_PROTOCOL, "webui.protocol", new String[]{ "http", "https" }, CONFIG_PROTOCOL_DEFAULT ); ParameterListener update_server_listener = new ParameterListener() { public void parameterChanged( Parameter param ) { setupServer(); } }; param_port.addListener( update_server_listener ); param_bind.addListener( update_server_listener ); param_protocol.addListener( update_server_listener ); if ( param_enable != null ){ COConfigurationManager.registerExportedParameter( plugin_id + ".enable", param_enable.getConfigKeyName()); } COConfigurationManager.registerExportedParameter( plugin_id + ".port", param_port.getConfigKeyName()); COConfigurationManager.registerExportedParameter( plugin_id + ".protocol", param_protocol.getConfigKeyName()); p_upnp_enable = config_model.addBooleanParameter2( CONFIG_UPNP_ENABLE, "webui.upnpenable", CONFIG_UPNP_ENABLE_DEFAULT ); p_upnp_enable.addListener( new ParameterListener() { public void parameterChanged( Parameter param ) { setupUPnP(); } }); plugin_interface.addListener( new PluginListener() { public void initializationComplete() { setupUPnP(); } public void closedownInitiated() { } public void closedownComplete() { } }); final String p_sid = (String)properties.get( PR_PAIRING_SID ); final LabelParameter pairing_info; final BooleanParameter pairing_enable; final HyperlinkParameter pairing_test; final HyperlinkParameter connection_test; if ( p_sid != null ){ final PairingManager pm = PairingManagerFactory.getSingleton(); pairing_info = config_model.addLabelParameter2( "webui.pairing.info." + (pm.isEnabled()?"y":"n")); pairing_enable = config_model.addBooleanParameter2( CONFIG_PAIRING_ENABLE, "webui.pairingenable", CONFIG_PAIRING_ENABLE_DEFAULT ); if ( !plugin_config.getPluginBooleanParameter( PAIRING_MIGRATED, false )){ // if they already have a password, don't override it by setting auto-auth boolean has_pw_enabled = plugin_config.getPluginBooleanParameter( CONFIG_PASSWORD_ENABLE, CONFIG_PASSWORD_ENABLE_DEFAULT ); if ( has_pw_enabled ){ plugin_config.setPluginParameter( CONFIG_PAIRING_AUTO_AUTH, false ); } plugin_config.setPluginParameter( PAIRING_MIGRATED, true ); } param_port_or = config_model.addIntParameter2( CONFIG_PORT_OVERRIDE, "webui.port.override", 0 ); param_auto_auth = config_model.addBooleanParameter2( CONFIG_PAIRING_AUTO_AUTH, "webui.pairing.autoauth", CONFIG_PAIRING_AUTO_AUTH_DEFAULT ); param_auto_auth.addListener( new ParameterListener() { public void parameterChanged( Parameter param ) { if ( pairing_enable.getValue() && pm.isEnabled()){ setupAutoAuth(); }else{ setupSessionCode( null ); } } }); connection_test = config_model.addHyperlinkParameter2( "webui.connectiontest", getConnectionTestURL( p_sid )); pairing_test = config_model.addHyperlinkParameter2( "webui.pairingtest", "http://remote.vuze.com/?sid=" + p_sid ); // listeners setup later as they depend on userame params etc String sid_key = "Plugin." + plugin_id + ".pairing.sid"; COConfigurationManager.setStringDefault( sid_key, p_sid ); COConfigurationManager.registerExportedParameter( plugin_id + ".pairing.sid", sid_key); COConfigurationManager.registerExportedParameter( plugin_id + ".pairing.enable", pairing_enable.getConfigKeyName()); COConfigurationManager.registerExportedParameter( plugin_id + ".pairing.auto_auth", param_auto_auth.getConfigKeyName()); }else{ pairing_info = null; pairing_enable = null; param_auto_auth = null; param_port_or = null; pairing_test = null; connection_test = null; } config_model.createGroup( "ConfigView.section.Pairing", new Parameter[]{ pairing_info, pairing_enable, param_port_or, param_auto_auth, connection_test, pairing_test, }); config_model.createGroup( "ConfigView.section.server", new Parameter[]{ param_port, param_bind, param_protocol, p_upnp_enable, }); param_home = config_model.addStringParameter2( CONFIG_HOME_PAGE, "webui.homepage", CONFIG_HOME_PAGE_DEFAULT ); param_rootdir = config_model.addStringParameter2( CONFIG_ROOT_DIR, "webui.rootdir", CONFIG_ROOT_DIR_DEFAULT ); 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 ); }else{ ParameterListener update_resources_listener = new ParameterListener() { public void parameterChanged( Parameter param ) { setupResources(); } }; param_home.addListener( update_resources_listener ); param_rootdir.addListener( update_resources_listener ); param_rootres.addListener( update_resources_listener ); } // access group LabelParameter a_label1 = config_model.addLabelParameter2( "webui.mode.info" ); StringListParameter param_mode = config_model.addStringListParameter2( CONFIG_MODE, "webui.mode", new String[]{ "full", "view" }, CONFIG_MODE_DEFAULT ); LabelParameter a_label2 = config_model.addLabelParameter2( "webui.access.info" ); param_access = config_model.addStringParameter2( CONFIG_ACCESS, "webui.access", CONFIG_ACCESS_DEFAULT ); param_access.addListener( new ParameterListener() { public void parameterChanged( Parameter param ) { setupAccess(); } }); pw_enable = config_model.addBooleanParameter2( CONFIG_PASSWORD_ENABLE, "webui.passwordenable", CONFIG_PASSWORD_ENABLE_DEFAULT ); p_user_name = config_model.addStringParameter2( CONFIG_USER, "webui.user", CONFIG_USER_DEFAULT ); p_password = config_model.addPasswordParameter2( CONFIG_PASSWORD, "webui.password", PasswordParameter.ET_SHA1, CONFIG_PASSWORD_DEFAULT ); pw_enable.addEnabledOnSelection( p_user_name ); pw_enable.addEnabledOnSelection( p_password ); ParameterListener auth_change_listener = new ParameterListener() { public void parameterChanged( Parameter param ) { if ( param_auto_auth != null ){ if ( !setting_auto_auth ){ log( "Disabling pairing auto-authentication as overridden by user" ); param_auto_auth.setValue( false ); } } if ( param == p_user_name || param == p_password ){ setupSessionCode( null ); } } }; p_user_name.addListener( auth_change_listener ); p_password.addListener( auth_change_listener ); pw_enable.addListener( auth_change_listener ); config_model.createGroup( "webui.group.access", new Parameter[]{ a_label1, param_mode, a_label2, param_access, pw_enable, p_user_name, p_password, }); if ( p_sid != null ){ final PairingManager pm = PairingManagerFactory.getSingleton(); pairing_enable.addListener( new ParameterListener() { public void parameterChanged( Parameter param ) { boolean enabled = pairing_enable.getValue(); param_auto_auth.setEnabled( pm.isEnabled() && enabled ); param_port_or.setEnabled( pm.isEnabled() && enabled ); boolean test_ok = pm.isEnabled() && pairing_enable.getValue() && pm.peekAccessCode() != null && !pm.hasActionOutstanding(); pairing_test.setEnabled( test_ok ); connection_test.setEnabled( test_ok ); setupPairing( p_sid, enabled ); } }); pairing_listener = new PairingManagerListener() { public void somethingChanged( PairingManager pm ) { pairing_info.setLabelKey( "webui.pairing.info." + (pm.isEnabled()?"y":"n")); if ( plugin_enabled ){ pairing_enable.setEnabled( pm.isEnabled()); param_auto_auth.setEnabled( pm.isEnabled() && pairing_enable.getValue() ); param_port_or.setEnabled( pm.isEnabled() && pairing_enable.getValue() ); boolean test_ok = pm.isEnabled() && pairing_enable.getValue() && pm.peekAccessCode() != null && !pm.hasActionOutstanding(); pairing_test.setEnabled( test_ok ); connection_test.setEnabled( test_ok ); } connection_test.setHyperlink( getConnectionTestURL( p_sid )); setupPairing( p_sid, pairing_enable.getValue()); } }; pairing_listener.somethingChanged( pm ); pm.addListener( pairing_listener ); setupPairing( p_sid, pairing_enable.getValue()); ParameterListener update_pairing_listener = new ParameterListener() { public void parameterChanged( Parameter param ) { updatePairing( p_sid ); setupUPnP(); } }; param_port.addListener( update_pairing_listener ); param_port_or.addListener( update_pairing_listener ); param_protocol.addListener( update_pairing_listener ); /* config_model.addActionParameter2( "test", "test" ).addListener( new ParameterListener() { public void parameterChanged( Parameter param ) { try{ pm.testService( p_sid, new PairingTestListener() { public void testStarted( PairingTest test ) { System.out.println( "Test starts" ); } public void testComplete( PairingTest test) { System.out.println( "Test complete: " + test.getOutcome() + "/" + test.getErrorMessage()); } }); }catch( Throwable e ){ Debug.out( e ); } } }); */ } if ( param_enable != null ){ final List<Parameter> changed_params = new ArrayList<Parameter>(); if ( !plugin_enabled){ Parameter[] params = config_model.getParameters(); for ( Parameter param: params ){ if ( param == param_enable ){ continue; } if ( param.isEnabled()){ changed_params.add( param ); param.setEnabled( false ); } } } param_enable.addListener( new ParameterListener() { public void parameterChanged( Parameter e_p ) { // this doesn't quite work as tne enabler/disabler parameter logic is implemented // badly and only toggles the UI component, not the enabled state of the // underlying parameter. grr. better than nothing though plugin_enabled = ((BooleanParameter)e_p).getValue(); if ( plugin_enabled ){ for ( Parameter p: changed_params ){ p.setEnabled( true ); } }else{ changed_params.clear(); Parameter[] params = config_model.getParameters(); for ( Parameter param: params ){ if ( param == e_p ){ continue; } if ( param.isEnabled()){ changed_params.add( param ); param.setEnabled( false ); } } } setupServer(); setupUPnP(); if ( p_sid != null ){ setupPairing( p_sid, pairing_enable.getValue()); } } }); } // end config setupResources(); setupAccess(); setupServer(); } protected void initStage( int num ) { } private String getConnectionTestURL( String sid ) { String res = "http://pair.vuze.com/pairing/web/test?sid=" + sid; PairingManager pm = PairingManagerFactory.getSingleton(); if ( pm.isEnabled()){ String ac = pm.peekAccessCode(); if ( ac != null ){ res += "&ac=" + ac; } } return( res ); } protected boolean isPluginEnabled() { return( plugin_enabled ); } protected void unloadPlugin() { if ( view_model != null ){ view_model.destroy(); view_model = null; } if ( config_model != null ){ config_model.destroy(); config_model = null; } if ( tracker_context != null ){ tracker_context.destroy(); tracker_context = null; } if ( upnp_mapping != null ){ upnp_mapping.destroy(); upnp_mapping = null; } if ( pairing_listener != null ){ PairingManager pm = PairingManagerFactory.getSingleton(); pm.removeListener( pairing_listener ); pairing_listener = null; } } private void setupResources() { 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); } 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{ if ( File.separatorChar != '/' && root_dir.contains( "/" )){ root_dir = root_dir.replace( '/', File.separatorChar ); } // try relative to plugin dir file_root = plugin_interface.getPluginDirectoryName(); if ( file_root != null ){ file_root = file_root + File.separator + root_dir; if ( !new File(file_root).exists()){ file_root = null; } } if ( file_root == null ){ 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 ); }else if ( !f_root.isDirectory()){ String error = "WebPlugin: root dir '" + file_root + "' isn't a directory"; log.log( LoggerChannel.LT_ERROR, 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] ); } } private void setupAccess() { String access_str = param_access.getValue().trim(); String ip_ranges_str = ""; ip_ranges = null; ip_range_all = false; if ( access_str.length() > 7 && Character.isDigit(access_str.charAt(0))){ String[] ranges = access_str.replace( ';', ',' ).split( "," ); ip_ranges = new ArrayList<IPRange>(); for ( String range: ranges ){ range = range.trim(); if ( range.length() > 7 ){ IPRange ip_range = plugin_interface.getIPFilter().createRange(true); int sep = range.indexOf("-"); if ( sep == -1 ){ ip_range.setStartIP( range ); ip_range.setEndIP( range ); }else{ ip_range.setStartIP( range.substring(0,sep).trim()); ip_range.setEndIP( range.substring( sep+1 ).trim()); } ip_range.checkValid(); if (!ip_range.isValid()){ log.log( LoggerChannel.LT_ERROR, "Access parameter '" + range + "' is invalid" ); }else{ ip_ranges.add( ip_range ); ip_ranges_str += (ip_ranges_str.length()==0?"":", ") + ip_range.getStartIP() + " - " + ip_range.getEndIP(); } } } if ( ip_ranges.size() == 0 ){ ip_ranges = null; } }else{ if ( access_str.equalsIgnoreCase( "all" ) || access_str.length() == 0 ){ ip_range_all = true; } } log.log( LoggerChannel.LT_INFORMATION, "Acceptable IP range = " + ( ip_ranges==null? (ip_range_all?"all":"local"): (ip_ranges_str))); } protected void setupServer() { try{ if ( !plugin_enabled ){ if ( tracker_context != null ){ tracker_context.destroy(); tracker_context = null; } return; } 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" ); } } if ( tracker_context != null ){ URL url = tracker_context.getURLs()[0]; String existing_protocol = url.getProtocol(); int existing_port = url.getPort()==-1?url.getDefaultPort():url.getPort(); InetAddress existing_bind_ip = tracker_context.getBindIP(); if ( existing_port == port && existing_protocol.equalsIgnoreCase( protocol_str ) && sameAddress( bind_ip, existing_bind_ip )){ return; } tracker_context.destroy(); tracker_context = null; } int protocol = protocol_str.equalsIgnoreCase( "HTTP")?Tracker.PR_HTTP:Tracker.PR_HTTPS; log.log( LoggerChannel.LT_INFORMATION, "Server initialisation: port = " + port + (bind_ip == null?"":(", bind = " + bind_ip_str + ")")) + ", protocol = " + protocol_str + (root_dir.length()==0?"":(", root = " + root_dir ))); tracker_context = plugin_interface.getTracker().createWebContext( Constants.APP_NAME + " - " + plugin_interface.getPluginName(), port, protocol, bind_ip ); Boolean pr_enable_keep_alive = (Boolean)properties.get( PR_ENABLE_KEEP_ALIVE ); if ( pr_enable_keep_alive != null && pr_enable_keep_alive ){ tracker_context.setEnableKeepAlive( true ); } tracker_context.addPageGenerator( this ); tracker_context.addAuthenticationListener( new TrackerAuthenticationAdapter() { private String last_pw = ""; private byte[] last_hash = {}; private final int DELAY = 10*1000; private Map<String,Object[]> fail_map = new HashMap<String, Object[]>(); public boolean authenticate( String headers, URL resource, String user, String pw ) { //System.out.println( resource + ": " + user + "/" + pw ); long now = SystemTime.getMonotonousTime(); String client_address = getHeaderField( headers, "X-Real-IP" ); if ( client_address == null ){ client_address = "<unknown>"; } synchronized( logout_timer ){ Long logout_time = logout_timer.get( client_address ); if ( logout_time != null && now - logout_time <= LOGOUT_GRACE_MILLIS ){ tls.set( GRACE_PERIOD_MARKER ); return( true ); } } boolean result = authenticateSupport( headers, resource, user, pw ); if ( !result ){ // don't delay clients that keep failing to send auth entirely (old Android browsers for example) if ( !pw.equals( "" )){ AESemaphore waiter = null; synchronized( fail_map ){ Object[] x = fail_map.get( client_address ); if ( x == null ){ x = new Object[]{ new AESemaphore( "af:waiter" ), new Long(-1), new Long(-1), now }; fail_map.put( client_address, x ); }else{ x[1] = x[2]; x[2] = x[3]; x[3] = now; long t = (Long)x[1]; if ( now - t < 10*1000 ){ log( "Too many recent authentication failures from '" + client_address + "' - rate limiting" ); x[2] = now+DELAY; // there's a bug where flipping the password on doesn't reset the pw so we automatically fail without checking // this is not the correct fix, but it works last_pw = ""; waiter = (AESemaphore)x[0]; } } } if ( waiter != null ){ waiter.reserve( DELAY ); } } } else { // Some clients have no cookie support and will always try with // no auth info, then, once getting a failed response, try again // with the auth info. // This results in a loop of 1 good, 1 bad. // Prevent this from causing the "too many recent failures" delay to kick in by removing from map // on goodness synchronized( fail_map ){ fail_map.remove( client_address ); } String cookies = getHeaderField( headers, "Cookie" ); if ( pairing_session_code != null ){ if ( cookies == null || !cookies.contains( pairing_session_code )){ tls.set( pairing_session_code ); } } } recordAuthRequest( client_address, result ); if ( !result ){ // going to be generous here as (old android browsers at least) sometimes fail to provide // auth on .png files // no I'm not, too many risks associated with this (e.g. xmwebui has some // prefix url logic which may be exploitable) //if ( resource.getPath().endsWith( ".png" )){ // // result = true; //} } return( result ); } private boolean authenticateSupport( String headers, URL resource, String user, String pw ) { boolean result; boolean auto_auth = param_auto_auth != null && param_auto_auth.getValue(); if ( !pw_enable.getValue()){ result = true; }else{ if ( auto_auth ){ user = user.trim().toLowerCase(); pw = pw.toUpperCase(); } if ( !user.equals( p_user_name.getValue())){ result = false; }else{ byte[] hash = last_hash; if ( !last_pw.equals( pw )){ hash = plugin_interface.getUtilities().getSecurityManager().calculateSHA1( auto_auth?pw.toUpperCase().getBytes():pw.getBytes()); last_pw = pw; last_hash = hash; } result = Arrays.equals( hash, p_password.getValue()); } } if ( result ){ // user name and password match, see if we've come from the pairing process checkCookieSet( headers, resource ); }else if ( auto_auth ){ // either the ac is in the url, referer or we have a cookie set int x = checkCookieSet( headers, resource ); if ( x == 1 ){ result = true; }else if ( x == 0 ){ result = hasOurCookie( getHeaderField( headers, "Cookie" )); } }else{ result = hasOurCookie( getHeaderField( headers, "Cookie" )); } return( result ); } /** * * @param headers * @param resource * @return 0 = unknown, 1 = ok, 2 = bad */ private int checkCookieSet( String headers, URL resource ) { if ( pairing_access_code == null ){ return( 2 ); } String[] locations = { resource.getQuery(), getHeaderField( headers, "Referer" )}; for ( String location: locations ){ if ( location != null ){ boolean skip_fail = false; int param_len = 0; int p1 = location.indexOf( "vuze_pairing_ac=" ); if ( p1 == -1 ){ p1 = location.indexOf( "ac=" ); if ( p1 != -1 ){ param_len = 3; skip_fail = true; } }else{ param_len = 16; } if ( p1 != -1 ){ int p2 = location.indexOf( '&', p1 ); String ac = location.substring( p1+param_len, p2==-1?location.length():p2 ).trim(); p2 = ac.indexOf( '#' ); if ( p2 != -1 ){ ac = ac.substring( 0, p2 ); } if ( ac.equalsIgnoreCase( pairing_access_code )){ tls.set( pairing_session_code ); return( 1 ); }else{ if ( !skip_fail ){ return( 2 ); } } } } } return( 0 ); } private String getHeaderField( String headers, String field ) { String lc_headers = headers.toLowerCase(); int p1 = lc_headers.indexOf( field.toLowerCase() + ":" ); if ( p1 != -1 ){ int p2 = lc_headers.indexOf( '\n', p1 ); if ( p2 != -1 ){ return( headers.substring( p1+field.length()+1, p2 ).trim()); } } return( null ); } }); }catch( TrackerException e ){ log.log( "Server initialisation failed", e ); } } private boolean hasOurCookie( String cookies ) { if ( cookies == null ){ return( false ); } String[] cookie_list = cookies.split( ";" ); for ( String cookie: cookie_list ){ String[] bits = cookie.split( "=" ); if ( bits.length == 2 ){ if ( bits[0].trim().equals( "vuze_pairing_sc" )){ if ( bits[1].trim().equals( pairing_session_code )){ return( true ); } } } } return( false ); } private boolean sameAddress( InetAddress a1, InetAddress a2 ) { if ( a1 == null && a2 == null ){ return( true ); }else if ( a1 == null || a2 == null ){ return( false ); }else{ return( a1.equals( a2 )); } } protected void setupUPnP() { if ( !plugin_enabled || !p_upnp_enable.getValue()){ if ( upnp_mapping != null ){ log( "Removing UPnP mapping" ); upnp_mapping.destroy(); upnp_mapping = null; } return; } PluginInterface pi_upnp = plugin_interface.getPluginManager().getPluginInterfaceByClass( UPnPPlugin.class ); if ( pi_upnp == null ){ log.log( "No UPnP plugin available, not attempting port mapping"); }else{ int port = param_port.getValue(); if ( upnp_mapping != null ){ if ( upnp_mapping.getPort() == port ){ return; } log( "Updating UPnP mapping" ); upnp_mapping.destroy(); }else{ log( "Creating UPnP mapping" ); } upnp_mapping = ((UPnPPlugin)pi_upnp.getPlugin()).addMapping( plugin_interface.getPluginName(), true, port, true ); } } protected void setupPairing( String sid, boolean pairing_enabled ) { PairingManager pm = PairingManagerFactory.getSingleton(); PairedService service = pm.getService( sid ); if ( plugin_enabled && pairing_enabled && pm.isEnabled()){ setupAutoAuth(); if ( service == null ){ log( "Adding pairing service" ); service = pm.addService( sid, new PairedServiceRequestHandler() { public byte[] handleRequest( InetAddress originator, String endpoint_url, byte[] request ) throws IOException { return( handleTunnelRequest( originator, endpoint_url, request )); } }); PairingConnectionData cd = service.getConnectionData(); try{ updatePairing( cd ); }finally{ cd.sync(); } } }else{ pairing_access_code = null; setupSessionCode( null ); if ( service != null ){ log( "Removing pairing service" ); service.remove(); } } } private void setupSessionCode( String key ) { if ( key == null ){ key = Base32.encode( p_user_name.getValue().getBytes()) + Base32.encode( p_password.getValue()); } synchronized( this ){ String existing_key = plugin_config.getPluginStringParameter( PAIRING_SESSION_KEY, "" ); String[] bits = existing_key.split( "=" ); if ( bits.length == 2 && bits[0].equals( key )){ pairing_session_code = bits[1]; }else{ pairing_session_code = Base32.encode( RandomUtils.nextSecureHash()); plugin_config.setPluginParameter( PAIRING_SESSION_KEY, key + "=" + pairing_session_code ); } } } protected void setupAutoAuth() { PairingManager pm = PairingManagerFactory.getSingleton(); String ac = pm.peekAccessCode(); pairing_access_code = ac; // good time to check the default pairing auth settings if ( pairing_access_code != null && param_auto_auth.getValue()){ setupSessionCode( ac ); try{ setting_auto_auth = true; if ( !p_user_name.getValue().equals( "vuze" )){ p_user_name.setValue( "vuze" ); } SHA1Hasher hasher = new SHA1Hasher(); byte[] encoded = hasher.calculateHash( pairing_access_code.getBytes()); if ( !Arrays.equals( p_password.getValue(), encoded )){ p_password.setValue( pairing_access_code ); } if ( !pw_enable.getValue()){ pw_enable.setValue( true ); } }finally{ setting_auto_auth = false; } }else{ setupSessionCode( null ); } } protected void updatePairing( String sid ) { PairingManager pm = PairingManagerFactory.getSingleton(); PairedService service = pm.getService( sid ); if ( service != null ){ PairingConnectionData cd = service.getConnectionData(); log( "Updating pairing information" ); try{ updatePairing( cd ); }finally{ cd.sync(); } } } protected void updatePairing( PairingConnectionData cd ) { cd.setAttribute( PairingConnectionData.ATTR_PORT, String.valueOf( param_port.getValue())); int override = param_port_or==null?0:param_port_or.getValue(); if ( override > 0 ){ cd.setAttribute( PairingConnectionData.ATTR_PORT_OVERRIDE, String.valueOf( override )); }else{ cd.setAttribute( PairingConnectionData.ATTR_PORT_OVERRIDE, null ); } cd.setAttribute( PairingConnectionData.ATTR_PROTOCOL, param_protocol.getValue()); } public int getPort() { return( param_port.getValue()); } public String getProtocol() { return( param_protocol.getValue()); } public void setUserAndPassword( String user, String password ) { p_user_name.setValue( user ); p_password.setValue( password ); pw_enable.setValue( true ); } public void unsetUserAndPassword() { pw_enable.setValue( false ); } private void recordAuthRequest( String client_ip, boolean good ) { PairingManager pm = PairingManagerFactory.getSingleton(); pm.recordRequest( plugin_interface.getPluginName(), client_ip, good ); } private void recordRequest( TrackerWebPageRequest request, boolean good, boolean is_tunnel ) { PairingManager pm = PairingManagerFactory.getSingleton(); String str = request.getClientAddress(); if ( is_tunnel ){ str = "Tunnel (" + str + ")"; } pm.recordRequest( plugin_interface.getPluginName(), str, good ); } public boolean generateSupport( TrackerWebPageRequest request, TrackerWebPageResponse response ) throws IOException { return( false ); } private byte[] handleTunnelRequest( final InetAddress originator, String endpoint_url, final byte[] request_bytes ) throws IOException { int q_pos = endpoint_url.indexOf( '?' ); boolean raw = true; if ( q_pos != -1 ){ String params = endpoint_url.substring( q_pos+1 ); String[] args = params.split( "&" ); String new_endpoint = endpoint_url.substring( 0, q_pos ); String sep = "?"; for ( String arg: args ){ if ( arg.startsWith( "tunnel_format=" )){ String temp = arg.substring( 14 ); if ( temp.startsWith( "h" )){ raw = false; } }else{ new_endpoint += sep + arg; sep = "&"; } } endpoint_url = new_endpoint; } final String f_endpoint_url = endpoint_url; final JSONObject request_headers = new JSONObject(); final int data_start; if ( raw ){ data_start = 0; }else{ int request_header_len = ((request_bytes[0]<<8)&0x0000ff00) | (request_bytes[1]&0x000000ff); String reply_json_str = new String( request_bytes, 2, request_header_len, "UTF-8" ); request_headers.putAll( JSONUtils.decodeJSON( reply_json_str )); data_start = request_header_len + 2; } TrackerWebPageRequest request = new TrackerWebPageRequest() { public Tracker getTracker() { return( null ); } public String getClientAddress() { return( originator.getHostAddress()); } public InetSocketAddress getClientAddress2() { return( new InetSocketAddress( originator, 0 )); } public InetSocketAddress getLocalAddress() { return( new InetSocketAddress( "127.0.0.1", 0 )); } public String getUser() { return( null ); } public String getURL() { String url = (String)request_headers.get( "HTTP-URL" ); if ( url != null ){ return( url ); } return( f_endpoint_url ); } public String getHeader() { return( "" ); } public Map getHeaders() { return( request_headers ); } public InputStream getInputStream() { return( new ByteArrayInputStream( request_bytes, data_start, request_bytes.length - data_start )); } public URL getAbsoluteURL() { try{ return( new URL( "http://127.0.0.1" + getURL())); }catch( Throwable e ){ return( null ); } } public TrackerWebContext getContext() { return( null ); } }; final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final Map reply_headers = new HashMap(); TrackerWebPageResponse response = new TrackerWebPageResponse() { public OutputStream getOutputStream() { return( baos ); } public void setReplyStatus( int status ) { reply_headers.put( "HTTP-Status", String.valueOf( status )); } public void setContentType( String type ) { reply_headers.put( "Content-Type", type ); } public void setLastModified( long time ) { } public void setExpires( long time ) { } public void setHeader( String name, String value ) { reply_headers.put( name, value ); } public void setGZIP( boolean gzip ) { } public boolean useFile( String root_dir, String relative_url ) throws IOException { Debug.out( "Not supported" ); return( false ); } public void useStream( String file_type, InputStream stream ) throws IOException { Debug.out( "Not supported" ); } public void writeTorrent( TrackerTorrent torrent ) throws IOException { Debug.out( "Not supported" ); } public void setAsynchronous( boolean async ) throws IOException { Debug.out( "Not supported" ); } public boolean getAsynchronous() { return( false ); } public OutputStream getRawOutputStream() throws IOException { Debug.out( "Not supported" ); throw( new IOException( "Not supported" )); } }; try{ byte[] bytes; if ( generate2( request, response, true )){ bytes = baos.toByteArray(); }else{ Debug.out( "Tunnelled request not handled: " + request.getURL()); response.setReplyStatus( 404 ); bytes = new byte[0]; } if ( raw ){ return( bytes ); }else{ String accept_encoding = (String)request_headers.get( "Accept-Encoding" ); if ( accept_encoding != null && accept_encoding.contains( "gzip" )){ reply_headers.put( "Content-Encoding", "gzip" ); ByteArrayOutputStream temp = new ByteArrayOutputStream( bytes.length + 512 ); GZIPOutputStream gos = new GZIPOutputStream( temp ); gos.write( bytes ); gos.close(); bytes = temp.toByteArray(); } ByteArrayOutputStream baos2 = new ByteArrayOutputStream( bytes.length + 512 ); String header_json = JSONUtils.encodeToJSON( reply_headers ); byte[] header_bytes = header_json.getBytes( "UTF-8" ); int header_len = header_bytes.length; byte[] header_len_bytes = new byte[]{ (byte)(header_len>>8), (byte)header_len }; baos2.write( header_len_bytes ); baos2.write( header_bytes ); baos2.write( bytes ); return( baos2.toByteArray()); } }catch( Throwable e ){ Debug.out( e ); return( new byte[0] ); } } public boolean generate( TrackerWebPageRequest request, TrackerWebPageResponse response ) throws IOException { String url = request.getURL(); if ( url.startsWith( "/pairing/tunnel/" )){ long error_code = 1; try{ final PairingManager pm = PairingManagerFactory.getSingleton(); if ( pm.isEnabled()){ if ( pm.isSRPEnabled()){ return( pm.handleLocalTunnel( request, response )); }else{ error_code = 5; throw( new IOException( "Secure pairing is not enabled" )); } }else{ error_code = 5; throw( new IOException( "Pairing is not enabled" )); } }catch( Throwable e ){ JSONObject json = new JSONObject(); JSONObject error = new JSONObject(); json.put( "error", error ); error.put( "msg", Debug.getNestedExceptionMessage(e)); error.put( "code", error_code ); return( returnJSON( response, JSONUtils.encodeToJSON( json ))); } } return( generate2( request, response, false )); } private boolean generate2( TrackerWebPageRequest request, TrackerWebPageResponse response, boolean is_tunnel ) throws IOException { // System.out.println( request.getURL()); String client = request.getClientAddress(); if ( !ip_range_all ){ // System.out.println( "client = " + client ); try{ boolean valid_ip = true; InetAddress ia = InetAddress.getByName( client ); if ( ip_ranges == null ){ if ( !ia.isLoopbackAddress()){ log.log( LoggerChannel.LT_ERROR, "Client '" + client + "' is not local, rejecting" ); valid_ip = false; } }else{ boolean ok = false; for ( IPRange range: ip_ranges ){ if ( range.isInRange( ia.getHostAddress())){ ok = true; } } if ( !ok ){ log.log( LoggerChannel.LT_ERROR, "Client '" + client + "' (" + ia.getHostAddress() + ") is not in range, rejecting" ); valid_ip = false; } } if ( !valid_ip ){ response.setReplyStatus( 403 ); recordRequest( request, false, is_tunnel ); return( returnTextPlain( response, "Cannot access resource from this IP address." )); } }catch( Throwable e ){ Debug.printStackTrace( e ); recordRequest( request, false, is_tunnel ); return( false ); } } recordRequest( request, true, is_tunnel ); String url = request.getURL(); if ( url.toString().endsWith(".class")){ System.out.println( "WebPlugin::generate:" + url ); } String cookie_to_set = tls.get(); if ( cookie_to_set == GRACE_PERIOD_MARKER ){ return( returnTextPlain( response, "Logout in progress, please try again later." )); } if ( cookie_to_set != null ){ // set session cookie response.setHeader( "Set-Cookie", "vuze_pairing_sc=" + cookie_to_set + "; path=/; HttpOnly" ); tls.set( null ); } URL full_url = request.getAbsoluteURL(); String full_url_path = full_url.getPath(); if ( full_url_path.equals( "/isPairedServiceAvailable" )){ String redirect = getArgumentFromURL( full_url, "redirect_to" ); if ( redirect != null ){ try{ URL target = new URL( redirect ); String host = target.getHost(); if ( !Constants.isAzureusDomain( host )){ if ( !InetAddress.getByName(host).isLoopbackAddress()){ log( "Invalid redirect host: " + host ); redirect = null; } } }catch( Throwable e ){ Debug.out( e ); redirect = null; } } if ( redirect != null ){ response.setReplyStatus( 302 ); response.setHeader( "Location", redirect ); return( true ); } String callback = getArgumentFromURL( full_url, "jsoncallback" ); if ( callback != null ){ return( returnTextPlain( response, callback + "( {'pairedserviceavailable':true} )")); } }else if ( full_url_path.equals( "/isServicePaired" )){ boolean paired = cookie_to_set != null || hasOurCookie((String)request.getHeaders().get( "cookie" )); // DON'T use returnJSON here as it DOESN'T work in the web ui for some reason! return( returnTextPlain( response, "{ 'servicepaired': " + ( paired?"true":"false" ) + " }" )); }else if ( full_url_path.equals( "/pairedServiceLogout")){ synchronized( logout_timer ){ logout_timer.put( client, SystemTime.getMonotonousTime()); } response.setHeader( "Set-Cookie", "vuze_pairing_sc=<deleted>, expires=" + TimeFormatter.getCookieDate(0)); String redirect = getArgumentFromURL( full_url, "redirect_to" ); if ( redirect != null ){ try{ URL target = new URL( redirect ); String host = target.getHost(); if ( !Constants.isAzureusDomain( host )){ if ( !InetAddress.getByName(host).isLoopbackAddress()){ log( "Invalid redirect host: " + host ); redirect = null; } } }catch( Throwable e ){ Debug.out( e ); redirect = null; } } if ( redirect == null ){ return( returnTextPlain( response, "" )); }else{ response.setReplyStatus( 302 ); response.setHeader( "Location", redirect ); return( true ); } } request.getHeaders().put( "x-vuze-is-tunnel", is_tunnel?"true":"false" ); if ( generateSupport( request, response )){ return(true); } if ( is_tunnel ){ return( false ); } if ( url.equals("/") || url.startsWith( "/?" )){ url = "/"; 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 ); } private String getArgumentFromURL( URL url, String argument ) { String query = url.getQuery(); if ( query != null ){ String[] args = query.split( "&" ); for ( String arg: args ){ String [] x = arg.split( "=" ); if ( x.length == 2 ){ if ( x[0].equals( argument )){ return( UrlUtils.decode( x[1] )); } } } } return( null ); } private boolean returnTextPlain( TrackerWebPageResponse response, String str ) { return( returnStuff( response, "text/plain", str )); } private boolean returnJSON( TrackerWebPageResponse response, String str ) throws IOException { response.setContentType( "application/json; charset=UTF-8" ); OutputStream os = response.getOutputStream(); os.write( str.getBytes( "UTF-8" )); return( true ); } private boolean returnStuff( TrackerWebPageResponse response, String content_type, String str ) { response.setContentType( content_type ); PrintWriter pw = new PrintWriter( response.getOutputStream()); pw.println( str ); pw.flush(); pw.close(); return( true ); } protected BasicPluginConfigModel getConfigModel() { return( config_model ); } protected BasicPluginViewModel getViewModel() { return this.view_model; } protected void log( String str ) { log.log( str ); } protected void log( String str, Throwable e ) { log.log( str, e ); } }