1 /* 2 * File : TOTorrentImpl.java 3 * Created : 5 Oct. 2003 4 * By : Parg 5 * 6 * Azureus - a Java Bittorrent client 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details ( see the LICENSE file ). 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 */ 22 23 package org.gudy.azureus2.core3.torrent.impl; 24 25 26 import java.io.*; 27 import java.net.*; 28 import java.util.*; 29 30 import org.gudy.azureus2.core3.logging.LogRelation; 31 import org.gudy.azureus2.core3.torrent.*; 32 import org.gudy.azureus2.core3.util.*; 33 34 import com.aelitis.azureus.core.AzureusCoreFactory; 35 36 public class 37 TOTorrentImpl 38 extends LogRelation 39 implements TOTorrent 40 { 41 protected static final String TK_ANNOUNCE = "announce"; 42 protected static final String TK_ANNOUNCE_LIST = "announce-list"; 43 protected static final String TK_COMMENT = "comment"; 44 protected static final String TK_CREATION_DATE = "creation date"; 45 protected static final String TK_CREATED_BY = "created by"; 46 47 protected static final String TK_INFO = "info"; 48 protected static final String TK_NAME = "name"; 49 protected static final String TK_LENGTH = "length"; 50 protected static final String TK_PATH = "path"; 51 protected static final String TK_FILES = "files"; 52 protected static final String TK_PIECE_LENGTH = "piece length"; 53 protected static final String TK_PIECES = "pieces"; 54 55 protected static final String TK_PRIVATE = "private"; 56 57 protected static final String TK_NAME_UTF8 = "name.utf-8"; 58 protected static final String TK_PATH_UTF8 = "path.utf-8"; 59 protected static final String TK_COMMENT_UTF8 = "comment.utf-8"; 60 61 protected static final String TK_WEBSEED_BT = "httpseeds"; 62 protected static final String TK_WEBSEED_GR = "url-list"; 63 64 protected static final String TK_HASH_OVERRIDE = "hash-override"; 65 66 protected static final List TK_ADDITIONAL_OK_ATTRS = 67 Arrays.asList(new String[]{ TK_COMMENT_UTF8, AZUREUS_PROPERTIES, TK_WEBSEED_BT, TK_WEBSEED_GR }); 68 69 private byte[] torrent_name; 70 private byte[] torrent_name_utf8; 71 72 private byte[] comment; 73 private URL announce_url; 74 private TOTorrentAnnounceURLGroupImpl announce_group = new TOTorrentAnnounceURLGroupImpl(this); 75 76 private long piece_length; 77 private byte[][] pieces; 78 private int number_of_pieces; 79 80 private byte[] torrent_hash_override; 81 82 private byte[] torrent_hash; 83 private HashWrapper torrent_hash_wrapper; 84 85 private boolean simple_torrent; 86 private TOTorrentFileImpl[] files; 87 88 private long creation_date; 89 private byte[] created_by; 90 91 private Map additional_properties = new LightHashMap(4); 92 private Map additional_info_properties = new LightHashMap(4); 93 94 private boolean created; 95 private boolean serialising; 96 97 private List<TOTorrentListener> listeners; 98 99 protected AEMonitor this_mon = new AEMonitor( "TOTorrent" ); 100 101 /** 102 * Constructor for deserialisation 103 */ 104 105 protected TOTorrentImpl()106 TOTorrentImpl() 107 { 108 } 109 110 /** 111 * Constructor for creation 112 */ 113 114 protected TOTorrentImpl( String _torrent_name, URL _announce_url, boolean _simple_torrent )115 TOTorrentImpl( 116 String _torrent_name, 117 URL _announce_url, 118 boolean _simple_torrent ) 119 120 throws TOTorrentException 121 { 122 created = true; 123 124 try{ 125 126 torrent_name = _torrent_name.getBytes( Constants.DEFAULT_ENCODING ); 127 128 torrent_name_utf8 = torrent_name; 129 130 setAnnounceURL( _announce_url ); 131 132 simple_torrent = _simple_torrent; 133 134 }catch( UnsupportedEncodingException e ){ 135 136 throw( new TOTorrentException( "Unsupported encoding for '" + _torrent_name + "'", 137 TOTorrentException.RT_UNSUPPORTED_ENCODING)); 138 } 139 } 140 141 public void serialiseToBEncodedFile( final File output_file )142 serialiseToBEncodedFile( 143 final File output_file ) 144 145 throws TOTorrentException 146 { 147 // we have to defer marking as created until some kind of persisting occurs as we don't know that the info-hash is "permanent" until# 148 // this point (external code can set info-hash internal properties between create + complete ) 149 150 if ( created ){ 151 152 TorrentUtils.addCreatedTorrent( this ); 153 } 154 155 byte[] res = serialiseToByteArray(); 156 157 BufferedOutputStream bos = null; 158 159 try{ 160 File parent = output_file.getParentFile(); 161 if (parent == null) { 162 throw new TOTorrentException( "Path '" + output_file + "' is invalid", TOTorrentException.RT_WRITE_FAILS); 163 } 164 165 // We would expect this to be normally true most of the time. 166 if (!parent.isDirectory()) { 167 168 // Try to create a directory. 169 boolean dir_created = FileUtil.mkdirs(parent); 170 171 // Something strange going on... 172 if (!dir_created) { 173 174 // Does it exist already? 175 if (parent.exists()) { 176 177 // And it really isn't a directory? 178 if (!parent.isDirectory()) { 179 180 // How strange. 181 throw new TOTorrentException( "Path '" + output_file + "' is invalid", TOTorrentException.RT_WRITE_FAILS); 182 183 } 184 185 // It is a directory which does exist. But we tested for that earlier. Perhaps it has been created in the 186 // meantime. 187 else { 188 /* do nothing */ 189 } 190 } 191 192 // It doesn't exist, and we couldn't create it. 193 else { 194 throw new TOTorrentException( "Failed to create directory '" + parent + "'", TOTorrentException.RT_WRITE_FAILS ); 195 } 196 } // end if (!dir_created) 197 198 } // end if (!parent.isDirectory) 199 200 201 File temp = new File( parent, output_file.getName() + ".saving"); 202 203 if ( temp.exists()){ 204 205 if ( !temp.delete()){ 206 207 throw( new TOTorrentException( "Insufficient permissions to delete '" + temp + "'", TOTorrentException.RT_WRITE_FAILS )); 208 } 209 }else{ 210 211 boolean ok = false; 212 213 try{ 214 ok = temp.createNewFile(); 215 216 }catch( Throwable e ){ 217 } 218 219 if ( !ok ){ 220 221 throw( new TOTorrentException( "Insufficient permissions to write '" + temp + "'", TOTorrentException.RT_WRITE_FAILS )); 222 223 } 224 } 225 226 FileOutputStream fos = new FileOutputStream( temp, false ); 227 228 bos = new BufferedOutputStream( fos, 8192 ); 229 230 bos.write( res ); 231 232 bos.flush(); 233 234 // thinking about removing this - just do so for CVS for the moment 235 236 if ( !Constants.isCVSVersion()){ 237 238 fos.getFD().sync(); 239 } 240 241 bos.close(); 242 243 bos = null; 244 245 //only use newly saved file if it got this far, i.e. it was written successfully 246 247 if ( temp.length() > 1L ) { 248 output_file.delete(); // Will fail silently if it doesn't exist. 249 temp.renameTo( output_file ); 250 } 251 252 }catch( TOTorrentException e ){ 253 254 throw( e ); 255 256 }catch( Throwable e){ 257 258 throw( new TOTorrentException( "Failed to serialise torrent: " + Debug.getNestedExceptionMessage(e), 259 TOTorrentException.RT_WRITE_FAILS )); 260 261 }finally{ 262 263 if ( bos != null ){ 264 265 try{ 266 bos.close(); 267 268 }catch( IOException e ){ 269 270 Debug.printStackTrace( e ); 271 } 272 } 273 } 274 } 275 276 protected byte[] serialiseToByteArray()277 serialiseToByteArray() 278 279 throws TOTorrentException 280 { 281 if ( created ){ 282 283 TorrentUtils.addCreatedTorrent( this ); 284 } 285 286 Map root = serialiseToMap(); 287 288 try{ 289 return( BEncoder.encode( root )); 290 291 }catch( IOException e ){ 292 293 throw( new TOTorrentException( 294 "Failed to serialise torrent: " + Debug.getNestedExceptionMessage(e), 295 TOTorrentException.RT_WRITE_FAILS )); 296 297 } 298 } 299 300 public Map serialiseToMap()301 serialiseToMap() 302 303 throws TOTorrentException 304 { 305 // protect against recursion when getting the hash 306 307 if ( created && !serialising ){ 308 309 try{ 310 serialising = true; // not thread safe but we can live without the hassle of using TLS or whatever 311 312 TorrentUtils.addCreatedTorrent( this ); 313 314 }finally{ 315 316 serialising = false; 317 } 318 } 319 320 Map root = new HashMap(); 321 322 // seen a NPE here, not sure of cause so handling null announce_url in case 323 324 writeStringToMetaData( root, TK_ANNOUNCE, (announce_url==null?TorrentUtils.getDecentralisedEmptyURL():announce_url).toString()); 325 326 TOTorrentAnnounceURLSet[] sets = announce_group.getAnnounceURLSets(); 327 328 if (sets.length > 0 ){ 329 330 List announce_list = new ArrayList(); 331 332 for (int i=0;i<sets.length;i++){ 333 334 TOTorrentAnnounceURLSet set = sets[i]; 335 336 URL[] urls = set.getAnnounceURLs(); 337 338 if ( urls.length == 0 ){ 339 340 continue; 341 } 342 343 List sub_list = new ArrayList(); 344 345 announce_list.add( sub_list ); 346 347 for (int j=0;j<urls.length;j++){ 348 349 sub_list.add( writeStringToMetaData( urls[j].toString())); 350 } 351 } 352 353 if ( announce_list.size() > 0 ){ 354 355 root.put( TK_ANNOUNCE_LIST, announce_list ); 356 } 357 } 358 359 if ( comment != null ){ 360 361 root.put( TK_COMMENT, comment ); 362 } 363 364 if ( creation_date != 0 ){ 365 366 root.put( TK_CREATION_DATE, new Long( creation_date )); 367 } 368 369 if ( created_by != null ){ 370 371 root.put( TK_CREATED_BY, created_by ); 372 } 373 374 Map info = new HashMap(); 375 376 root.put( TK_INFO, info ); 377 378 info.put( TK_PIECE_LENGTH, new Long( piece_length )); 379 380 if ( pieces == null ){ 381 382 throw( new TOTorrentException( "Pieces is null", TOTorrentException.RT_WRITE_FAILS )); 383 } 384 385 byte[] flat_pieces = new byte[pieces.length*20]; 386 387 for (int i=0;i<pieces.length;i++){ 388 389 System.arraycopy( pieces[i], 0, flat_pieces, i*20, 20 ); 390 } 391 392 info.put( TK_PIECES, flat_pieces ); 393 394 info.put( TK_NAME, torrent_name ); 395 396 if ( torrent_name_utf8 != null ){ 397 398 info.put( TK_NAME_UTF8, torrent_name_utf8 ); 399 } 400 401 if ( torrent_hash_override != null ){ 402 403 info.put( TK_HASH_OVERRIDE, torrent_hash_override ); 404 } 405 406 if ( simple_torrent ){ 407 408 TOTorrentFile file = files[0]; 409 410 info.put( TK_LENGTH, new Long( file.getLength())); 411 412 }else{ 413 414 List meta_files = new ArrayList(); 415 416 info.put( TK_FILES, meta_files ); 417 418 for (int i=0;i<files.length;i++){ 419 420 TOTorrentFileImpl file = files[i]; 421 422 Map file_map = file.serializeToMap(); 423 424 meta_files.add( file_map ); 425 426 } 427 } 428 429 Iterator info_it = additional_info_properties.keySet().iterator(); 430 431 while( info_it.hasNext()){ 432 433 String key = (String)info_it.next(); 434 435 info.put( key, additional_info_properties.get( key )); 436 } 437 438 Iterator it = additional_properties.keySet().iterator(); 439 440 while( it.hasNext()){ 441 442 String key = (String)it.next(); 443 444 Object value = additional_properties.get( key ); 445 446 if ( value != null ){ 447 448 root.put( key, value ); 449 } 450 } 451 452 return( root ); 453 } 454 455 public void serialiseToXMLFile( File file )456 serialiseToXMLFile( 457 File file ) 458 459 throws TOTorrentException 460 { 461 if ( created ){ 462 463 TorrentUtils.addCreatedTorrent( this ); 464 } 465 466 TOTorrentXMLSerialiser serialiser = new TOTorrentXMLSerialiser( this ); 467 468 serialiser.serialiseToFile( file ); 469 } 470 471 public byte[] getName()472 getName() 473 { 474 return( torrent_name ); 475 } 476 477 protected void setName( byte[] _name )478 setName( 479 byte[] _name ) 480 { 481 torrent_name = _name; 482 } 483 484 public String getUTF8Name()485 getUTF8Name() 486 { 487 try { 488 return torrent_name_utf8 == null ? null : new String(torrent_name_utf8, 489 "utf8"); 490 } catch (UnsupportedEncodingException e) { 491 return null; 492 } 493 } 494 495 protected void setNameUTF8( byte[] _name )496 setNameUTF8( 497 byte[] _name ) 498 { 499 torrent_name_utf8 = _name; 500 } 501 502 public boolean isSimpleTorrent()503 isSimpleTorrent() 504 { 505 return( simple_torrent ); 506 } 507 508 public byte[] getComment()509 getComment() 510 { 511 return( comment ); 512 } 513 514 protected void setComment( byte[] _comment )515 setComment( 516 byte[] _comment ) 517 518 { 519 comment = _comment; 520 } 521 522 public void setComment( String _comment )523 setComment( 524 String _comment ) 525 { 526 try{ 527 528 byte[] utf8_comment = _comment.getBytes( Constants.DEFAULT_ENCODING ); 529 530 setComment( utf8_comment ); 531 532 setAdditionalByteArrayProperty( TK_COMMENT_UTF8, utf8_comment ); 533 534 }catch( UnsupportedEncodingException e ){ 535 536 Debug.printStackTrace( e ); 537 538 comment = null; 539 } 540 } 541 542 public URL getAnnounceURL()543 getAnnounceURL() 544 { 545 return( announce_url ); 546 } 547 548 public boolean setAnnounceURL( URL url )549 setAnnounceURL( 550 URL url ) 551 { 552 URL newURL = anonymityTransform( url ); 553 String s0 = (newURL == null) ? "" : newURL.toString(); 554 String s1 = (announce_url == null) ? "" : announce_url.toString(); 555 if (s0.equals(s1)) 556 return false; 557 558 if ( newURL == null ){ 559 560 // anything's better than null... 561 562 newURL = TorrentUtils.getDecentralisedEmptyURL(); 563 } 564 565 announce_url = StringInterner.internURL(newURL); 566 567 fireChanged( TOTorrentListener.CT_ANNOUNCE_URLS ); 568 569 return true; 570 } 571 572 public boolean isDecentralised()573 isDecentralised() 574 { 575 return( TorrentUtils.isDecentralised( getAnnounceURL())); 576 } 577 578 public long getCreationDate()579 getCreationDate() 580 { 581 return( creation_date ); 582 } 583 584 public void setCreationDate( long _creation_date )585 setCreationDate( 586 long _creation_date ) 587 { 588 // supposed to be in seconds, not millis. Some torrents have millis so try and 589 // fix it 590 591 long now_secs = SystemTime.getCurrentTime()/1000; 592 593 if ( _creation_date > now_secs + 100*365*24*60*60L){ 594 595 _creation_date = _creation_date/1000; 596 } 597 598 creation_date = _creation_date; 599 } 600 601 public void setCreatedBy( byte[] _created_by )602 setCreatedBy( 603 byte[] _created_by ) 604 { 605 created_by = _created_by; 606 } 607 608 protected void setCreatedBy( String _created_by )609 setCreatedBy( 610 String _created_by ) 611 { 612 try{ 613 614 setCreatedBy( _created_by.getBytes( Constants.DEFAULT_ENCODING )); 615 616 }catch( UnsupportedEncodingException e ){ 617 618 Debug.printStackTrace( e ); 619 620 created_by = null; 621 } 622 } 623 624 public byte[] getCreatedBy()625 getCreatedBy() 626 { 627 return( created_by ); 628 } 629 630 public boolean isCreated()631 isCreated() 632 { 633 return( created ); 634 } 635 636 public byte[] getHash()637 getHash() 638 639 throws TOTorrentException 640 { 641 if ( torrent_hash == null ){ 642 643 Map root = serialiseToMap(); 644 645 Map info = (Map)root.get( TK_INFO ); 646 647 setHashFromInfo( info ); 648 } 649 650 return( torrent_hash ); 651 } 652 653 public HashWrapper getHashWrapper()654 getHashWrapper() 655 656 throws TOTorrentException 657 { 658 if ( torrent_hash_wrapper == null ){ 659 660 getHash(); 661 } 662 663 return( torrent_hash_wrapper ); 664 } 665 666 public boolean hasSameHashAs( TOTorrent other )667 hasSameHashAs( 668 TOTorrent other ) 669 { 670 try{ 671 byte[] other_hash = other.getHash(); 672 673 return( Arrays.equals( getHash(), other_hash )); 674 675 }catch( TOTorrentException e ){ 676 677 Debug.printStackTrace( e ); 678 679 return( false ); 680 } 681 } 682 683 protected void setHashFromInfo( Map info )684 setHashFromInfo( 685 Map info ) 686 687 throws TOTorrentException 688 { 689 try{ 690 if ( torrent_hash_override == null ){ 691 692 SHA1Hasher s = new SHA1Hasher(); 693 694 torrent_hash = s.calculateHash(BEncoder.encode(info)); 695 696 }else{ 697 698 torrent_hash = torrent_hash_override; 699 } 700 701 torrent_hash_wrapper = new HashWrapper( torrent_hash ); 702 703 }catch( Throwable e ){ 704 705 throw( new TOTorrentException( "Failed to calculate hash: " + Debug.getNestedExceptionMessage(e), 706 TOTorrentException.RT_HASH_FAILS )); 707 } 708 } 709 710 public void setHashOverride( byte[] hash )711 setHashOverride( 712 byte[] hash ) 713 714 throws TOTorrentException 715 { 716 if ( torrent_hash_override != null ){ 717 718 if ( Arrays.equals( hash, torrent_hash_override )){ 719 720 return; 721 722 }else{ 723 724 throw( new TOTorrentException( "Hash override can only be set once", 725 TOTorrentException.RT_HASH_FAILS )); 726 } 727 } 728 729 /* support this for fixing borked torrents 730 if ( !TorrentUtils.isDecentralised( announce_url )){ 731 732 throw( new TOTorrentException( 733 "Hash override can only be set on decentralised torrents", 734 TOTorrentException.RT_HASH_FAILS )); 735 } 736 */ 737 738 torrent_hash_override = hash; 739 740 torrent_hash = null; 741 742 getHash(); 743 } 744 745 protected byte[] getHashOverride()746 getHashOverride() 747 { 748 return( torrent_hash_override ); 749 } 750 751 public void setPrivate( boolean _private_torrent )752 setPrivate( 753 boolean _private_torrent ) 754 755 throws TOTorrentException 756 { 757 additional_info_properties.put( TK_PRIVATE, new Long(_private_torrent?1:0)); 758 759 // update torrent hash 760 761 torrent_hash = null; 762 763 getHash(); 764 } 765 766 public boolean getPrivate()767 getPrivate() 768 { 769 Object o = additional_info_properties.get( TK_PRIVATE ); 770 771 if ( o instanceof Long ){ 772 773 return(((Long)o).intValue() != 0 ); 774 } 775 776 return( false ); 777 } 778 779 public TOTorrentAnnounceURLGroup getAnnounceURLGroup()780 getAnnounceURLGroup() 781 { 782 return( announce_group ); 783 } 784 785 protected void addTorrentAnnounceURLSet( URL[] urls )786 addTorrentAnnounceURLSet( 787 URL[] urls ) 788 { 789 announce_group.addSet( new TOTorrentAnnounceURLSetImpl( this, urls )); 790 } 791 792 public long getSize()793 getSize() 794 { 795 long res = 0; 796 797 for (int i=0;i<files.length;i++){ 798 799 res += files[i].getLength(); 800 } 801 802 return( res ); 803 } 804 805 public long getPieceLength()806 getPieceLength() 807 { 808 return( piece_length ); 809 } 810 811 protected void setPieceLength( long _length )812 setPieceLength( 813 long _length ) 814 { 815 piece_length = _length; 816 } 817 818 public int getNumberOfPieces()819 getNumberOfPieces() 820 { 821 // to support buggy torrents with extraneous pieces (they seem to exist) we calculate 822 // the required number of pieces rather than the using the actual. Note that we 823 // can't adjust the pieces array itself as this results in incorrect torrent hashes 824 // being derived later after a save + restore 825 826 if ( number_of_pieces == 0 ){ 827 828 number_of_pieces = (int)((getSize() + (piece_length-1)) / piece_length ); 829 } 830 831 return( number_of_pieces ); 832 } 833 834 public byte[][] getPieces()835 getPieces() 836 { 837 return( pieces ); 838 } 839 840 public void setPieces( byte[][] _pieces )841 setPieces( 842 byte[][] _pieces ) 843 { 844 pieces = _pieces; 845 } 846 847 public int getFileCount()848 getFileCount() 849 { 850 return( files.length ); 851 } 852 853 public TOTorrentFile[] getFiles()854 getFiles() 855 { 856 return( files ); 857 } 858 859 protected void setFiles( TOTorrentFileImpl[] _files )860 setFiles( 861 TOTorrentFileImpl[] _files ) 862 { 863 files = _files; 864 } 865 866 protected boolean getSimpleTorrent()867 getSimpleTorrent() 868 { 869 return( simple_torrent ); 870 } 871 872 protected void setSimpleTorrent( boolean _simple_torrent )873 setSimpleTorrent( 874 boolean _simple_torrent ) 875 { 876 simple_torrent = _simple_torrent; 877 } 878 879 protected Map getAdditionalProperties()880 getAdditionalProperties() 881 { 882 return( additional_properties ); 883 } 884 885 public void setAdditionalStringProperty( String name, String value )886 setAdditionalStringProperty( 887 String name, 888 String value ) 889 { 890 try{ 891 892 setAdditionalByteArrayProperty( name, writeStringToMetaData( value )); 893 894 }catch( TOTorrentException e ){ 895 896 // hide encoding exceptions as default encoding must be available 897 898 Debug.printStackTrace( e ); 899 } 900 } 901 902 public String getAdditionalStringProperty( String name )903 getAdditionalStringProperty( 904 String name ) 905 { 906 try{ 907 908 return( readStringFromMetaData( getAdditionalByteArrayProperty(name))); 909 910 }catch( TOTorrentException e ){ 911 912 // hide encoding exceptions as default encoding must be available 913 914 Debug.printStackTrace( e ); 915 916 return( null ); 917 } 918 } 919 920 public void setAdditionalByteArrayProperty( String name, byte[] value )921 setAdditionalByteArrayProperty( 922 String name, 923 byte[] value ) 924 { 925 additional_properties.put( name, value ); 926 } 927 928 public void setAdditionalProperty( String name, Object value )929 setAdditionalProperty( 930 String name, 931 Object value ) 932 { 933 if ( value instanceof String ){ 934 935 setAdditionalStringProperty(name,(String)value); 936 937 }else{ 938 939 additional_properties.put( name, value ); 940 } 941 } 942 943 public byte[] getAdditionalByteArrayProperty( String name )944 getAdditionalByteArrayProperty( 945 String name ) 946 { 947 Object obj = additional_properties.get( name ); 948 949 if ( obj instanceof byte[] ){ 950 951 return((byte[])obj); 952 } 953 954 return( null ); 955 } 956 957 public void setAdditionalLongProperty( String name, Long value )958 setAdditionalLongProperty( 959 String name, 960 Long value ) 961 { 962 additional_properties.put( name, value ); 963 } 964 965 public Long getAdditionalLongProperty( String name )966 getAdditionalLongProperty( 967 String name ) 968 { 969 Object obj = additional_properties.get( name ); 970 971 if ( obj instanceof Long ){ 972 973 return((Long)obj); 974 } 975 976 return( null ); 977 } 978 979 public void setAdditionalListProperty( String name, List value )980 setAdditionalListProperty( 981 String name, 982 List value ) 983 { 984 additional_properties.put( name, value ); 985 } 986 987 public List getAdditionalListProperty( String name )988 getAdditionalListProperty( 989 String name ) 990 { 991 Object obj = additional_properties.get( name ); 992 993 if ( obj instanceof List ){ 994 995 return((List)obj); 996 } 997 998 return( null ); 999 } 1000 1001 public void setAdditionalMapProperty( String name, Map value )1002 setAdditionalMapProperty( 1003 String name, 1004 Map value ) 1005 { 1006 additional_properties.put( name, value ); 1007 } 1008 1009 public Map getAdditionalMapProperty( String name )1010 getAdditionalMapProperty( 1011 String name ) 1012 { 1013 Object obj = additional_properties.get( name ); 1014 1015 if ( obj instanceof Map ){ 1016 1017 return((Map)obj); 1018 } 1019 1020 return( null ); 1021 } 1022 1023 public Object getAdditionalProperty( String name )1024 getAdditionalProperty( 1025 String name ) 1026 { 1027 return(additional_properties.get( name )); 1028 } 1029 1030 public void removeAdditionalProperty( String name )1031 removeAdditionalProperty( 1032 String name ) 1033 { 1034 additional_properties.remove( name ); 1035 } 1036 1037 public void removeAdditionalProperties()1038 removeAdditionalProperties() 1039 { 1040 Map new_props = new HashMap(); 1041 1042 Iterator it = additional_properties.keySet().iterator(); 1043 1044 while( it.hasNext()){ 1045 1046 String key = (String)it.next(); 1047 1048 if ( TK_ADDITIONAL_OK_ATTRS.contains(key)){ 1049 1050 new_props.put( key, additional_properties.get( key )); 1051 } 1052 } 1053 1054 additional_properties = new_props; 1055 } 1056 1057 protected void addAdditionalProperty( String name, Object value )1058 addAdditionalProperty( 1059 String name, 1060 Object value ) 1061 { 1062 additional_properties.put( name, value ); 1063 } 1064 1065 protected void addAdditionalInfoProperty( String name, Object value )1066 addAdditionalInfoProperty( 1067 String name, 1068 Object value ) 1069 { 1070 additional_info_properties.put( name, value ); 1071 } 1072 1073 protected Map getAdditionalInfoProperties()1074 getAdditionalInfoProperties() 1075 { 1076 return( additional_info_properties ); 1077 } 1078 1079 protected String readStringFromMetaData( Map meta_data, String name )1080 readStringFromMetaData( 1081 Map meta_data, 1082 String name ) 1083 1084 throws TOTorrentException 1085 { 1086 Object obj = meta_data.get(name); 1087 1088 if ( obj instanceof byte[]){ 1089 1090 return(readStringFromMetaData((byte[])obj)); 1091 } 1092 1093 return( null ); 1094 } 1095 1096 protected String readStringFromMetaData( byte[] value )1097 readStringFromMetaData( 1098 byte[] value ) 1099 1100 throws TOTorrentException 1101 { 1102 try{ 1103 if ( value == null ){ 1104 1105 return( null ); 1106 } 1107 1108 return( new String(value, Constants.DEFAULT_ENCODING )); 1109 1110 }catch( UnsupportedEncodingException e ){ 1111 1112 throw( new TOTorrentException( "Unsupported encoding for '" + value + "'", 1113 TOTorrentException.RT_UNSUPPORTED_ENCODING)); 1114 } 1115 } 1116 1117 protected void writeStringToMetaData( Map meta_data, String name, String value )1118 writeStringToMetaData( 1119 Map meta_data, 1120 String name, 1121 String value ) 1122 1123 throws TOTorrentException 1124 { 1125 meta_data.put( name, writeStringToMetaData( value )); 1126 } 1127 1128 protected byte[] writeStringToMetaData( String value )1129 writeStringToMetaData( 1130 String value ) 1131 1132 throws TOTorrentException 1133 { 1134 try{ 1135 1136 return( value.getBytes( Constants.DEFAULT_ENCODING )); 1137 1138 }catch( UnsupportedEncodingException e ){ 1139 1140 throw( new TOTorrentException( "Unsupported encoding for '" + value + "'", 1141 TOTorrentException.RT_UNSUPPORTED_ENCODING)); 1142 } 1143 } 1144 1145 protected URL anonymityTransform( URL url )1146 anonymityTransform( 1147 URL url ) 1148 { 1149 /* 1150 * hmm, doing this is harder than it looks as we have issues hosting 1151 * (both starting tracker instances and also short-cut loopback for seeding 1152 * leave as is for the moment 1153 if ( HostNameToIPResolver.isNonDNSName( url.getHost())){ 1154 1155 // remove the port as it is uninteresting and could leak information about the 1156 // tracker 1157 1158 String url_string = url.toString(); 1159 1160 String port_string = ":" + (url.getPort()==-1?url.getDefaultPort():url.getPort()); 1161 1162 int port_pos = url_string.indexOf( ":" + url.getPort()); 1163 1164 if ( port_pos != -1 ){ 1165 1166 try{ 1167 1168 return( new URL( url_string.substring(0,port_pos) + url_string.substring(port_pos+port_string.length()))); 1169 1170 }catch( MalformedURLException e){ 1171 1172 Debug.printStackTrace(e); 1173 } 1174 } 1175 } 1176 */ 1177 1178 return( url ); 1179 } 1180 1181 public void print()1182 print() 1183 { 1184 try{ 1185 byte[] hash = getHash(); 1186 1187 System.out.println( "name = " + torrent_name ); 1188 System.out.println( "announce url = " + announce_url ); 1189 System.out.println( "announce group = " + announce_group.getAnnounceURLSets().length ); 1190 System.out.println( "creation date = " + creation_date ); 1191 System.out.println( "creation by = " + created_by ); 1192 System.out.println( "comment = " + comment ); 1193 System.out.println( "hash = " + ByteFormatter.nicePrint( hash )); 1194 System.out.println( "piece length = " + getPieceLength() ); 1195 System.out.println( "pieces = " + getNumberOfPieces() ); 1196 1197 Iterator info_it = additional_info_properties.keySet().iterator(); 1198 1199 while( info_it.hasNext()){ 1200 1201 String key = (String)info_it.next(); 1202 Object value = additional_info_properties.get( key ); 1203 1204 try{ 1205 1206 System.out.println( "info prop '" + key + "' = '" + 1207 ( value instanceof byte[]?new String((byte[])value, Constants.DEFAULT_ENCODING):value.toString()) + "'" ); 1208 }catch( UnsupportedEncodingException e){ 1209 1210 System.out.println( "info prop '" + key + "' = unsupported encoding!!!!"); 1211 } 1212 } 1213 1214 Iterator it = additional_properties.keySet().iterator(); 1215 1216 while( it.hasNext()){ 1217 1218 String key = (String)it.next(); 1219 Object value = additional_properties.get( key ); 1220 1221 try{ 1222 1223 System.out.println( "prop '" + key + "' = '" + 1224 ( value instanceof byte[]?new String((byte[])value, Constants.DEFAULT_ENCODING):value.toString()) + "'" ); 1225 }catch( UnsupportedEncodingException e){ 1226 1227 System.out.println( "prop '" + key + "' = unsupported encoding!!!!"); 1228 } 1229 } 1230 1231 if ( pieces == null ){ 1232 1233 System.out.println( "\tpieces = null" ); 1234 1235 }else{ 1236 for (int i=0;i<pieces.length;i++){ 1237 1238 System.out.println( "\t" + ByteFormatter.nicePrint(pieces[i])); 1239 } 1240 } 1241 1242 for (int i=0;i<files.length;i++){ 1243 1244 byte[][]path_comps = files[i].getPathComponents(); 1245 1246 String path_str = ""; 1247 1248 for (int j=0;j<path_comps.length;j++){ 1249 1250 try{ 1251 1252 path_str += (j==0?"":File.separator) + new String( path_comps[j], Constants.DEFAULT_ENCODING ); 1253 1254 }catch( UnsupportedEncodingException e ){ 1255 1256 System.out.println( "file - unsupported encoding!!!!"); 1257 } 1258 } 1259 1260 System.out.println( "\t" + path_str + " (" + files[i].getLength() + ")" ); 1261 } 1262 }catch( TOTorrentException e ){ 1263 1264 Debug.printStackTrace( e ); 1265 } 1266 } 1267 1268 protected void fireChanged( int type )1269 fireChanged( 1270 int type ) 1271 { 1272 List<TOTorrentListener> to_fire = null; 1273 1274 try{ 1275 this_mon.enter(); 1276 1277 if ( listeners != null ){ 1278 1279 to_fire = new ArrayList<TOTorrentListener>( listeners ); 1280 } 1281 }finally{ 1282 1283 this_mon.exit(); 1284 } 1285 1286 if ( to_fire != null ){ 1287 1288 for ( TOTorrentListener l: to_fire ){ 1289 1290 try{ 1291 l.torrentChanged( this, type ); 1292 1293 }catch( Throwable e ){ 1294 1295 Debug.out(e); 1296 } 1297 } 1298 } 1299 } 1300 1301 public void addListener( TOTorrentListener l )1302 addListener( 1303 TOTorrentListener l ) 1304 { 1305 try{ 1306 this_mon.enter(); 1307 1308 if ( listeners == null ){ 1309 1310 listeners = new ArrayList<TOTorrentListener>(); 1311 } 1312 1313 listeners.add( l ); 1314 1315 }finally{ 1316 1317 this_mon.exit(); 1318 } 1319 } 1320 1321 public void removeListener( TOTorrentListener l )1322 removeListener( 1323 TOTorrentListener l ) 1324 { 1325 try{ 1326 this_mon.enter(); 1327 1328 if ( listeners != null ){ 1329 1330 listeners.remove( l ); 1331 1332 if ( listeners.size() == 0 ){ 1333 1334 listeners = null; 1335 } 1336 } 1337 }finally{ 1338 1339 this_mon.exit(); 1340 } 1341 } 1342 1343 public AEMonitor getMonitor()1344 getMonitor() 1345 { 1346 return( this_mon ); 1347 } 1348 1349 /* (non-Javadoc) 1350 * @see org.gudy.azureus2.core3.logging.LogRelation#getLogRelationText() 1351 */ getRelationText()1352 public String getRelationText() { 1353 return "Torrent: '" + new String(torrent_name) + "'"; 1354 } 1355 1356 /* (non-Javadoc) 1357 * @see org.gudy.azureus2.core3.logging.LogRelation#queryForClass(java.lang.Class) 1358 */ getQueryableInterfaces()1359 public Object[] getQueryableInterfaces() { 1360 // yuck 1361 try { 1362 return new Object[] { AzureusCoreFactory.getSingleton() 1363 .getGlobalManager().getDownloadManager(this) }; 1364 } catch (Exception e) { 1365 } 1366 1367 return null; 1368 } 1369 }