1 /* 2 * File : TRHostImpl.java 3 * Created : 24-Oct-2003 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.core3.tracker.host.impl; 24 25 /** 26 * @author parg 27 */ 28 29 import java.util.*; 30 import java.io.*; 31 import java.net.*; 32 33 import org.gudy.azureus2.core3.logging.*; 34 import org.gudy.azureus2.core3.config.*; 35 import org.gudy.azureus2.core3.util.*; 36 import org.gudy.azureus2.core3.tracker.host.*; 37 import org.gudy.azureus2.core3.tracker.server.*; 38 import org.gudy.azureus2.core3.tracker.util.TRTrackerUtils; 39 import org.gudy.azureus2.core3.tracker.client.*; 40 import org.gudy.azureus2.core3.torrent.*; 41 42 import com.aelitis.azureus.core.util.CopyOnWriteList; 43 44 public class 45 TRHostImpl 46 implements TRHost, TRTrackerAnnouncerFactoryListener, 47 TRTrackerServerListener2, TRTrackerServerListener, 48 TRTrackerServerFactoryListener, 49 TRTrackerServerRequestListener, TRTrackerServerAuthenticationListener 50 { 51 private static final LogIDs LOGID = LogIDs.TRACKER; 52 private static final int URL_DEFAULT_PORT = 80; // port to use if none in announce URL 53 private static final int URL_DEFAULT_PORT_SSL = 443; // port to use if none in announce URL 54 55 public static final int STATS_PERIOD_SECS = 60; 56 private static final int TICK_PERIOD_SECS = 10; 57 private static final int TICKS_PER_STATS_PERIOD = STATS_PERIOD_SECS/TICK_PERIOD_SECS; 58 59 private static TRHostImpl singleton; 60 private static AEMonitor class_mon = new AEMonitor( "TRHost:class" ); 61 62 private TRHostConfigImpl config; 63 64 private Hashtable server_map = new Hashtable(); 65 66 private List host_torrents = new ArrayList(); 67 private Map host_torrent_hash_map = new HashMap(); 68 69 private Map host_torrent_map = new HashMap(); 70 private Map tracker_client_map = new HashMap(); 71 72 private static final int LDT_TORRENT_ADDED = 1; 73 private static final int LDT_TORRENT_REMOVED = 2; 74 private static final int LDT_TORRENT_CHANGED = 3; 75 76 private ListenerManager<TRHostListener> listeners = ListenerManager.createAsyncManager( 77 "TRHost:ListenDispatcher", 78 new ListenerManagerDispatcher<TRHostListener>() 79 { 80 public void 81 dispatch( 82 TRHostListener _listener, 83 int type, 84 Object value ) 85 { 86 TRHostListener target = (TRHostListener)_listener; 87 88 if ( type == LDT_TORRENT_ADDED ){ 89 90 target.torrentAdded((TRHostTorrent)value); 91 92 }else if ( type == LDT_TORRENT_REMOVED ){ 93 94 target.torrentRemoved((TRHostTorrent)value); 95 96 }else if ( type == LDT_TORRENT_CHANGED ){ 97 98 target.torrentChanged((TRHostTorrent)value); 99 } 100 } 101 }); 102 103 private CopyOnWriteList<TRHostListener2> listeners2 = new CopyOnWriteList<TRHostListener2>(); 104 105 private static boolean host_add_announce_urls; 106 107 static{ 108 COConfigurationManager.addAndFireParameterListener( 109 "Tracker Host Add Our Announce URLs", 110 new ParameterListener() 111 { 112 public void 113 parameterChanged( 114 String name ) 115 { 116 host_add_announce_urls = COConfigurationManager.getBooleanParameter( name ); 117 } 118 }); 119 } 120 121 private List<TRHostAuthenticationListener> auth_listeners = new ArrayList<TRHostAuthenticationListener>(); 122 123 private boolean server_factory_listener_added; 124 125 protected AEMonitor this_mon = new AEMonitor( "TRHost" ); 126 127 private volatile boolean closed; 128 129 public static TRHost create()130 create() 131 { 132 try{ 133 class_mon.enter(); 134 135 if ( singleton == null ){ 136 137 singleton = new TRHostImpl(); 138 } 139 140 return( singleton ); 141 142 }finally{ 143 144 class_mon.exit(); 145 } 146 } 147 148 protected TRHostImpl()149 TRHostImpl() 150 { 151 // we need to synchronize this so that the async (possible) establishment of 152 // a server within the stats loop (to deal with public trackers with no locally 153 // hosted torrents) doesn't get ahead of the reading of persisted torrents 154 // If we allow the server to start early then it can potentially receive an 155 // announce/scrape and result in the creation of an "external" torrent when 156 // it should really be using an existing torrent 157 158 try{ 159 this_mon.enter(); 160 161 config = new TRHostConfigImpl(this); 162 163 TRTrackerAnnouncerFactory.addListener( this ); 164 165 Thread t = new AEThread("TRHost::stats.loop") 166 { 167 private int tick_count = 0; 168 169 private Set failed_ports = new HashSet(); 170 171 public void 172 runSupport() 173 { 174 while(true){ 175 176 try{ 177 178 URL[][] url_sets = TRTrackerUtils.getAnnounceURLs(); 179 180 for (int i=0;i<url_sets.length;i++){ 181 182 URL[] urls = url_sets[i]; 183 184 for (int j=0;j<urls.length;j++){ 185 186 URL url = urls[j]; 187 188 int port = url.getPort(); 189 190 if ( port == -1 ){ 191 192 port = url.getDefaultPort(); 193 } 194 195 String protocol = url.getProtocol().toLowerCase(); 196 197 try{ 198 if ( protocol.equals( "http" )){ 199 200 startServer( TRTrackerServerFactory.PR_TCP, port, false ); 201 202 }else if ( protocol.equals( "udp" )){ 203 204 startServer( TRTrackerServerFactory.PR_UDP, port, false ); 205 206 }else if ( protocol.equals( "https" )){ 207 208 startServer( TRTrackerServerFactory.PR_TCP, port, true ); 209 210 }else{ 211 212 Debug.out( "Unknown protocol '" + protocol + "'" ); 213 } 214 215 }catch( Throwable e ){ 216 217 Integer port_i = new Integer(port); 218 219 if ( !failed_ports.contains(port_i)){ 220 221 failed_ports.add( port_i ); 222 223 Logger.log( 224 new LogEvent(LOGID, 225 "Tracker Host: failed to start server", e)); 226 } 227 } 228 } 229 } 230 231 Thread.sleep( TICK_PERIOD_SECS*1000 ); 232 233 if ( closed ){ 234 235 break; 236 } 237 238 if ( tick_count % TICKS_PER_STATS_PERIOD == 0 ){ 239 240 try{ 241 this_mon.enter(); 242 243 for (int i=0;i<host_torrents.size();i++){ 244 245 TRHostTorrent ht = (TRHostTorrent)host_torrents.get(i); 246 247 if ( ht instanceof TRHostTorrentHostImpl ){ 248 249 ((TRHostTorrentHostImpl)ht).updateStats(); 250 251 }else{ 252 253 ((TRHostTorrentPublishImpl)ht).updateStats(); 254 255 } 256 } 257 }finally{ 258 259 this_mon.exit(); 260 } 261 262 config.saveConfig( true ); 263 264 }else{ 265 266 config.saveConfig( false ); 267 } 268 269 }catch( InterruptedException e ){ 270 271 Debug.printStackTrace( e ); 272 273 break; 274 }finally{ 275 276 tick_count++; 277 } 278 } 279 } 280 }; 281 282 t.setDaemon(true); 283 284 // try to ensure that the tracker stats are collected reasonably 285 // regularly 286 287 t.setPriority( Thread.MAX_PRIORITY -1); 288 289 t.start(); 290 291 }finally{ 292 293 this_mon.exit(); 294 } 295 } 296 297 public void initialise( TRHostTorrentFinder finder )298 initialise( 299 TRHostTorrentFinder finder ) 300 { 301 config.loadConfig( finder ); 302 } 303 304 public String getName()305 getName() 306 { 307 return( TRTrackerServer.DEFAULT_NAME ); 308 } 309 310 public TRHostTorrent hostTorrent( TOTorrent torrent, boolean persistent, boolean passive )311 hostTorrent( 312 TOTorrent torrent, 313 boolean persistent, 314 boolean passive ) 315 316 throws TRHostException 317 { 318 return( addTorrent( torrent, TRHostTorrent.TS_STARTED, persistent, passive, SystemTime.getCurrentTime() )); 319 } 320 321 public TRHostTorrent publishTorrent( TOTorrent torrent )322 publishTorrent( 323 TOTorrent torrent ) 324 325 throws TRHostException 326 { 327 return( addTorrent( torrent, TRHostTorrent.TS_PUBLISHED, true, false, SystemTime.getCurrentTime())); 328 } 329 330 protected TRHostTorrent addTorrent( TOTorrent torrent, int state, boolean persistent, boolean passive, long date_added )331 addTorrent( 332 TOTorrent torrent, 333 int state, 334 boolean persistent, 335 boolean passive, 336 long date_added ) 337 338 throws TRHostException 339 { 340 try{ 341 this_mon.enter(); 342 343 // non-persistent additions should know what they're doing regarding 344 // announce URL 345 346 if ( persistent && state != TRHostTorrent.TS_PUBLISHED ){ 347 348 if ( host_add_announce_urls ){ 349 350 addTrackerAnnounce( torrent ); 351 } 352 } 353 354 TRHostTorrent ht = lookupHostTorrent( torrent ); 355 356 if ( ht != null ){ 357 358 // check that this isn't the explicit publish/host of a torrent already there 359 // as an external torrent. If so then just replace the torrent 360 361 try{ 362 363 ht = lookupHostTorrentViaHash( torrent.getHash()); 364 365 if ( ht instanceof TRHostTorrentHostImpl ){ 366 367 TRHostTorrentHostImpl hti = (TRHostTorrentHostImpl)ht; 368 369 if ( hti.getTorrent() != torrent ){ 370 371 hti.setTorrentInternal( torrent ); 372 373 if ( persistent && !hti.isPersistent()){ 374 375 hti.setPersistent( true ); 376 } 377 378 if ( passive && !hti.isPassive()){ 379 380 hti.setPassive( true ); 381 } 382 383 if ( state != TRHostTorrent.TS_PUBLISHED ){ 384 385 startHosting( hti ); 386 387 if ( state == TRHostTorrent.TS_STARTED ){ 388 389 hti.start(); 390 } 391 } 392 393 listeners.dispatch( LDT_TORRENT_CHANGED, ht ); 394 } 395 } 396 }catch( TOTorrentException e ){ 397 398 Debug.printStackTrace( e ); 399 } 400 401 return( ht ); 402 } 403 404 int port; 405 boolean ssl; 406 int protocol = TRTrackerServerFactory.PR_TCP; 407 408 if ( state == TRHostTorrent.TS_PUBLISHED ){ 409 410 port = COConfigurationManager.getIntParameter("Tracker Port", TRHost.DEFAULT_PORT ); 411 412 ssl = false; 413 }else{ 414 415 URL announce_url = torrent.getAnnounceURL(); 416 417 String protocol_str = announce_url.getProtocol(); 418 419 ssl = protocol_str.equalsIgnoreCase("https"); 420 421 if ( protocol_str.equalsIgnoreCase("udp")){ 422 423 protocol = TRTrackerServerFactory.PR_UDP; 424 425 }else if ( TorrentUtils.isDecentralised( torrent )){ 426 427 protocol = TRTrackerServerFactory.PR_DHT; 428 } 429 430 boolean force_external = COConfigurationManager.getBooleanParameter("Tracker Port Force External"); 431 432 port = announce_url.getPort(); 433 434 if ( force_external ){ 435 436 String tracker_ip = COConfigurationManager.getStringParameter("Tracker IP", ""); 437 438 if ( tracker_ip.length() > 0 && 439 !announce_url.getHost().equalsIgnoreCase( tracker_ip )){ 440 441 if ( ssl ){ 442 443 port = COConfigurationManager.getIntParameter("Tracker Port SSL", TRHost.DEFAULT_PORT_SSL ); 444 445 }else{ 446 447 port = COConfigurationManager.getIntParameter("Tracker Port", TRHost.DEFAULT_PORT ); 448 449 } 450 } 451 } 452 453 if ( port == -1 ){ 454 455 port = ssl?URL_DEFAULT_PORT_SSL:URL_DEFAULT_PORT; 456 } 457 } 458 459 TRTrackerServer server = startServer( protocol, port, ssl ); 460 461 TRHostTorrent host_torrent; 462 463 if ( state == TRHostTorrent.TS_PUBLISHED ){ 464 465 TRHostTorrentPublishImpl new_torrent = new TRHostTorrentPublishImpl( this, torrent, date_added ); 466 467 new_torrent.setPersistent( persistent ); 468 469 host_torrent = new_torrent; 470 }else{ 471 472 TRHostTorrentHostImpl new_torrent = new TRHostTorrentHostImpl( this, server, torrent, port, date_added ); 473 474 new_torrent.setPersistent( persistent ); 475 476 new_torrent.setPassive( passive ); 477 478 host_torrent = new_torrent; 479 } 480 481 host_torrents.add( host_torrent ); 482 483 try{ 484 host_torrent_hash_map.put( new HashWrapper( torrent.getHash()), host_torrent ); 485 486 }catch( TOTorrentException e ){ 487 488 Debug.printStackTrace( e ); 489 } 490 491 host_torrent_map.put( torrent, host_torrent ); 492 493 if ( state != TRHostTorrent.TS_PUBLISHED ){ 494 495 startHosting((TRHostTorrentHostImpl)host_torrent ); 496 497 if ( state == TRHostTorrent.TS_STARTED ){ 498 499 host_torrent.start(); 500 } 501 502 // if not persistent, see if we can recover the stats 503 504 if ( !persistent ){ 505 506 config.recoverStats( (TRHostTorrentHostImpl)host_torrent ); 507 } 508 } 509 510 listeners.dispatch( LDT_TORRENT_ADDED, host_torrent ); 511 512 config.saveRequired(); 513 514 return( host_torrent ); 515 516 }finally{ 517 518 this_mon.exit(); 519 } 520 } 521 522 protected void torrentUpdated( TRHostTorrentHostImpl hti )523 torrentUpdated( 524 TRHostTorrentHostImpl hti ) 525 { 526 int state = hti.getStatus(); 527 528 if ( state != TRHostTorrent.TS_PUBLISHED ){ 529 530 startHosting( hti ); 531 532 if ( state == TRHostTorrent.TS_STARTED ){ 533 534 hti.start(); 535 } 536 } 537 538 listeners.dispatch( LDT_TORRENT_CHANGED, hti ); 539 } 540 541 public InetAddress getBindIP()542 getBindIP() 543 { 544 return( null ); 545 } 546 547 protected TRTrackerServer startServer( int protocol, int port, boolean ssl )548 startServer( 549 int protocol, 550 int port, 551 boolean ssl ) 552 553 throws TRHostException 554 { 555 try{ 556 this_mon.enter(); 557 558 String key = ""+protocol+ ":" + port; 559 560 TRTrackerServer server = (TRTrackerServer)server_map.get( key ); 561 562 if ( server == null ){ 563 564 try{ 565 566 if ( ssl ){ 567 568 server = TRTrackerServerFactory.createSSL( "tracker", protocol, port, true, true ); 569 570 }else{ 571 572 server = TRTrackerServerFactory.create( "tracker", protocol, port, true, true ); 573 } 574 575 server_map.put( key, server ); 576 577 if ( auth_listeners.size() > 0 ){ 578 579 server.addAuthenticationListener( this ); 580 } 581 582 server.addListener( this ); 583 server.addListener2( this ); 584 585 }catch( TRTrackerServerException e ){ 586 587 throw( new TRHostException( "startServer failed", e )); 588 } 589 } 590 591 return( server ); 592 593 }finally{ 594 595 this_mon.exit(); 596 } 597 } 598 599 protected TRHostTorrent lookupHostTorrent( TOTorrent torrent )600 lookupHostTorrent( 601 TOTorrent torrent ) 602 { 603 if (torrent == null) 604 return null; 605 606 try{ 607 return((TRHostTorrent)host_torrent_hash_map.get( torrent.getHashWrapper())); 608 609 }catch( TOTorrentException e ){ 610 611 Debug.printStackTrace( e ); 612 } 613 614 return( null ); 615 } 616 617 protected void startHosting( TRHostTorrentHostImpl host_torrent )618 startHosting( 619 TRHostTorrentHostImpl host_torrent ) 620 { 621 TOTorrent torrent = host_torrent.getTorrent(); 622 623 TRTrackerAnnouncer tc = (TRTrackerAnnouncer)tracker_client_map.get( torrent ); 624 625 if ( tc != null ){ 626 627 startHosting( host_torrent, tc ); 628 } 629 } 630 631 protected void startHosting( TRTrackerAnnouncer tracker_client )632 startHosting( 633 TRTrackerAnnouncer tracker_client ) 634 { 635 TRHostTorrent host_torrent = (TRHostTorrent)host_torrent_map.get( tracker_client.getTorrent()); 636 637 if ( host_torrent instanceof TRHostTorrentHostImpl ){ 638 639 startHosting( (TRHostTorrentHostImpl)host_torrent, tracker_client ); 640 } 641 } 642 643 protected void startHosting( TRHostTorrentHostImpl host_torrent, final TRTrackerAnnouncer tracker_client )644 startHosting( 645 TRHostTorrentHostImpl host_torrent, 646 final TRTrackerAnnouncer tracker_client ) 647 { 648 final TOTorrent torrent = host_torrent.getTorrent(); 649 650 // set the ip override so that we announce ourselves to other peers via the 651 // real external address, not the local one used to connect to the tracker 652 653 URL announce = torrent.getAnnounceURL(); 654 655 if ( host_add_announce_urls ){ 656 657 tracker_client.setIPOverride( announce.getHost()); 658 659 }else{ 660 661 // prolly a backup tracker, we only want to override the IP if we're hosting it 662 663 if ( TRTrackerUtils.isHosting( announce )){ 664 665 tracker_client.setIPOverride( announce.getHost()); 666 667 } 668 } 669 670 // hook into the client so that when the announce succeeds after the refresh below 671 // we can force a rescrape to pick up the new status 672 673 TRTrackerAnnouncerListener listener = 674 new TRTrackerAnnouncerListener() 675 { 676 public void 677 receivedTrackerResponse( 678 TRTrackerAnnouncerResponse response ) 679 { 680 try{ 681 TRTrackerScraperFactory.getSingleton().scrape( torrent, true ); 682 683 }finally{ 684 685 tracker_client.removeListener( this ); 686 } 687 } 688 689 public void 690 urlChanged( 691 TRTrackerAnnouncer announcer, 692 URL old_url, 693 URL new_url, 694 boolean explicit ) 695 { 696 } 697 698 public void 699 urlRefresh() 700 { 701 } 702 }; 703 704 tracker_client.addListener(listener); 705 706 tracker_client.refreshListeners(); 707 } 708 709 protected void remove( TRHostTorrent host_torrent )710 remove( 711 TRHostTorrent host_torrent ) 712 { 713 try{ 714 this_mon.enter(); 715 716 if ( !host_torrents.contains( host_torrent )){ 717 718 return; 719 } 720 721 host_torrents.remove( host_torrent ); 722 723 TOTorrent torrent = host_torrent.getTorrent(); 724 725 try{ 726 host_torrent_hash_map.remove(new HashWrapper(torrent.getHash())); 727 728 }catch( TOTorrentException e ){ 729 730 Debug.printStackTrace( e ); 731 } 732 733 host_torrent_map.remove( torrent ); 734 735 if ( host_torrent instanceof TRHostTorrentHostImpl ){ 736 737 stopHosting((TRHostTorrentHostImpl)host_torrent ); 738 } 739 740 listeners.dispatch( LDT_TORRENT_REMOVED, host_torrent ); 741 742 // this'll get saved sometime soon anyway - performance problems 743 // here when removing multiple torrents from a large set (e.g. 1000) 744 745 // config.saveConfig(); 746 747 }finally{ 748 749 this_mon.exit(); 750 } 751 } 752 753 protected void stopHosting( TRHostTorrentHostImpl host_torrent )754 stopHosting( 755 TRHostTorrentHostImpl host_torrent ) 756 { 757 TOTorrent torrent = host_torrent.getTorrent(); 758 759 TRTrackerAnnouncer tc = (TRTrackerAnnouncer)tracker_client_map.get( torrent ); 760 761 if ( tc != null ){ 762 763 stopHosting( host_torrent, tc ); 764 } 765 } 766 767 protected void stopHosting( TRTrackerAnnouncer tracker_client )768 stopHosting( 769 TRTrackerAnnouncer tracker_client ) 770 { 771 TRHostTorrent host_torrent = (TRHostTorrent)host_torrent_map.get( tracker_client.getTorrent()); 772 773 if ( host_torrent instanceof TRHostTorrentHostImpl ){ 774 775 // we only "connect" the announcer and the hosted torrent if it isn't passive. This allows 776 // us to make a torrent passive without losing the tracker stats by 777 // 1) making it passive 778 // 2) removing the Download 779 780 //if ( !host_torrent.isPassive()){ 781 782 stopHosting( (TRHostTorrentHostImpl)host_torrent, tracker_client ); 783 //} 784 } 785 } 786 787 private AsyncDispatcher dispatcher = new AsyncDispatcher( "TRHost:stopHosting" ); 788 789 protected void stopHosting( final TRHostTorrentHostImpl host_torrent, final TRTrackerAnnouncer tracker_client )790 stopHosting( 791 final TRHostTorrentHostImpl host_torrent, 792 final TRTrackerAnnouncer tracker_client ) 793 { 794 // unfortunately a lot of the "stop" operations that occur when a tracker client 795 // connection is closed happen async. In particular the "stopped" message to the 796 // tracker. Hence, if we switch the URL back here the "stopped" doesn't get 797 // through. 798 799 // for the moment stick a delay in to allow any async stuff to complete 800 801 SimpleTimer.addEvent( 802 "StopHosting", 803 SystemTime.getOffsetTime( 2500 ), 804 new TimerEventPerformer() { 805 806 public void 807 perform(TimerEvent event) 808 { 809 dispatcher.dispatch( 810 new AERunnable() 811 { 812 public void 813 runSupport() 814 { 815 try{ 816 this_mon.enter(); 817 818 // got to look up the host torrent again as may have been 819 // removed and re-added 820 821 TRHostTorrent ht = lookupHostTorrent( host_torrent.getTorrent()); 822 823 // check it's still in stopped state and hasn't been restarted 824 825 if ( ht == null || 826 ( ht == host_torrent && 827 ht.getStatus() == TRHostTorrent.TS_STOPPED )){ 828 829 tracker_client.clearIPOverride(); 830 } 831 }finally{ 832 833 this_mon.exit(); 834 } 835 } 836 }); 837 } 838 }); 839 } 840 841 protected TRTrackerAnnouncer getTrackerClient( TRHostTorrent host_torrent )842 getTrackerClient( 843 TRHostTorrent host_torrent ) 844 { 845 try{ 846 this_mon.enter(); 847 848 return((TRTrackerAnnouncer)tracker_client_map.get( host_torrent.getTorrent())); 849 850 }finally{ 851 852 this_mon.exit(); 853 } 854 } 855 856 protected void hostTorrentStateChange( TRHostTorrent host_torrent )857 hostTorrentStateChange( 858 TRHostTorrent host_torrent ) 859 { 860 try{ 861 this_mon.enter(); 862 863 TOTorrent torrent = host_torrent.getTorrent(); 864 865 TRTrackerAnnouncer tc = (TRTrackerAnnouncer)tracker_client_map.get( torrent ); 866 867 if ( tc != null ){ 868 869 tc.refreshListeners(); 870 } 871 872 // config will get saved soon anyway (periodic or on closedown) - perf issues 873 // here with multiple torrent removal if we save each time 874 // config.saveConfig(); 875 876 }finally{ 877 878 this_mon.exit(); 879 } 880 } 881 882 public TRHostTorrent[] getTorrents()883 getTorrents() 884 { 885 try{ 886 this_mon.enter(); 887 888 TRHostTorrent[] res = new TRHostTorrent[host_torrents.size()]; 889 890 host_torrents.toArray( res ); 891 892 return( res ); 893 894 }finally{ 895 896 this_mon.exit(); 897 } 898 } 899 900 public void clientCreated( TRTrackerAnnouncer client )901 clientCreated( 902 TRTrackerAnnouncer client ) 903 { 904 try{ 905 this_mon.enter(); 906 907 tracker_client_map.put( client.getTorrent(), client ); 908 909 startHosting( client ); 910 911 }finally{ 912 913 this_mon.exit(); 914 } 915 } 916 917 public void clientDestroyed( TRTrackerAnnouncer client )918 clientDestroyed( 919 TRTrackerAnnouncer client ) 920 { 921 try{ 922 this_mon.enter(); 923 924 tracker_client_map.remove( client.getTorrent()); 925 926 stopHosting( client ); 927 928 }finally{ 929 930 this_mon.exit(); 931 } 932 } 933 934 protected TRHostTorrent lookupHostTorrentViaHash( byte[] hash )935 lookupHostTorrentViaHash( 936 byte[] hash ) 937 { 938 return((TRHostTorrent)host_torrent_hash_map.get(new HashWrapper(hash))); 939 } 940 941 // reports from TRTrackerServer regarding state of hashes 942 // if we get a "permitted" event for a torrent we know nothing about 943 // the the server is allowing public hosting and this is a new hash 944 // create an 'external' entry for it 945 946 public boolean permitted( String originator, byte[] hash, boolean explicit )947 permitted( 948 String originator, 949 byte[] hash, 950 boolean explicit ) 951 { 952 try{ 953 this_mon.enter(); 954 955 TRHostTorrent ht = lookupHostTorrentViaHash( hash ); 956 957 if ( ht != null ){ 958 959 if ( !explicit ){ 960 961 if ( ht.getStatus() != TRHostTorrent.TS_STARTED ){ 962 963 return( false ); 964 } 965 } 966 967 return( true ); 968 } 969 970 addExternalTorrent( hash, TRHostTorrent.TS_STARTED, SystemTime.getCurrentTime()); 971 972 return( true ); 973 974 }finally{ 975 976 this_mon.exit(); 977 } 978 } 979 980 protected void addExternalTorrent( byte[] hash, int state, long date_added )981 addExternalTorrent( 982 byte[] hash, 983 int state, 984 long date_added ) 985 { 986 try{ 987 this_mon.enter(); 988 989 if ( lookupHostTorrentViaHash( hash ) != null ){ 990 991 return; 992 } 993 994 String tracker_ip = COConfigurationManager.getStringParameter("Tracker IP", "127.0.0.1"); 995 996 // external torrents don't care whether ssl or not so just assume non-ssl for simplicity 997 998 int port = COConfigurationManager.getIntParameter("Tracker Port", TRHost.DEFAULT_PORT ); 999 1000 try{ 1001 TOTorrent external_torrent = new TRHostExternalTorrent(hash, new URL( "http://" + UrlUtils.convertIPV6Host(tracker_ip) + ":" + port + "/announce")); 1002 1003 addTorrent( external_torrent, state, true, false, date_added ); 1004 1005 }catch( Throwable e ){ 1006 1007 Debug.printStackTrace( e ); 1008 } 1009 1010 }finally{ 1011 1012 this_mon.exit(); 1013 } 1014 } 1015 1016 public boolean denied( byte[] hash, boolean permitted )1017 denied( 1018 byte[] hash, 1019 boolean permitted ) 1020 { 1021 return( true ); 1022 } 1023 1024 public boolean handleExternalRequest( InetSocketAddress client_address, String user, String url, URL absolute_url, String header, InputStream is, OutputStream os, AsyncController async )1025 handleExternalRequest( 1026 InetSocketAddress client_address, 1027 String user, 1028 String url, 1029 URL absolute_url, 1030 String header, 1031 InputStream is, 1032 OutputStream os, 1033 AsyncController async ) 1034 1035 throws IOException 1036 { 1037 List<TRHostListener> listeners_copy = listeners.getListenersCopy(); 1038 1039 for (int i=0;i<listeners_copy.size();i++){ 1040 1041 TRHostListener listener = listeners_copy.get(i); 1042 1043 try{ 1044 if ( listener.handleExternalRequest( client_address, user, url, absolute_url, header, is, os, async )){ 1045 1046 return( true ); 1047 } 1048 }catch( Throwable e ){ 1049 1050 Debug.out( e ); 1051 } 1052 } 1053 1054 return( false ); 1055 } 1056 1057 public boolean handleExternalRequest( ExternalRequest request )1058 handleExternalRequest( 1059 ExternalRequest request ) 1060 1061 throws IOException 1062 { 1063 Iterator<TRHostListener2> it = listeners2.iterator(); 1064 1065 while( it.hasNext()){ 1066 1067 try{ 1068 if ( it.next().handleExternalRequest(request)){ 1069 1070 return( true ); 1071 } 1072 }catch( Throwable e ){ 1073 1074 Debug.out( e ); 1075 } 1076 } 1077 1078 return( false ); 1079 } 1080 1081 public TRHostTorrent getHostTorrent( TOTorrent torrent )1082 getHostTorrent( 1083 TOTorrent torrent ) 1084 { 1085 return( lookupHostTorrent( torrent )); 1086 } 1087 1088 /** 1089 * Add and fire listener for each torrent already hosted 1090 */ 1091 public void addListener( TRHostListener l )1092 addListener( 1093 TRHostListener l ) 1094 { 1095 try{ 1096 this_mon.enter(); 1097 1098 listeners.addListener( l ); 1099 1100 for (int i=0;i<host_torrents.size();i++){ 1101 1102 listeners.dispatch( l, LDT_TORRENT_ADDED, host_torrents.get(i)); 1103 } 1104 }finally{ 1105 1106 this_mon.exit(); 1107 } 1108 } 1109 1110 public void removeListener( TRHostListener l )1111 removeListener( 1112 TRHostListener l ) 1113 { 1114 listeners.removeListener( l ); 1115 } 1116 1117 public void addListener2( TRHostListener2 l )1118 addListener2( 1119 TRHostListener2 l ) 1120 { 1121 listeners2.add(l); 1122 } 1123 1124 public void removeListener2( TRHostListener2 l )1125 removeListener2( 1126 TRHostListener2 l ) 1127 { 1128 listeners2.remove(l); 1129 } 1130 1131 protected void torrentListenerRegistered()1132 torrentListenerRegistered() 1133 { 1134 try{ 1135 this_mon.enter(); 1136 1137 if ( !server_factory_listener_added ){ 1138 1139 server_factory_listener_added = true; 1140 1141 TRTrackerServerFactory.addListener( this ); 1142 } 1143 }finally{ 1144 1145 this_mon.exit(); 1146 } 1147 } 1148 1149 public void serverCreated( TRTrackerServer server )1150 serverCreated( 1151 TRTrackerServer server ) 1152 { 1153 server.addRequestListener(this); 1154 } 1155 1156 public void serverDestroyed( TRTrackerServer server )1157 serverDestroyed( 1158 TRTrackerServer server ) 1159 { 1160 server.removeRequestListener(this); 1161 } 1162 1163 public void preProcess( TRTrackerServerRequest request )1164 preProcess( 1165 TRTrackerServerRequest request ) 1166 1167 throws TRTrackerServerException 1168 { 1169 if ( request.getType() == TRTrackerServerRequest.RT_ANNOUNCE || 1170 request.getType() == TRTrackerServerRequest.RT_SCRAPE ){ 1171 1172 TRTrackerServerTorrent ts_torrent = request.getTorrent(); 1173 1174 HashWrapper hash_wrapper = ts_torrent.getHash(); 1175 1176 TRHostTorrent h_torrent = lookupHostTorrentViaHash( hash_wrapper.getHash()); 1177 1178 if ( h_torrent != null ){ 1179 1180 TRHostTorrentRequest req = new TRHostTorrentRequestImpl( h_torrent, new TRHostPeerHostImpl(request.getPeer()), request ); 1181 1182 try{ 1183 if ( h_torrent instanceof TRHostTorrentHostImpl ){ 1184 1185 ((TRHostTorrentHostImpl)h_torrent).preProcess( req ); 1186 }else{ 1187 1188 ((TRHostTorrentPublishImpl)h_torrent).preProcess( req ); 1189 } 1190 }catch( TRHostException e ){ 1191 1192 throw( new TRTrackerServerException( e.getMessage(), e )); 1193 1194 }catch( Throwable e ){ 1195 1196 throw( new TRTrackerServerException( "Pre-process fails", e )); 1197 } 1198 } 1199 } 1200 } 1201 1202 public void postProcess( TRTrackerServerRequest request )1203 postProcess( 1204 TRTrackerServerRequest request ) 1205 1206 throws TRTrackerServerException 1207 { 1208 if ( request.getType() == TRTrackerServerRequest.RT_ANNOUNCE || 1209 request.getType() == TRTrackerServerRequest.RT_SCRAPE ){ 1210 1211 TRTrackerServerTorrent ts_torrent = request.getTorrent(); 1212 1213 // can be null for multi-hash scrapes... should fix this sometime I guess 1214 1215 if ( ts_torrent != null ){ 1216 1217 HashWrapper hash_wrapper = ts_torrent.getHash(); 1218 1219 TRHostTorrent h_torrent = lookupHostTorrentViaHash( hash_wrapper.getHash()); 1220 1221 if ( h_torrent != null ){ 1222 1223 TRHostTorrentRequest req = new TRHostTorrentRequestImpl( h_torrent, new TRHostPeerHostImpl(request.getPeer()), request ); 1224 1225 try{ 1226 if ( h_torrent instanceof TRHostTorrentHostImpl ){ 1227 1228 ((TRHostTorrentHostImpl)h_torrent).postProcess( req ); 1229 }else{ 1230 1231 ((TRHostTorrentPublishImpl)h_torrent).postProcess( req ); 1232 } 1233 }catch( TRHostException e ){ 1234 1235 throw( new TRTrackerServerException( "Post process fails", e )); 1236 } 1237 } 1238 } 1239 } 1240 } 1241 1242 public void close()1243 close() 1244 { 1245 closed = true; 1246 1247 config.saveConfig( true ); 1248 } 1249 1250 public boolean authenticate( String headers, URL resource, String user, String password )1251 authenticate( 1252 String headers, 1253 URL resource, 1254 String user, 1255 String password ) 1256 { 1257 for (int i=0;i<auth_listeners.size();i++){ 1258 1259 try{ 1260 boolean res = auth_listeners.get(i).authenticate( headers, resource, user, password ); 1261 1262 if ( res ){ 1263 1264 return(true ); 1265 } 1266 }catch( Throwable e ){ 1267 1268 Debug.printStackTrace( e ); 1269 } 1270 } 1271 1272 return( false ); 1273 } 1274 1275 public byte[] authenticate( URL resource, String user )1276 authenticate( 1277 URL resource, 1278 String user ) 1279 { 1280 for (int i=0;i<auth_listeners.size();i++){ 1281 1282 try{ 1283 byte[] res = auth_listeners.get(i).authenticate( resource, user ); 1284 1285 if ( res != null ){ 1286 1287 return( res ); 1288 } 1289 }catch( Throwable e ){ 1290 1291 Debug.printStackTrace( e ); 1292 } 1293 } 1294 1295 return( null ); 1296 } 1297 1298 public void addAuthenticationListener( TRHostAuthenticationListener l )1299 addAuthenticationListener( 1300 TRHostAuthenticationListener l ) 1301 { 1302 try{ 1303 this_mon.enter(); 1304 1305 auth_listeners.add(l); 1306 1307 if ( auth_listeners.size() == 1 ){ 1308 1309 Iterator it = server_map.values().iterator(); 1310 1311 while( it.hasNext()){ 1312 1313 ((TRTrackerServer)it.next()).addAuthenticationListener( this ); 1314 } 1315 } 1316 }finally{ 1317 1318 this_mon.exit(); 1319 } 1320 } 1321 1322 public void removeAuthenticationListener( TRHostAuthenticationListener l )1323 removeAuthenticationListener( 1324 TRHostAuthenticationListener l ) 1325 { 1326 try{ 1327 this_mon.enter(); 1328 1329 auth_listeners.remove(l); 1330 1331 if ( auth_listeners.size() == 0 ){ 1332 1333 Iterator it = server_map.values().iterator(); 1334 1335 while( it.hasNext()){ 1336 1337 ((TRTrackerServer)it.next()).removeAuthenticationListener( this ); 1338 } 1339 } 1340 }finally{ 1341 1342 this_mon.exit(); 1343 } 1344 } 1345 1346 // see comment in TRHostTorrentHost impl for reason for this delegation + monitor 1347 // aquisition 1348 1349 protected void startTorrent( TRHostTorrentHostImpl torrent )1350 startTorrent( 1351 TRHostTorrentHostImpl torrent ) 1352 { 1353 try{ 1354 this_mon.enter(); 1355 1356 torrent.startSupport(); 1357 1358 }finally{ 1359 1360 this_mon.exit(); 1361 } 1362 } 1363 1364 protected void stopTorrent( TRHostTorrentHostImpl torrent )1365 stopTorrent( 1366 TRHostTorrentHostImpl torrent ) 1367 { 1368 try{ 1369 this_mon.enter(); 1370 1371 torrent.stopSupport(); 1372 1373 }finally{ 1374 1375 this_mon.exit(); 1376 } 1377 } 1378 1379 protected void addTrackerAnnounce( TOTorrent torrent )1380 addTrackerAnnounce( 1381 TOTorrent torrent ) 1382 { 1383 if ( TorrentUtils.isDecentralised( torrent )){ 1384 1385 return; 1386 } 1387 1388 // ensure that the tracker's announce details are in the torrent 1389 1390 URL[][] url_sets = TRTrackerUtils.getAnnounceURLs(); 1391 1392 if ( url_sets.length == 0 ){ 1393 1394 // fall back to decentralised, no tracker defined 1395 1396 TorrentUtils.setDecentralised( torrent ); 1397 1398 }else{ 1399 1400 URL[] primary_urls = url_sets[0]; 1401 1402 // backwards so that they end up in right order 1403 1404 for (int i=primary_urls.length-1;i>=0;i--){ 1405 1406 String url_str = primary_urls[i].toString(); 1407 1408 if ( TorrentUtils.announceGroupsContainsURL( torrent, url_str )){ 1409 1410 TorrentUtils.announceGroupsSetFirst( torrent, url_str ); 1411 1412 }else{ 1413 1414 TorrentUtils.announceGroupsInsertFirst( torrent, url_str ); 1415 } 1416 } 1417 } 1418 } 1419 } 1420