1 /* 2 * File : TorrentUtils.java 3 * Created : 13-Oct-2003 4 * By : stuff 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.util; 24 25 /** 26 * @author parg 27 * 28 */ 29 30 import java.io.*; 31 import java.net.*; 32 import java.util.*; 33 import java.util.concurrent.atomic.AtomicLong; 34 import java.util.regex.Matcher; 35 import java.util.regex.Pattern; 36 import java.util.zip.GZIPInputStream; 37 38 import com.aelitis.azureus.core.*; 39 import com.aelitis.azureus.core.proxy.AEProxyFactory; 40 import com.aelitis.azureus.core.proxy.AEProxyFactory.PluginProxy; 41 import com.aelitis.azureus.core.util.CopyOnWriteList; 42 import com.aelitis.azureus.core.util.DNSUtils; 43 44 import org.gudy.azureus2.core3.config.COConfigurationManager; 45 import org.gudy.azureus2.core3.config.ParameterListener; 46 import org.gudy.azureus2.core3.internat.*; 47 import org.gudy.azureus2.core3.logging.LogRelation; 48 import org.gudy.azureus2.core3.torrent.*; 49 import org.gudy.azureus2.core3.disk.*; 50 import org.gudy.azureus2.core3.download.*; 51 import org.gudy.azureus2.plugins.utils.resourcedownloader.ResourceDownloader; 52 import org.gudy.azureus2.pluginsimpl.local.utils.resourcedownloader.ResourceDownloaderFactoryImpl; 53 54 55 public class 56 TorrentUtils 57 { 58 public static final long MAX_TORRENT_FILE_SIZE = 64*1024*1024L; 59 60 private static final String NO_VALID_URL_URL = "http://no.valid.urls.defined/announce"; 61 62 static{ AEDiagnostics.addEvidenceGenerator( new AEDiagnosticsEvidenceGenerator() { public void generate( IndentWriter writer) { writer.println( R ); try{ writer.indent(); Set<String> names = COConfigurationManager.getDefinedParameters(); String prefix = R; for ( String name: names ){ if ( name.startsWith( prefix )){ try{ String tracker = new String( Base32.decode( name.substring( prefix.length())), R ); String str = R; List<byte[]> txts = (List<byte[]>)COConfigurationManager.getListParameter( name, null ); if ( txts != null ){ for ( byte[] txt: txts ){ str += (str.length()==0?R:R) + new String( txt, R ); } } writer.println( tracker + R + str + R ); }catch( Throwable e ){ Debug.out( e ); } } } }finally{ writer.exdent(); } } })63 AEDiagnostics.addEvidenceGenerator( 64 new AEDiagnosticsEvidenceGenerator() 65 { 66 public void 67 generate( 68 IndentWriter writer) 69 { 70 writer.println( "DNS TXT Records" ); 71 72 try{ 73 writer.indent(); 74 75 Set<String> names = COConfigurationManager.getDefinedParameters(); 76 77 String prefix = "dns.txts.cache."; 78 79 for ( String name: names ){ 80 81 if ( name.startsWith( prefix )){ 82 83 try{ 84 String tracker = new String( Base32.decode( name.substring( prefix.length())), "UTF-8" ); 85 86 String str = ""; 87 88 List<byte[]> txts = (List<byte[]>)COConfigurationManager.getListParameter( name, null ); 89 90 if ( txts != null ){ 91 92 for ( byte[] txt: txts ){ 93 94 str += (str.length()==0?"":", ") + new String( txt, "UTF-8" ); 95 } 96 } 97 98 writer.println( tracker + " -> [" + str + "]" ); 99 100 }catch( Throwable e ){ 101 102 Debug.out( e ); 103 } 104 } 105 } 106 }finally{ 107 108 writer.exdent(); 109 } 110 } 111 }); 112 } 113 114 public static final int TORRENT_FLAG_LOW_NOISE = 0x00000001; 115 public static final int TORRENT_FLAG_METADATA_TORRENT = 0x00000002; 116 117 private static final String TORRENT_AZ_PROP_DHT_BACKUP_ENABLE = "dht_backup_enable"; 118 private static final String TORRENT_AZ_PROP_DHT_BACKUP_REQUESTED = "dht_backup_requested"; 119 private static final String TORRENT_AZ_PROP_TORRENT_FLAGS = "torrent_flags"; 120 private static final String TORRENT_AZ_PROP_PLUGINS = "plugins"; 121 122 public static final String TORRENT_AZ_PROP_OBTAINED_FROM = "obtained_from"; 123 private static final String TORRENT_AZ_PROP_NETWORK_CACHE = "network_cache"; 124 private static final String TORRENT_AZ_PROP_TAG_CACHE = "tag_cache"; 125 private static final String TORRENT_AZ_PROP_PEER_CACHE = "peer_cache"; 126 private static final String TORRENT_AZ_PROP_PEER_CACHE_VALID = "peer_cache_valid"; 127 public static final String TORRENT_AZ_PROP_INITIAL_LINKAGE = "initial_linkage"; 128 public static final String TORRENT_AZ_PROP_INITIAL_LINKAGE2 = "initial_linkage2"; 129 130 private static final String MEM_ONLY_TORRENT_PATH = "?/\\!:mem_only:!\\/?"; 131 132 private static final long PC_MARKER = RandomUtils.nextLong(); 133 134 private static final List<byte[]> created_torrents; 135 private static final Set<HashWrapper> created_torrents_set; 136 137 private static ThreadLocal<Map<String,Object>> tls = 138 new ThreadLocal<Map<String,Object>>() 139 { 140 public Map<String,Object> 141 initialValue() 142 { 143 return( new HashMap<String,Object>()); 144 } 145 }; 146 147 private static volatile Set<String> ignore_files_set; 148 private static volatile Set<String> skip_extensions_set; 149 150 private static boolean bSaveTorrentBackup; 151 152 private static CopyOnWriteList<torrentAttributeListener> torrent_attribute_listeners = new CopyOnWriteList<torrentAttributeListener>(); 153 private static CopyOnWriteList<TorrentAnnounceURLChangeListener> torrent_url_changed_listeners = new CopyOnWriteList<TorrentAnnounceURLChangeListener>(); 154 155 private static AsyncDispatcher dispatcher = new AsyncDispatcher(); 156 157 private static boolean DNS_HANDLING_ENABLE = true; 158 private static final boolean TRACE_DNS = false; 159 private static int DNS_HISTORY_TIMEOUT = 4*60*60*1000; 160 161 private static Map<String,DNSTXTEntry> dns_mapping = new HashMap<String, DNSTXTEntry>(); 162 private static volatile int dns_mapping_seq_count; 163 private static ThreadPool dns_threads = new ThreadPool( "DNS:lookups", 16, true ); 164 165 static{ 166 SimpleTimer.addPeriodicEvent( 167 "TU:dnstimer", 168 DNS_HISTORY_TIMEOUT/2, 169 new TimerEventPerformer() 170 { 171 public void 172 perform( 173 TimerEvent event ) 174 { 175 if ( DNS_HANDLING_ENABLE ){ 176 177 checkDNSTimeouts(); 178 } 179 } 180 }); 181 } 182 183 static DNSUtils.DNSUtilsIntf dns_utils = DNSUtils.getSingleton(); 184 185 static { COConfigurationManager.addAndFireParameterListeners( new String[]{ R, R, R }, new ParameterListener() { public void parameterChanged( String _name) { bSaveTorrentBackup = COConfigurationManager.getBooleanParameter( R ); boolean enable_proxy = COConfigurationManager.getBooleanParameter( R ); DNS_HANDLING_ENABLE = dns_utils != null && COConfigurationManager.getBooleanParameter( R ) && !enable_proxy; } })186 COConfigurationManager.addAndFireParameterListeners( 187 new String[]{ 188 "Save Torrent Backup", 189 "Tracker DNS Records Enable", 190 "Enable.Proxy" }, 191 new ParameterListener() { 192 public void 193 parameterChanged( 194 String _name) 195 { 196 bSaveTorrentBackup = COConfigurationManager.getBooleanParameter( "Save Torrent Backup" ); 197 198 boolean enable_proxy = COConfigurationManager.getBooleanParameter( "Enable.Proxy" ); 199 200 DNS_HANDLING_ENABLE = dns_utils != null && COConfigurationManager.getBooleanParameter( "Tracker DNS Records Enable" ) && !enable_proxy; 201 } 202 }); 203 204 created_torrents = COConfigurationManager.getListParameter( "my.created.torrents", new ArrayList()); 205 206 created_torrents_set = new HashSet(); 207 208 Iterator it = created_torrents.iterator(); 209 210 while( it.hasNext()){ 211 created_torrents_set.add( new HashWrapper((byte[])it.next()))212 created_torrents_set.add( new HashWrapper((byte[])it.next())); 213 } 214 } 215 216 static AtomicLong torrent_delete_level = new AtomicLong(); 217 static long torrent_delete_time; 218 219 220 public static TOTorrent readFromFile( File file, boolean create_delegate )221 readFromFile( 222 File file, 223 boolean create_delegate ) 224 225 throws TOTorrentException 226 { 227 return( readFromFile( file, create_delegate, false )); 228 } 229 230 /** 231 * If you set "create_delegate" to true then you must understand that this results 232 * is piece hashes being discarded and then re-read from the torrent file if needed 233 * Therefore, if you delete the original torrent file you're going to get errors 234 * if you access the pieces after this (and they've been discarded) 235 * @param file 236 * @param create_delegate 237 * @param force_initial_discard - use to get rid of pieces immediately 238 * @return 239 * @throws TOTorrentException 240 */ 241 242 public static ExtendedTorrent readDelegateFromFile( File file, boolean force_initial_discard )243 readDelegateFromFile( 244 File file, 245 boolean force_initial_discard ) 246 247 throws TOTorrentException 248 { 249 return((ExtendedTorrent)readFromFile( file, true, force_initial_discard )); 250 } 251 252 public static TOTorrent readFromFile( File file, boolean create_delegate, boolean force_initial_discard )253 readFromFile( 254 File file, 255 boolean create_delegate, 256 boolean force_initial_discard ) 257 258 throws TOTorrentException 259 { 260 TOTorrent torrent; 261 262 try{ 263 torrent = TOTorrentFactory.deserialiseFromBEncodedFile(file); 264 265 // make an immediate backup if requested and one doesn't exist 266 267 if (bSaveTorrentBackup) { 268 269 File torrent_file_bak = new File(file.getParent(), file.getName() + ".bak"); 270 271 if ( !torrent_file_bak.exists()){ 272 273 try{ 274 torrent.serialiseToBEncodedFile(torrent_file_bak); 275 276 }catch( Throwable e ){ 277 278 Debug.printStackTrace(e); 279 } 280 } 281 } 282 283 }catch (TOTorrentException e){ 284 285 // Debug.outNoStack( e.getMessage() ); 286 287 File torrentBackup = new File(file.getParent(), file.getName() + ".bak"); 288 289 if( torrentBackup.exists()){ 290 291 torrent = TOTorrentFactory.deserialiseFromBEncodedFile(torrentBackup); 292 293 // use the original torrent's file name so that when this gets saved 294 // it writes back to the original and backups are made as required 295 // - set below 296 }else{ 297 298 throw e; 299 } 300 } 301 302 torrent.setAdditionalStringProperty("torrent filename", file.toString()); 303 304 if ( create_delegate ){ 305 306 torrentDelegate res = new torrentDelegate( torrent, file ); 307 308 if ( force_initial_discard ){ 309 310 res.discardPieces( SystemTime.getCurrentTime(), true ); 311 } 312 313 return( res ); 314 315 }else{ 316 317 return( torrent ); 318 } 319 } 320 321 public static TOTorrent readFromBEncodedInputStream( InputStream is )322 readFromBEncodedInputStream( 323 InputStream is ) 324 325 throws TOTorrentException 326 { 327 TOTorrent torrent = TOTorrentFactory.deserialiseFromBEncodedInputStream( is ); 328 329 // as we've just imported this torrent we want to clear out any possible attributes that we 330 // don't want such as "torrent filename" 331 332 torrent.removeAdditionalProperties(); 333 334 return( torrent ); 335 } 336 337 public static TOTorrent cloneTorrent( TOTorrent torrent )338 cloneTorrent( 339 TOTorrent torrent ) 340 341 throws TOTorrentException 342 { 343 return( TOTorrentFactory.deserialiseFromMap( torrent.serialiseToMap())); 344 } 345 346 public static void setMemoryOnly( TOTorrent torrent, boolean mem_only )347 setMemoryOnly( 348 TOTorrent torrent, 349 boolean mem_only ) 350 { 351 if ( mem_only ){ 352 353 torrent.setAdditionalStringProperty("torrent filename", MEM_ONLY_TORRENT_PATH ); 354 355 }else{ 356 357 String s = torrent.getAdditionalStringProperty("torrent filename"); 358 359 if ( s != null && s.equals( MEM_ONLY_TORRENT_PATH )){ 360 361 torrent.removeAdditionalProperty( "torrent filename" ); 362 } 363 } 364 } 365 366 public static void writeToFile( final TOTorrent torrent )367 writeToFile( 368 final TOTorrent torrent ) 369 370 throws TOTorrentException 371 { 372 writeToFile( torrent, false ); 373 } 374 375 public static void writeToFile( TOTorrent torrent, boolean force_backup )376 writeToFile( 377 TOTorrent torrent, 378 boolean force_backup ) 379 380 throws TOTorrentException 381 { 382 try{ 383 torrent.getMonitor().enter(); 384 385 String str = torrent.getAdditionalStringProperty("torrent filename"); 386 387 if ( str == null ){ 388 389 throw (new TOTorrentException("TorrentUtils::writeToFile: no 'torrent filename' attribute defined", TOTorrentException.RT_FILE_NOT_FOUND)); 390 } 391 392 if ( str.equals( MEM_ONLY_TORRENT_PATH )){ 393 394 return; 395 } 396 397 // save first to temporary file as serialisation may require state to be re-read from 398 // the existing file first and if we rename to .bak first then this aint good 399 400 File torrent_file_tmp = new File(str + "._az"); 401 402 torrent.serialiseToBEncodedFile( torrent_file_tmp ); 403 404 // now backup if required 405 406 File torrent_file = new File(str); 407 408 if ( ( force_backup ||COConfigurationManager.getBooleanParameter("Save Torrent Backup")) && 409 torrent_file.exists()) { 410 411 File torrent_file_bak = new File(str + ".bak"); 412 413 try{ 414 415 // Will return false if it cannot be deleted (including if the file doesn't exist). 416 417 torrent_file_bak.delete(); 418 419 torrent_file.renameTo(torrent_file_bak); 420 421 }catch( SecurityException e){ 422 423 Debug.printStackTrace( e ); 424 } 425 } 426 427 // now rename the temp file to required one 428 429 if ( torrent_file.exists()){ 430 431 torrent_file.delete(); 432 } 433 434 torrent_file_tmp.renameTo( torrent_file ); 435 436 }finally{ 437 438 torrent.getMonitor().exit(); 439 } 440 } 441 442 public static void writeToFile( TOTorrent torrent, File file )443 writeToFile( 444 TOTorrent torrent, 445 File file ) 446 447 throws TOTorrentException 448 { 449 writeToFile( torrent, file, false ); 450 } 451 452 public static void writeToFile( TOTorrent torrent, File file, boolean force_backup )453 writeToFile( 454 TOTorrent torrent, 455 File file, 456 boolean force_backup ) 457 458 throws TOTorrentException 459 { 460 torrent.setAdditionalStringProperty("torrent filename", file.toString()); 461 462 writeToFile( torrent, force_backup ); 463 } 464 465 public static String getTorrentFileName( TOTorrent torrent )466 getTorrentFileName( 467 TOTorrent torrent ) 468 469 throws TOTorrentException 470 { 471 String str = torrent.getAdditionalStringProperty("torrent filename"); 472 473 if ( str == null ){ 474 475 throw( new TOTorrentException("TorrentUtils::getTorrentFileName: no 'torrent filename' attribute defined", TOTorrentException.RT_FILE_NOT_FOUND)); 476 } 477 478 if ( str.equals( MEM_ONLY_TORRENT_PATH )){ 479 480 return( null ); 481 } 482 483 return( str ); 484 } 485 486 public static void copyToFile( TOTorrent torrent, File file )487 copyToFile( 488 TOTorrent torrent, 489 File file ) 490 491 throws TOTorrentException 492 { 493 torrent.serialiseToBEncodedFile(file); 494 } 495 496 public static void delete( TOTorrent torrent )497 delete( 498 TOTorrent torrent ) 499 500 throws TOTorrentException 501 { 502 try{ 503 torrent.getMonitor().enter(); 504 505 String str = torrent.getAdditionalStringProperty("torrent filename"); 506 507 if ( str == null ){ 508 509 throw( new TOTorrentException("TorrentUtils::delete: no 'torrent filename' attribute defined", TOTorrentException.RT_FILE_NOT_FOUND)); 510 } 511 512 if ( str.equals( MEM_ONLY_TORRENT_PATH )){ 513 514 return; 515 } 516 517 File file = new File(str); 518 519 if ( !file.delete()){ 520 521 if ( file.exists()){ 522 523 throw( new TOTorrentException("TorrentUtils::delete: failed to delete '" + str + "'", TOTorrentException.RT_WRITE_FAILS)); 524 } 525 } 526 527 new File( str + ".bak" ).delete(); 528 529 }finally{ 530 531 torrent.getMonitor().exit(); 532 } 533 } 534 535 public static void delete( File torrent_file, boolean force_no_recycle )536 delete( 537 File torrent_file, 538 boolean force_no_recycle ) 539 { 540 if ( !FileUtil.deleteWithRecycle( torrent_file, force_no_recycle )){ 541 542 if ( torrent_file.exists()){ 543 544 Debug.out( "TorrentUtils::delete: failed to delete '" + torrent_file + "'" ); 545 } 546 } 547 548 new File( torrent_file.toString() + ".bak" ).delete(); 549 } 550 551 public static boolean move( File from_torrent, File to_torrent )552 move( 553 File from_torrent, 554 File to_torrent ) 555 { 556 if ( !FileUtil.renameFile(from_torrent, to_torrent )){ 557 558 return( false ); 559 } 560 561 if ( new File( from_torrent.toString() + ".bak").exists()){ 562 563 FileUtil.renameFile( 564 new File( from_torrent.toString() + ".bak"), 565 new File( to_torrent.toString() + ".bak")); 566 } 567 568 return( true ); 569 } 570 571 public static String exceptionToText( TOTorrentException e )572 exceptionToText( 573 TOTorrentException e ) 574 { 575 String errorDetail; 576 577 int reason = e.getReason(); 578 579 if ( reason == TOTorrentException.RT_FILE_NOT_FOUND ){ 580 581 errorDetail = MessageText.getString("DownloadManager.error.filenotfound" ); 582 583 }else if ( reason == TOTorrentException.RT_ZERO_LENGTH ){ 584 585 errorDetail = MessageText.getString("DownloadManager.error.fileempty"); 586 587 }else if ( reason == TOTorrentException.RT_TOO_BIG ){ 588 589 errorDetail = MessageText.getString("DownloadManager.error.filetoobig"); 590 591 }else if ( reason == TOTorrentException.RT_DECODE_FAILS ){ 592 593 errorDetail = MessageText.getString("DownloadManager.error.filewithouttorrentinfo" ); 594 595 }else if ( reason == TOTorrentException.RT_UNSUPPORTED_ENCODING ){ 596 597 errorDetail = MessageText.getString("DownloadManager.error.unsupportedencoding"); 598 599 }else if ( reason == TOTorrentException.RT_READ_FAILS ){ 600 601 errorDetail = MessageText.getString("DownloadManager.error.ioerror"); 602 603 }else if ( reason == TOTorrentException.RT_HASH_FAILS ){ 604 605 errorDetail = MessageText.getString("DownloadManager.error.sha1"); 606 607 }else if ( reason == TOTorrentException.RT_CANCELLED ){ 608 609 errorDetail = MessageText.getString("DownloadManager.error.operationcancancelled"); 610 611 }else{ 612 613 errorDetail = Debug.getNestedExceptionMessage(e); 614 } 615 616 String msg = Debug.getNestedExceptionMessage(e); 617 618 if ( errorDetail.indexOf( msg ) == -1){ 619 620 errorDetail += " (" + msg + ")"; 621 } 622 623 return( errorDetail ); 624 } 625 626 public static Set<String> getUniqueTrackerHosts( TOTorrent torrent )627 getUniqueTrackerHosts( 628 TOTorrent torrent ) 629 { 630 Set<String> hosts = new HashSet<String>(); 631 632 if ( torrent != null ){ 633 634 URL announce_url = torrent.getAnnounceURL(); 635 636 if ( announce_url != null ){ 637 638 String host = announce_url.getHost(); 639 640 if ( host != null ){ 641 642 hosts.add( host.toLowerCase( Locale.US )); 643 } 644 } 645 646 TOTorrentAnnounceURLGroup group = torrent.getAnnounceURLGroup(); 647 648 TOTorrentAnnounceURLSet[] sets = group.getAnnounceURLSets(); 649 650 for ( TOTorrentAnnounceURLSet set: sets ){ 651 652 URL[] urls = set.getAnnounceURLs(); 653 654 for ( URL u: urls ){ 655 656 String host = u.getHost(); 657 658 if ( host != null ){ 659 660 hosts.add( host.toLowerCase( Locale.US )); 661 } 662 } 663 } 664 } 665 666 return( hosts ); 667 } 668 669 public static String announceGroupsToText( TOTorrent torrent )670 announceGroupsToText( 671 TOTorrent torrent ) 672 { 673 URL announce_url = torrent.getAnnounceURL(); 674 675 String announce_url_str = announce_url==null?"":announce_url.toString().trim(); 676 677 TOTorrentAnnounceURLGroup group = torrent.getAnnounceURLGroup(); 678 679 TOTorrentAnnounceURLSet[] sets = group.getAnnounceURLSets(); 680 681 if ( sets.length == 0 ){ 682 683 return( announce_url_str ); 684 685 }else{ 686 687 StringBuffer sb = new StringBuffer(1024); 688 689 boolean announce_found = false; 690 691 for (int i=0;i<sets.length;i++){ 692 693 TOTorrentAnnounceURLSet set = sets[i]; 694 695 URL[] urls = set.getAnnounceURLs(); 696 697 if ( urls.length > 0 ){ 698 699 for (int j=0;j<urls.length;j++){ 700 701 String str = urls[j].toString().trim(); 702 703 if ( str.equals( announce_url_str )){ 704 705 announce_found = true; 706 } 707 708 sb.append( str ); 709 sb.append( "\r\n" ); 710 } 711 712 sb.append( "\r\n" ); 713 } 714 } 715 716 String result = sb.toString().trim(); 717 718 if ( !announce_found ){ 719 720 if ( announce_url_str.length() > 0 ){ 721 722 if ( result.length() == 0 ){ 723 724 result = announce_url_str; 725 726 }else{ 727 728 result = "\r\n\r\n" + announce_url_str; 729 } 730 } 731 } 732 733 return( result ); 734 } 735 } 736 737 public static String announceGroupsToText( List<List<String>> group )738 announceGroupsToText( 739 List<List<String>> group ) 740 { 741 StringBuffer sb = new StringBuffer(1024); 742 743 for ( List<String> urls: group ){ 744 745 if ( sb.length() > 0 ){ 746 747 sb.append( "\r\n" ); 748 } 749 750 for ( String str: urls ){ 751 752 sb.append( str ); 753 sb.append( "\r\n" ); 754 } 755 } 756 757 return( sb.toString().trim()); 758 } 759 760 public static List<List<String>> announceTextToGroups( String text )761 announceTextToGroups( 762 String text ) 763 { 764 List<List<String>> groups = new ArrayList<List<String>>(); 765 766 String[] lines = text.split( "\n" ); 767 768 List<String> current_group = new ArrayList<String>(); 769 770 Set<String> hits = new HashSet<String>(); 771 772 for( String line: lines ){ 773 774 line = line.trim(); 775 776 if ( line.length() == 0 ){ 777 778 if ( current_group.size() > 0 ){ 779 780 groups.add( current_group ); 781 782 current_group = new ArrayList<String>(); 783 } 784 }else{ 785 String lc_line = line.toLowerCase(); 786 787 if ( hits.contains( lc_line )){ 788 789 continue; 790 } 791 792 hits.add( lc_line ); 793 794 current_group.add( line ); 795 } 796 } 797 798 if ( current_group.size() > 0 ){ 799 800 groups.add( current_group ); 801 } 802 803 return( groups ); 804 } 805 806 public static List<List<String>> announceGroupsToList( TOTorrent torrent )807 announceGroupsToList( 808 TOTorrent torrent ) 809 { 810 List<List<String>> groups = new ArrayList<List<String>>(); 811 812 TOTorrentAnnounceURLGroup group = torrent.getAnnounceURLGroup(); 813 814 TOTorrentAnnounceURLSet[] sets = group.getAnnounceURLSets(); 815 816 if ( sets.length == 0 ){ 817 818 List<String> s = new ArrayList<String>(); 819 820 s.add( UrlUtils.getCanonicalString( torrent.getAnnounceURL())); 821 822 groups.add(s); 823 824 }else{ 825 826 Set<String> all_urls = new HashSet<String>(); 827 828 for (int i=0;i<sets.length;i++){ 829 830 List<String> s = new ArrayList<String>(); 831 832 TOTorrentAnnounceURLSet set = sets[i]; 833 834 URL[] urls = set.getAnnounceURLs(); 835 836 for (int j=0;j<urls.length;j++){ 837 838 String u = UrlUtils.getCanonicalString( urls[j] ); 839 840 s.add( u ); 841 842 all_urls.add( u ); 843 } 844 845 if ( s.size() > 0 ){ 846 847 groups.add(s); 848 } 849 } 850 851 String a = UrlUtils.getCanonicalString( torrent.getAnnounceURL()); 852 853 if ( !all_urls.contains( a )){ 854 855 List<String> s = new ArrayList<String>(); 856 857 s.add( a ); 858 859 groups.add( 0, s ); 860 } 861 } 862 863 return( groups ); 864 } 865 866 /** 867 * This method DOES NOT MODIFY THE TORRENT 868 * @param groups 869 * @param torrent 870 * @return 871 */ 872 873 public static TOTorrentAnnounceURLSet[] listToAnnounceSets( List<List<String>> groups, TOTorrent torrent )874 listToAnnounceSets( 875 List<List<String>> groups, 876 TOTorrent torrent ) 877 { 878 List<TOTorrentAnnounceURLSet> sets = new ArrayList<TOTorrentAnnounceURLSet>(); 879 880 for ( List<String> group: groups ){ 881 882 List<URL> urls = new ArrayList<URL>( group.size()); 883 884 for ( String s: group ){ 885 886 try{ 887 urls.add( new URL( s)); 888 889 }catch( Throwable e ){ 890 } 891 } 892 893 if ( urls.size() > 0 ){ 894 895 sets.add( torrent.getAnnounceURLGroup().createAnnounceURLSet(urls.toArray( new URL[urls.size()]))); 896 } 897 } 898 899 return( sets.toArray( new TOTorrentAnnounceURLSet[sets.size()])); 900 } 901 902 public static void listToAnnounceGroups( List<List<String>> groups, TOTorrent torrent )903 listToAnnounceGroups( 904 List<List<String>> groups, 905 TOTorrent torrent ) 906 { 907 // if the new groups no longer contain the main announce url then we replace this with the first in the groups. The primary reason for this 908 // is that the DNS TXT record munging code always considers the announce-url as input to the generation of (potentially) modified URLs and if we 909 // leave the announce-url there it will magically re-appear 910 // 911 912 try{ 913 TOTorrentAnnounceURLGroup tg = torrent.getAnnounceURLGroup(); 914 915 if ( groups.size() == 1 ){ 916 917 List set = (List)groups.get(0); 918 919 if ( set.size() == 1 ){ 920 921 torrent.setAnnounceURL( new URL((String)set.get(0))); 922 923 tg.setAnnounceURLSets( new TOTorrentAnnounceURLSet[0]); 924 925 return; 926 } 927 } 928 929 String announce_url = torrent.getAnnounceURL().toExternalForm(); 930 931 URL first_url = null; 932 933 Vector g = new Vector(); 934 935 for (int i=0;i<groups.size();i++){ 936 937 List<String> set = (List<String>)groups.get(i); 938 939 URL[] urls = new URL[set.size()]; 940 941 for (int j=0;j<set.size();j++){ 942 943 String url_str = set.get(j); 944 945 if ( announce_url != null && url_str.equals( announce_url )){ 946 947 announce_url = null; 948 } 949 950 urls[j] = new URL((String)set.get(j)); 951 952 if ( first_url == null ){ 953 954 first_url = urls[j]; 955 } 956 } 957 958 if ( urls.length > 0 ){ 959 960 g.add( tg.createAnnounceURLSet( urls )); 961 } 962 } 963 964 TOTorrentAnnounceURLSet[] sets = new TOTorrentAnnounceURLSet[g.size()]; 965 966 if ( sets.length == 0 ){ 967 968 // hmm, no valid urls at all 969 970 torrent.setAnnounceURL( new URL( NO_VALID_URL_URL )); 971 972 }else{ 973 974 if ( announce_url != null && first_url != null ){ 975 976 torrent.setAnnounceURL( first_url ); 977 } 978 } 979 980 g.copyInto( sets ); 981 982 tg.setAnnounceURLSets( sets ); 983 984 }catch( MalformedURLException e ){ 985 986 Debug.printStackTrace( e ); 987 } 988 } 989 990 public static void announceGroupsInsertFirst( TOTorrent torrent, String first_url )991 announceGroupsInsertFirst( 992 TOTorrent torrent, 993 String first_url ) 994 { 995 try{ 996 997 announceGroupsInsertFirst( torrent, new URL( first_url )); 998 999 }catch( MalformedURLException e ){ 1000 1001 Debug.printStackTrace( e ); 1002 } 1003 } 1004 1005 public static void announceGroupsInsertFirst( TOTorrent torrent, URL first_url )1006 announceGroupsInsertFirst( 1007 TOTorrent torrent, 1008 URL first_url ) 1009 { 1010 announceGroupsInsertFirst( torrent, new URL[]{ first_url }); 1011 } 1012 1013 public static void announceGroupsInsertFirst( TOTorrent torrent, URL[] first_urls )1014 announceGroupsInsertFirst( 1015 TOTorrent torrent, 1016 URL[] first_urls ) 1017 { 1018 TOTorrentAnnounceURLGroup group = torrent.getAnnounceURLGroup(); 1019 1020 TOTorrentAnnounceURLSet[] sets = group.getAnnounceURLSets(); 1021 1022 TOTorrentAnnounceURLSet set1 = group.createAnnounceURLSet( first_urls ); 1023 1024 1025 if ( sets.length > 0 ){ 1026 1027 TOTorrentAnnounceURLSet[] new_sets = new TOTorrentAnnounceURLSet[sets.length+1]; 1028 1029 new_sets[0] = set1; 1030 1031 System.arraycopy( sets, 0, new_sets, 1, sets.length ); 1032 1033 group.setAnnounceURLSets( new_sets ); 1034 1035 }else{ 1036 1037 TOTorrentAnnounceURLSet set2 = group.createAnnounceURLSet(new URL[]{torrent.getAnnounceURL()}); 1038 1039 group.setAnnounceURLSets( 1040 new TOTorrentAnnounceURLSet[]{ set1, set2 }); 1041 } 1042 } 1043 1044 public static void announceGroupsInsertLast( TOTorrent torrent, URL[] first_urls )1045 announceGroupsInsertLast( 1046 TOTorrent torrent, 1047 URL[] first_urls ) 1048 { 1049 TOTorrentAnnounceURLGroup group = torrent.getAnnounceURLGroup(); 1050 1051 TOTorrentAnnounceURLSet[] sets = group.getAnnounceURLSets(); 1052 1053 TOTorrentAnnounceURLSet set1 = group.createAnnounceURLSet( first_urls ); 1054 1055 1056 if ( sets.length > 0 ){ 1057 1058 TOTorrentAnnounceURLSet[] new_sets = new TOTorrentAnnounceURLSet[sets.length+1]; 1059 1060 new_sets[sets.length] = set1; 1061 1062 System.arraycopy( sets, 0, new_sets, 0, sets.length ); 1063 1064 group.setAnnounceURLSets( new_sets ); 1065 1066 }else{ 1067 1068 TOTorrentAnnounceURLSet set2 = group.createAnnounceURLSet(new URL[]{torrent.getAnnounceURL()}); 1069 1070 group.setAnnounceURLSets( 1071 new TOTorrentAnnounceURLSet[]{ set2, set1 }); 1072 } 1073 } 1074 1075 public static void announceGroupsSetFirst( TOTorrent torrent, String first_url )1076 announceGroupsSetFirst( 1077 TOTorrent torrent, 1078 String first_url ) 1079 { 1080 List groups = announceGroupsToList( torrent ); 1081 1082 boolean found = false; 1083 1084 outer: 1085 for (int i=0;i<groups.size();i++){ 1086 1087 List set = (List)groups.get(i); 1088 1089 for (int j=0;j<set.size();j++){ 1090 1091 if ( first_url.equals(set.get(j))){ 1092 1093 set.remove(j); 1094 1095 set.add(0, first_url); 1096 1097 groups.remove(set); 1098 1099 groups.add(0,set); 1100 1101 found = true; 1102 1103 break outer; 1104 } 1105 } 1106 } 1107 1108 if ( !found ){ 1109 1110 System.out.println( "TorrentUtils::announceGroupsSetFirst - failed to find '" + first_url + "'" ); 1111 } 1112 1113 listToAnnounceGroups( groups, torrent ); 1114 } 1115 1116 public static boolean announceGroupsContainsURL( TOTorrent torrent, String url )1117 announceGroupsContainsURL( 1118 TOTorrent torrent, 1119 String url ) 1120 { 1121 List groups = announceGroupsToList( torrent ); 1122 1123 for (int i=0;i<groups.size();i++){ 1124 1125 List set = (List)groups.get(i); 1126 1127 for (int j=0;j<set.size();j++){ 1128 1129 if ( url.equals(set.get(j))){ 1130 1131 return( true ); 1132 } 1133 } 1134 } 1135 1136 return( false ); 1137 } 1138 1139 public static boolean canMergeAnnounceURLs( TOTorrent new_torrent, TOTorrent dest_torrent )1140 canMergeAnnounceURLs( 1141 TOTorrent new_torrent, 1142 TOTorrent dest_torrent ) 1143 { 1144 try{ 1145 List<List<String>> new_groups = announceGroupsToList( new_torrent ); 1146 List<List<String>> dest_groups = announceGroupsToList( dest_torrent ); 1147 1148 Set<String> all_dest = new HashSet<String>(); 1149 1150 for ( List<String> l: dest_groups ){ 1151 1152 all_dest.addAll( l ); 1153 } 1154 1155 for ( List<String> l: new_groups ){ 1156 1157 for ( String u: l ){ 1158 1159 List<URL> mods = applyAllDNSMods( new URL( u )); 1160 1161 if ( mods != null ){ 1162 1163 for ( URL m: mods ){ 1164 1165 if ( !all_dest.contains( UrlUtils.getCanonicalString( m ))){ 1166 1167 return( true ); 1168 } 1169 } 1170 } 1171 } 1172 } 1173 }catch( Throwable e ){ 1174 1175 Debug.out( e ); 1176 } 1177 1178 return( false ); 1179 } 1180 1181 public static boolean mergeAnnounceURLs( TOTorrent new_torrent, TOTorrent dest_torrent )1182 mergeAnnounceURLs( 1183 TOTorrent new_torrent, 1184 TOTorrent dest_torrent ) 1185 { 1186 if ( new_torrent == null || dest_torrent == null ){ 1187 1188 return( false); 1189 } 1190 1191 List new_groups = announceGroupsToList( new_torrent ); 1192 List dest_groups = announceGroupsToList( dest_torrent ); 1193 1194 List groups_to_add = new ArrayList(); 1195 1196 for (int i=0;i<new_groups.size();i++){ 1197 1198 List new_set = (List)new_groups.get(i); 1199 1200 boolean match = false; 1201 1202 for (int j=0;j<dest_groups.size();j++){ 1203 1204 List dest_set = (List)dest_groups.get(j); 1205 1206 boolean same = new_set.size() == dest_set.size(); 1207 1208 if ( same ){ 1209 1210 for (int k=0;k<new_set.size();k++){ 1211 1212 String new_url = (String)new_set.get(k); 1213 1214 if ( !dest_set.contains(new_url)){ 1215 1216 same = false; 1217 1218 break; 1219 } 1220 } 1221 } 1222 1223 if ( same ){ 1224 1225 match = true; 1226 1227 break; 1228 } 1229 } 1230 1231 if ( !match ){ 1232 1233 groups_to_add.add( new_set ); 1234 } 1235 } 1236 1237 if ( groups_to_add.size() == 0 ){ 1238 1239 return( false ); 1240 } 1241 1242 for (int i=0;i<groups_to_add.size();i++){ 1243 1244 dest_groups.add(i,groups_to_add.get(i)); 1245 } 1246 1247 listToAnnounceGroups( dest_groups, dest_torrent ); 1248 1249 return( true ); 1250 } 1251 1252 public static List<List<String>> mergeAnnounceURLs( List<List<String>> base_urls, List<List<String>> merge_urls )1253 mergeAnnounceURLs( 1254 List<List<String>> base_urls, 1255 List<List<String>> merge_urls ) 1256 { 1257 base_urls = getClone( base_urls ); 1258 if ( merge_urls == null ){ 1259 return( base_urls ); 1260 } 1261 Set<String> mergesSet = new HashSet<String>(); 1262 mergesSet.add( NO_VALID_URL_URL ); // this results in removal of this dummy url if present 1263 for ( List<String> l: merge_urls ){ 1264 mergesSet.addAll(l); 1265 } 1266 Iterator<List<String>> it1 = base_urls.iterator(); 1267 while( it1.hasNext()){ 1268 List<String> l = it1.next(); 1269 Iterator<String> it2 = l.iterator(); 1270 while( it2.hasNext()){ 1271 if ( mergesSet.contains( it2.next())){ 1272 it2.remove(); 1273 } 1274 } 1275 if ( l.isEmpty()){ 1276 it1.remove(); 1277 } 1278 } 1279 1280 for (List<String> l: merge_urls ){ 1281 if ( !l.isEmpty()){ 1282 base_urls.add( l ); 1283 } 1284 } 1285 1286 return( base_urls ); 1287 } 1288 1289 public static List<List<String>> removeAnnounceURLs( List<List<String>> base_urls, List<List<String>> remove_urls, boolean use_prefix_match )1290 removeAnnounceURLs( 1291 List<List<String>> base_urls, 1292 List<List<String>> remove_urls, 1293 boolean use_prefix_match ) 1294 { 1295 base_urls = getClone( base_urls ); 1296 if ( remove_urls == null ){ 1297 return( base_urls ); 1298 } 1299 Set<String> removeSet = new HashSet<String>(); 1300 removeSet.add( NO_VALID_URL_URL ); // this results in removal of this dummy url if present 1301 for ( List<String> l: remove_urls ){ 1302 for ( String s: l ){ 1303 removeSet.add( s.toLowerCase( Locale.US )); 1304 } 1305 } 1306 Iterator<List<String>> it1 = base_urls.iterator(); 1307 while( it1.hasNext()){ 1308 List<String> l = it1.next(); 1309 Iterator<String> it2 = l.iterator(); 1310 while( it2.hasNext()){ 1311 String url = it2.next(); 1312 if ( url.equals( NO_VALID_URL_URL )){ 1313 it2.remove(); 1314 }else{ 1315 url = url.toLowerCase( Locale.US ); 1316 1317 if ( use_prefix_match ){ 1318 1319 for ( String s: removeSet ){ 1320 1321 if ( url.startsWith( s )){ 1322 1323 it2.remove(); 1324 break; 1325 } 1326 } 1327 }else{ 1328 if ( removeSet.contains( url )){ 1329 1330 it2.remove(); 1331 } 1332 } 1333 } 1334 } 1335 if ( l.isEmpty()){ 1336 it1.remove(); 1337 } 1338 } 1339 1340 return( base_urls ); 1341 } 1342 1343 public static List<List<String>> removeAnnounceURLs2( List<List<String>> base_urls, List<String> remove_urls, boolean use_prefix_match )1344 removeAnnounceURLs2( 1345 List<List<String>> base_urls, 1346 List<String> remove_urls, 1347 boolean use_prefix_match ) 1348 { 1349 if ( remove_urls == null ){ 1350 // general semantics are to return a clone so caller can modify 1351 return( getClone( base_urls ) ); 1352 } 1353 1354 List<List<String>> temp = new ArrayList<List<String>>(1); 1355 1356 temp.add( remove_urls ); 1357 1358 return( removeAnnounceURLs( base_urls, temp, use_prefix_match )); 1359 } 1360 1361 public static List<List<String>> getClone( List<List<String>> lls )1362 getClone( 1363 List<List<String>> lls ) 1364 { 1365 if ( lls == null ){ 1366 return( lls ); 1367 } 1368 List<List<String>> result = new ArrayList<List<String>>( lls.size()); 1369 for ( List<String> l: lls ){ 1370 result.add(new ArrayList<String>( l )); 1371 } 1372 return( result ); 1373 } 1374 1375 public static boolean replaceAnnounceURL( TOTorrent torrent, URL old_url, URL new_url )1376 replaceAnnounceURL( 1377 TOTorrent torrent, 1378 URL old_url, 1379 URL new_url ) 1380 { 1381 boolean found = false; 1382 1383 String old_str = old_url.toString(); 1384 String new_str = new_url.toString(); 1385 1386 List l = announceGroupsToList( torrent ); 1387 1388 for (int i=0;i<l.size();i++){ 1389 1390 List set = (List)l.get(i); 1391 1392 for (int j=0;j<set.size();j++){ 1393 1394 if (((String)set.get(j)).equals(old_str)){ 1395 1396 found = true; 1397 1398 set.set( j, new_str ); 1399 } 1400 } 1401 } 1402 1403 if ( found ){ 1404 1405 listToAnnounceGroups( l, torrent ); 1406 } 1407 1408 if ( torrent.getAnnounceURL().toString().equals( old_str )){ 1409 1410 torrent.setAnnounceURL( new_url ); 1411 1412 found = true; 1413 } 1414 1415 if ( found ){ 1416 1417 try{ 1418 writeToFile( torrent ); 1419 1420 }catch( Throwable e ){ 1421 1422 Debug.printStackTrace(e); 1423 1424 return( false ); 1425 } 1426 } 1427 1428 return( found ); 1429 } 1430 1431 public static void setResumeDataCompletelyValid( DownloadManagerState download_manager_state )1432 setResumeDataCompletelyValid( 1433 DownloadManagerState download_manager_state ) 1434 { 1435 DiskManagerFactory.setResumeDataCompletelyValid( download_manager_state ); 1436 } 1437 1438 public static String getLocalisedName( TOTorrent torrent )1439 getLocalisedName( 1440 TOTorrent torrent ) 1441 { 1442 if (torrent == null) { 1443 return ""; 1444 } 1445 try{ 1446 String utf8Name = torrent.getUTF8Name(); 1447 if (utf8Name != null) { 1448 return utf8Name; 1449 } 1450 1451 LocaleUtilDecoder decoder = LocaleTorrentUtil.getTorrentEncodingIfAvailable( torrent ); 1452 1453 if ( decoder == null ){ 1454 1455 return( new String(torrent.getName(),Constants.DEFAULT_ENCODING)); 1456 } 1457 1458 return( decoder.decodeString(torrent.getName())); 1459 1460 }catch( Throwable e ){ 1461 1462 Debug.printStackTrace( e ); 1463 1464 return( new String( torrent.getName())); 1465 } 1466 } 1467 1468 public static void setTLSTorrentHash( HashWrapper hash )1469 setTLSTorrentHash( 1470 HashWrapper hash ) 1471 { 1472 tls.get().put( "hash", hash ); 1473 } 1474 1475 public static HashWrapper getTLSTorrentHash()1476 getTLSTorrentHash() 1477 { 1478 return((HashWrapper)tls.get().get( "hash" )); 1479 } 1480 1481 public static TOTorrent getTLSTorrent()1482 getTLSTorrent() 1483 { 1484 HashWrapper hash = (HashWrapper)(tls.get()).get("hash"); 1485 1486 if ( hash != null ){ 1487 1488 try{ 1489 AzureusCore core = AzureusCoreFactory.getSingleton(); 1490 1491 DownloadManager dm = core.getGlobalManager().getDownloadManager( hash ); 1492 1493 if ( dm != null ){ 1494 1495 return( dm.getTorrent()); 1496 } 1497 }catch( Throwable e ){ 1498 1499 Debug.printStackTrace(e); 1500 } 1501 } 1502 1503 return( null ); 1504 } 1505 1506 public static void setTLSDescription( String desc )1507 setTLSDescription( 1508 String desc ) 1509 { 1510 tls.get().put( "desc", desc ); 1511 } 1512 1513 public static String getTLSDescription()1514 getTLSDescription() 1515 { 1516 return((String)tls.get().get( "desc" )); 1517 } 1518 1519 /** 1520 * get tls for cloning onto another thread 1521 * @return 1522 */ 1523 1524 public static Object getTLS()1525 getTLS() 1526 { 1527 return( new HashMap<String,Object>(tls.get())); 1528 } 1529 1530 public static void setTLS( Object obj )1531 setTLS( 1532 Object obj ) 1533 { 1534 Map<String,Object> m = (Map<String,Object>)obj; 1535 1536 Map<String,Object> tls_map = tls.get(); 1537 1538 tls_map.clear(); 1539 1540 tls_map.putAll(m); 1541 } 1542 1543 public static URL getDecentralisedEmptyURL()1544 getDecentralisedEmptyURL() 1545 { 1546 try{ 1547 return( new URL( "dht://" )); 1548 1549 }catch( Throwable e ){ 1550 1551 Debug.printStackTrace(e); 1552 1553 return( null ); 1554 } 1555 } 1556 1557 public static URL getDecentralisedURL( byte[] hash )1558 getDecentralisedURL( 1559 byte[] hash ) 1560 { 1561 try{ 1562 return( new URL( "dht://" + ByteFormatter.encodeString( hash ) + ".dht/announce" )); 1563 1564 }catch( Throwable e ){ 1565 1566 Debug.out( e ); 1567 1568 return( getDecentralisedEmptyURL()); 1569 } 1570 } 1571 1572 public static URL getDecentralisedURL( TOTorrent torrent )1573 getDecentralisedURL( 1574 TOTorrent torrent ) 1575 { 1576 try{ 1577 return( new URL( "dht://" + ByteFormatter.encodeString( torrent.getHash()) + ".dht/announce" )); 1578 1579 }catch( Throwable e ){ 1580 1581 Debug.out( e ); 1582 1583 return( getDecentralisedEmptyURL()); 1584 } 1585 } 1586 1587 public static void setDecentralised( TOTorrent torrent )1588 setDecentralised( 1589 TOTorrent torrent ) 1590 { 1591 torrent.setAnnounceURL( getDecentralisedURL( torrent )); 1592 } 1593 1594 public static boolean isDecentralised( TOTorrent torrent )1595 isDecentralised( 1596 TOTorrent torrent ) 1597 { 1598 if ( torrent == null ){ 1599 1600 return( false ); 1601 } 1602 1603 return( torrent.isDecentralised()); 1604 } 1605 1606 1607 public static boolean isDecentralised( URL url )1608 isDecentralised( 1609 URL url ) 1610 { 1611 if ( url == null ){ 1612 1613 return( false ); 1614 } 1615 1616 return( url.getProtocol().equalsIgnoreCase( "dht" )); 1617 } 1618 1619 public static boolean isDecentralised( String host )1620 isDecentralised( 1621 String host ) 1622 { 1623 if ( host == null ){ 1624 1625 return( false ); 1626 } 1627 1628 return( host.endsWith( ".dht" )); 1629 } 1630 1631 private static Map getAzureusProperties( TOTorrent torrent )1632 getAzureusProperties( 1633 TOTorrent torrent ) 1634 { 1635 Map m = torrent.getAdditionalMapProperty( TOTorrent.AZUREUS_PROPERTIES ); 1636 1637 if ( m == null ){ 1638 1639 m = new HashMap(); 1640 1641 torrent.setAdditionalMapProperty( TOTorrent.AZUREUS_PROPERTIES, m ); 1642 } 1643 1644 return( m ); 1645 } 1646 1647 private static Map getAzureusPrivateProperties( TOTorrent torrent )1648 getAzureusPrivateProperties( 1649 TOTorrent torrent ) 1650 { 1651 Map m = torrent.getAdditionalMapProperty( TOTorrent.AZUREUS_PRIVATE_PROPERTIES ); 1652 1653 if ( m == null ){ 1654 1655 m = new HashMap(); 1656 1657 torrent.setAdditionalMapProperty( TOTorrent.AZUREUS_PRIVATE_PROPERTIES, m ); 1658 } 1659 1660 return( m ); 1661 } 1662 1663 private static String getContentMapString( TOTorrent torrent, String key )1664 getContentMapString( 1665 TOTorrent torrent, 1666 String key ) 1667 { 1668 Map m = getAzureusProperties( torrent ); 1669 1670 Object content = m.get( "Content" ); 1671 1672 if ( !(content instanceof Map )){ 1673 1674 return null; 1675 } 1676 1677 Map mapContent = (Map)content; 1678 1679 Object obj = mapContent.get(key); 1680 1681 if ( obj instanceof String ){ 1682 1683 return (String) obj; 1684 1685 }else if ( obj instanceof byte[] ){ 1686 1687 try{ 1688 return new String((byte[]) obj, Constants.DEFAULT_ENCODING); 1689 1690 }catch ( UnsupportedEncodingException e ){ 1691 1692 e.printStackTrace(); 1693 } 1694 } 1695 1696 return null; 1697 } 1698 1699 public static boolean isFeaturedContent( TOTorrent torrent )1700 isFeaturedContent( 1701 TOTorrent torrent ) 1702 { 1703 String content_type = getContentMapString( torrent, "Content Type" ); 1704 1705 return( content_type != null && content_type.equalsIgnoreCase( "featured" )); 1706 } 1707 1708 public static void setObtainedFrom( File file, String str )1709 setObtainedFrom( 1710 File file, 1711 String str ) 1712 { 1713 try{ 1714 TOTorrent torrent = readFromFile( file, false, false ); 1715 1716 setObtainedFrom( torrent, str ); 1717 1718 writeToFile( torrent ); 1719 1720 } catch (TOTorrentException e) { 1721 // ignore, file probably not torrent 1722 1723 }catch( Throwable e ){ 1724 1725 Debug.out( e ); 1726 } 1727 } 1728 1729 public static void setObtainedFrom( TOTorrent torrent, String str )1730 setObtainedFrom( 1731 TOTorrent torrent, 1732 String str ) 1733 { 1734 Map m = getAzureusPrivateProperties( torrent ); 1735 1736 try{ 1737 str = str.trim(); 1738 1739 if ( str == null || str.length() == 0 ){ 1740 1741 m.remove( TORRENT_AZ_PROP_OBTAINED_FROM ); 1742 1743 }else{ 1744 1745 m.put( TORRENT_AZ_PROP_OBTAINED_FROM, str.getBytes( "UTF-8" )); 1746 } 1747 1748 fireAttributeListener( torrent, TORRENT_AZ_PROP_OBTAINED_FROM, str ); 1749 1750 }catch( Throwable e ){ 1751 1752 Debug.printStackTrace(e); 1753 } 1754 } 1755 1756 public static String getObtainedFrom( TOTorrent torrent )1757 getObtainedFrom( 1758 TOTorrent torrent ) 1759 { 1760 Map m = getAzureusPrivateProperties( torrent ); 1761 1762 byte[] from = (byte[])m.get( TORRENT_AZ_PROP_OBTAINED_FROM ); 1763 1764 if ( from != null ){ 1765 1766 try{ 1767 return( new String( from, "UTF-8" )); 1768 1769 }catch( Throwable e ){ 1770 1771 Debug.printStackTrace(e); 1772 } 1773 } 1774 1775 return( null ); 1776 } 1777 1778 public static void setNetworkCache( TOTorrent torrent, List<String> networks )1779 setNetworkCache( 1780 TOTorrent torrent, 1781 List<String> networks ) 1782 { 1783 Map m = getAzureusPrivateProperties( torrent ); 1784 1785 try{ 1786 m.put( TORRENT_AZ_PROP_NETWORK_CACHE, networks ); 1787 1788 }catch( Throwable e ){ 1789 1790 Debug.printStackTrace(e); 1791 } 1792 } 1793 1794 public static List<String> getNetworkCache( TOTorrent torrent )1795 getNetworkCache( 1796 TOTorrent torrent ) 1797 { 1798 List<String> result = new ArrayList<String>(); 1799 1800 Map m = getAzureusPrivateProperties( torrent ); 1801 1802 try{ 1803 List l = (List)m.get( TORRENT_AZ_PROP_NETWORK_CACHE ); 1804 1805 if ( l != null ){ 1806 1807 for (Object o: l ){ 1808 1809 if ( o instanceof String ){ 1810 1811 result.add((String)o); 1812 1813 }else if ( o instanceof byte[] ){ 1814 1815 String s = new String((byte[])o, "UTF-8" ); 1816 1817 for ( String x: AENetworkClassifier.AT_NETWORKS ){ 1818 1819 if ( s.equals( x )){ 1820 1821 result.add( x ); 1822 1823 break; 1824 } 1825 } 1826 } 1827 } 1828 } 1829 }catch( Throwable e ){ 1830 1831 Debug.printStackTrace(e); 1832 } 1833 1834 return( result ); 1835 } 1836 1837 public static void setTagCache( TOTorrent torrent, List<String> networks )1838 setTagCache( 1839 TOTorrent torrent, 1840 List<String> networks ) 1841 { 1842 Map m = getAzureusPrivateProperties( torrent ); 1843 1844 try{ 1845 m.put( TORRENT_AZ_PROP_TAG_CACHE, networks ); 1846 1847 }catch( Throwable e ){ 1848 1849 Debug.printStackTrace(e); 1850 } 1851 } 1852 1853 public static List<String> getTagCache( TOTorrent torrent )1854 getTagCache( 1855 TOTorrent torrent ) 1856 { 1857 List<String> result = new ArrayList<String>(); 1858 1859 Map m = getAzureusPrivateProperties( torrent ); 1860 1861 try{ 1862 List l = (List)m.get( TORRENT_AZ_PROP_TAG_CACHE ); 1863 1864 if ( l != null ){ 1865 1866 for (Object o: l ){ 1867 1868 if ( o instanceof String ){ 1869 1870 result.add((String)o); 1871 1872 }else if ( o instanceof byte[] ){ 1873 1874 String s = new String((byte[])o, "UTF-8" ); 1875 1876 result.add( s ); 1877 } 1878 } 1879 } 1880 }catch( Throwable e ){ 1881 1882 Debug.printStackTrace(e); 1883 } 1884 1885 return( result ); 1886 } 1887 1888 public static void setPeerCache( TOTorrent torrent, Map pc )1889 setPeerCache( 1890 TOTorrent torrent, 1891 Map pc ) 1892 { 1893 Map m = getAzureusPrivateProperties( torrent ); 1894 1895 try{ 1896 m.put( TORRENT_AZ_PROP_PEER_CACHE, pc ); 1897 1898 setPeerCacheValid( torrent ); 1899 1900 }catch( Throwable e ){ 1901 1902 Debug.printStackTrace(e); 1903 } 1904 } 1905 1906 public static void setPeerCacheValid( TOTorrent torrent )1907 setPeerCacheValid( 1908 TOTorrent torrent ) 1909 { 1910 Map m = getAzureusPrivateProperties( torrent ); 1911 1912 try{ 1913 m.put( TORRENT_AZ_PROP_PEER_CACHE_VALID, new Long( PC_MARKER )); 1914 1915 }catch( Throwable e ){ 1916 1917 Debug.printStackTrace(e); 1918 } 1919 } 1920 1921 public static Map getPeerCache( TOTorrent torrent )1922 getPeerCache( 1923 TOTorrent torrent ) 1924 { 1925 try{ 1926 Map m = getAzureusPrivateProperties( torrent ); 1927 1928 Long value = (Long)m.get( TORRENT_AZ_PROP_PEER_CACHE_VALID ); 1929 1930 if ( value != null && value == PC_MARKER ){ 1931 1932 Map pc = (Map)m.get( TORRENT_AZ_PROP_PEER_CACHE ); 1933 1934 return( pc ); 1935 } 1936 1937 }catch( Throwable e ){ 1938 1939 Debug.out( e ); 1940 } 1941 1942 return( null ); 1943 } 1944 1945 public static void setFlag( TOTorrent torrent, int flag, boolean value )1946 setFlag( 1947 TOTorrent torrent, 1948 int flag, 1949 boolean value ) 1950 { 1951 Map m = getAzureusProperties( torrent ); 1952 1953 Long flags = (Long)m.get( TORRENT_AZ_PROP_TORRENT_FLAGS ); 1954 1955 if ( flags == null ){ 1956 1957 flags = new Long(0); 1958 } 1959 1960 m.put( TORRENT_AZ_PROP_TORRENT_FLAGS, new Long(flags.intValue() | flag )); 1961 } 1962 1963 public static boolean getFlag( TOTorrent torrent, int flag )1964 getFlag( 1965 TOTorrent torrent, 1966 int flag ) 1967 { 1968 Map m = getAzureusProperties( torrent ); 1969 1970 Long flags = (Long)m.get( TORRENT_AZ_PROP_TORRENT_FLAGS ); 1971 1972 if ( flags == null ){ 1973 1974 return( false ); 1975 } 1976 1977 return(( flags.intValue() & flag ) != 0 ); 1978 } 1979 1980 public static Map<Integer,File> getInitialLinkage( TOTorrent torrent )1981 getInitialLinkage( 1982 TOTorrent torrent ) 1983 { 1984 Map<Integer,File> result = new HashMap<Integer, File>(); 1985 1986 try{ 1987 Map pp = torrent.getAdditionalMapProperty( TOTorrent.AZUREUS_PRIVATE_PROPERTIES ); 1988 1989 if ( pp != null ){ 1990 1991 Map<String,Object> _links; 1992 1993 byte[] g_data = (byte[])pp.get( TorrentUtils.TORRENT_AZ_PROP_INITIAL_LINKAGE2 ); 1994 1995 if ( g_data == null ){ 1996 1997 _links = (Map<String,Object>)pp.get( TorrentUtils.TORRENT_AZ_PROP_INITIAL_LINKAGE ); 1998 1999 }else{ 2000 2001 _links = BDecoder.decode(new BufferedInputStream( new GZIPInputStream( new ByteArrayInputStream( g_data )))); 2002 2003 } 2004 if ( _links != null ){//&& TorrentUtils.isCreatedTorrent( torrent )){ 2005 2006 Map<String,String> links = (Map<String,String>)BDecoder.decodeStrings( _links ); 2007 2008 for ( Map.Entry<String,String> entry: links.entrySet()){ 2009 2010 int file_index = Integer.parseInt( entry.getKey()); 2011 String file = entry.getValue(); 2012 2013 result.put( file_index, new File( file )); 2014 } 2015 } 2016 } 2017 }catch( Throwable e ){ 2018 2019 Debug.out( "Failed to read linkage map", e ); 2020 } 2021 2022 return( result ); 2023 } 2024 2025 public static void setPluginStringProperty( TOTorrent torrent, String name, String value )2026 setPluginStringProperty( 2027 TOTorrent torrent, 2028 String name, 2029 String value ) 2030 { 2031 Map m = getAzureusProperties( torrent ); 2032 2033 Object obj = m.get( TORRENT_AZ_PROP_PLUGINS ); 2034 2035 Map p; 2036 2037 if ( obj instanceof Map ){ 2038 2039 p = (Map)obj; 2040 2041 }else{ 2042 2043 p = new HashMap(); 2044 2045 m.put( TORRENT_AZ_PROP_PLUGINS, p ); 2046 } 2047 2048 if ( value == null ){ 2049 2050 p.remove( name ); 2051 2052 }else{ 2053 2054 p.put( name, value.getBytes()); 2055 } 2056 } 2057 2058 public static String getPluginStringProperty( TOTorrent torrent, String name )2059 getPluginStringProperty( 2060 TOTorrent torrent, 2061 String name ) 2062 { 2063 Map m = getAzureusProperties( torrent ); 2064 2065 Object obj = m.get( TORRENT_AZ_PROP_PLUGINS ); 2066 2067 if ( obj instanceof Map ){ 2068 2069 Map p = (Map)obj; 2070 2071 obj = p.get( name ); 2072 2073 if ( obj instanceof byte[]){ 2074 2075 return( new String((byte[])obj)); 2076 } 2077 } 2078 2079 return( null ); 2080 } 2081 2082 public static void setPluginMapProperty( TOTorrent torrent, String name, Map value )2083 setPluginMapProperty( 2084 TOTorrent torrent, 2085 String name, 2086 Map value ) 2087 { 2088 Map m = getAzureusProperties( torrent ); 2089 2090 Object obj = m.get( TORRENT_AZ_PROP_PLUGINS ); 2091 2092 Map p; 2093 2094 if ( obj instanceof Map ){ 2095 2096 p = (Map)obj; 2097 2098 }else{ 2099 2100 p = new HashMap(); 2101 2102 m.put( TORRENT_AZ_PROP_PLUGINS, p ); 2103 } 2104 2105 if ( value == null ){ 2106 2107 p.remove( name ); 2108 2109 }else{ 2110 2111 p.put( name, value ); 2112 } 2113 } 2114 2115 public static Map getPluginMapProperty( TOTorrent torrent, String name )2116 getPluginMapProperty( 2117 TOTorrent torrent, 2118 String name ) 2119 { 2120 Map m = getAzureusProperties( torrent ); 2121 2122 Object obj = m.get( TORRENT_AZ_PROP_PLUGINS ); 2123 2124 if ( obj instanceof Map ){ 2125 2126 Map p = (Map)obj; 2127 2128 obj = p.get( name ); 2129 2130 if ( obj instanceof Map ){ 2131 2132 return((Map)obj); 2133 } 2134 } 2135 2136 return( null ); 2137 } 2138 2139 public static void setDHTBackupEnabled( TOTorrent torrent, boolean enabled )2140 setDHTBackupEnabled( 2141 TOTorrent torrent, 2142 boolean enabled ) 2143 { 2144 Map m = getAzureusProperties( torrent ); 2145 2146 m.put( TORRENT_AZ_PROP_DHT_BACKUP_ENABLE, new Long(enabled?1:0)); 2147 } 2148 2149 public static boolean getDHTBackupEnabled( TOTorrent torrent )2150 getDHTBackupEnabled( 2151 TOTorrent torrent ) 2152 { 2153 // missing -> true 2154 2155 Map m = getAzureusProperties( torrent ); 2156 2157 Object obj = m.get( TORRENT_AZ_PROP_DHT_BACKUP_ENABLE ); 2158 2159 if ( obj instanceof Long ){ 2160 2161 return( ((Long)obj).longValue() == 1 ); 2162 } 2163 2164 return( true ); 2165 } 2166 2167 public static boolean isDHTBackupRequested( TOTorrent torrent )2168 isDHTBackupRequested( 2169 TOTorrent torrent ) 2170 { 2171 // missing -> false 2172 2173 Map m = getAzureusProperties( torrent ); 2174 2175 Object obj = m.get( TORRENT_AZ_PROP_DHT_BACKUP_REQUESTED ); 2176 2177 if ( obj instanceof Long ){ 2178 2179 return( ((Long)obj).longValue() == 1 ); 2180 } 2181 2182 return( false ); 2183 } 2184 2185 public static void setDHTBackupRequested( TOTorrent torrent, boolean requested )2186 setDHTBackupRequested( 2187 TOTorrent torrent, 2188 boolean requested ) 2189 { 2190 Map m = getAzureusProperties( torrent ); 2191 2192 m.put( TORRENT_AZ_PROP_DHT_BACKUP_REQUESTED, new Long(requested?1:0)); 2193 } 2194 2195 2196 public static boolean isReallyPrivate( TOTorrent torrent)2197 isReallyPrivate( 2198 TOTorrent torrent) 2199 { 2200 if ( torrent == null ){ 2201 2202 return( false ); 2203 } 2204 2205 URL url = torrent.getAnnounceURL(); 2206 2207 if ( url == null ){ 2208 2209 TOTorrentAnnounceURLSet[] sets = torrent.getAnnounceURLGroup().getAnnounceURLSets(); 2210 2211 if ( sets != null && sets.length > 0 ){ 2212 2213 URL[] urls = sets[0].getAnnounceURLs(); 2214 2215 if ( urls.length > 0 ){ 2216 2217 url = urls[0]; 2218 } 2219 } 2220 } 2221 2222 if ( url != null ){ 2223 2224 if ( UrlUtils.containsPasskey( url )){ 2225 2226 return torrent.getPrivate(); 2227 } 2228 } 2229 2230 return false; 2231 } 2232 2233 public static boolean getPrivate( TOTorrent torrent )2234 getPrivate( 2235 TOTorrent torrent ) 2236 { 2237 if ( torrent == null ){ 2238 2239 return( false ); 2240 } 2241 2242 return( torrent.getPrivate()); 2243 } 2244 2245 public static void setPrivate( TOTorrent torrent, boolean _private )2246 setPrivate( 2247 TOTorrent torrent, 2248 boolean _private ) 2249 { 2250 if ( torrent == null ){ 2251 2252 return; 2253 } 2254 2255 try{ 2256 torrent.setPrivate( _private ); 2257 2258 }catch( Throwable e ){ 2259 2260 Debug.printStackTrace(e); 2261 } 2262 } 2263 2264 // skip extensions 2265 2266 2267 public static Set<String> getSkipExtensionsSet()2268 getSkipExtensionsSet() 2269 { 2270 return(getSkipExtensionsSetSupport(false)); 2271 } 2272 2273 private static synchronized Set<String> getSkipExtensionsSetSupport( boolean force )2274 getSkipExtensionsSetSupport( 2275 boolean force ) 2276 { 2277 if ( skip_extensions_set == null || force ){ 2278 2279 Set<String> new_skip_set = new HashSet<String>(); 2280 2281 String skip_list = COConfigurationManager.getStringParameter( "File.Torrent.AutoSkipExtensions" ); 2282 2283 skip_list = skip_list.replace( ',', ';' ); 2284 2285 if ( skip_extensions_set == null ){ 2286 2287 // first time - add the listener 2288 2289 COConfigurationManager.addParameterListener( 2290 "File.Torrent.AutoSkipExtensions", 2291 new ParameterListener() 2292 { 2293 public void 2294 parameterChanged( 2295 String parameterName) 2296 { 2297 getSkipExtensionsSetSupport( true ); 2298 } 2299 }); 2300 } 2301 2302 int pos = 0; 2303 2304 while( true ){ 2305 2306 int p1 = skip_list.indexOf( ";", pos ); 2307 2308 String bit; 2309 2310 if ( p1 == -1 ){ 2311 2312 bit = skip_list.substring(pos); 2313 2314 }else{ 2315 2316 bit = skip_list.substring( pos, p1 ); 2317 2318 pos = p1+1; 2319 } 2320 2321 String ext = bit.trim().toLowerCase(); // use default locale as we're dealing with local file names 2322 2323 if ( ext.startsWith(".")){ 2324 2325 ext = ext.substring(1); 2326 } 2327 2328 if ( ext.length() > 0 ){ 2329 2330 new_skip_set.add( ext ); 2331 } 2332 2333 if ( p1 == -1 ){ 2334 2335 break; 2336 } 2337 } 2338 2339 skip_extensions_set = new_skip_set; 2340 } 2341 2342 return( skip_extensions_set ); 2343 } 2344 2345 2346 // ignore files 2347 2348 public static Set<String> getIgnoreSet()2349 getIgnoreSet() 2350 { 2351 return(getIgnoreSetSupport(false)); 2352 } 2353 2354 private static synchronized Set<String> getIgnoreSetSupport( boolean force )2355 getIgnoreSetSupport( 2356 boolean force ) 2357 { 2358 if ( ignore_files_set == null || force ){ 2359 2360 Set<String> new_ignore_set = new HashSet<String>(); 2361 2362 String ignore_list = COConfigurationManager.getStringParameter( "File.Torrent.IgnoreFiles", TOTorrent.DEFAULT_IGNORE_FILES ); 2363 2364 if ( ignore_files_set == null ){ 2365 2366 // first time - add the listener 2367 2368 COConfigurationManager.addParameterListener( 2369 "File.Torrent.IgnoreFiles", 2370 new ParameterListener() 2371 { 2372 public void 2373 parameterChanged( 2374 String parameterName) 2375 { 2376 getIgnoreSetSupport( true ); 2377 } 2378 }); 2379 } 2380 2381 int pos = 0; 2382 2383 while(true){ 2384 2385 int p1 = ignore_list.indexOf( ";", pos ); 2386 2387 String bit; 2388 2389 if ( p1 == -1 ){ 2390 2391 bit = ignore_list.substring(pos); 2392 2393 }else{ 2394 2395 bit = ignore_list.substring( pos, p1 ); 2396 2397 pos = p1+1; 2398 } 2399 2400 new_ignore_set.add(bit.trim().toLowerCase()); // use default locale as we're dealing with local file names 2401 2402 if ( p1 == -1 ){ 2403 2404 break; 2405 } 2406 } 2407 2408 ignore_files_set = new_ignore_set; 2409 } 2410 2411 return( ignore_files_set ); 2412 } 2413 2414 2415 2416 // this class exists to minimise memory requirements by discarding the piece hash values 2417 // when "idle" 2418 2419 private static final int PIECE_HASH_TIMEOUT = 3*60*1000; 2420 2421 private static Map torrent_delegates = new WeakHashMap(); 2422 2423 static{ 2424 SimpleTimer.addPeriodicEvent( 2425 "TorrentUtils:pieceDiscard", 2426 PIECE_HASH_TIMEOUT/2, 2427 new TimerEventPerformer() 2428 { 2429 public void 2430 perform( 2431 TimerEvent event ) 2432 { 2433 long now = SystemTime.getCurrentTime(); 2434 2435 synchronized( torrent_delegates ){ 2436 2437 Iterator it = torrent_delegates.keySet().iterator(); 2438 2439 while( it.hasNext()){ 2440 2441 ((torrentDelegate)it.next()).discardPieces(now,false); 2442 } 2443 } 2444 } 2445 }); 2446 } 2447 2448 private static HashSet torrentFluffKeyset = new HashSet(2); 2449 private static Map fluffThombstone = new HashMap(1); 2450 2451 /** 2452 * Register keys that are used for heavyweight maps that should be discarded when the torrent is not in use 2453 * Make sure these keys are only ever used for Map objects! 2454 */ 2455 public static void registerMapFluff( String[] fluff )2456 registerMapFluff( 2457 String[] fluff ) 2458 { 2459 synchronized (TorrentUtils.class) 2460 { 2461 for (int i = 0; i < fluff.length; i++) 2462 torrentFluffKeyset.add(fluff[i]); 2463 } 2464 } 2465 2466 public interface 2467 ExtendedTorrent 2468 extends TOTorrent 2469 { 2470 public byte[][] peekPieces()2471 peekPieces() 2472 2473 throws TOTorrentException; 2474 2475 public void setDiscardFluff( boolean discard )2476 setDiscardFluff( 2477 boolean discard ); 2478 } 2479 2480 public static class 2481 torrentDelegate 2482 extends LogRelation 2483 implements ExtendedTorrent 2484 { 2485 private TOTorrent delegate; 2486 private File file; 2487 2488 private boolean fluff_dirty; 2489 2490 private long last_pieces_read_time = SystemTime.getCurrentTime(); 2491 2492 private URL url_mod_last_pre; 2493 private URL url_mod_last_post; 2494 private int url_mod_last_seq; 2495 2496 private List<URL> urlg_mod_last_pre; 2497 private TOTorrentAnnounceURLGroup urlg_mod_last_post; 2498 private int urlg_mod_last_seq; 2499 2500 protected torrentDelegate( TOTorrent _delegate, File _file )2501 torrentDelegate( 2502 TOTorrent _delegate, 2503 File _file ) 2504 { 2505 delegate = _delegate; 2506 file = _file; 2507 2508 synchronized( torrent_delegates ){ 2509 2510 torrent_delegates.put( this, null ); 2511 } 2512 } 2513 2514 public void setDiscardFluff( boolean discard )2515 setDiscardFluff( 2516 boolean discard ) 2517 { 2518 if ( discard && !torrentFluffKeyset.isEmpty() ){ 2519 2520 //System.out.println( "Discarded fluff for " + new String(getName())); 2521 2522 try{ 2523 getMonitor().enter(); 2524 2525 try{ 2526 // if file is out of sync with fluff then force a write 2527 2528 if ( fluff_dirty ){ 2529 2530 boolean[] restored = restoreState( true, true ); 2531 2532 delegate.serialiseToBEncodedFile( file ); 2533 2534 fluff_dirty = false; 2535 2536 if ( restored[0] ){ 2537 2538 discardPieces( SystemTime.getCurrentTime(), true ); 2539 } 2540 } 2541 2542 for(Iterator it = torrentFluffKeyset.iterator();it.hasNext();){ 2543 2544 delegate.setAdditionalMapProperty( (String)it.next(), fluffThombstone ); 2545 } 2546 }catch( Throwable e ){ 2547 2548 Debug.printStackTrace( e ); 2549 } 2550 }finally{ 2551 2552 getMonitor().exit(); 2553 } 2554 } 2555 } 2556 2557 public byte[] getName()2558 getName() 2559 { 2560 return( delegate.getName()); 2561 } 2562 2563 public boolean isSimpleTorrent()2564 isSimpleTorrent() 2565 { 2566 return( delegate.isSimpleTorrent()); 2567 } 2568 2569 public byte[] getComment()2570 getComment() 2571 { 2572 return( delegate.getComment()); 2573 } 2574 2575 public void setComment( String comment )2576 setComment( 2577 String comment ) 2578 { 2579 delegate.setComment( comment ); 2580 } 2581 2582 public long getCreationDate()2583 getCreationDate() 2584 { 2585 return( delegate.getCreationDate()); 2586 } 2587 2588 public void setCreationDate( long date )2589 setCreationDate( 2590 long date ) 2591 { 2592 delegate.setCreationDate( date ); 2593 } 2594 2595 public byte[] getCreatedBy()2596 getCreatedBy() 2597 { 2598 return( delegate.getCreatedBy()); 2599 } 2600 2601 public void setCreatedBy( byte[] cb )2602 setCreatedBy( 2603 byte[] cb ) 2604 { 2605 delegate.setCreatedBy( cb ); 2606 } 2607 2608 public boolean isCreated()2609 isCreated() 2610 { 2611 return( delegate.isCreated()); 2612 } 2613 2614 public boolean isDecentralised()2615 isDecentralised() 2616 { 2617 URL url = getAnnounceURLSupport(); 2618 2619 return( TorrentUtils.isDecentralised( url )); 2620 } 2621 2622 public URL getAnnounceURL()2623 getAnnounceURL() 2624 { 2625 URL url = getAnnounceURLSupport(); 2626 2627 int seq = dns_mapping_seq_count; 2628 2629 if ( url == url_mod_last_pre && 2630 url_mod_last_post != null && 2631 seq == url_mod_last_seq ){ 2632 2633 // System.out.println( "using old url: " + url + " -> " + url_mod_last_post ); 2634 2635 return( url_mod_last_post ); 2636 } 2637 2638 url_mod_last_post = applyDNSMods( url ); 2639 url_mod_last_pre = url; 2640 url_mod_last_seq = seq; 2641 2642 return( url_mod_last_post ); 2643 } 2644 2645 public TOTorrentAnnounceURLGroup getAnnounceURLGroup()2646 getAnnounceURLGroup() 2647 { 2648 TOTorrentAnnounceURLGroup group = getAnnounceURLGroupSupport(); 2649 2650 int seq = dns_mapping_seq_count; 2651 2652 if ( seq == urlg_mod_last_seq && urlg_mod_last_pre != null && urlg_mod_last_post != null ){ 2653 2654 boolean match = !(urlg_mod_last_post instanceof URLGroup && ((URLGroup)urlg_mod_last_post).hasBeenModified()); 2655 2656 if ( match ){ 2657 2658 TOTorrentAnnounceURLSet[] sets = group.getAnnounceURLSets(); 2659 2660 Iterator<URL> it = urlg_mod_last_pre.iterator(); 2661 2662 outer: 2663 for (int i=0;i<sets.length;i++){ 2664 2665 URL[] urls = sets[i].getAnnounceURLs(); 2666 2667 for ( int j=0;j<urls.length;j++){ 2668 2669 if ( !it.hasNext()){ 2670 2671 match = false; 2672 2673 break outer; 2674 } 2675 2676 if ( it.next() != urls[j] ){ 2677 2678 match = false; 2679 2680 break; 2681 } 2682 } 2683 } 2684 2685 if (it.hasNext()){ 2686 2687 match = false; 2688 } 2689 } 2690 2691 if ( match ){ 2692 2693 // System.out.println( "using old urlg: " + group + " -> " + urlg_mod_last_post ); 2694 2695 return( urlg_mod_last_post ); 2696 } 2697 } 2698 2699 List<URL> url_list = new ArrayList<URL>(); 2700 2701 TOTorrentAnnounceURLSet[] sets = group.getAnnounceURLSets(); 2702 2703 for (int i=0;i<sets.length;i++){ 2704 2705 URL[] urls = sets[i].getAnnounceURLs(); 2706 2707 for ( URL u: urls ){ 2708 2709 url_list.add( u ); 2710 } 2711 } 2712 2713 urlg_mod_last_post = applyDNSMods( getAnnounceURL(), group ); 2714 urlg_mod_last_pre = url_list; 2715 urlg_mod_last_seq = seq; 2716 2717 return( urlg_mod_last_post ); 2718 } 2719 2720 public URL getAnnounceURLSupport()2721 getAnnounceURLSupport() 2722 { 2723 return( delegate.getAnnounceURL()); 2724 } 2725 2726 public boolean setAnnounceURL( URL url )2727 setAnnounceURL( 2728 URL url ) 2729 { 2730 return delegate.setAnnounceURL( url ); 2731 } 2732 2733 2734 public TOTorrentAnnounceURLGroup getAnnounceURLGroupSupport()2735 getAnnounceURLGroupSupport() 2736 { 2737 return( delegate.getAnnounceURLGroup()); 2738 } 2739 2740 protected void discardPieces( long now, boolean force )2741 discardPieces( 2742 long now, 2743 boolean force ) 2744 { 2745 // handle clock changes backwards 2746 2747 if ( now < last_pieces_read_time && !force ){ 2748 2749 last_pieces_read_time = now; 2750 2751 }else{ 2752 2753 try{ 2754 if( ( now - last_pieces_read_time > PIECE_HASH_TIMEOUT || force ) && 2755 delegate.getPieces() != null ){ 2756 2757 try{ 2758 getMonitor().enter(); 2759 2760 // System.out.println( "clearing pieces for '" + new String(getName()) + "'"); 2761 2762 delegate.setPieces( null ); 2763 }finally{ 2764 2765 getMonitor().exit(); 2766 } 2767 } 2768 }catch( Throwable e ){ 2769 2770 Debug.printStackTrace(e); 2771 } 2772 } 2773 } 2774 2775 public byte[][] getPieces()2776 getPieces() 2777 2778 throws TOTorrentException 2779 { 2780 byte[][] res = delegate.getPieces(); 2781 2782 last_pieces_read_time = SystemTime.getCurrentTime(); 2783 2784 if ( res == null ){ 2785 2786 // System.out.println( "recovering pieces for '" + new String(getName()) + "'"); 2787 2788 try{ 2789 getMonitor().enter(); 2790 2791 restoreState( true, false ); 2792 2793 res = delegate.getPieces(); 2794 2795 }finally{ 2796 2797 getMonitor().exit(); 2798 } 2799 } 2800 2801 return( res ); 2802 } 2803 2804 /** 2805 * monitor must be held before calling me 2806 * @param do_pieces 2807 * @param do_fluff 2808 * @throws TOTorrentException 2809 */ 2810 2811 protected boolean[] restoreState( boolean do_pieces, boolean do_fluff )2812 restoreState( 2813 boolean do_pieces, 2814 boolean do_fluff ) 2815 2816 throws TOTorrentException 2817 { 2818 boolean had_pieces = delegate.getPieces() != null; 2819 2820 boolean had_fluff = true; 2821 2822 for(Iterator it = torrentFluffKeyset.iterator();it.hasNext();){ 2823 2824 had_fluff &= delegate.getAdditionalMapProperty( (String)it.next() ) != fluffThombstone; 2825 } 2826 2827 if ( had_pieces ){ 2828 2829 do_pieces = false; 2830 } 2831 2832 if ( had_fluff ){ 2833 2834 do_fluff = false; 2835 } 2836 2837 if ( do_pieces || do_fluff ){ 2838 2839 TOTorrent temp = readFromFile( file, false ); 2840 2841 if ( do_pieces ){ 2842 2843 byte[][] res = temp.getPieces(); 2844 2845 delegate.setPieces( res ); 2846 } 2847 2848 if ( do_fluff ){ 2849 2850 for (Iterator it = torrentFluffKeyset.iterator(); it.hasNext();){ 2851 2852 String fluffKey = (String) it.next(); 2853 2854 // only update the discarded entries as non-discarded may be out of sync 2855 // with the file contents 2856 2857 if ( delegate.getAdditionalMapProperty( fluffKey ) == fluffThombstone ){ 2858 2859 delegate.setAdditionalMapProperty(fluffKey, temp.getAdditionalMapProperty(fluffKey)); 2860 } 2861 } 2862 } 2863 } 2864 2865 return( new boolean[]{ do_pieces, do_fluff }); 2866 } 2867 2868 /** 2869 * peeks the pieces, will return null if they are discarded 2870 * @return 2871 */ 2872 2873 public byte[][] peekPieces()2874 peekPieces() 2875 2876 throws TOTorrentException 2877 { 2878 return( delegate.getPieces()); 2879 } 2880 2881 public void setPieces( byte[][] pieces )2882 setPieces( 2883 byte[][] pieces ) 2884 2885 throws TOTorrentException 2886 { 2887 throw( new TOTorrentException( "Unsupported Operation", TOTorrentException.RT_WRITE_FAILS )); 2888 } 2889 2890 public long getPieceLength()2891 getPieceLength() 2892 { 2893 return( delegate.getPieceLength()); 2894 } 2895 2896 public int getNumberOfPieces()2897 getNumberOfPieces() 2898 { 2899 return( delegate.getNumberOfPieces()); 2900 } 2901 2902 public long getSize()2903 getSize() 2904 { 2905 return( delegate.getSize()); 2906 } 2907 2908 public int getFileCount()2909 getFileCount() 2910 { 2911 return( delegate.getFileCount()); 2912 } 2913 2914 public TOTorrentFile[] getFiles()2915 getFiles() 2916 { 2917 return( delegate.getFiles()); 2918 } 2919 2920 public byte[] getHash()2921 getHash() 2922 2923 throws TOTorrentException 2924 { 2925 return( delegate.getHash()); 2926 } 2927 2928 public HashWrapper getHashWrapper()2929 getHashWrapper() 2930 2931 throws TOTorrentException 2932 { 2933 return( delegate.getHashWrapper()); 2934 } 2935 2936 public void setHashOverride( byte[] hash )2937 setHashOverride( 2938 byte[] hash ) 2939 2940 throws TOTorrentException 2941 { 2942 throw( new TOTorrentException( "Not supported", TOTorrentException.RT_HASH_FAILS )); 2943 } 2944 2945 public boolean getPrivate()2946 getPrivate() 2947 { 2948 return( delegate.getPrivate()); 2949 } 2950 2951 public void setPrivate( boolean _private )2952 setPrivate( 2953 boolean _private ) 2954 2955 throws TOTorrentException 2956 { 2957 // don't support this as it changes teh torrent hash 2958 2959 throw( new TOTorrentException( "Can't amend private attribute", TOTorrentException.RT_WRITE_FAILS )); 2960 } 2961 2962 public boolean hasSameHashAs( TOTorrent other )2963 hasSameHashAs( 2964 TOTorrent other ) 2965 { 2966 return( delegate.hasSameHashAs( other )); 2967 } 2968 2969 public void setAdditionalStringProperty( String name, String value )2970 setAdditionalStringProperty( 2971 String name, 2972 String value ) 2973 { 2974 delegate.setAdditionalStringProperty( name, value ); 2975 } 2976 2977 public String getAdditionalStringProperty( String name )2978 getAdditionalStringProperty( 2979 String name ) 2980 { 2981 return( delegate.getAdditionalStringProperty( name )); 2982 } 2983 2984 public void setAdditionalByteArrayProperty( String name, byte[] value )2985 setAdditionalByteArrayProperty( 2986 String name, 2987 byte[] value ) 2988 { 2989 delegate.setAdditionalByteArrayProperty( name, value ); 2990 } 2991 2992 public byte[] getAdditionalByteArrayProperty( String name )2993 getAdditionalByteArrayProperty( 2994 String name ) 2995 { 2996 return( delegate.getAdditionalByteArrayProperty( name )); 2997 } 2998 2999 public void setAdditionalLongProperty( String name, Long value )3000 setAdditionalLongProperty( 3001 String name, 3002 Long value ) 3003 { 3004 delegate.setAdditionalLongProperty( name, value ); 3005 } 3006 3007 public Long getAdditionalLongProperty( String name )3008 getAdditionalLongProperty( 3009 String name ) 3010 { 3011 return( delegate.getAdditionalLongProperty( name )); 3012 } 3013 3014 3015 public void setAdditionalListProperty( String name, List value )3016 setAdditionalListProperty( 3017 String name, 3018 List value ) 3019 { 3020 delegate.setAdditionalListProperty( name, value ); 3021 } 3022 3023 public List getAdditionalListProperty( String name )3024 getAdditionalListProperty( 3025 String name ) 3026 { 3027 return( delegate.getAdditionalListProperty( name )); 3028 } 3029 3030 public void setAdditionalMapProperty( String name, Map value )3031 setAdditionalMapProperty( 3032 String name, 3033 Map value ) 3034 { 3035 if ( torrentFluffKeyset.contains(name)){ 3036 3037 //System.out.println( "Set fluff for " + new String(getName()) + " to " + value ); 3038 3039 try{ 3040 getMonitor().enter(); 3041 3042 delegate.setAdditionalMapProperty( name, value ); 3043 3044 fluff_dirty = true; 3045 3046 }finally{ 3047 3048 getMonitor().exit(); 3049 } 3050 }else{ 3051 3052 delegate.setAdditionalMapProperty( name, value ); 3053 } 3054 } 3055 3056 public Map getAdditionalMapProperty( String name )3057 getAdditionalMapProperty( 3058 String name ) 3059 { 3060 if (torrentFluffKeyset.contains(name)){ 3061 3062 try{ 3063 getMonitor().enter(); 3064 3065 Map result = delegate.getAdditionalMapProperty( name ); 3066 3067 if ( result == fluffThombstone ){ 3068 3069 try{ 3070 restoreState( false, true ); 3071 3072 Map res = delegate.getAdditionalMapProperty( name ); 3073 3074 //System.out.println( "Restored fluff for " + new String(getName()) + " to " + res ); 3075 3076 return( res ); 3077 3078 }catch( Throwable e ){ 3079 3080 Debug.out( "Property '" + name + " lost due to torrent read error", e ); 3081 } 3082 } 3083 }finally{ 3084 3085 getMonitor().exit(); 3086 } 3087 } 3088 3089 return( delegate.getAdditionalMapProperty( name )); 3090 } 3091 getAdditionalProperty(String name)3092 public Object getAdditionalProperty(String name) { 3093 if (torrentFluffKeyset.contains(name)) 3094 { 3095 try 3096 { 3097 getMonitor().enter(); 3098 3099 Object result = delegate.getAdditionalProperty(name); 3100 if (result == fluffThombstone) 3101 { 3102 try 3103 { 3104 restoreState(false, true); 3105 Object res = delegate.getAdditionalProperty(name); 3106 //System.out.println( "Restored fluff for " + new String(getName()) + " to " + res ); 3107 3108 return (res); 3109 3110 } catch (Throwable e) 3111 { 3112 Debug.out("Property '" + name + " lost due to torrent read error", e); 3113 } 3114 } 3115 } finally 3116 { 3117 getMonitor().exit(); 3118 } 3119 } 3120 3121 return delegate.getAdditionalProperty(name); 3122 } 3123 3124 public void setAdditionalProperty( String name, Object value )3125 setAdditionalProperty( 3126 String name, 3127 Object value ) 3128 { 3129 if ( torrentFluffKeyset.contains(name)){ 3130 3131 //System.out.println( "Set fluff for " + new String(getName()) + " to " + value ); 3132 3133 try{ 3134 getMonitor().enter(); 3135 3136 delegate.setAdditionalProperty( name, value ); 3137 3138 fluff_dirty = true; 3139 3140 }finally{ 3141 3142 getMonitor().exit(); 3143 } 3144 }else{ 3145 3146 delegate.setAdditionalProperty( name, value ); 3147 } 3148 } 3149 3150 public void removeAdditionalProperty( String name )3151 removeAdditionalProperty( 3152 String name ) 3153 { 3154 if(delegate.getAdditionalProperty(name) == null) 3155 return; 3156 3157 if ( torrentFluffKeyset.contains(name)){ 3158 3159 //System.out.println( "Set fluff for " + new String(getName()) + " to " + value ); 3160 3161 try{ 3162 getMonitor().enter(); 3163 3164 delegate.removeAdditionalProperty( name ); 3165 3166 fluff_dirty = true; 3167 3168 }finally{ 3169 3170 getMonitor().exit(); 3171 } 3172 }else{ 3173 3174 delegate.removeAdditionalProperty( name ); 3175 } 3176 } 3177 3178 public void removeAdditionalProperties()3179 removeAdditionalProperties() 3180 { 3181 try{ 3182 getMonitor().enter(); 3183 3184 delegate.removeAdditionalProperties(); 3185 3186 fluff_dirty = true; 3187 3188 }finally{ 3189 3190 getMonitor().exit(); 3191 } 3192 } 3193 3194 public void serialiseToBEncodedFile( File target_file )3195 serialiseToBEncodedFile( 3196 File target_file ) 3197 3198 throws TOTorrentException 3199 { 3200 // make sure pieces are current 3201 3202 try{ 3203 getMonitor().enter(); 3204 3205 boolean[] restored = restoreState( true, true ); 3206 3207 delegate.serialiseToBEncodedFile( target_file ); 3208 3209 if ( target_file.equals( file )){ 3210 3211 fluff_dirty = false; 3212 } 3213 3214 if ( restored[0] ){ 3215 3216 discardPieces( SystemTime.getCurrentTime(), true ); 3217 } 3218 3219 if ( restored[1] ){ 3220 3221 for (Iterator it = torrentFluffKeyset.iterator(); it.hasNext();){ 3222 3223 delegate.setAdditionalMapProperty( (String)it.next(), fluffThombstone ); 3224 } 3225 } 3226 }finally{ 3227 3228 getMonitor().exit(); 3229 } 3230 } 3231 3232 3233 public Map serialiseToMap()3234 serialiseToMap() 3235 3236 throws TOTorrentException 3237 { 3238 // make sure pieces are current 3239 3240 try{ 3241 getMonitor().enter(); 3242 3243 boolean[] restored = restoreState( true, true ); 3244 3245 Map result = delegate.serialiseToMap(); 3246 3247 if ( restored[0] ){ 3248 3249 discardPieces( SystemTime.getCurrentTime(), true ); 3250 } 3251 3252 if ( restored[1]){ 3253 3254 for (Iterator it = torrentFluffKeyset.iterator(); it.hasNext();){ 3255 3256 delegate.setAdditionalMapProperty((String) it.next(), fluffThombstone); 3257 } 3258 } 3259 3260 return( result ); 3261 3262 }finally{ 3263 3264 getMonitor().exit(); 3265 } 3266 } 3267 3268 3269 public void serialiseToXMLFile( File target_file )3270 serialiseToXMLFile( 3271 File target_file ) 3272 3273 throws TOTorrentException 3274 { 3275 // make sure pieces are current 3276 3277 try{ 3278 getMonitor().enter(); 3279 3280 boolean[] restored = restoreState( true, true ); 3281 3282 delegate.serialiseToXMLFile( target_file ); 3283 3284 if ( restored[0] ){ 3285 3286 discardPieces( SystemTime.getCurrentTime(), true ); 3287 } 3288 3289 if ( restored[1]){ 3290 3291 for (Iterator it = torrentFluffKeyset.iterator(); it.hasNext();){ 3292 3293 delegate.setAdditionalMapProperty((String) it.next(), fluffThombstone); 3294 } 3295 } 3296 }finally{ 3297 3298 getMonitor().exit(); 3299 } 3300 } 3301 3302 public void addListener( TOTorrentListener l )3303 addListener( 3304 TOTorrentListener l ) 3305 { 3306 delegate.addListener( l ); 3307 } 3308 3309 public void removeListener( TOTorrentListener l )3310 removeListener( 3311 TOTorrentListener l ) 3312 { 3313 delegate.removeListener( l ); 3314 } 3315 3316 public AEMonitor getMonitor()3317 getMonitor() 3318 { 3319 return( delegate.getMonitor()); 3320 } 3321 3322 3323 public void print()3324 print() 3325 { 3326 delegate.print(); 3327 } 3328 getRelationText()3329 public String getRelationText() { 3330 if (delegate instanceof LogRelation) 3331 return ((LogRelation)delegate).getRelationText(); 3332 return delegate.toString(); 3333 } 3334 getQueryableInterfaces()3335 public Object[] getQueryableInterfaces() { 3336 if (delegate instanceof LogRelation) 3337 return ((LogRelation)delegate).getQueryableInterfaces(); 3338 return super.getQueryableInterfaces(); 3339 } 3340 getUTF8Name()3341 public String getUTF8Name() { 3342 return delegate.getUTF8Name(); 3343 } 3344 } 3345 3346 /** 3347 * Copy a file to the Torrent Save Directory, taking into account all the 3348 * user config options related to that. 3349 * <p> 3350 * Also makes the directory if it doesn't exist. 3351 * 3352 * @param f File to copy 3353 * @param persistent Whether the torrent is persistent 3354 * @return File after it's been copied (may be the same as f) 3355 * @throws IOException 3356 */ copyTorrentFileToSaveDir(File f, boolean persistent)3357 public static File copyTorrentFileToSaveDir(File f, boolean persistent) 3358 throws IOException { 3359 File torrentDir; 3360 boolean saveTorrents = persistent 3361 && COConfigurationManager.getBooleanParameter("Save Torrent Files"); 3362 if (saveTorrents) 3363 torrentDir = new File(COConfigurationManager 3364 .getDirectoryParameter("General_sDefaultTorrent_Directory")); 3365 else 3366 torrentDir = new File(f.getParent()); 3367 3368 //if the torrent is already in the completed files dir, use this 3369 //torrent instead of creating a new one in the default dir 3370 boolean moveWhenDone = COConfigurationManager.getBooleanParameter("Move Completed When Done"); 3371 String completedDir = COConfigurationManager.getStringParameter( 3372 "Completed Files Directory", ""); 3373 if (moveWhenDone && completedDir.length() > 0) { 3374 File cFile = new File(completedDir, f.getName()); 3375 if (cFile.exists()) { 3376 //set the torrentDir to the completedDir 3377 torrentDir = new File(completedDir); 3378 } 3379 } 3380 3381 FileUtil.mkdirs(torrentDir); 3382 3383 File fDest = new File(torrentDir, f.getName().replaceAll("%20", ".")); 3384 if (fDest.equals(f)) { 3385 return f; 3386 } 3387 3388 while (fDest.exists()) { 3389 fDest = new File(torrentDir, "_" + fDest.getName()); 3390 } 3391 3392 fDest.createNewFile(); 3393 3394 if (!FileUtil.copyFile(f, fDest)) { 3395 throw new IOException("File copy failed"); 3396 } 3397 3398 if ( shouldDeleteTorrentFileAfterAdd( f, persistent )){ 3399 3400 f.delete(); 3401 } 3402 3403 return fDest; 3404 } 3405 3406 public static boolean shouldDeleteTorrentFileAfterAdd( File f, boolean persistent )3407 shouldDeleteTorrentFileAfterAdd( 3408 File f, 3409 boolean persistent ) 3410 { 3411 if ( !persistent ){ 3412 3413 return( false ); 3414 } 3415 3416 boolean delTorrents = COConfigurationManager.getBooleanParameter("Delete Original Torrent Files"); 3417 3418 if ( !delTorrents ){ 3419 3420 return( false ); 3421 } 3422 3423 boolean saveTorrents = COConfigurationManager.getBooleanParameter("Save Torrent Files"); 3424 3425 if ( saveTorrents ){ 3426 3427 try{ 3428 File torrentDir = new File(COConfigurationManager.getDirectoryParameter("General_sDefaultTorrent_Directory")); 3429 3430 if ( torrentDir.isDirectory() && torrentDir.equals( f.getParentFile())){ 3431 3432 return( false ); 3433 } 3434 }catch( Throwable e ){ 3435 3436 return( false ); 3437 } 3438 } 3439 3440 File active_dir = FileUtil.getUserFile( "active" ); 3441 3442 return( !active_dir.equals( f.getParentFile())); 3443 } 3444 3445 /** 3446 * Get the DownloadManager related to a torrent's hashBytes 3447 * 3448 * @param hashBytes 3449 * @return 3450 */ getDownloadManager( HashWrapper hash )3451 public static DownloadManager getDownloadManager( HashWrapper hash ) { 3452 try { 3453 return AzureusCoreFactory.getSingleton().getGlobalManager() 3454 .getDownloadManager(hash); 3455 } catch (Exception e) { 3456 return null; 3457 } 3458 } 3459 3460 /** 3461 * Deletes the given dir and all dirs underneath if empty. 3462 * Don't delete default save path or completed files directory, however, 3463 * allow deletion of their empty subdirectories 3464 * Files defined to be ignored for the sake of torrent creation are automatically deleted 3465 * For example, by default this includes thumbs.db 3466 */ recursiveEmptyDirDelete(File f)3467 public static void recursiveEmptyDirDelete(File f) { 3468 TorrentUtils.recursiveEmptyDirDelete(f, true); 3469 } 3470 3471 /** 3472 * Same as #recursiveEmptyDirDelete(File), except allows disabling of logging 3473 * of any warnings 3474 * 3475 * @param f Dir to delete 3476 * @param log_warnings Whether to log warning 3477 */ recursiveEmptyDirDelete(File f, boolean log_warnings)3478 public static void recursiveEmptyDirDelete(File f, boolean log_warnings) { 3479 Set ignore_map = getIgnoreSet(); 3480 3481 FileUtil.recursiveEmptyDirDelete(f, ignore_map, log_warnings); 3482 } 3483 3484 /** 3485 * A nice string of a Torrent's hash 3486 * 3487 * @param torrent Torrent to fromat hash of 3488 * @return Hash string in a nice format 3489 */ nicePrintTorrentHash(TOTorrent torrent)3490 public static String nicePrintTorrentHash(TOTorrent torrent) { 3491 return nicePrintTorrentHash(torrent, false); 3492 } 3493 3494 /** 3495 * A nice string of a Torrent's hash 3496 * 3497 * @param torrent Torrent to fromat hash of 3498 * @param tight No spaces between groups of numbers 3499 * 3500 * @return Hash string in a nice format 3501 */ nicePrintTorrentHash(TOTorrent torrent, boolean tight)3502 public static String nicePrintTorrentHash(TOTorrent torrent, boolean tight) { 3503 byte[] hash; 3504 3505 if (torrent == null) { 3506 3507 hash = new byte[20]; 3508 } else { 3509 try { 3510 hash = torrent.getHash(); 3511 3512 } catch (TOTorrentException e) { 3513 3514 Debug.printStackTrace(e); 3515 3516 hash = new byte[20]; 3517 } 3518 } 3519 3520 return (ByteFormatter.nicePrint(hash, tight)); 3521 } 3522 3523 /** 3524 * Runs a file through a series of test to verify if it is a torrent. 3525 * 3526 * @param filename File to test 3527 * @return true - file is a valid torrent file 3528 * 3529 * @throws FileNotFoundException 3530 * @throws IOException 3531 */ isTorrentFile(String filename)3532 public static boolean isTorrentFile(String filename) throws FileNotFoundException, IOException { 3533 File check = new File(filename); 3534 if (!check.exists()) 3535 throw new FileNotFoundException("File "+filename+" not found."); 3536 if (!check.canRead()) 3537 throw new IOException("File "+filename+" cannot be read."); 3538 if (check.isDirectory()) 3539 throw new FileIsADirectoryException("File "+filename+" is a directory."); 3540 try { 3541 TOTorrentFactory.deserialiseFromBEncodedFile(check); 3542 return true; 3543 } catch (Throwable e) { 3544 return false; 3545 } 3546 } 3547 3548 public static void addCreatedTorrent( TOTorrent torrent )3549 addCreatedTorrent( 3550 TOTorrent torrent ) 3551 { 3552 synchronized( created_torrents ){ 3553 3554 try{ 3555 byte[] hash = torrent.getHash(); 3556 3557 HashWrapper hw = new HashWrapper( hash ); 3558 3559 boolean dirty = false; 3560 3561 long check = COConfigurationManager.getLongParameter("my.created.torrents.check", 0 ); 3562 3563 COConfigurationManager.setParameter("my.created.torrents.check", check+1 ); 3564 3565 if ( check % 200 == 0 ){ 3566 3567 try{ 3568 List<DownloadManager> dms = AzureusCoreFactory.getSingleton().getGlobalManager().getDownloadManagers(); 3569 3570 Set<HashWrapper> actual_hashes = new HashSet<HashWrapper>(); 3571 3572 for ( DownloadManager dm: dms ){ 3573 3574 TOTorrent t = dm.getTorrent(); 3575 3576 if ( t != null ){ 3577 3578 try{ 3579 actual_hashes.add( t.getHashWrapper()); 3580 3581 }catch( Throwable e ){ 3582 3583 } 3584 } 3585 } 3586 3587 Iterator<byte[]> it = created_torrents.iterator(); 3588 3589 int deleted = 0; 3590 3591 while( it.hasNext()){ 3592 3593 HashWrapper existing_hw = new HashWrapper( it.next()); 3594 3595 if ( !actual_hashes.contains( existing_hw ) && !existing_hw.equals( hw )){ 3596 3597 it.remove(); 3598 3599 created_torrents_set.remove( existing_hw ); 3600 3601 deleted++; 3602 3603 dirty = true; 3604 } 3605 } 3606 }catch( Throwable e ){ 3607 } 3608 } 3609 3610 //System.out.println( "addCreated:" + new String(torrent.getName()) + "/" + ByteFormatter.encodeString( hash )); 3611 3612 if ( created_torrents.size() == 0 ){ 3613 3614 COConfigurationManager.setParameter( "my.created.torrents", created_torrents ); 3615 } 3616 3617 if ( !created_torrents_set.contains( hw )){ 3618 3619 created_torrents.add( hash ); 3620 3621 created_torrents_set.add( hw ); 3622 3623 dirty = true; 3624 } 3625 3626 if ( dirty ){ 3627 3628 COConfigurationManager.setDirty(); 3629 } 3630 }catch( TOTorrentException e ){ 3631 3632 } 3633 } 3634 } 3635 3636 public static void removeCreatedTorrent( TOTorrent torrent )3637 removeCreatedTorrent( 3638 TOTorrent torrent ) 3639 { 3640 synchronized( created_torrents ){ 3641 3642 try{ 3643 HashWrapper hw = torrent.getHashWrapper(); 3644 3645 byte[] hash = hw.getBytes(); 3646 3647 //System.out.println( "removeCreated:" + new String(torrent.getName()) + "/" + ByteFormatter.encodeString( hash )); 3648 3649 Iterator<byte[]> it = created_torrents.iterator(); 3650 3651 while( it.hasNext()){ 3652 3653 byte[] h = it.next(); 3654 3655 if ( Arrays.equals( hash, h )){ 3656 3657 it.remove(); 3658 } 3659 } 3660 3661 COConfigurationManager.setDirty(); 3662 3663 created_torrents_set.remove( hw ); 3664 3665 }catch( TOTorrentException e ){ 3666 3667 } 3668 } 3669 } 3670 3671 public static boolean isCreatedTorrent( TOTorrent torrent )3672 isCreatedTorrent( 3673 TOTorrent torrent ) 3674 { 3675 synchronized( created_torrents ){ 3676 3677 try{ 3678 HashWrapper hw = torrent.getHashWrapper(); 3679 3680 boolean res = created_torrents_set.contains( hw ); 3681 3682 // if we don't have a persistent record of creation, check the non-persisted version 3683 3684 if ( !res ){ 3685 3686 res = torrent.isCreated(); 3687 } 3688 3689 // System.out.println( "isCreated:" + new String(torrent.getName()) + "/" + ByteFormatter.encodeString( hw.getBytes()) + " -> " + res ); 3690 3691 return( res ); 3692 3693 }catch( TOTorrentException e ){ 3694 3695 Debug.printStackTrace(e); 3696 3697 return( false ); 3698 3699 } 3700 } 3701 } 3702 3703 public static TOTorrent download( URL url )3704 download( 3705 URL url ) 3706 3707 throws IOException 3708 { 3709 return( download( url, 0 )); 3710 } 3711 3712 public static TOTorrent download( URL url, long timeout )3713 download( 3714 URL url, 3715 long timeout ) 3716 3717 throws IOException 3718 { 3719 try{ 3720 PluginProxy plugin_proxy = null; 3721 3722 try{ 3723 if ( AENetworkClassifier.categoriseAddress( url.getHost()) != AENetworkClassifier.AT_PUBLIC ){ 3724 3725 plugin_proxy = AEProxyFactory.getPluginProxy( "torrent download", url ); 3726 } 3727 3728 ResourceDownloader rd; 3729 3730 if ( plugin_proxy == null ){ 3731 3732 rd = new ResourceDownloaderFactoryImpl().create( url ); 3733 3734 }else{ 3735 3736 rd = new ResourceDownloaderFactoryImpl().create( plugin_proxy.getURL(), plugin_proxy.getProxy()); 3737 3738 rd.setProperty( "URL_HOST", url.getHost()); 3739 } 3740 3741 if ( timeout > 0 ){ 3742 3743 rd.setProperty( "URL_Connect_Timeout", timeout ); 3744 rd.setProperty( "URL_Read_Timeout", timeout ); 3745 } 3746 3747 byte[] bytes = FileUtil.readInputStreamAsByteArray( rd.download(), BDecoder.MAX_BYTE_ARRAY_SIZE ); 3748 3749 return( TOTorrentFactory.deserialiseFromBEncodedByteArray( bytes )); 3750 3751 }finally{ 3752 3753 if (plugin_proxy != null ){ 3754 3755 plugin_proxy.setOK( true ); 3756 } 3757 } 3758 }catch( IOException e ){ 3759 3760 throw((IOException)e); 3761 3762 }catch( Throwable e ){ 3763 3764 throw( new IOException( Debug.getNestedExceptionMessage( e ))); 3765 } 3766 } 3767 3768 private static void fireAttributeListener( TOTorrent torrent, String attribute, Object value )3769 fireAttributeListener( 3770 TOTorrent torrent, 3771 String attribute, 3772 Object value ) 3773 { 3774 Iterator it = torrent_attribute_listeners.iterator(); 3775 3776 while( it.hasNext()){ 3777 3778 try{ 3779 ((torrentAttributeListener)it.next()).attributeSet(torrent, attribute, value); 3780 3781 }catch( Throwable e ){ 3782 3783 Debug.printStackTrace(e); 3784 } 3785 } 3786 } 3787 3788 public static void addTorrentAttributeListener( torrentAttributeListener listener )3789 addTorrentAttributeListener( 3790 torrentAttributeListener listener ) 3791 { 3792 torrent_attribute_listeners.add( listener ); 3793 } 3794 3795 public static void removeTorrentAttributeListener( torrentAttributeListener listener )3796 removeTorrentAttributeListener( 3797 torrentAttributeListener listener ) 3798 { 3799 torrent_attribute_listeners.remove( listener ); 3800 } 3801 3802 public static void addTorrentURLChangeListener( TorrentAnnounceURLChangeListener listener )3803 addTorrentURLChangeListener( 3804 TorrentAnnounceURLChangeListener listener ) 3805 { 3806 torrent_url_changed_listeners.add( listener ); 3807 } 3808 3809 public static void removeTorrentURLChangeListener( TorrentAnnounceURLChangeListener listener )3810 removeTorrentURLChangeListener( 3811 TorrentAnnounceURLChangeListener listener ) 3812 { 3813 torrent_url_changed_listeners.remove( listener ); 3814 } 3815 3816 3817 3818 private static final Pattern txt_pattern = Pattern.compile( "(UDP|TCP):([0-9]+)"); 3819 3820 private static DNSTXTEntry getDNSTXTEntry( URL url )3821 getDNSTXTEntry( 3822 URL url ) 3823 { 3824 if ( isDecentralised( url )){ 3825 3826 return( null ); 3827 } 3828 3829 String host = url.getHost(); 3830 3831 String tracker_network = AENetworkClassifier.categoriseAddress( host ); 3832 3833 if ( tracker_network != AENetworkClassifier.AT_PUBLIC ){ 3834 3835 return( null ); 3836 } 3837 3838 return( getDNSTXTEntry( host, false, null )); 3839 } 3840 3841 private static void checkDNSTimeouts()3842 checkDNSTimeouts() 3843 { 3844 final List<String> hosts = new ArrayList<String>(); 3845 3846 long now = SystemTime.getMonotonousTime(); 3847 3848 synchronized( dns_mapping ){ 3849 3850 for ( Map.Entry<String,DNSTXTEntry> entry: dns_mapping.entrySet()){ 3851 3852 DNSTXTEntry txt_entry = entry.getValue(); 3853 3854 if ( now - txt_entry.getCreateTime() > DNS_HISTORY_TIMEOUT ){ 3855 3856 hosts.add( entry.getKey()); 3857 } 3858 } 3859 } 3860 3861 if ( hosts.size() > 0 ){ 3862 3863 new AEThread2( "DNS:updates" ) 3864 { 3865 public void 3866 run() 3867 { 3868 for ( String host: hosts ){ 3869 3870 getDNSTXTEntry( host, true, null ); 3871 } 3872 } 3873 }.start(); 3874 } 3875 } 3876 3877 private static DNSTXTEntry getDNSTXTEntry( final String host, boolean force_update, final List<String> already_got_records )3878 getDNSTXTEntry( 3879 final String host, 3880 boolean force_update, 3881 final List<String> already_got_records ) 3882 { 3883 if ( TRACE_DNS ){ 3884 System.out.println( "Getting DNS records for " + host + ", force=" + force_update + ", got=" + already_got_records ); 3885 } 3886 3887 DNSTXTEntry txt_entry; 3888 DNSTXTEntry old_txt_entry; 3889 3890 boolean is_new = false; 3891 3892 synchronized( dns_mapping ){ 3893 3894 old_txt_entry = txt_entry = dns_mapping.get( host ); 3895 3896 if ( txt_entry != null && SystemTime.getMonotonousTime() - txt_entry.getCreateTime() > DNS_HISTORY_TIMEOUT ){ 3897 3898 force_update = true; 3899 } 3900 3901 if ( force_update || txt_entry == null ){ 3902 3903 txt_entry = new DNSTXTEntry(); 3904 3905 dns_mapping.put( host, txt_entry ); 3906 3907 is_new = true; 3908 } 3909 } 3910 3911 if ( is_new ){ 3912 3913 String _config_key = ""; 3914 3915 try{ 3916 _config_key = "dns.txts.cache." + Base32.encode( host.getBytes( "UTF-8" )); 3917 3918 }catch( Throwable e ){ 3919 3920 Debug.out( e ); 3921 } 3922 3923 final String config_key = _config_key; 3924 3925 if ( TRACE_DNS ){ 3926 System.out.println( "Updating DNS records for " + host ); 3927 } 3928 3929 try{ 3930 List<String> txts; 3931 3932 if ( already_got_records != null ){ 3933 3934 txts = already_got_records; 3935 3936 }else{ 3937 3938 final AESemaphore lookup_sem = new AESemaphore( "DU:ls" ); 3939 3940 final Object[] result = { null, null }; 3941 3942 final DNSTXTEntry f_txt_entry = txt_entry; 3943 3944 dns_threads.run( 3945 new AERunnable() 3946 { 3947 public void 3948 runSupport() 3949 { 3950 try{ 3951 List<String> txts = dns_utils.getTXTRecords( host ); 3952 3953 if ( TRACE_DNS ){ 3954 System.out.println( "Actual lookup: " + host + " -> " + txts ); 3955 } 3956 3957 synchronized( result ){ 3958 3959 if ( result[0] == null ){ 3960 3961 result[1] = txts; 3962 3963 return; 3964 } 3965 } 3966 3967 // they gave up waiting 3968 3969 try{ 3970 List txts_cache = new ArrayList(); 3971 3972 for ( String str: txts ){ 3973 3974 txts_cache.add( str.getBytes( "UTF-8" )); 3975 } 3976 3977 List old_txts_cache = COConfigurationManager.getListParameter( config_key, null ); 3978 3979 boolean same = false; 3980 3981 if ( old_txts_cache != null ){ 3982 3983 same = old_txts_cache.size() == txts_cache.size(); 3984 3985 if ( same ){ 3986 3987 for ( int i=0;i<old_txts_cache.size();i++){ 3988 3989 if ( !Arrays.equals((byte[])old_txts_cache.get(i),(byte[])txts_cache.get(i))){ 3990 3991 same = false; 3992 3993 break; 3994 } 3995 } 3996 } 3997 } 3998 3999 if ( !same ){ 4000 4001 COConfigurationManager.setParameter( config_key, txts_cache ); 4002 4003 f_txt_entry.getSemaphore().reserve(); 4004 4005 if ( already_got_records == null ){ 4006 4007 getDNSTXTEntry( host, true, txts ); 4008 } 4009 } 4010 }catch( Throwable e ){ 4011 4012 Debug.out( e ); 4013 } 4014 4015 }finally{ 4016 4017 lookup_sem.release(); 4018 } 4019 } 4020 }); 4021 4022 List txts_cache = COConfigurationManager.getListParameter( config_key, null ); 4023 4024 // if we have a cache and this isn't a force update and start of day then well just go with the cache 4025 4026 if ( old_txt_entry != null || txts_cache == null || force_update ){ 4027 4028 lookup_sem.reserve( 2500 ); 4029 } 4030 4031 synchronized( result ){ 4032 4033 result[0] = ""; 4034 4035 txts = (List<String>)result[1]; 4036 } 4037 4038 try{ 4039 if ( txts == null ){ 4040 4041 txts = new ArrayList<String>(); 4042 4043 if ( txts_cache == null ){ 4044 4045 if ( TRACE_DNS ){ 4046 System.out.println( " No cache" ); 4047 } 4048 }else{ 4049 4050 for ( Object o: txts_cache ){ 4051 4052 txts.add( new String((byte[])o, "UTF-8" )); 4053 } 4054 4055 if ( TRACE_DNS ){ 4056 System.out.println( " Using cache: " + txts ); 4057 } 4058 } 4059 }else{ 4060 4061 txts_cache = new ArrayList(); 4062 4063 for ( String str: txts ){ 4064 4065 txts_cache.add( str.getBytes( "UTF-8" )); 4066 } 4067 4068 COConfigurationManager.setParameter( config_key, txts_cache ); 4069 } 4070 }catch( Throwable e ){ 4071 4072 Debug.out( e ); 4073 } 4074 } 4075 4076 boolean found_bt = false; 4077 4078 for ( String txt: txts ){ 4079 4080 if ( txt.startsWith( "BITTORRENT" )){ 4081 4082 found_bt = true; 4083 4084 Matcher matcher = txt_pattern.matcher( txt.substring( 10 )); 4085 4086 while( matcher.find()){ 4087 4088 boolean is_tcp = matcher.group(1).startsWith( "T" ); 4089 Integer port = Integer.parseInt( matcher.group(2)); 4090 4091 txt_entry.addPort( is_tcp, port ); 4092 } 4093 } 4094 } 4095 4096 txt_entry.setHasRecords( found_bt ); 4097 4098 if ( old_txt_entry == null ){ 4099 4100 dns_mapping_seq_count++; 4101 4102 if ( TRACE_DNS ){ 4103 System.out.println( " New DNS override for " + host + ": " + txt_entry.getString()); 4104 } 4105 }else{ 4106 4107 if ( !old_txt_entry.sameAs( txt_entry )){ 4108 4109 if ( TRACE_DNS ){ 4110 System.out.println( " Updated DNS override for " + host + ": " + txt_entry.getString()); 4111 } 4112 4113 dns_mapping_seq_count++; 4114 4115 dispatcher.dispatch( 4116 new AERunnable() 4117 { 4118 public void 4119 runSupport() 4120 { 4121 for ( TorrentAnnounceURLChangeListener l: torrent_url_changed_listeners ){ 4122 4123 try{ 4124 l.changed(); 4125 4126 }catch( Throwable e ){ 4127 4128 Debug.out(e ); 4129 } 4130 } 4131 } 4132 }); 4133 } 4134 } 4135 }finally{ 4136 4137 txt_entry.getSemaphore().releaseForever(); 4138 } 4139 } 4140 4141 txt_entry.getSemaphore().reserve(); 4142 4143 return( txt_entry ); 4144 } 4145 4146 private static URL applyDNSMods( URL url )4147 applyDNSMods( 4148 URL url ) 4149 { 4150 if ( DNS_HANDLING_ENABLE ){ 4151 4152 DNSTXTEntry txt_entry = getDNSTXTEntry( url ); 4153 4154 if ( txt_entry != null && txt_entry.hasRecords()){ 4155 4156 boolean url_is_tcp = url.getProtocol().toLowerCase().startsWith( "http" ); 4157 int url_port = url.getPort(); 4158 4159 if ( url_port == -1 ){ 4160 4161 url_port = url.getDefaultPort(); 4162 } 4163 4164 List<DNSTXTPortInfo> ports = txt_entry.getPorts(); 4165 4166 if ( ports.size() == 0 ){ 4167 4168 return( UrlUtils.setHost( url, url.getHost() + ".disabled_by_tracker" )); 4169 4170 }else{ 4171 4172 DNSTXTPortInfo first_port = ports.get(0); 4173 4174 if ( url_port != first_port.getPort()){ 4175 4176 url = UrlUtils.setPort( url, first_port.getPort()); 4177 } 4178 4179 if ( url_is_tcp == first_port.isTCP()){ 4180 4181 return( url ); 4182 4183 }else{ 4184 4185 return( UrlUtils.setProtocol( url, first_port.isTCP()?"http":"udp" )); 4186 } 4187 } 4188 }else{ 4189 4190 return( url ); 4191 } 4192 }else{ 4193 4194 return( url ); 4195 } 4196 } 4197 4198 private static List<URL> applyAllDNSMods( URL url )4199 applyAllDNSMods( 4200 URL url ) 4201 { 4202 if ( DNS_HANDLING_ENABLE ){ 4203 4204 DNSTXTEntry txt_entry = getDNSTXTEntry( url ); 4205 4206 if ( txt_entry != null && txt_entry.hasRecords()){ 4207 4208 boolean url_is_tcp = url.getProtocol().toLowerCase().startsWith( "http" ); 4209 int url_port = url.getPort(); 4210 4211 if ( url_port == -1 ){ 4212 4213 url_port = url.getDefaultPort(); 4214 } 4215 4216 List<DNSTXTPortInfo> ports = txt_entry.getPorts(); 4217 4218 if ( ports.size() == 0 ){ 4219 4220 return( null ); 4221 4222 }else{ 4223 4224 List<URL> result = new ArrayList<URL>(); 4225 4226 for ( DNSTXTPortInfo port: ports ){ 4227 4228 URL mod_url = url; 4229 4230 if ( url_port != port.getPort()){ 4231 4232 mod_url = UrlUtils.setPort( mod_url, port.getPort()); 4233 } 4234 4235 if ( url_is_tcp != port.isTCP()){ 4236 4237 mod_url = UrlUtils.setProtocol( mod_url, port.isTCP()?"http":"udp" ); 4238 } 4239 4240 result.add( mod_url ); 4241 } 4242 4243 return( result ); 4244 } 4245 }else{ 4246 4247 return( null ); 4248 } 4249 }else{ 4250 4251 return( null ); 4252 } 4253 } 4254 4255 private static TOTorrentAnnounceURLGroup applyDNSMods( URL announce_url, TOTorrentAnnounceURLGroup group )4256 applyDNSMods( 4257 URL announce_url, 4258 TOTorrentAnnounceURLGroup group ) 4259 { 4260 if ( DNS_HANDLING_ENABLE ){ 4261 4262 Map<String,Object[]> dns_maps = new HashMap<String, Object[]>(); 4263 4264 DNSTXTEntry announce_txt_entry = getDNSTXTEntry( announce_url ); 4265 4266 if ( announce_txt_entry != null && announce_txt_entry.hasRecords()){ 4267 4268 dns_maps.put( announce_url.getHost(), new Object[]{ announce_url, announce_txt_entry }); 4269 } 4270 4271 TOTorrentAnnounceURLSet[] sets = group.getAnnounceURLSets(); 4272 4273 List<TOTorrentAnnounceURLSet> mod_sets = new ArrayList<TOTorrentAnnounceURLSet>(); 4274 4275 for ( TOTorrentAnnounceURLSet set: sets ){ 4276 4277 URL[] urls = set.getAnnounceURLs(); 4278 4279 List<URL> mod_urls = new ArrayList<URL>(); 4280 4281 for ( URL url: urls ){ 4282 4283 DNSTXTEntry txt_entry = getDNSTXTEntry( url ); 4284 4285 if ( txt_entry == null || !txt_entry.hasRecords()){ 4286 4287 mod_urls.add( url ); 4288 4289 }else{ 4290 4291 // remove any affected entries here, we'll add them in if needed later 4292 4293 dns_maps.put( url.getHost(), new Object[]{ url, txt_entry }); 4294 } 4295 } 4296 4297 if ( mod_urls.size() != urls.length ){ 4298 4299 if ( mod_urls.size() > 0 ){ 4300 4301 mod_sets.add( group.createAnnounceURLSet( mod_urls.toArray( new URL[ mod_urls.size()]))); 4302 } 4303 }else{ 4304 4305 mod_sets.add( set ); 4306 } 4307 } 4308 4309 if ( dns_maps.size() > 0 ){ 4310 4311 for( Map.Entry<String,Object[]> entry: dns_maps.entrySet()){ 4312 4313 Object[] stuff = entry.getValue(); 4314 4315 URL url = (URL)stuff[0]; 4316 DNSTXTEntry dns = (DNSTXTEntry)stuff[1]; 4317 4318 List<DNSTXTPortInfo> ports = dns.getPorts(); 4319 4320 if ( ports.size() > 0 ){ 4321 4322 List<URL> urls = new ArrayList<URL>(); 4323 4324 for ( DNSTXTPortInfo port: ports ){ 4325 4326 int url_port = url.getPort(); 4327 boolean url_is_tcp = url.getProtocol().toLowerCase().startsWith( "http" ); 4328 4329 if ( url_port != port.getPort()){ 4330 4331 url = UrlUtils.setPort( url, port.getPort()); 4332 } 4333 4334 if ( url_is_tcp != port.isTCP()){ 4335 4336 url = UrlUtils.setProtocol( url, port.isTCP()?"http":"udp" ); 4337 } 4338 4339 urls.add( url ); 4340 } 4341 4342 if ( urls.size() > 0 ){ 4343 4344 mod_sets.add( group.createAnnounceURLSet( urls.toArray( new URL[ urls.size()]))); 4345 } 4346 } 4347 } 4348 4349 return( new URLGroup( group, mod_sets )); 4350 4351 }else{ 4352 4353 return( group ); 4354 } 4355 }else{ 4356 4357 return( group ); 4358 } 4359 } 4360 4361 public interface 4362 torrentAttributeListener 4363 { 4364 public void attributeSet( TOTorrent torrent, String attribute, Object value )4365 attributeSet( 4366 TOTorrent torrent, 4367 String attribute, 4368 Object value ); 4369 } 4370 4371 public interface 4372 TorrentAnnounceURLChangeListener 4373 { 4374 public void changed()4375 changed(); 4376 } 4377 4378 private static class 4379 URLGroup 4380 implements TOTorrentAnnounceURLGroup 4381 { 4382 private TOTorrentAnnounceURLGroup delegate; 4383 private TOTorrentAnnounceURLSet[] sets; 4384 4385 private boolean modified; 4386 4387 private URLGroup( TOTorrentAnnounceURLGroup _delegate, List<TOTorrentAnnounceURLSet> mod_sets )4388 URLGroup( 4389 TOTorrentAnnounceURLGroup _delegate, 4390 List<TOTorrentAnnounceURLSet> mod_sets ) 4391 { 4392 delegate = _delegate; 4393 4394 sets = mod_sets.toArray( new TOTorrentAnnounceURLSet[mod_sets.size()]); 4395 } 4396 4397 public TOTorrentAnnounceURLSet[] getAnnounceURLSets()4398 getAnnounceURLSets() 4399 { 4400 return( sets ); 4401 } 4402 4403 public void setAnnounceURLSets( TOTorrentAnnounceURLSet[] _sets )4404 setAnnounceURLSets( 4405 TOTorrentAnnounceURLSet[] _sets ) 4406 { 4407 modified = true; 4408 4409 sets = _sets; 4410 4411 delegate.setAnnounceURLSets(_sets ); 4412 } 4413 4414 public TOTorrentAnnounceURLSet createAnnounceURLSet( URL[] urls )4415 createAnnounceURLSet( 4416 URL[] urls ) 4417 { 4418 return( delegate.createAnnounceURLSet( urls )); 4419 } 4420 4421 protected boolean hasBeenModified()4422 hasBeenModified() 4423 { 4424 return( modified ); 4425 } 4426 } 4427 4428 private static class 4429 DNSTXTEntry 4430 { 4431 private long create_time = SystemTime.getMonotonousTime(); 4432 4433 private AESemaphore sem = new AESemaphore( "DNSTXTEntry" ); 4434 4435 private boolean has_records; 4436 private List<DNSTXTPortInfo> ports = new ArrayList<DNSTXTPortInfo>(); 4437 4438 private long getCreateTime()4439 getCreateTime() 4440 { 4441 return( create_time ); 4442 } 4443 4444 private AESemaphore getSemaphore()4445 getSemaphore() 4446 { 4447 return( sem ); 4448 } 4449 4450 private void setHasRecords( boolean has )4451 setHasRecords( 4452 boolean has ) 4453 { 4454 has_records = has; 4455 } 4456 4457 private boolean hasRecords()4458 hasRecords() 4459 { 4460 return( has_records ); 4461 } 4462 4463 private void addPort( boolean is_tcp, int port )4464 addPort( 4465 boolean is_tcp, 4466 int port ) 4467 { 4468 ports.add( new DNSTXTPortInfo( is_tcp, port )); 4469 } 4470 4471 private List<DNSTXTPortInfo> getPorts()4472 getPorts() 4473 { 4474 return( ports ); 4475 } 4476 4477 private boolean sameAs( DNSTXTEntry other )4478 sameAs( 4479 DNSTXTEntry other ) 4480 { 4481 if ( has_records != other.has_records ){ 4482 4483 return( false ); 4484 } 4485 4486 if ( ports.size() != other.ports.size()){ 4487 4488 return( false ); 4489 } 4490 4491 for ( int i=0;i<ports.size();i++ ){ 4492 4493 if ( !ports.get(i).sameAs( other.ports.get(i))){ 4494 4495 return( false ); 4496 } 4497 } 4498 4499 return( true ); 4500 } 4501 4502 private String getString()4503 getString() 4504 { 4505 if ( has_records ){ 4506 4507 if ( ports.size() == 0 ){ 4508 4509 return( "Deny all" ); 4510 4511 }else{ 4512 4513 String res = ""; 4514 4515 for ( DNSTXTPortInfo port: ports ){ 4516 4517 res += (res.length()==0?"":", ") + port.getString(); 4518 } 4519 4520 return( "Permit " + res ); 4521 } 4522 }else{ 4523 4524 return( "No records" ); 4525 } 4526 } 4527 } 4528 4529 private static class 4530 DNSTXTPortInfo 4531 { 4532 private boolean is_tcp; 4533 private int port; 4534 4535 private DNSTXTPortInfo( boolean _is_tcp, int _port )4536 DNSTXTPortInfo( 4537 boolean _is_tcp, 4538 int _port ) 4539 { 4540 is_tcp = _is_tcp; 4541 port = _port; 4542 } 4543 4544 private boolean sameAs( DNSTXTPortInfo other )4545 sameAs( 4546 DNSTXTPortInfo other ) 4547 { 4548 return( is_tcp == other.is_tcp && port == other.port ); 4549 } 4550 4551 private boolean isTCP()4552 isTCP() 4553 { 4554 return( is_tcp ); 4555 } 4556 4557 private int getPort()4558 getPort() 4559 { 4560 return( port ); 4561 } 4562 4563 private String getString()4564 getString() 4565 { 4566 return( (is_tcp?"TCP" :"UDP ") + port ); 4567 } 4568 } 4569 4570 public static void startTorrentDelete()4571 startTorrentDelete() 4572 { 4573 long val = torrent_delete_level.incrementAndGet(); 4574 4575 //System.out.println( "delete level++ -> " + val ); 4576 } 4577 4578 public static void endTorrentDelete()4579 endTorrentDelete() 4580 { 4581 long val = torrent_delete_level.decrementAndGet(); 4582 4583 //System.out.println( "delete level-- -> " + val ); 4584 } 4585 4586 public static void runTorrentDelete( Runnable target )4587 runTorrentDelete( 4588 Runnable target ) 4589 { 4590 try{ 4591 startTorrentDelete(); 4592 4593 target.run(); 4594 4595 }finally{ 4596 4597 endTorrentDelete(); 4598 } 4599 } 4600 4601 public static boolean isTorrentDeleting()4602 isTorrentDeleting() 4603 { 4604 return( torrent_delete_level.get() > 0 ); 4605 } 4606 4607 public static void setTorrentDeleted()4608 setTorrentDeleted() 4609 { 4610 synchronized( TorrentUtils.class ){ 4611 4612 torrent_delete_time = SystemTime.getMonotonousTime(); 4613 } 4614 } 4615 4616 public static long getMillisecondsSinceLastTorrentDelete()4617 getMillisecondsSinceLastTorrentDelete() 4618 { 4619 synchronized( TorrentUtils.class ){ 4620 4621 if ( torrent_delete_time == 0 ){ 4622 4623 return( Long.MAX_VALUE ); 4624 } 4625 return( SystemTime.getMonotonousTime() - torrent_delete_time ); 4626 } 4627 } 4628 4629 public static void main( String[] args )4630 main( 4631 String[] args ) 4632 { 4633 try{ 4634 //URL url = new URL( "http://tracker.openbittorrent.com/"); 4635 URL url = new URL( "http://inferno.demonoid.com:3413/announce"); 4636 4637 System.out.println( applyDNSMods( url )); 4638 4639 Thread.sleep( 1000*1000 ); 4640 4641 }catch( Throwable e ){ 4642 4643 e.printStackTrace(); 4644 } 4645 } 4646 } 4647