1 /* 2 * File : WebPlugin.java 3 * Created : 23-Jan-2004 4 * By : parg 5 * 6 * Azureus - a Java Bittorrent client 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details ( see the LICENSE file ). 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 */ 22 23 package org.gudy.azureus2.ui.webplugin; 24 25 /** 26 * @author parg 27 * 28 */ 29 30 import java.io.*; 31 import java.util.*; 32 import java.util.zip.GZIPOutputStream; 33 import java.net.*; 34 35 import org.gudy.azureus2.core3.config.COConfigurationManager; 36 import org.gudy.azureus2.core3.util.*; 37 import org.gudy.azureus2.plugins.*; 38 import org.gudy.azureus2.plugins.logging.*; 39 import org.gudy.azureus2.plugins.ipfilter.*; 40 import org.gudy.azureus2.plugins.tracker.*; 41 import org.gudy.azureus2.plugins.tracker.web.*; 42 import org.gudy.azureus2.plugins.ui.*; 43 import org.gudy.azureus2.plugins.ui.config.*; 44 import org.gudy.azureus2.plugins.ui.model.*; 45 import org.json.simple.JSONObject; 46 47 import com.aelitis.azureus.core.networkmanager.admin.NetworkAdmin; 48 import com.aelitis.azureus.core.networkmanager.admin.NetworkAdminPropertyChangeListener; 49 import com.aelitis.azureus.core.pairing.PairedService; 50 import com.aelitis.azureus.core.pairing.PairedServiceRequestHandler; 51 import com.aelitis.azureus.core.pairing.PairingConnectionData; 52 import com.aelitis.azureus.core.pairing.PairingManager; 53 import com.aelitis.azureus.core.pairing.PairingManagerFactory; 54 import com.aelitis.azureus.core.pairing.PairingManagerListener; 55 import com.aelitis.azureus.core.proxy.AEProxyFactory; 56 import com.aelitis.azureus.plugins.upnp.UPnPMapping; 57 import com.aelitis.azureus.plugins.upnp.UPnPPlugin; 58 import com.aelitis.azureus.util.JSONUtils; 59 60 public class 61 WebPlugin 62 implements Plugin, TrackerWebPageGenerator 63 { 64 public static final String PR_ENABLE = "Enable"; // Boolean 65 public static final String PR_DISABLABLE = "Disablable"; // Boolean 66 public static final String PR_PORT = "Port"; // Integer 67 public static final String PR_BIND_IP = "Bind IP"; // String 68 public static final String PR_ROOT_RESOURCE = "Root Resource"; // String 69 public static final String PR_HOME_PAGE = "Home Page"; // String 70 public static final String PR_ROOT_DIR = "Root Dir"; // String 71 public static final String PR_ACCESS = "Access"; // String 72 public static final String PR_LOG = "DefaultLoggerChannel"; // LoggerChannel 73 public static final String PR_CONFIG_MODEL_PARAMS = "DefaultConfigModelParams"; // String[] params to use when creating config model 74 public static final String PR_CONFIG_MODEL = "DefaultConfigModel"; // BasicPluginConfigModel 75 public static final String PR_VIEW_MODEL = "DefaultViewModel"; // BasicPluginViewModel 76 public static final String PR_HIDE_RESOURCE_CONFIG = "DefaultHideResourceConfig"; // Boolean 77 public static final String PR_ENABLE_KEEP_ALIVE = "DefaultEnableKeepAlive"; // Boolean 78 public static final String PR_PAIRING_SID = "PairingSID"; // String 79 public static final String PR_NON_BLOCKING = "NonBlocking"; // Boolean 80 public static final String PR_ENABLE_PAIRING = "EnablePairing"; // Boolean 81 public static final String PR_ENABLE_I2P = "EnableI2P"; // Boolean 82 public static final String PR_ENABLE_TOR = "EnableTor"; // Boolean 83 public static final String PR_ENABLE_UPNP = "EnableUPNP"; // Boolean 84 85 public static final String PROPERTIES_MIGRATED = "Properties Migrated"; 86 public static final String CONFIG_MIGRATED = "Config Migrated"; 87 public static final String PAIRING_MIGRATED = "Pairing Migrated"; 88 public static final String PAIRING_SESSION_KEY = "Pairing Session Key"; 89 90 public static final String CONFIG_PASSWORD_ENABLE = "Password Enable"; 91 public static final boolean CONFIG_PASSWORD_ENABLE_DEFAULT = false; 92 93 public static final String CONFIG_PAIRING_ENABLE = "Pairing Enable"; 94 public static final boolean CONFIG_PAIRING_ENABLE_DEFAULT = true; 95 96 public static final String CONFIG_PORT_OVERRIDE = "Port Override"; 97 98 public static final String CONFIG_PAIRING_AUTO_AUTH = "Pairing Auto Auth"; 99 public static final boolean CONFIG_PAIRING_AUTO_AUTH_DEFAULT = true; 100 101 102 public static final String CONFIG_ENABLE = PR_ENABLE; 103 public boolean CONFIG_ENABLE_DEFAULT = true; 104 105 public static final String CONFIG_USER = "User"; 106 public static final String CONFIG_USER_DEFAULT = ""; 107 108 public static final String CONFIG_PASSWORD = "Password"; 109 public static final byte[] CONFIG_PASSWORD_DEFAULT = {}; 110 111 public static final String CONFIG_PORT = PR_PORT; 112 public int CONFIG_PORT_DEFAULT = 8089; 113 114 public static final String CONFIG_BIND_IP = PR_BIND_IP; 115 public String CONFIG_BIND_IP_DEFAULT = ""; 116 117 public static final String CONFIG_PROTOCOL = "Protocol"; 118 public static final String CONFIG_PROTOCOL_DEFAULT = "HTTP"; 119 120 public static final String CONFIG_UPNP_ENABLE = "UPnP Enable"; 121 public boolean CONFIG_UPNP_ENABLE_DEFAULT = true; 122 123 public static final String CONFIG_HOME_PAGE = PR_HOME_PAGE; 124 public String CONFIG_HOME_PAGE_DEFAULT = "index.html"; 125 126 public static final String CONFIG_ROOT_DIR = PR_ROOT_DIR; 127 public String CONFIG_ROOT_DIR_DEFAULT = ""; 128 129 public static final String CONFIG_ROOT_RESOURCE = PR_ROOT_RESOURCE; 130 public String CONFIG_ROOT_RESOURCE_DEFAULT = ""; 131 132 public static final String CONFIG_MODE = "Mode"; 133 public static final String CONFIG_MODE_FULL = "full"; 134 public static final String CONFIG_MODE_DEFAULT = CONFIG_MODE_FULL; 135 136 public static final String CONFIG_ACCESS = PR_ACCESS; 137 public String CONFIG_ACCESS_DEFAULT = "all"; 138 139 protected static final String NL = "\r\n"; 140 141 protected static final String[] welcome_pages = { "index.html", "index.htm", "index.php", "index.tmpl" }; 142 protected static File[] welcome_files; 143 144 private static final AsyncDispatcher network_dispatcher = new AsyncDispatcher( "webplugin:netdispatch", 5000 ); 145 146 protected PluginInterface plugin_interface; // unfortunately this is accessed by webui - fix sometime 147 148 private LoggerChannel log; 149 private PluginConfig plugin_config; 150 private BasicPluginViewModel view_model; 151 private BasicPluginConfigModel config_model; 152 153 private String p_sid; 154 155 private StringParameter param_home; 156 private StringParameter param_rootdir; 157 private StringParameter param_rootres; 158 159 private IntParameter param_port; 160 private StringListParameter param_protocol; 161 private StringParameter param_bind; 162 163 private StringParameter param_access; 164 165 private InfoParameter param_i2p_dest; 166 private InfoParameter param_tor_dest; 167 168 private BooleanParameter p_upnp_enable; 169 170 private BooleanParameter pw_enable; 171 private StringParameter p_user_name; 172 private PasswordParameter p_password; 173 174 private BooleanParameter param_auto_auth; 175 private IntParameter param_port_or; 176 private boolean setting_auto_auth; 177 private String pairing_access_code; 178 private String pairing_session_code; 179 180 private boolean plugin_enabled; 181 182 private boolean na_intf_listener_added; 183 184 private String home_page; 185 private String file_root; 186 private String resource_root; 187 188 private String root_dir; 189 190 private boolean ip_range_all = false; 191 private List<IPRange> ip_ranges; 192 193 private TrackerWebContext tracker_context; 194 private UPnPMapping upnp_mapping; 195 private PairingManagerListener pairing_listener; 196 197 private Properties properties; 198 199 private static ThreadLocal<String> tls = 200 new ThreadLocal<String>() 201 { 202 public String 203 initialValue() 204 { 205 return( null ); 206 } 207 }; 208 209 private static final int LOGOUT_GRACE_MILLIS = 5*1000; 210 private static final String GRACE_PERIOD_MARKER = "<grace_period>"; 211 212 private Map<String,Long> logout_timer = new HashMap<String, Long>(); 213 214 private boolean unloaded; 215 216 public WebPlugin()217 WebPlugin() 218 { 219 properties = new Properties(); 220 } 221 222 public WebPlugin( Properties defaults )223 WebPlugin( 224 Properties defaults ) 225 { 226 properties = defaults; 227 } 228 229 public void initialize( PluginInterface _plugin_interface )230 initialize( 231 PluginInterface _plugin_interface ) 232 233 throws PluginException 234 { 235 plugin_interface = _plugin_interface; 236 237 plugin_config = plugin_interface.getPluginconfig(); 238 239 Properties plugin_properties = plugin_interface.getPluginProperties(); 240 241 if ( plugin_properties != null ){ 242 243 Object o = plugin_properties.get( "plugin." + PR_ROOT_DIR.replaceAll( " ", "_" )); 244 245 if ( o instanceof String ){ 246 247 properties.put( PR_ROOT_DIR, o ); 248 } 249 } 250 251 Boolean pr_enable = (Boolean)properties.get(PR_ENABLE); 252 253 if ( pr_enable != null ){ 254 255 CONFIG_ENABLE_DEFAULT = pr_enable.booleanValue(); 256 } 257 258 Integer pr_port = (Integer)properties.get(PR_PORT); 259 260 if ( pr_port != null ){ 261 262 CONFIG_PORT_DEFAULT = pr_port.intValue(); 263 } 264 265 String pr_bind_ip = (String)properties.get(PR_BIND_IP); 266 267 if ( pr_bind_ip != null ){ 268 269 CONFIG_BIND_IP_DEFAULT = pr_bind_ip.trim(); 270 } 271 272 String pr_root_resource = (String)properties.get( PR_ROOT_RESOURCE ); 273 274 if( pr_root_resource != null ){ 275 276 CONFIG_ROOT_RESOURCE_DEFAULT = pr_root_resource; 277 } 278 279 String pr_home_page = (String)properties.get( PR_HOME_PAGE ); 280 281 if( pr_home_page != null ){ 282 283 CONFIG_HOME_PAGE_DEFAULT = pr_home_page; 284 } 285 286 String pr_root_dir = (String)properties.get( PR_ROOT_DIR ); 287 288 if( pr_root_dir != null ){ 289 290 CONFIG_ROOT_DIR_DEFAULT = pr_root_dir; 291 } 292 293 String pr_access = (String)properties.get( PR_ACCESS ); 294 295 if( pr_access != null ){ 296 297 CONFIG_ACCESS_DEFAULT = pr_access; 298 } 299 300 Boolean pr_enable_upnp = (Boolean)properties.get(PR_ENABLE_UPNP); 301 302 if ( pr_enable_upnp != null ){ 303 304 CONFIG_UPNP_ENABLE_DEFAULT = pr_enable_upnp.booleanValue(); 305 } 306 307 Boolean pr_hide_resource_config = (Boolean)properties.get( PR_HIDE_RESOURCE_CONFIG ); 308 309 log = (LoggerChannel)properties.get( PR_LOG ); 310 311 if ( log == null ){ 312 313 log = plugin_interface.getLogger().getChannel("WebPlugin"); 314 } 315 316 Boolean prop_pairing_enable = (Boolean)properties.get( PR_ENABLE_PAIRING ); 317 318 if ( prop_pairing_enable == null || prop_pairing_enable ){ 319 320 // default is based on sid availability 321 322 p_sid = (String)properties.get( PR_PAIRING_SID ); 323 } 324 325 UIManager ui_manager = plugin_interface.getUIManager(); 326 327 view_model = (BasicPluginViewModel)properties.get( PR_VIEW_MODEL ); 328 329 if ( view_model == null ){ 330 331 view_model = ui_manager.createBasicPluginViewModel( plugin_interface.getPluginName()); 332 } 333 334 String plugin_id = plugin_interface.getPluginID(); 335 336 String sConfigSectionID = "plugins." + plugin_id; 337 338 view_model.setConfigSectionID(sConfigSectionID); 339 view_model.getStatus().setText( "Running" ); 340 view_model.getActivity().setVisible( false ); 341 view_model.getProgress().setVisible( false ); 342 343 log.addListener( 344 new LoggerChannelListener() 345 { 346 public void 347 messageLogged( 348 int type, 349 String message ) 350 { 351 view_model.getLogArea().appendText( message+"\n"); 352 } 353 354 public void 355 messageLogged( 356 String str, 357 Throwable error ) 358 { 359 view_model.getLogArea().appendText( str + "\n" ); 360 view_model.getLogArea().appendText( error.toString() + "\n" ); 361 } 362 }); 363 364 365 config_model = (BasicPluginConfigModel)properties.get( PR_CONFIG_MODEL ); 366 367 if ( config_model == null ){ 368 369 String[] cm_params = (String[])properties.get( PR_CONFIG_MODEL_PARAMS ); 370 371 if ( cm_params == null || cm_params.length == 0 ){ 372 373 config_model = ui_manager.createBasicPluginConfigModel(ConfigSection.SECTION_PLUGINS, sConfigSectionID); 374 375 }else if ( cm_params.length == 1 ){ 376 377 config_model = ui_manager.createBasicPluginConfigModel( cm_params[0] ); 378 379 }else{ 380 381 config_model = ui_manager.createBasicPluginConfigModel( cm_params[0], cm_params[1] ); 382 } 383 } 384 385 boolean save_needed = false; 386 387 if ( !plugin_config.getPluginBooleanParameter( CONFIG_MIGRATED, false )){ 388 389 plugin_config.setPluginParameter( CONFIG_MIGRATED, true ); 390 391 save_needed = true; 392 393 plugin_config.setPluginParameter( 394 CONFIG_PASSWORD_ENABLE, 395 plugin_config.getBooleanParameter( 396 "Tracker Password Enable Web", CONFIG_PASSWORD_ENABLE_DEFAULT )); 397 398 plugin_config.setPluginParameter( 399 CONFIG_USER, 400 plugin_config.getStringParameter( 401 "Tracker Username", CONFIG_USER_DEFAULT )); 402 403 plugin_config.setPluginParameter( 404 CONFIG_PASSWORD, 405 plugin_config.getByteParameter( 406 "Tracker Password", CONFIG_PASSWORD_DEFAULT )); 407 408 } 409 410 if ( !plugin_config.getPluginBooleanParameter( PROPERTIES_MIGRATED, false )){ 411 412 plugin_config.setPluginParameter( PROPERTIES_MIGRATED, true ); 413 414 Properties props = plugin_interface.getPluginProperties(); 415 416 // make sure we've got an old properties file too 417 418 if ( props.getProperty( "port", "" ).length() > 0 ){ 419 420 save_needed = true; 421 422 String prop_port = props.getProperty( "port", ""+CONFIG_PORT_DEFAULT ); 423 String prop_protocol = props.getProperty( "protocol", CONFIG_PROTOCOL_DEFAULT ); 424 String prop_home = props.getProperty( "homepage", CONFIG_HOME_PAGE_DEFAULT ); 425 String prop_rootdir = props.getProperty( "rootdir", CONFIG_ROOT_DIR_DEFAULT ); 426 String prop_rootres = props.getProperty( "rootresource", CONFIG_ROOT_RESOURCE_DEFAULT ); 427 String prop_mode = props.getProperty( "mode", CONFIG_MODE_DEFAULT ); 428 String prop_access = props.getProperty( "access", CONFIG_ACCESS_DEFAULT ); 429 430 int prop_port_int = CONFIG_PORT_DEFAULT; 431 432 try{ 433 prop_port_int = Integer.parseInt( prop_port ); 434 435 }catch( Throwable e ){ 436 } 437 438 plugin_config.setPluginParameter(CONFIG_PORT, prop_port_int ); 439 plugin_config.setPluginParameter(CONFIG_PROTOCOL, prop_protocol ); 440 plugin_config.setPluginParameter(CONFIG_HOME_PAGE, prop_home ); 441 plugin_config.setPluginParameter(CONFIG_ROOT_DIR, prop_rootdir ); 442 plugin_config.setPluginParameter(CONFIG_ROOT_RESOURCE, prop_rootres ); 443 plugin_config.setPluginParameter(CONFIG_MODE, prop_mode ); 444 plugin_config.setPluginParameter(CONFIG_ACCESS, prop_access ); 445 446 File props_file = new File( plugin_interface.getPluginDirectoryName(), "plugin.properties" ); 447 448 PrintWriter pw = null; 449 450 try{ 451 File backup = new File( plugin_interface.getPluginDirectoryName(), "plugin.properties.bak" ); 452 453 props_file.renameTo( backup ); 454 455 pw = new PrintWriter( new FileWriter( props_file )); 456 457 pw.println( "plugin.class=" + props.getProperty( "plugin.class" )); 458 pw.println( "plugin.name=" + props.getProperty( "plugin.name" )); 459 pw.println( "plugin.version=" + props.getProperty( "plugin.version" )); 460 pw.println( "plugin.id=" + props.getProperty( "plugin.id" )); 461 pw.println( "" ); 462 pw.println( "# configuration has been migrated to plugin config - see view->config->plugins" ); 463 pw.println( "# in the SWT user interface" ); 464 465 log.logAlert( LoggerChannel.LT_INFORMATION, 466 plugin_interface.getPluginName() + " - plugin.properties settings migrated to plugin configuration." ); 467 468 }catch( Throwable e ){ 469 470 Debug.printStackTrace( e ); 471 472 log.logAlert( LoggerChannel.LT_ERROR, 473 plugin_interface.getPluginName() + " - plugin.properties settings migration failed." ); 474 475 }finally{ 476 477 if ( pw != null ){ 478 479 pw.close(); 480 } 481 } 482 } 483 } 484 485 if ( save_needed ){ 486 487 plugin_config.save(); 488 } 489 490 Boolean disablable = (Boolean)properties.get( PR_DISABLABLE ); 491 492 final BooleanParameter param_enable; 493 494 if ( disablable != null && disablable ){ 495 496 param_enable = 497 config_model.addBooleanParameter2( CONFIG_ENABLE, "webui.enable", CONFIG_ENABLE_DEFAULT ); 498 499 plugin_enabled = param_enable.getValue(); 500 501 }else{ 502 param_enable = null; 503 504 plugin_enabled = true; 505 } 506 507 initStage(1); 508 509 // connection group 510 511 param_port = config_model.addIntParameter2( CONFIG_PORT, "webui.port", CONFIG_PORT_DEFAULT ); 512 513 param_port.setGenerateIntermediateEvents( false ); 514 515 param_bind = config_model.addStringParameter2( CONFIG_BIND_IP, "webui.bindip", CONFIG_BIND_IP_DEFAULT ); 516 517 param_bind.setGenerateIntermediateEvents( false ); 518 519 param_protocol = 520 config_model.addStringListParameter2( 521 CONFIG_PROTOCOL, "webui.protocol", new String[]{ "http", "https" }, CONFIG_PROTOCOL_DEFAULT ); 522 523 param_protocol.setGenerateIntermediateEvents( false ); 524 525 ParameterListener update_server_listener = 526 new ParameterListener() 527 { 528 public void 529 parameterChanged( 530 Parameter param ) 531 { 532 setupServer(); 533 } 534 }; 535 536 param_port.addListener( update_server_listener ); 537 param_bind.addListener( update_server_listener ); 538 param_protocol.addListener( update_server_listener ); 539 540 param_i2p_dest = config_model.addInfoParameter2( "webui.i2p_dest", "" ); 541 param_i2p_dest.setVisible( false ); 542 543 param_tor_dest = config_model.addInfoParameter2( "webui.tor_dest", "" ); 544 param_tor_dest.setVisible( false ); 545 546 if ( param_enable != null ){ 547 COConfigurationManager.registerExportedParameter( plugin_id + ".enable", param_enable.getConfigKeyName()); 548 } 549 COConfigurationManager.registerExportedParameter( plugin_id + ".port", param_port.getConfigKeyName()); 550 COConfigurationManager.registerExportedParameter( plugin_id + ".protocol", param_protocol.getConfigKeyName()); 551 552 p_upnp_enable = 553 config_model.addBooleanParameter2( 554 CONFIG_UPNP_ENABLE, 555 "webui.upnpenable", 556 CONFIG_UPNP_ENABLE_DEFAULT ); 557 558 p_upnp_enable.addListener( 559 new ParameterListener() 560 { 561 public void 562 parameterChanged( 563 Parameter param ) 564 { 565 setupUPnP(); 566 } 567 }); 568 569 plugin_interface.addListener( 570 new PluginListener() 571 { 572 public void 573 initializationComplete() 574 { 575 setupUPnP(); 576 } 577 578 public void 579 closedownInitiated() 580 { 581 } 582 583 public void 584 closedownComplete() 585 { 586 } 587 }); 588 589 590 final LabelParameter pairing_info; 591 final BooleanParameter pairing_enable; 592 final HyperlinkParameter pairing_test; 593 final HyperlinkParameter connection_test; 594 595 if ( p_sid != null ){ 596 597 final PairingManager pm = PairingManagerFactory.getSingleton(); 598 599 pairing_info = config_model.addLabelParameter2( "webui.pairing.info." + (pm.isEnabled()?"y":"n")); 600 601 pairing_enable = config_model.addBooleanParameter2( CONFIG_PAIRING_ENABLE, "webui.pairingenable", CONFIG_PAIRING_ENABLE_DEFAULT ); 602 603 if ( !plugin_config.getPluginBooleanParameter( PAIRING_MIGRATED, false )){ 604 605 // if they already have a password, don't override it by setting auto-auth 606 607 boolean has_pw_enabled = plugin_config.getPluginBooleanParameter( CONFIG_PASSWORD_ENABLE, CONFIG_PASSWORD_ENABLE_DEFAULT ); 608 609 if ( has_pw_enabled ){ 610 611 plugin_config.setPluginParameter( CONFIG_PAIRING_AUTO_AUTH, false ); 612 } 613 614 plugin_config.setPluginParameter( PAIRING_MIGRATED, true ); 615 } 616 617 param_port_or = config_model.addIntParameter2( CONFIG_PORT_OVERRIDE, "webui.port.override", 0 ); 618 619 param_auto_auth = config_model.addBooleanParameter2( CONFIG_PAIRING_AUTO_AUTH, "webui.pairing.autoauth", CONFIG_PAIRING_AUTO_AUTH_DEFAULT ); 620 621 param_auto_auth.addListener( 622 new ParameterListener() 623 { 624 public void 625 parameterChanged( 626 Parameter param ) 627 { 628 if ( pairing_enable.getValue() && pm.isEnabled()){ 629 630 setupAutoAuth(); 631 632 }else{ 633 634 setupSessionCode( null ); 635 } 636 } 637 }); 638 639 connection_test = config_model.addHyperlinkParameter2( "webui.connectiontest", getConnectionTestURL( p_sid )); 640 641 pairing_test = config_model.addHyperlinkParameter2( "webui.pairingtest", "http://remote.vuze.com/?sid=" + p_sid ); 642 643 // listeners setup later as they depend on userame params etc 644 645 String sid_key = "Plugin." + plugin_id + ".pairing.sid"; 646 647 COConfigurationManager.setStringDefault( sid_key, p_sid ); 648 649 COConfigurationManager.registerExportedParameter( plugin_id + ".pairing.sid", sid_key); 650 COConfigurationManager.registerExportedParameter( plugin_id + ".pairing.enable", pairing_enable.getConfigKeyName()); 651 COConfigurationManager.registerExportedParameter( plugin_id + ".pairing.auto_auth", param_auto_auth.getConfigKeyName()); 652 653 }else{ 654 pairing_info = null; 655 pairing_enable = null; 656 param_auto_auth = null; 657 param_port_or = null; 658 pairing_test = null; 659 connection_test = null; 660 } 661 662 config_model.createGroup( 663 "ConfigView.section.Pairing", 664 new Parameter[]{ 665 pairing_info, pairing_enable, param_port_or, param_auto_auth, connection_test, pairing_test, 666 }); 667 668 config_model.createGroup( 669 "ConfigView.section.server", 670 new Parameter[]{ 671 param_port, param_bind, param_protocol, param_i2p_dest, param_tor_dest, p_upnp_enable, 672 }); 673 674 param_home = config_model.addStringParameter2( CONFIG_HOME_PAGE, "webui.homepage", CONFIG_HOME_PAGE_DEFAULT ); 675 param_rootdir = config_model.addStringParameter2( CONFIG_ROOT_DIR, "webui.rootdir", CONFIG_ROOT_DIR_DEFAULT ); 676 param_rootres = config_model.addStringParameter2( CONFIG_ROOT_RESOURCE, "webui.rootres", CONFIG_ROOT_RESOURCE_DEFAULT ); 677 678 if ( pr_hide_resource_config != null && pr_hide_resource_config.booleanValue()){ 679 680 param_home.setVisible( false ); 681 param_rootdir.setVisible( false ); 682 param_rootres.setVisible( false ); 683 684 }else{ 685 686 ParameterListener update_resources_listener = 687 new ParameterListener() 688 { 689 public void 690 parameterChanged( 691 Parameter param ) 692 { 693 setupResources(); 694 } 695 }; 696 697 param_home.addListener( update_resources_listener ); 698 param_rootdir.addListener( update_resources_listener ); 699 param_rootres.addListener( update_resources_listener ); 700 } 701 702 // access group 703 704 LabelParameter a_label1 = config_model.addLabelParameter2( "webui.mode.info" ); 705 StringListParameter param_mode = 706 config_model.addStringListParameter2( 707 CONFIG_MODE, "webui.mode", new String[]{ "full", "view" }, CONFIG_MODE_DEFAULT ); 708 709 710 LabelParameter a_label2 = config_model.addLabelParameter2( "webui.access.info" ); 711 712 param_access = config_model.addStringParameter2( CONFIG_ACCESS, "webui.access", CONFIG_ACCESS_DEFAULT ); 713 714 param_access.addListener( 715 new ParameterListener() 716 { 717 public void 718 parameterChanged( 719 Parameter param ) 720 { 721 setupAccess(); 722 } 723 }); 724 725 pw_enable = 726 config_model.addBooleanParameter2( 727 CONFIG_PASSWORD_ENABLE, 728 "webui.passwordenable", 729 CONFIG_PASSWORD_ENABLE_DEFAULT ); 730 731 p_user_name = 732 config_model.addStringParameter2( 733 CONFIG_USER, 734 "webui.user", 735 CONFIG_USER_DEFAULT ); 736 737 p_password = 738 config_model.addPasswordParameter2( 739 CONFIG_PASSWORD, 740 "webui.password", 741 PasswordParameter.ET_SHA1, 742 CONFIG_PASSWORD_DEFAULT ); 743 744 pw_enable.addEnabledOnSelection( p_user_name ); 745 pw_enable.addEnabledOnSelection( p_password ); 746 747 ParameterListener auth_change_listener = 748 new ParameterListener() 749 { 750 public void 751 parameterChanged( 752 Parameter param ) 753 { 754 if ( param_auto_auth != null ){ 755 756 if ( !setting_auto_auth ){ 757 758 log( "Disabling pairing auto-authentication as overridden by user" ); 759 760 param_auto_auth.setValue( false ); 761 } 762 } 763 764 if ( param == p_user_name || param == p_password ){ 765 766 setupSessionCode( null ); 767 } 768 } 769 }; 770 771 p_user_name.addListener( auth_change_listener ); 772 p_password.addListener( auth_change_listener ); 773 pw_enable.addListener( auth_change_listener ); 774 775 config_model.createGroup( 776 "webui.group.access", 777 new Parameter[]{ 778 a_label1, param_mode, a_label2, param_access, 779 pw_enable, p_user_name, p_password, 780 }); 781 782 if ( p_sid != null ){ 783 784 final PairingManager pm = PairingManagerFactory.getSingleton(); 785 786 pairing_enable.addListener( 787 new ParameterListener() 788 { 789 public void 790 parameterChanged( 791 Parameter param ) 792 { 793 boolean enabled = pairing_enable.getValue(); 794 795 param_auto_auth.setEnabled( pm.isEnabled() && enabled ); 796 param_port_or.setEnabled( pm.isEnabled() && enabled ); 797 798 boolean test_ok = pm.isEnabled() && pairing_enable.getValue() && pm.peekAccessCode() != null && !pm.hasActionOutstanding(); 799 800 pairing_test.setEnabled( test_ok ); 801 connection_test.setEnabled( test_ok ); 802 803 setupPairing( p_sid, enabled ); 804 } 805 }); 806 807 pairing_listener = 808 new PairingManagerListener() 809 { 810 public void 811 somethingChanged( 812 PairingManager pm ) 813 { 814 pairing_info.setLabelKey( "webui.pairing.info." + (pm.isEnabled()?"y":"n")); 815 816 if ( plugin_enabled ){ 817 818 pairing_enable.setEnabled( pm.isEnabled()); 819 820 param_auto_auth.setEnabled( pm.isEnabled() && pairing_enable.getValue() ); 821 param_port_or.setEnabled( pm.isEnabled() && pairing_enable.getValue() ); 822 823 boolean test_ok = pm.isEnabled() && pairing_enable.getValue() && pm.peekAccessCode() != null && !pm.hasActionOutstanding(); 824 825 pairing_test.setEnabled( test_ok ); 826 connection_test.setEnabled( test_ok ); 827 } 828 829 connection_test.setHyperlink( getConnectionTestURL( p_sid )); 830 831 setupPairing( p_sid, pairing_enable.getValue()); 832 } 833 }; 834 835 pairing_listener.somethingChanged( pm ); 836 837 pm.addListener( pairing_listener ); 838 839 setupPairing( p_sid, pairing_enable.getValue()); 840 841 ParameterListener update_pairing_listener = 842 new ParameterListener() 843 { 844 public void 845 parameterChanged( 846 Parameter param ) 847 { 848 updatePairing( p_sid ); 849 850 setupUPnP(); 851 } 852 }; 853 854 param_port.addListener( update_pairing_listener ); 855 856 param_port_or.addListener( update_pairing_listener ); 857 858 param_protocol.addListener( update_pairing_listener ); 859 860 /* 861 config_model.addActionParameter2( "test", "test" ).addListener( 862 new ParameterListener() 863 { 864 public void 865 parameterChanged( 866 Parameter param ) 867 { 868 try{ 869 pm.testService( 870 p_sid, 871 new PairingTestListener() 872 { 873 public void 874 testStarted( 875 PairingTest test ) 876 { 877 System.out.println( "Test starts" ); 878 } 879 880 public void 881 testComplete( 882 PairingTest test) 883 { 884 System.out.println( "Test complete: " + test.getOutcome() + "/" + test.getErrorMessage()); 885 } 886 }); 887 }catch( Throwable e ){ 888 889 Debug.out( e ); 890 } 891 } 892 }); 893 */ 894 } 895 896 if ( param_enable != null ){ 897 898 final List<Parameter> changed_params = new ArrayList<Parameter>(); 899 900 if ( !plugin_enabled){ 901 902 Parameter[] params = config_model.getParameters(); 903 904 for ( Parameter param: params ){ 905 906 if ( param == param_enable ){ 907 908 continue; 909 } 910 911 if ( param.isEnabled()){ 912 913 changed_params.add( param ); 914 915 param.setEnabled( false ); 916 } 917 } 918 } 919 920 param_enable.addListener( 921 new ParameterListener() 922 { 923 public void 924 parameterChanged( 925 Parameter e_p ) 926 { 927 // this doesn't quite work as tne enabler/disabler parameter logic is implemented 928 // badly and only toggles the UI component, not the enabled state of the 929 // underlying parameter. grr. better than nothing though 930 931 plugin_enabled = ((BooleanParameter)e_p).getValue(); 932 933 if ( plugin_enabled ){ 934 935 for ( Parameter p: changed_params ){ 936 937 p.setEnabled( true ); 938 } 939 }else{ 940 941 changed_params.clear(); 942 943 Parameter[] params = config_model.getParameters(); 944 945 for ( Parameter param: params ){ 946 947 if ( param == e_p ){ 948 949 continue; 950 } 951 952 if ( param.isEnabled()){ 953 954 changed_params.add( param ); 955 956 param.setEnabled( false ); 957 } 958 } 959 } 960 961 setupServer(); 962 963 setupUPnP(); 964 965 if ( p_sid != null ){ 966 967 setupPairing( p_sid, pairing_enable.getValue()); 968 } 969 } 970 }); 971 } 972 973 // end config 974 975 setupResources(); 976 977 setupAccess(); 978 979 setupServer(); 980 } 981 982 protected void initStage( int num )983 initStage( 984 int num ) 985 { 986 } 987 988 private String getConnectionTestURL( String sid )989 getConnectionTestURL( 990 String sid ) 991 { 992 String res = "http://pair.vuze.com/pairing/web/test?sid=" + sid; 993 994 PairingManager pm = PairingManagerFactory.getSingleton(); 995 996 if ( pm.isEnabled()){ 997 998 String ac = pm.peekAccessCode(); 999 1000 if ( ac != null ){ 1001 1002 res += "&ac=" + ac; 1003 } 1004 } 1005 1006 return( res ); 1007 } 1008 1009 protected boolean isPluginEnabled()1010 isPluginEnabled() 1011 { 1012 return( plugin_enabled ); 1013 } 1014 1015 protected void unloadPlugin()1016 unloadPlugin() 1017 { 1018 if ( view_model != null ){ 1019 1020 view_model.destroy(); 1021 1022 view_model = null; 1023 } 1024 1025 if ( config_model != null ){ 1026 1027 config_model.destroy(); 1028 1029 config_model = null; 1030 } 1031 1032 if ( tracker_context != null ){ 1033 1034 tracker_context.destroy(); 1035 1036 tracker_context = null; 1037 } 1038 1039 if ( upnp_mapping != null ){ 1040 1041 upnp_mapping.destroy(); 1042 1043 upnp_mapping = null; 1044 } 1045 1046 if ( pairing_listener != null ){ 1047 1048 PairingManager pm = PairingManagerFactory.getSingleton(); 1049 1050 pm.removeListener( pairing_listener ); 1051 1052 pairing_listener = null; 1053 } 1054 1055 unloaded = true; 1056 } 1057 1058 private void setupResources()1059 setupResources() 1060 { 1061 home_page = param_home.getValue().trim(); 1062 1063 if ( home_page.length() == 0 ){ 1064 1065 home_page = null; 1066 1067 }else if ( !home_page.startsWith("/" )){ 1068 1069 home_page = "/" + home_page; 1070 } 1071 1072 resource_root = param_rootres.getValue().trim(); 1073 1074 if ( resource_root.length() == 0 ){ 1075 1076 resource_root = null; 1077 1078 }else if ( resource_root.startsWith("/" )){ 1079 1080 resource_root = resource_root.substring(1); 1081 } 1082 1083 root_dir = param_rootdir.getValue().trim(); 1084 1085 if ( root_dir.length() == 0 ){ 1086 1087 file_root = plugin_interface.getPluginDirectoryName(); 1088 1089 if ( file_root == null ){ 1090 1091 file_root = SystemProperties.getUserPath() + "web"; 1092 } 1093 }else{ 1094 1095 // absolute or relative 1096 1097 if ( root_dir.startsWith(File.separator) || root_dir.indexOf(":") != -1 ){ 1098 1099 file_root = root_dir; 1100 1101 }else{ 1102 1103 if ( File.separatorChar != '/' && root_dir.contains( "/" )){ 1104 1105 root_dir = root_dir.replace( '/', File.separatorChar ); 1106 } 1107 1108 // try relative to plugin dir 1109 1110 file_root = plugin_interface.getPluginDirectoryName(); 1111 1112 if ( file_root != null ){ 1113 1114 file_root = file_root + File.separator + root_dir; 1115 1116 if ( !new File(file_root).exists()){ 1117 1118 // try relative to plugin classpath 1119 try { 1120 String pluginClass = plugin_interface.getPluginProperties().getProperty( 1121 "plugin.class"); 1122 file_root = new File( 1123 Class.forName( 1124 pluginClass).getProtectionDomain().getCodeSource().getLocation().getPath(), 1125 root_dir).getAbsolutePath(); 1126 if (!new File(file_root).exists()) { 1127 1128 file_root = null; 1129 } 1130 } catch (Throwable e) { 1131 } 1132 1133 } 1134 } 1135 1136 if ( file_root == null ){ 1137 1138 file_root = SystemProperties.getUserPath() + "web" + File.separator + root_dir; 1139 } 1140 } 1141 } 1142 1143 File f_root = new File( file_root ); 1144 1145 if ( !f_root.exists()){ 1146 1147 String error = "WebPlugin: root dir '" + file_root + "' doesn't exist"; 1148 1149 log.log( LoggerChannel.LT_ERROR, error ); 1150 1151 }else if ( !f_root.isDirectory()){ 1152 1153 String error = "WebPlugin: root dir '" + file_root + "' isn't a directory"; 1154 1155 log.log( LoggerChannel.LT_ERROR, error ); 1156 } 1157 1158 welcome_files = new File[welcome_pages.length]; 1159 1160 for (int i=0;i<welcome_pages.length;i++){ 1161 1162 welcome_files[i] = new File( file_root + File.separator + welcome_pages[i] ); 1163 } 1164 } 1165 1166 private void setupAccess()1167 setupAccess() 1168 { 1169 String access_str = param_access.getValue().trim(); 1170 1171 String ip_ranges_str = ""; 1172 1173 ip_ranges = null; 1174 ip_range_all = false; 1175 1176 if ( access_str.length() > 7 && Character.isDigit(access_str.charAt(0))){ 1177 1178 String[] ranges = access_str.replace( ';', ',' ).split( "," ); 1179 1180 ip_ranges = new ArrayList<IPRange>(); 1181 1182 for ( String range: ranges ){ 1183 1184 range = range.trim(); 1185 1186 if ( range.length() > 7 ){ 1187 1188 IPRange ip_range = plugin_interface.getIPFilter().createRange(true); 1189 1190 int sep = range.indexOf("-"); 1191 1192 if ( sep == -1 ){ 1193 1194 ip_range.setStartIP( range ); 1195 1196 ip_range.setEndIP( range ); 1197 1198 }else{ 1199 1200 ip_range.setStartIP( range.substring(0,sep).trim()); 1201 1202 ip_range.setEndIP( range.substring( sep+1 ).trim()); 1203 } 1204 1205 ip_range.checkValid(); 1206 1207 if (!ip_range.isValid()){ 1208 1209 log.log( LoggerChannel.LT_ERROR, "Access parameter '" + range + "' is invalid" ); 1210 1211 }else{ 1212 1213 ip_ranges.add( ip_range ); 1214 1215 ip_ranges_str += (ip_ranges_str.length()==0?"":", ") + ip_range.getStartIP() + " - " + ip_range.getEndIP(); 1216 } 1217 } 1218 } 1219 1220 if ( ip_ranges.size() == 0 ){ 1221 1222 ip_ranges = null; 1223 } 1224 }else{ 1225 1226 if ( access_str.equalsIgnoreCase( "all" ) || access_str.length() == 0 ){ 1227 1228 ip_range_all = true; 1229 } 1230 } 1231 1232 log.log( LoggerChannel.LT_INFORMATION, 1233 "Acceptable IP range = " + 1234 ( ip_ranges==null? 1235 (ip_range_all?"all":"local"): 1236 (ip_ranges_str))); 1237 } 1238 1239 protected void setupServer()1240 setupServer() 1241 { 1242 try{ 1243 if ( !plugin_enabled ){ 1244 1245 if ( tracker_context != null ){ 1246 1247 tracker_context.destroy(); 1248 1249 tracker_context = null; 1250 } 1251 1252 return; 1253 } 1254 1255 final int port = param_port.getValue(); 1256 1257 String protocol_str = param_protocol.getValue().trim(); 1258 1259 String bind_str = param_bind.getValue().trim(); 1260 1261 InetAddress bind_ip = null; 1262 1263 if ( bind_str.length() > 0 ){ 1264 1265 try{ 1266 bind_ip = InetAddress.getByName( bind_str ); 1267 1268 }catch( Throwable e ){ 1269 } 1270 1271 if ( bind_ip == null ){ 1272 1273 // might be an interface name, see if we can resolve it 1274 1275 final NetworkAdmin na = NetworkAdmin.getSingleton(); 1276 1277 InetAddress[] addresses = na.resolveBindAddresses( bind_str ); 1278 1279 if ( addresses.length > 0 ){ 1280 1281 bind_ip = addresses[0]; 1282 1283 if ( !na_intf_listener_added ){ 1284 1285 na_intf_listener_added = true; 1286 1287 na.addPropertyChangeListener( 1288 new NetworkAdminPropertyChangeListener() 1289 { 1290 public void 1291 propertyChanged( 1292 String property) 1293 { 1294 if ( unloaded ){ 1295 1296 na.removePropertyChangeListener( this ); 1297 1298 }else{ 1299 1300 if ( property == NetworkAdmin.PR_NETWORK_INTERFACES ){ 1301 1302 new AEThread2( "setupserver" ) 1303 { 1304 public void 1305 run() 1306 { 1307 setupServer(); 1308 } 1309 }.start(); 1310 } 1311 } 1312 } 1313 }); 1314 } 1315 } 1316 } 1317 1318 if ( bind_ip == null ){ 1319 1320 log.log( LoggerChannel.LT_ERROR, "Bind IP parameter '" + bind_str + "' is invalid" ); 1321 } 1322 } 1323 1324 if ( tracker_context != null ){ 1325 1326 URL url = tracker_context.getURLs()[0]; 1327 1328 String existing_protocol = url.getProtocol(); 1329 int existing_port = url.getPort()==-1?url.getDefaultPort():url.getPort(); 1330 InetAddress existing_bind_ip = tracker_context.getBindIP(); 1331 1332 if ( existing_port == port && 1333 existing_protocol.equalsIgnoreCase( protocol_str ) && 1334 sameAddress( bind_ip, existing_bind_ip )){ 1335 1336 return; 1337 } 1338 1339 tracker_context.destroy(); 1340 1341 tracker_context = null; 1342 } 1343 1344 1345 1346 int protocol = protocol_str.equalsIgnoreCase( "HTTP")?Tracker.PR_HTTP:Tracker.PR_HTTPS; 1347 1348 Map<String,Object> tc_properties = new HashMap<String, Object>(); 1349 1350 Boolean prop_non_blocking = (Boolean)properties.get( PR_NON_BLOCKING ); 1351 1352 if ( prop_non_blocking != null && prop_non_blocking ){ 1353 1354 tc_properties.put( Tracker.PR_NON_BLOCKING, true ); 1355 } 1356 1357 log.log( LoggerChannel.LT_INFORMATION, 1358 "Server initialisation: port=" + port + 1359 (bind_ip == null?"":(", bind=" + bind_str + "->" + bind_ip + ")")) + 1360 ", protocol=" + protocol_str + 1361 (root_dir.length()==0?"":(", root=" + root_dir )) + 1362 (properties.size()==0?"":(", props=" + properties ))); 1363 1364 tracker_context = 1365 plugin_interface.getTracker().createWebContext( 1366 Constants.APP_NAME + " - " + plugin_interface.getPluginName(), 1367 port, protocol, bind_ip, tc_properties ); 1368 1369 Boolean prop_enable_i2p = (Boolean)properties.get( PR_ENABLE_I2P ); 1370 1371 if ( prop_enable_i2p == null || prop_enable_i2p ){ 1372 1373 network_dispatcher.dispatch( 1374 new AERunnable() 1375 { 1376 public void 1377 runSupport() 1378 { 1379 Map<String,Object> options = new HashMap<String, Object>(); 1380 1381 options.put( AEProxyFactory.SP_PORT, port ); 1382 1383 Map<String,Object> reply = 1384 AEProxyFactory.getPluginServerProxy( 1385 plugin_interface.getPluginName(), 1386 AENetworkClassifier.AT_I2P, 1387 plugin_interface.getPluginID(), 1388 options ); 1389 1390 if ( reply != null ){ 1391 1392 param_i2p_dest.setVisible( true ); 1393 1394 String host = (String)reply.get( "host" ); 1395 1396 if ( !param_i2p_dest.getValue().equals( host )){ 1397 1398 param_i2p_dest.setValue( host ); 1399 1400 if ( p_sid != null ){ 1401 1402 updatePairing( p_sid ); 1403 } 1404 } 1405 } 1406 } 1407 }); 1408 } 1409 1410 Boolean prop_enable_tor = (Boolean)properties.get( PR_ENABLE_TOR ); 1411 1412 if ( prop_enable_tor == null || prop_enable_tor ){ 1413 1414 network_dispatcher.dispatch( 1415 new AERunnable() 1416 { 1417 public void 1418 runSupport() 1419 { 1420 Map<String,Object> options = new HashMap<String, Object>(); 1421 1422 options.put( AEProxyFactory.SP_PORT, port ); 1423 1424 Map<String,Object> reply = 1425 AEProxyFactory.getPluginServerProxy( 1426 plugin_interface.getPluginName(), 1427 AENetworkClassifier.AT_TOR, 1428 plugin_interface.getPluginID(), 1429 options ); 1430 1431 if ( reply != null ){ 1432 1433 param_tor_dest.setVisible( true ); 1434 1435 String host = (String)reply.get( "host" ); 1436 1437 if ( !param_tor_dest.getValue().equals( host )){ 1438 1439 param_tor_dest.setValue( host ); 1440 1441 if ( p_sid != null ){ 1442 1443 updatePairing( p_sid ); 1444 } 1445 } 1446 } 1447 } 1448 }); 1449 } 1450 1451 1452 Boolean pr_enable_keep_alive = (Boolean)properties.get( PR_ENABLE_KEEP_ALIVE ); 1453 1454 if ( pr_enable_keep_alive != null && pr_enable_keep_alive ){ 1455 1456 tracker_context.setEnableKeepAlive( true ); 1457 } 1458 1459 tracker_context.addPageGenerator( this ); 1460 1461 tracker_context.addAuthenticationListener( 1462 new TrackerAuthenticationAdapter() 1463 { 1464 private String last_pw = ""; 1465 private byte[] last_hash = {}; 1466 1467 private final int DELAY = 10*1000; 1468 1469 private Map<String,Object[]> fail_map = new HashMap<String, Object[]>(); 1470 1471 public boolean 1472 authenticate( 1473 String headers, 1474 URL resource, 1475 String user, 1476 String pw ) 1477 { 1478 //System.out.println( resource + ": " + user + "/" + pw ); 1479 1480 long now = SystemTime.getMonotonousTime(); 1481 1482 String client_address = getHeaderField( headers, "X-Real-IP" ); 1483 1484 if ( client_address == null ){ 1485 1486 client_address = "<unknown>"; 1487 } 1488 1489 synchronized( logout_timer ){ 1490 1491 Long logout_time = logout_timer.get( client_address ); 1492 1493 if ( logout_time != null && now - logout_time <= LOGOUT_GRACE_MILLIS ){ 1494 1495 tls.set( GRACE_PERIOD_MARKER ); 1496 1497 return( true ); 1498 } 1499 } 1500 1501 boolean result = authenticateSupport( headers, resource, user, pw ); 1502 1503 if ( !result ){ 1504 1505 // don't delay clients that keep failing to send auth entirely (old Android browsers for example) 1506 1507 if ( !pw.equals( "" )){ 1508 1509 AESemaphore waiter = null; 1510 1511 synchronized( fail_map ){ 1512 1513 1514 Object[] x = fail_map.get( client_address ); 1515 1516 if ( x == null ){ 1517 1518 x = new Object[]{ new AESemaphore( "af:waiter" ), new Long(-1), new Long(-1), now }; 1519 1520 fail_map.put( client_address, x ); 1521 1522 }else{ 1523 1524 x[1] = x[2]; 1525 x[2] = x[3]; 1526 x[3] = now; 1527 1528 long t = (Long)x[1]; 1529 1530 if ( now - t < 10*1000 ){ 1531 1532 log( "Too many recent authentication failures from '" + client_address + "' - rate limiting" ); 1533 1534 x[2] = now+DELAY; 1535 // there's a bug where flipping the password on doesn't reset the pw so we automatically fail without checking 1536 // this is not the correct fix, but it works 1537 last_pw = ""; 1538 waiter = (AESemaphore)x[0]; 1539 } 1540 } 1541 } 1542 1543 if ( waiter != null ){ 1544 1545 waiter.reserve( DELAY ); 1546 } 1547 } 1548 } else { 1549 // Some clients have no cookie support and will always try with 1550 // no auth info, then, once getting a failed response, try again 1551 // with the auth info. 1552 // This results in a loop of 1 good, 1 bad. 1553 // Prevent this from causing the "too many recent failures" delay to kick in by removing from map 1554 // on goodness 1555 1556 synchronized( fail_map ){ 1557 1558 fail_map.remove( client_address ); 1559 } 1560 1561 String cookies = getHeaderField( headers, "Cookie" ); 1562 1563 if ( pairing_session_code != null ){ 1564 1565 if ( cookies == null || !cookies.contains( pairing_session_code )){ 1566 1567 tls.set( pairing_session_code ); 1568 } 1569 } 1570 } 1571 1572 recordAuthRequest( client_address, result ); 1573 1574 if ( !result ){ 1575 1576 // going to be generous here as (old android browsers at least) sometimes fail to provide 1577 // auth on .png files 1578 1579 // no I'm not, too many risks associated with this (e.g. xmwebui has some 1580 // prefix url logic which may be exploitable) 1581 1582 //if ( resource.getPath().endsWith( ".png" )){ 1583 // 1584 // result = true; 1585 //} 1586 } 1587 1588 return( result ); 1589 } 1590 1591 private boolean 1592 authenticateSupport( 1593 String headers, 1594 URL resource, 1595 String user, 1596 String pw ) 1597 { 1598 boolean result; 1599 1600 boolean auto_auth = param_auto_auth != null && param_auto_auth.getValue(); 1601 1602 if ( !pw_enable.getValue()){ 1603 1604 result = true; 1605 1606 }else{ 1607 1608 if ( auto_auth ){ 1609 1610 user = user.trim().toLowerCase(); 1611 1612 pw = pw.toUpperCase(); 1613 } 1614 1615 if ( !user.equals( p_user_name.getValue())){ 1616 1617 result = false; 1618 1619 }else{ 1620 1621 byte[] hash = last_hash; 1622 1623 if ( !last_pw.equals( pw )){ 1624 1625 hash = plugin_interface.getUtilities().getSecurityManager().calculateSHA1( 1626 auto_auth?pw.toUpperCase().getBytes():pw.getBytes()); 1627 1628 last_pw = pw; 1629 last_hash = hash; 1630 } 1631 1632 result = Arrays.equals( hash, p_password.getValue()); 1633 } 1634 } 1635 1636 if ( result ){ 1637 1638 // user name and password match, see if we've come from the pairing process 1639 1640 checkCookieSet( headers, resource ); 1641 1642 }else if ( auto_auth ){ 1643 1644 // either the ac is in the url, referer or we have a cookie set 1645 1646 int x = checkCookieSet( headers, resource ); 1647 1648 if ( x == 1 ){ 1649 1650 result = true; 1651 1652 }else if ( x == 0 ){ 1653 1654 result = hasOurCookie( getHeaderField( headers, "Cookie" )); 1655 } 1656 }else{ 1657 1658 result = hasOurCookie( getHeaderField( headers, "Cookie" )); 1659 } 1660 1661 return( result ); 1662 } 1663 1664 /** 1665 * 1666 * @param headers 1667 * @param resource 1668 * @return 0 = unknown, 1 = ok, 2 = bad 1669 */ 1670 1671 private int 1672 checkCookieSet( 1673 String headers, 1674 URL resource ) 1675 { 1676 if ( pairing_access_code == null ){ 1677 1678 return( 2 ); 1679 } 1680 1681 String[] locations = { resource.getQuery(), getHeaderField( headers, "Referer" )}; 1682 1683 for ( String location: locations ){ 1684 1685 if ( location != null ){ 1686 1687 boolean skip_fail = false; 1688 int param_len = 0; 1689 1690 int p1 = location.indexOf( "vuze_pairing_ac=" ); 1691 1692 if ( p1 == -1 ){ 1693 1694 p1 = location.indexOf( "ac=" ); 1695 1696 if ( p1 != -1 ){ 1697 1698 param_len = 3; 1699 1700 skip_fail = true; 1701 } 1702 }else{ 1703 1704 param_len = 16; 1705 } 1706 1707 if ( p1 != -1 ){ 1708 1709 int p2 = location.indexOf( '&', p1 ); 1710 1711 String ac = location.substring( p1+param_len, p2==-1?location.length():p2 ).trim(); 1712 1713 p2 = ac.indexOf( '#' ); 1714 1715 if ( p2 != -1 ){ 1716 1717 ac = ac.substring( 0, p2 ); 1718 } 1719 1720 if ( ac.equalsIgnoreCase( pairing_access_code )){ 1721 1722 tls.set( pairing_session_code ); 1723 1724 return( 1 ); 1725 1726 }else{ 1727 1728 if ( !skip_fail ){ 1729 1730 return( 2 ); 1731 } 1732 } 1733 } 1734 } 1735 } 1736 1737 return( 0 ); 1738 } 1739 1740 private String 1741 getHeaderField( 1742 String headers, 1743 String field ) 1744 { 1745 String lc_headers = headers.toLowerCase(); 1746 1747 int p1 = lc_headers.indexOf( field.toLowerCase() + ":" ); 1748 1749 if ( p1 != -1 ){ 1750 1751 int p2 = lc_headers.indexOf( '\n', p1 ); 1752 1753 if ( p2 != -1 ){ 1754 1755 return( headers.substring( p1+field.length()+1, p2 ).trim()); 1756 } 1757 } 1758 1759 return( null ); 1760 } 1761 }); 1762 1763 }catch( TrackerException e ){ 1764 1765 log.log( "Server initialisation failed", e ); 1766 } 1767 } 1768 1769 private boolean hasOurCookie( String cookies )1770 hasOurCookie( 1771 String cookies ) 1772 { 1773 if ( cookies == null ){ 1774 1775 return( false ); 1776 } 1777 1778 String[] cookie_list = cookies.split( ";" ); 1779 1780 for ( String cookie: cookie_list ){ 1781 1782 String[] bits = cookie.split( "=" ); 1783 1784 if ( bits.length == 2 ){ 1785 1786 if ( bits[0].trim().equals( "vuze_pairing_sc" )){ 1787 1788 if ( bits[1].trim().equals( pairing_session_code )){ 1789 1790 return( true ); 1791 } 1792 } 1793 } 1794 } 1795 1796 return( false ); 1797 } 1798 1799 private boolean sameAddress( InetAddress a1, InetAddress a2 )1800 sameAddress( 1801 InetAddress a1, 1802 InetAddress a2 ) 1803 { 1804 if ( a1 == null && a2 == null ){ 1805 1806 return( true ); 1807 1808 }else if ( a1 == null || a2 == null ){ 1809 1810 return( false ); 1811 1812 }else{ 1813 1814 return( a1.equals( a2 )); 1815 } 1816 } 1817 1818 protected void setupUPnP()1819 setupUPnP() 1820 { 1821 if ( !plugin_enabled || !p_upnp_enable.getValue()){ 1822 1823 if ( upnp_mapping != null ){ 1824 1825 log( "Removing UPnP mapping" ); 1826 1827 upnp_mapping.destroy(); 1828 1829 upnp_mapping = null; 1830 } 1831 1832 return; 1833 } 1834 1835 PluginInterface pi_upnp = plugin_interface.getPluginManager().getPluginInterfaceByClass( UPnPPlugin.class ); 1836 1837 if ( pi_upnp == null ){ 1838 1839 log.log( "No UPnP plugin available, not attempting port mapping"); 1840 1841 }else{ 1842 1843 int port = param_port.getValue(); 1844 1845 if ( upnp_mapping != null ){ 1846 1847 if ( upnp_mapping.getPort() == port ){ 1848 1849 return; 1850 } 1851 1852 log( "Updating UPnP mapping" ); 1853 1854 upnp_mapping.destroy(); 1855 1856 }else{ 1857 1858 log( "Creating UPnP mapping" ); 1859 } 1860 1861 upnp_mapping = ((UPnPPlugin)pi_upnp.getPlugin()).addMapping( plugin_interface.getPluginName(), true, port, true ); 1862 } 1863 } 1864 1865 protected void setupPairing( String sid, boolean pairing_enabled )1866 setupPairing( 1867 String sid, 1868 boolean pairing_enabled ) 1869 { 1870 PairingManager pm = PairingManagerFactory.getSingleton(); 1871 1872 PairedService service = pm.getService( sid ); 1873 1874 if ( plugin_enabled && pairing_enabled && pm.isEnabled()){ 1875 1876 setupAutoAuth(); 1877 1878 if ( service == null ){ 1879 1880 log( "Adding pairing service" ); 1881 1882 service = 1883 pm.addService( 1884 sid, 1885 new PairedServiceRequestHandler() 1886 { 1887 public byte[] 1888 handleRequest( 1889 InetAddress originator, 1890 String endpoint_url, 1891 byte[] request ) 1892 1893 throws IOException 1894 { 1895 return( handleTunnelRequest( originator, endpoint_url, request )); 1896 } 1897 }); 1898 1899 PairingConnectionData cd = service.getConnectionData(); 1900 1901 try{ 1902 updatePairing( cd ); 1903 1904 }finally{ 1905 1906 cd.sync(); 1907 } 1908 } 1909 }else{ 1910 1911 pairing_access_code = null; 1912 1913 setupSessionCode( null ); 1914 1915 if ( service != null ){ 1916 1917 log( "Removing pairing service" ); 1918 1919 service.remove(); 1920 } 1921 } 1922 } 1923 1924 private void setupSessionCode( String key )1925 setupSessionCode( 1926 String key ) 1927 { 1928 if ( key == null ){ 1929 1930 key = Base32.encode( p_user_name.getValue().getBytes()) + Base32.encode( p_password.getValue()); 1931 } 1932 1933 synchronized( this ){ 1934 1935 String existing_key = plugin_config.getPluginStringParameter( PAIRING_SESSION_KEY, "" ); 1936 1937 String[] bits = existing_key.split( "=" ); 1938 1939 if ( bits.length == 2 && bits[0].equals( key )){ 1940 1941 pairing_session_code = bits[1]; 1942 1943 }else{ 1944 1945 pairing_session_code = Base32.encode( RandomUtils.nextSecureHash()); 1946 1947 plugin_config.setPluginParameter( PAIRING_SESSION_KEY, key + "=" + pairing_session_code ); 1948 } 1949 } 1950 } 1951 1952 protected void setupAutoAuth()1953 setupAutoAuth() 1954 { 1955 PairingManager pm = PairingManagerFactory.getSingleton(); 1956 1957 String ac = pm.peekAccessCode(); 1958 1959 pairing_access_code = ac; 1960 1961 // good time to check the default pairing auth settings 1962 1963 if ( pairing_access_code != null && param_auto_auth.getValue()){ 1964 1965 setupSessionCode( ac ); 1966 1967 try{ 1968 setting_auto_auth = true; 1969 1970 if ( !p_user_name.getValue().equals( "vuze" )){ 1971 1972 p_user_name.setValue( "vuze" ); 1973 } 1974 1975 SHA1Hasher hasher = new SHA1Hasher(); 1976 1977 byte[] encoded = hasher.calculateHash( pairing_access_code.getBytes()); 1978 1979 if ( !Arrays.equals( p_password.getValue(), encoded )){ 1980 1981 p_password.setValue( pairing_access_code ); 1982 } 1983 1984 if ( !pw_enable.getValue()){ 1985 1986 pw_enable.setValue( true ); 1987 } 1988 }finally{ 1989 1990 setting_auto_auth = false; 1991 } 1992 }else{ 1993 1994 setupSessionCode( null ); 1995 } 1996 } 1997 1998 protected void updatePairing( String sid )1999 updatePairing( 2000 String sid ) 2001 { 2002 PairingManager pm = PairingManagerFactory.getSingleton(); 2003 2004 PairedService service = pm.getService( sid ); 2005 2006 if ( service != null ){ 2007 2008 PairingConnectionData cd = service.getConnectionData(); 2009 2010 log( "Updating pairing information" ); 2011 2012 try{ 2013 updatePairing( cd ); 2014 2015 }finally{ 2016 2017 cd.sync(); 2018 } 2019 } 2020 } 2021 2022 protected void updatePairing( PairingConnectionData cd )2023 updatePairing( 2024 PairingConnectionData cd ) 2025 { 2026 cd.setAttribute( PairingConnectionData.ATTR_PORT, String.valueOf( param_port.getValue())); 2027 2028 int override = param_port_or==null?0:param_port_or.getValue(); 2029 2030 if ( override > 0 ){ 2031 2032 cd.setAttribute( PairingConnectionData.ATTR_PORT_OVERRIDE, String.valueOf( override )); 2033 2034 }else{ 2035 2036 cd.setAttribute( PairingConnectionData.ATTR_PORT_OVERRIDE, null ); 2037 } 2038 2039 cd.setAttribute( PairingConnectionData.ATTR_PROTOCOL, param_protocol.getValue()); 2040 2041 if ( param_i2p_dest.isVisible()){ 2042 2043 String host = param_i2p_dest.getValue(); 2044 2045 if ( host.length() > 0 ){ 2046 2047 cd.setAttribute( PairingConnectionData.ATTR_I2P, host ); 2048 } 2049 } 2050 2051 if ( param_tor_dest.isVisible()){ 2052 2053 String host = param_tor_dest.getValue(); 2054 2055 if ( host.length() > 0 ){ 2056 2057 cd.setAttribute( PairingConnectionData.ATTR_TOR, host ); 2058 } 2059 } 2060 } 2061 2062 public InetAddress getServerBindIP()2063 getServerBindIP() 2064 { 2065 if ( tracker_context == null ){ 2066 2067 return( new InetSocketAddress(0).getAddress()); 2068 } 2069 2070 InetAddress address = tracker_context.getBindIP(); 2071 2072 if ( address == null ){ 2073 2074 return( new InetSocketAddress(0).getAddress()); 2075 } 2076 2077 return( address ); 2078 } 2079 2080 public int getServerPort()2081 getServerPort() 2082 { 2083 if ( tracker_context == null ){ 2084 2085 return( 0 ); 2086 } 2087 2088 URL url = tracker_context.getURLs()[0]; 2089 2090 return( url.getPort()==-1?url.getDefaultPort():url.getPort()); 2091 } 2092 2093 public int getPort()2094 getPort() 2095 { 2096 return( param_port.getValue()); 2097 } 2098 2099 public String getProtocol()2100 getProtocol() 2101 { 2102 return( param_protocol.getValue()); 2103 } 2104 2105 public void setUserAndPassword( String user, String password )2106 setUserAndPassword( 2107 String user, 2108 String password ) 2109 { 2110 p_user_name.setValue( user ); 2111 p_password.setValue( password ); 2112 pw_enable.setValue( true ); 2113 } 2114 2115 public void unsetUserAndPassword()2116 unsetUserAndPassword() 2117 { 2118 pw_enable.setValue( false ); 2119 } 2120 2121 private void recordAuthRequest( String client_ip, boolean good )2122 recordAuthRequest( 2123 String client_ip, 2124 boolean good ) 2125 { 2126 PairingManager pm = PairingManagerFactory.getSingleton(); 2127 2128 pm.recordRequest( plugin_interface.getPluginName(), client_ip, good ); 2129 } 2130 2131 private void recordRequest( TrackerWebPageRequest request, boolean good, boolean is_tunnel )2132 recordRequest( 2133 TrackerWebPageRequest request, 2134 boolean good, 2135 boolean is_tunnel ) 2136 { 2137 PairingManager pm = PairingManagerFactory.getSingleton(); 2138 2139 String str = request.getClientAddress(); 2140 2141 if ( is_tunnel ){ 2142 2143 str = "Tunnel (" + str + ")"; 2144 } 2145 2146 pm.recordRequest( plugin_interface.getPluginName(), str, good ); 2147 } 2148 2149 public boolean generateSupport( TrackerWebPageRequest request, TrackerWebPageResponse response )2150 generateSupport( 2151 TrackerWebPageRequest request, 2152 TrackerWebPageResponse response ) 2153 2154 throws IOException 2155 { 2156 return( false ); 2157 } 2158 2159 private byte[] handleTunnelRequest( final InetAddress originator, String endpoint_url, final byte[] request_bytes )2160 handleTunnelRequest( 2161 final InetAddress originator, 2162 String endpoint_url, 2163 final byte[] request_bytes ) 2164 2165 throws IOException 2166 { 2167 int q_pos = endpoint_url.indexOf( '?' ); 2168 2169 boolean raw = true; 2170 2171 if ( q_pos != -1 ){ 2172 2173 String params = endpoint_url.substring( q_pos+1 ); 2174 2175 String[] args = params.split( "&" ); 2176 2177 String new_endpoint = endpoint_url.substring( 0, q_pos ); 2178 2179 String sep = "?"; 2180 2181 for ( String arg: args ){ 2182 2183 if ( arg.startsWith( "tunnel_format=" )){ 2184 2185 String temp = arg.substring( 14 ); 2186 2187 if ( temp.startsWith( "h" )){ 2188 2189 raw = false; 2190 } 2191 }else{ 2192 2193 new_endpoint += sep + arg; 2194 2195 sep = "&"; 2196 } 2197 } 2198 2199 endpoint_url = new_endpoint; 2200 } 2201 2202 final String f_endpoint_url = endpoint_url; 2203 final JSONObject request_headers = new JSONObject(); 2204 2205 final int data_start; 2206 2207 if ( raw ){ 2208 2209 data_start = 0; 2210 2211 }else{ 2212 int request_header_len = ((request_bytes[0]<<8)&0x0000ff00) | (request_bytes[1]&0x000000ff); 2213 2214 String reply_json_str = new String( request_bytes, 2, request_header_len, "UTF-8" ); 2215 2216 request_headers.putAll( JSONUtils.decodeJSON( reply_json_str )); 2217 2218 data_start = request_header_len + 2; 2219 } 2220 2221 TrackerWebPageRequest request = 2222 new TrackerWebPageRequest() 2223 { 2224 public Tracker 2225 getTracker() 2226 { 2227 return( null ); 2228 } 2229 2230 public String 2231 getClientAddress() 2232 { 2233 return( originator.getHostAddress()); 2234 } 2235 2236 public InetSocketAddress 2237 getClientAddress2() 2238 { 2239 return( new InetSocketAddress( originator, 0 )); 2240 } 2241 2242 public InetSocketAddress 2243 getLocalAddress() 2244 { 2245 return( new InetSocketAddress( "127.0.0.1", 0 )); 2246 } 2247 2248 public String 2249 getUser() 2250 { 2251 return( null ); 2252 } 2253 2254 public String 2255 getURL() 2256 { 2257 String url = (String)request_headers.get( "HTTP-URL" ); 2258 2259 if ( url != null ){ 2260 2261 return( url ); 2262 } 2263 2264 return( f_endpoint_url ); 2265 } 2266 2267 public String 2268 getHeader() 2269 { 2270 return( "" ); 2271 } 2272 2273 public Map 2274 getHeaders() 2275 { 2276 return( request_headers ); 2277 } 2278 2279 public InputStream 2280 getInputStream() 2281 { 2282 return( new ByteArrayInputStream( request_bytes, data_start, request_bytes.length - data_start )); 2283 } 2284 2285 public URL 2286 getAbsoluteURL() 2287 { 2288 try{ 2289 return( new URL( "http://127.0.0.1" + getURL())); 2290 2291 }catch( Throwable e ){ 2292 2293 return( null ); 2294 } 2295 } 2296 2297 public TrackerWebContext 2298 getContext() 2299 { 2300 return( null ); 2301 } 2302 }; 2303 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 2304 2305 final Map reply_headers = new HashMap(); 2306 2307 TrackerWebPageResponse response = 2308 new TrackerWebPageResponse() 2309 { 2310 public OutputStream 2311 getOutputStream() 2312 { 2313 return( baos ); 2314 } 2315 2316 public void 2317 setReplyStatus( 2318 int status ) 2319 { 2320 reply_headers.put( "HTTP-Status", String.valueOf( status )); 2321 } 2322 2323 public void 2324 setContentType( 2325 String type ) 2326 { 2327 reply_headers.put( "Content-Type", type ); 2328 } 2329 2330 public void 2331 setLastModified( 2332 long time ) 2333 { 2334 } 2335 2336 public void 2337 setExpires( 2338 long time ) 2339 { 2340 } 2341 2342 public void 2343 setHeader( 2344 String name, 2345 String value ) 2346 { 2347 reply_headers.put( name, value ); 2348 } 2349 2350 public void 2351 setGZIP( 2352 boolean gzip ) 2353 { 2354 } 2355 2356 public boolean 2357 useFile( 2358 String root_dir, 2359 String relative_url ) 2360 2361 throws IOException 2362 { 2363 Debug.out( "Not supported" ); 2364 2365 return( false ); 2366 } 2367 2368 public void 2369 useStream( 2370 String file_type, 2371 InputStream stream ) 2372 2373 throws IOException 2374 { 2375 Debug.out( "Not supported" ); 2376 } 2377 2378 public void 2379 writeTorrent( 2380 TrackerTorrent torrent ) 2381 2382 throws IOException 2383 { 2384 Debug.out( "Not supported" ); 2385 } 2386 2387 public void 2388 setAsynchronous( 2389 boolean async ) 2390 2391 throws IOException 2392 { 2393 Debug.out( "Not supported" ); 2394 } 2395 2396 public boolean 2397 getAsynchronous() 2398 { 2399 return( false ); 2400 } 2401 2402 public OutputStream 2403 getRawOutputStream() 2404 2405 throws IOException 2406 { 2407 Debug.out( "Not supported" ); 2408 2409 throw( new IOException( "Not supported" )); 2410 } 2411 2412 public boolean 2413 isActive() 2414 { 2415 return( true ); 2416 } 2417 }; 2418 2419 try{ 2420 byte[] bytes; 2421 2422 if ( generate2( request, response, true )){ 2423 2424 bytes = baos.toByteArray(); 2425 2426 }else{ 2427 2428 Debug.out( "Tunnelled request not handled: " + request.getURL()); 2429 2430 response.setReplyStatus( 404 ); 2431 2432 bytes = new byte[0]; 2433 } 2434 2435 if ( raw ){ 2436 2437 return( bytes ); 2438 2439 }else{ 2440 2441 String accept_encoding = (String)request_headers.get( "Accept-Encoding" ); 2442 2443 if ( accept_encoding != null && accept_encoding.contains( "gzip" )){ 2444 2445 reply_headers.put( "Content-Encoding", "gzip" ); 2446 2447 ByteArrayOutputStream temp = new ByteArrayOutputStream( bytes.length + 512 ); 2448 2449 GZIPOutputStream gos = new GZIPOutputStream( temp ); 2450 2451 gos.write( bytes ); 2452 2453 gos.close(); 2454 2455 bytes = temp.toByteArray(); 2456 } 2457 2458 ByteArrayOutputStream baos2 = new ByteArrayOutputStream( bytes.length + 512 ); 2459 2460 String header_json = JSONUtils.encodeToJSON( reply_headers ); 2461 2462 byte[] header_bytes = header_json.getBytes( "UTF-8" ); 2463 2464 int header_len = header_bytes.length; 2465 2466 byte[] header_len_bytes = new byte[]{ (byte)(header_len>>8), (byte)header_len }; 2467 2468 baos2.write( header_len_bytes ); 2469 baos2.write( header_bytes ); 2470 baos2.write( bytes ); 2471 2472 return( baos2.toByteArray()); 2473 } 2474 }catch( Throwable e ){ 2475 2476 Debug.out( e ); 2477 2478 return( new byte[0] ); 2479 } 2480 } 2481 2482 public boolean generate( TrackerWebPageRequest request, TrackerWebPageResponse response )2483 generate( 2484 TrackerWebPageRequest request, 2485 TrackerWebPageResponse response ) 2486 2487 throws IOException 2488 { 2489 String url = request.getURL(); 2490 2491 if ( url.startsWith( "/pairing/tunnel/" )){ 2492 2493 long error_code = 1; 2494 2495 try{ 2496 final PairingManager pm = PairingManagerFactory.getSingleton(); 2497 2498 if ( pm.isEnabled()){ 2499 2500 if ( pm.isSRPEnabled()){ 2501 2502 return( pm.handleLocalTunnel( request, response )); 2503 2504 }else{ 2505 2506 error_code = 5; 2507 2508 throw( new IOException( "Secure pairing is not enabled" )); 2509 } 2510 }else{ 2511 2512 error_code = 5; 2513 2514 throw( new IOException( "Pairing is not enabled" )); 2515 } 2516 }catch( Throwable e ){ 2517 2518 JSONObject json = new JSONObject(); 2519 2520 JSONObject error = new JSONObject(); 2521 2522 json.put( "error", error ); 2523 2524 error.put( "msg", Debug.getNestedExceptionMessage(e)); 2525 error.put( "code", error_code ); 2526 2527 return( returnJSON( response, JSONUtils.encodeToJSON( json ))); 2528 } 2529 } 2530 2531 return( generate2( request, response, false )); 2532 } 2533 2534 private boolean generate2( TrackerWebPageRequest request, TrackerWebPageResponse response, boolean is_tunnel )2535 generate2( 2536 TrackerWebPageRequest request, 2537 TrackerWebPageResponse response, 2538 boolean is_tunnel ) 2539 2540 throws IOException 2541 { 2542 // System.out.println( request.getURL()); 2543 2544 String client = request.getClientAddress(); 2545 2546 if ( !ip_range_all ){ 2547 2548 // System.out.println( "client = " + client ); 2549 2550 try{ 2551 boolean valid_ip = true; 2552 2553 InetAddress client_ia = InetAddress.getByName( client ); 2554 2555 if ( ip_ranges == null ){ 2556 2557 if ( !client_ia.isLoopbackAddress()){ 2558 2559 InetAddress bind_ia = getServerBindIP(); 2560 2561 if ( bind_ia.isAnyLocalAddress() || !bind_ia.equals( client_ia )){ 2562 2563 log.log( LoggerChannel.LT_ERROR, "Client '" + client + "' is not local, rejecting" ); 2564 2565 valid_ip = false; 2566 } 2567 } 2568 }else{ 2569 2570 boolean ok = false; 2571 2572 for ( IPRange range: ip_ranges ){ 2573 2574 if ( range.isInRange( client_ia.getHostAddress())){ 2575 2576 ok = true; 2577 } 2578 } 2579 2580 if ( !ok ){ 2581 2582 log.log( LoggerChannel.LT_ERROR, "Client '" + client + "' (" + client_ia.getHostAddress() + ") is not in range, rejecting" ); 2583 2584 valid_ip = false; 2585 } 2586 } 2587 2588 if ( !valid_ip ){ 2589 2590 response.setReplyStatus( 403 ); 2591 2592 recordRequest( request, false, is_tunnel ); 2593 2594 return( returnTextPlain( response, "Cannot access resource from this IP address." )); 2595 } 2596 2597 }catch( Throwable e ){ 2598 2599 Debug.printStackTrace( e ); 2600 2601 recordRequest( request, false, is_tunnel ); 2602 2603 return( false ); 2604 } 2605 } 2606 2607 recordRequest( request, true, is_tunnel ); 2608 2609 String url = request.getURL(); 2610 2611 if ( url.toString().endsWith(".class")){ 2612 2613 System.out.println( "WebPlugin::generate:" + url ); 2614 } 2615 2616 String cookie_to_set = tls.get(); 2617 2618 if ( cookie_to_set == GRACE_PERIOD_MARKER ){ 2619 2620 return( returnTextPlain( response, "Logout in progress, please try again later." )); 2621 } 2622 2623 if ( cookie_to_set != null ){ 2624 2625 // set session cookie 2626 2627 response.setHeader( "Set-Cookie", "vuze_pairing_sc=" + cookie_to_set + "; path=/; HttpOnly" ); 2628 2629 tls.set( null ); 2630 } 2631 2632 URL full_url = request.getAbsoluteURL(); 2633 2634 String full_url_path = full_url.getPath(); 2635 2636 if ( full_url_path.equals( "/isPairedServiceAvailable" )){ 2637 2638 String redirect = getArgumentFromURL( full_url, "redirect_to" ); 2639 2640 if ( redirect != null ){ 2641 2642 try{ 2643 URL target = new URL( redirect ); 2644 2645 String host = target.getHost(); 2646 2647 if ( !Constants.isAzureusDomain( host )){ 2648 2649 if ( !InetAddress.getByName(host).isLoopbackAddress()){ 2650 2651 log( "Invalid redirect host: " + host ); 2652 2653 redirect = null; 2654 } 2655 } 2656 }catch( Throwable e ){ 2657 2658 Debug.out( e ); 2659 2660 redirect = null; 2661 } 2662 } 2663 2664 if ( redirect != null ){ 2665 2666 response.setReplyStatus( 302 ); 2667 2668 response.setHeader( "Location", redirect ); 2669 2670 return( true ); 2671 } 2672 2673 String callback = getArgumentFromURL( full_url, "jsoncallback" ); 2674 2675 if ( callback != null ){ 2676 2677 return( returnTextPlain( response, callback + "( {'pairedserviceavailable':true} )")); 2678 } 2679 }else if ( full_url_path.equals( "/isServicePaired" )){ 2680 2681 boolean paired = cookie_to_set != null || hasOurCookie((String)request.getHeaders().get( "cookie" )); 2682 2683 // DON'T use returnJSON here as it DOESN'T work in the web ui for some reason! 2684 2685 return( returnTextPlain( response, "{ 'servicepaired': " + ( paired?"true":"false" ) + " }" )); 2686 2687 }else if ( full_url_path.equals( "/pairedServiceLogout")){ 2688 2689 synchronized( logout_timer ){ 2690 2691 logout_timer.put( client, SystemTime.getMonotonousTime()); 2692 } 2693 2694 response.setHeader( "Set-Cookie", "vuze_pairing_sc=<deleted>, expires=" + TimeFormatter.getCookieDate(0)); 2695 2696 String redirect = getArgumentFromURL( full_url, "redirect_to" ); 2697 2698 if ( redirect != null ){ 2699 2700 try{ 2701 URL target = new URL( redirect ); 2702 2703 String host = target.getHost(); 2704 2705 if ( !Constants.isAzureusDomain( host )){ 2706 2707 if ( !InetAddress.getByName(host).isLoopbackAddress()){ 2708 2709 log( "Invalid redirect host: " + host ); 2710 2711 redirect = null; 2712 } 2713 } 2714 }catch( Throwable e ){ 2715 2716 Debug.out( e ); 2717 2718 redirect = null; 2719 } 2720 } 2721 if ( redirect == null ){ 2722 2723 return( returnTextPlain( response, "" )); 2724 2725 }else{ 2726 2727 response.setReplyStatus( 302 ); 2728 2729 response.setHeader( "Location", redirect ); 2730 2731 return( true ); 2732 } 2733 } 2734 2735 request.getHeaders().put( "x-vuze-is-tunnel", is_tunnel?"true":"false" ); 2736 2737 if ( generateSupport( request, response )){ 2738 2739 return(true); 2740 } 2741 2742 if ( is_tunnel ){ 2743 2744 return( false ); 2745 } 2746 2747 if ( url.equals("/") || url.startsWith( "/?" )){ 2748 2749 url = "/"; 2750 2751 if ( home_page != null ){ 2752 2753 url = home_page; 2754 2755 }else{ 2756 2757 for (int i=0;i<welcome_files.length;i++){ 2758 2759 if ( welcome_files[i].exists()){ 2760 2761 url = "/" + welcome_pages[i]; 2762 2763 break; 2764 } 2765 } 2766 } 2767 } 2768 2769 // first try file system for data 2770 2771 if ( useFile( request, response, file_root, UrlUtils.decode( url ))){ 2772 2773 return( true ); 2774 } 2775 2776 // now try jars 2777 2778 String resource_name = url; 2779 2780 if (resource_name.startsWith("/")){ 2781 2782 resource_name = resource_name.substring(1); 2783 } 2784 2785 int pos = resource_name.lastIndexOf("."); 2786 2787 if ( pos != -1 ){ 2788 2789 String type = resource_name.substring( pos+1 ); 2790 2791 ClassLoader cl = plugin_interface.getPluginClassLoader(); 2792 2793 InputStream is = cl.getResourceAsStream( resource_name ); 2794 2795 if ( is == null ){ 2796 2797 // failed absolute load, try relative 2798 2799 if ( resource_root != null ){ 2800 2801 resource_name = resource_root + "/" + resource_name; 2802 2803 is = cl.getResourceAsStream( resource_name ); 2804 } 2805 } 2806 2807 // System.out.println( resource_name + "->" + is + ", url = " + url ); 2808 2809 if (is != null ){ 2810 2811 try{ 2812 response.useStream( type, is ); 2813 2814 }finally{ 2815 2816 is.close(); 2817 } 2818 2819 return( true ); 2820 } 2821 } 2822 2823 return( false ); 2824 } 2825 2826 /** 2827 * this method can be over-ridden to handle custom file delivery 2828 * @param request 2829 * @param response 2830 * @param root 2831 * @param relative_url 2832 * @return 2833 * @throws IOException 2834 */ 2835 2836 protected boolean useFile( TrackerWebPageRequest request, TrackerWebPageResponse response, String root, String relative_url )2837 useFile( 2838 TrackerWebPageRequest request, 2839 TrackerWebPageResponse response, 2840 String root, 2841 String relative_url ) 2842 2843 throws IOException 2844 { 2845 return( response.useFile( file_root, relative_url )); 2846 } 2847 2848 private String getArgumentFromURL( URL url, String argument )2849 getArgumentFromURL( 2850 URL url, 2851 String argument ) 2852 { 2853 String query = url.getQuery(); 2854 2855 if ( query != null ){ 2856 2857 String[] args = query.split( "&" ); 2858 2859 for ( String arg: args ){ 2860 2861 String [] x = arg.split( "=" ); 2862 2863 if ( x.length == 2 ){ 2864 2865 if ( x[0].equals( argument )){ 2866 2867 return( UrlUtils.decode( x[1] )); 2868 } 2869 } 2870 } 2871 } 2872 2873 return( null ); 2874 } 2875 2876 private boolean returnTextPlain( TrackerWebPageResponse response, String str )2877 returnTextPlain( 2878 TrackerWebPageResponse response, 2879 String str ) 2880 { 2881 return( returnStuff( response, "text/plain", str )); 2882 } 2883 2884 private boolean returnJSON( TrackerWebPageResponse response, String str )2885 returnJSON( 2886 TrackerWebPageResponse response, 2887 String str ) 2888 2889 throws IOException 2890 { 2891 response.setContentType( "application/json; charset=UTF-8" ); 2892 2893 OutputStream os = response.getOutputStream(); 2894 2895 os.write( str.getBytes( "UTF-8" )); 2896 2897 return( true ); 2898 } 2899 2900 private boolean returnStuff( TrackerWebPageResponse response, String content_type, String str )2901 returnStuff( 2902 TrackerWebPageResponse response, 2903 String content_type, 2904 String str ) 2905 { 2906 response.setContentType( content_type ); 2907 2908 PrintWriter pw = new PrintWriter( response.getOutputStream()); 2909 2910 pw.println( str ); 2911 2912 pw.flush(); 2913 2914 pw.close(); 2915 2916 return( true ); 2917 } 2918 2919 protected BasicPluginConfigModel getConfigModel()2920 getConfigModel() 2921 { 2922 return( config_model ); 2923 } 2924 getViewModel()2925 protected BasicPluginViewModel getViewModel() { 2926 return this.view_model; 2927 } 2928 2929 protected void log( String str )2930 log( 2931 String str ) 2932 { 2933 log.log( str ); 2934 } 2935 2936 protected void log( String str, Throwable e )2937 log( 2938 String str, 2939 Throwable e ) 2940 { 2941 log.log( str, e ); 2942 } 2943 } 2944