FileDocCategorySizeDatePackage
TOTorrentImpl.javaAPI DocAzureus 3.0.3.424883Tue May 29 09:05:28 BST 2007org.gudy.azureus2.core3.torrent.impl

TOTorrentImpl

public class TOTorrentImpl extends org.gudy.azureus2.core3.logging.LogRelation implements TOTorrent

Fields Summary
protected static final String
TK_ANNOUNCE
protected static final String
TK_ANNOUNCE_LIST
protected static final String
TK_COMMENT
protected static final String
TK_CREATION_DATE
protected static final String
TK_CREATED_BY
protected static final String
TK_INFO
protected static final String
TK_NAME
protected static final String
TK_LENGTH
protected static final String
TK_PATH
protected static final String
TK_FILES
protected static final String
TK_PIECE_LENGTH
protected static final String
TK_PIECES
protected static final String
TK_PRIVATE
protected static final String
TK_NAME_UTF8
protected static final String
TK_PATH_UTF8
protected static final String
TK_COMMENT_UTF8
protected static final String
TK_WEBSEED_BT
protected static final String
TK_WEBSEED_GR
protected static final List
TK_ADDITIONAL_OK_ATTRS
private byte[]
torrent_name
private byte[]
torrent_name_utf8
private byte[]
comment
private URL
announce_url
private TOTorrentAnnounceURLGroupImpl
announce_group
private long
piece_length
private byte[]
pieces
private int
number_of_pieces
private byte[]
torrent_hash
private HashWrapper
torrent_hash_wrapper
private boolean
simple_torrent
private TOTorrentFileImpl[]
files
private long
creation_date
private byte[]
created_by
private Map
additional_properties
private Map
additional_info_properties
private boolean
created
private boolean
serialising
protected AEMonitor
this_mon
Constructors Summary
protected TOTorrentImpl()
Constructor for deserialisation


	    	 
	
	
	
	
	
protected TOTorrentImpl(String _torrent_name, URL _announce_url, boolean _simple_torrent)
Constructor for creation

		created	= true;
		
		try{
		
			torrent_name		= _torrent_name.getBytes( Constants.DEFAULT_ENCODING );
			
			torrent_name_utf8	= torrent_name;
			
			setAnnounceURL( _announce_url );
			
			simple_torrent		= _simple_torrent;
			
		}catch( UnsupportedEncodingException e ){
			
			throw( new TOTorrentException( 	"Unsupported encoding for '" + _torrent_name + "'",
											TOTorrentException.RT_UNSUPPORTED_ENCODING));
		}
	
Methods Summary
protected voidaddAdditionalInfoProperty(java.lang.String name, java.lang.Object value)

		additional_info_properties.put( name, value );
	
protected voidaddAdditionalProperty(java.lang.String name, java.lang.Object value)

		additional_properties.put( name, value );
	
protected voidaddTorrentAnnounceURLSet(java.net.URL[] urls)

		announce_group.addSet( new TOTorrentAnnounceURLSetImpl( this, urls ));
	
protected java.net.URLanonymityTransform(java.net.URL url)

		/*
		 * 	hmm, doing this is harder than it looks as we have issues hosting
		 *  (both starting tracker instances and also short-cut loopback for seeding
		 *  leave as is for the moment
		if ( HostNameToIPResolver.isNonDNSName( url.getHost())){
			
			// remove the port as it is uninteresting and could leak information about the
			// tracker
			
			String	url_string = url.toString();
			
			String	port_string = ":" + (url.getPort()==-1?url.getDefaultPort():url.getPort());
			
			int	port_pos = url_string.indexOf( ":" + url.getPort());
			
			if ( port_pos != -1 ){
				
				try{
					
					return( new URL( url_string.substring(0,port_pos) + url_string.substring(port_pos+port_string.length())));
					
				}catch( MalformedURLException e){
					
					Debug.printStackTrace(e);
				}
			}
		}
		*/
		
		return( url );
	
public byte[]getAdditionalByteArrayProperty(java.lang.String name)

		Object	obj = additional_properties.get( name );
		
		if ( obj instanceof byte[] ){
			
			return((byte[])obj);
		}
		
		return( null );
	
protected java.util.MapgetAdditionalInfoProperties()

		return( additional_info_properties );	
	
public java.util.ListgetAdditionalListProperty(java.lang.String name)

		Object	obj = additional_properties.get( name );
		
		if ( obj instanceof List ){
			
			return((List)obj);
		}
		
		return( null );	
	
public java.lang.LonggetAdditionalLongProperty(java.lang.String name)

		Object	obj = additional_properties.get( name );
		
		if ( obj instanceof Long ){
			
			return((Long)obj);
		}
		
		return( null );
	
public java.util.MapgetAdditionalMapProperty(java.lang.String name)

		Object	obj = additional_properties.get( name );
		
		if ( obj instanceof Map ){
			
			return((Map)obj);
		}
		
		return( null );	
	
protected java.util.MapgetAdditionalProperties()

		return( additional_properties );
	
public java.lang.ObjectgetAdditionalProperty(java.lang.String name)

		return(additional_properties.get( name ));
	
public java.lang.StringgetAdditionalStringProperty(java.lang.String name)

	
		try{			
		
			return( readStringFromMetaData( getAdditionalByteArrayProperty(name)));
			
		}catch( TOTorrentException e ){
			
				// hide encoding exceptions as default encoding must be available
			
			Debug.printStackTrace( e );
			
			return( null );
		}
	
public java.net.URLgetAnnounceURL()

		return( announce_url );
	
public TOTorrentAnnounceURLGroupgetAnnounceURLGroup()

		return( announce_group );
	
public byte[]getComment()

		return( comment );
	
public byte[]getCreatedBy()

		return( created_by );
	
public longgetCreationDate()

		return( creation_date );
	
public TOTorrentFile[]getFiles()

		return( files );
	
public byte[]getHash()

		if ( torrent_hash == null ){
			
			Map	root = serialiseToMap();
				
			Map info = (Map)root.get( TK_INFO );
				
			setHashFromInfo( info );		
		}
		
		return( torrent_hash );
	
public HashWrappergetHashWrapper()

		if ( torrent_hash_wrapper == null ){
			getHash();
		}
		
		return( torrent_hash_wrapper );
	
public AEMonitorgetMonitor()

		return( this_mon );
	
public byte[]getName()

		return( torrent_name );
	
public intgetNumberOfPieces()

			// to support buggy torrents with extraneous pieces (they seem to exist) we calculate
			// the required number of pieces rather than the using the actual. Note that we 
			// can't adjust the pieces array itself as this results in incorrect torrent hashes
			// being derived later after a save + restore
		
		if ( number_of_pieces == 0 ){
		
			number_of_pieces = (int)((getSize() + (piece_length-1)) / piece_length );
		}
		
		return( number_of_pieces );
	
public longgetPieceLength()

		return( piece_length );
	
public byte[][]getPieces()

		return( pieces );
	
public booleangetPrivate()

		Object o = additional_info_properties.get( TK_PRIVATE );
		
		if ( o instanceof Long ){
			
			return(((Long)o).intValue() != 0 );
		}
		
		return( false );
	
public java.lang.Object[]getQueryableInterfaces()

		// yuck
		try {
			return new Object[] { AzureusCoreFactory.getSingleton()
					.getGlobalManager().getDownloadManager(this) };
		} catch (Exception e) {
		}

		return null;
	
public java.lang.StringgetRelationText()

		return "Torrent: '" + new String(torrent_name) + "'";  
	
protected booleangetSimpleTorrent()

		return( simple_torrent );
	
public longgetSize()

		long	res = 0;
		
		for (int i=0;i<files.length;i++){
			
			res += files[i].getLength();
		}
		
		return( res );
	
public booleanhasSameHashAs(TOTorrent other)

		try{
			byte[]	other_hash = other.getHash();
				
			return( Arrays.equals( getHash(), other_hash ));
				
		}catch( TOTorrentException e ){
			
			Debug.printStackTrace( e );
			
			return( false );
		}
	
public booleanisCreated()

		return( created );
	
public booleanisSimpleTorrent()

		return( simple_torrent );
	
public voidprint()

		try{
			byte[]	hash = getHash();
			
			System.out.println( "name = " + torrent_name );
			System.out.println( "announce url = " + announce_url );
			System.out.println( "announce group = " + announce_group.getAnnounceURLSets().length );
			System.out.println( "creation date = " + creation_date );
			System.out.println( "creation by = " + created_by );
			System.out.println( "comment = " + comment );
			System.out.println( "hash = " + ByteFormatter.nicePrint( hash ));
			System.out.println( "piece length = " + getPieceLength() );
			System.out.println( "pieces = " + getNumberOfPieces() );
			
			Iterator info_it = additional_info_properties.keySet().iterator();
			
			while( info_it.hasNext()){
			
				String	key = (String)info_it.next();
				Object	value = additional_info_properties.get( key );
				
				try{
				
					System.out.println( "info prop '" + key + "' = '" + 
										( value instanceof byte[]?new String((byte[])value, Constants.DEFAULT_ENCODING):value.toString()) + "'" );
				}catch( UnsupportedEncodingException e){
				
					System.out.println( "info prop '" + key + "' = unsupported encoding!!!!");	
				}
			}	
					
			Iterator it = additional_properties.keySet().iterator();
			
			while( it.hasNext()){
			
				String	key = (String)it.next();
				Object	value = additional_properties.get( key );
				
				try{
				
					System.out.println( "prop '" + key + "' = '" + 
										( value instanceof byte[]?new String((byte[])value, Constants.DEFAULT_ENCODING):value.toString()) + "'" );
				}catch( UnsupportedEncodingException e){
				
					System.out.println( "prop '" + key + "' = unsupported encoding!!!!");	
				}
			}
			
			if ( pieces == null ){
			
				System.out.println( "\tpieces = null" );
				
			}else{
				for (int i=0;i<pieces.length;i++){
				
					System.out.println( "\t" + ByteFormatter.nicePrint(pieces[i]));
				}
			}
											 
			for (int i=0;i<files.length;i++){
				
				byte[][]path_comps = files[i].getPathComponents();
				
				String	path_str = "";
				
				for (int j=0;j<path_comps.length;j++){
					
					try{
					
						path_str += (j==0?"":File.separator) + new String( path_comps[j], Constants.DEFAULT_ENCODING );

					}catch( UnsupportedEncodingException e ){
	
						System.out.println( "file - unsupported encoding!!!!");	
					}
				}
				
				System.out.println( "\t" + path_str + " (" + files[i].getLength() + ")" );
			}
		}catch( TOTorrentException e ){
			
			Debug.printStackTrace( e );
		}
	
protected java.lang.StringreadStringFromMetaData(java.util.Map meta_data, java.lang.String name)

		Object	obj = meta_data.get(name);
		
		if ( obj instanceof byte[]){
			
			return(readStringFromMetaData((byte[])obj));
		}
		
		return( null );
	
protected java.lang.StringreadStringFromMetaData(byte[] value)

		try{
			if ( value == null ){
				
				return( null );
			}
			
			return(	new String(value, Constants.DEFAULT_ENCODING ));
			
		}catch( UnsupportedEncodingException e ){
			
			throw( new TOTorrentException( 	"Unsupported encoding for '" + value + "'",
											TOTorrentException.RT_UNSUPPORTED_ENCODING));
		}
	
public voidremoveAdditionalProperties()

		Map	new_props = new HashMap();
		
		Iterator it = additional_properties.keySet().iterator();
		
		while( it.hasNext()){
			
			String	key = (String)it.next();
			
			if ( TK_ADDITIONAL_OK_ATTRS.contains(key)){
			
				new_props.put( key, additional_properties.get( key ));
			}
		}
		
		additional_properties = new_props;
	
public voidremoveAdditionalProperty(java.lang.String name)

		additional_properties.remove( name );
	
public voidserialiseToBEncodedFile(java.io.File output_file)

	
			// 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#
			// this point (external code can set info-hash internal properties between create + complete )
		
		if ( created ){
			
			TorrentUtils.addCreatedTorrent( this );
		}
		
		byte[]	res = serialiseToByteArray();
						
        BufferedOutputStream bos = null;
						
		try{
			File parent = output_file.getParentFile();
			if (parent == null) {
				throw new TOTorrentException( "Path '" + output_file + "' is invalid", TOTorrentException.RT_WRITE_FAILS);
			}
			
			// We would expect this to be normally true most of the time.
			if (!parent.isDirectory()) {
				
				// Try to create a directory.
				boolean dir_created = FileUtil.mkdirs(parent);
				
				// Something strange going on...
				if (!dir_created) {
					
					// Does it exist already?
					if (parent.exists()) {
						
						// And it really isn't a directory?
						if (!parent.isDirectory()) {
							
							// How strange.
							throw new TOTorrentException( "Path '" + output_file + "' is invalid", TOTorrentException.RT_WRITE_FAILS);
							
						}
						
						// It is a directory which does exist. But we tested for that earlier. Perhaps it has been created in the
						// meantime.
						else {
							/* do nothing */
						}
					}
					
					// It doesn't exist, and we couldn't create it.
					else {
						throw new TOTorrentException( "Failed to create directory '" + parent + "'", TOTorrentException.RT_WRITE_FAILS );
					}
				} // end if (!dir_created)
				
			} // end if (!parent.isDirectory)
			
			
			File temp = new File( parent, output_file.getName() + ".saving");
            
			if ( temp.exists()){
				
				if ( !temp.delete()){
					
					throw( new TOTorrentException( "Insufficient permissions to delete '" + temp + "'", TOTorrentException.RT_WRITE_FAILS ));
				}
			}else{
				
				boolean	ok = false;
				
				try{
					ok = temp.createNewFile();
					
				}catch( Throwable e ){
				}
				
				if ( !ok ){
					
					throw( new TOTorrentException( "Insufficient permissions to write '" + temp + "'", TOTorrentException.RT_WRITE_FAILS ));

				}
			}
			
            FileOutputStream fos = new FileOutputStream( temp, false );
			
            bos = new BufferedOutputStream( fos, 8192 );
			
            bos.write( res );
			
            bos.flush();
			
            fos.getFD().sync();
            
            bos.close();
            
            bos = null;
              
              //only use newly saved file if it got this far, i.e. it was written successfully
            
            if ( temp.length() > 1L ) {
            	output_file.delete(); // Will fail silently if it doesn't exist.
                temp.renameTo( output_file );
            }
             	
		}catch( TOTorrentException e ){
			
			throw( e );
			
		}catch( Throwable e){
							
			throw( new TOTorrentException( 	"Failed to serialise torrent: " + Debug.getNestedExceptionMessage(e),
											TOTorrentException.RT_WRITE_FAILS ));
							
		}finally{
							
			if ( bos != null ){
				
				try{
					bos.close();
					
				}catch( IOException e ){
				
					Debug.printStackTrace( e );
				}
			}
		}
	
protected byte[]serialiseToByteArray()

		if ( created ){
			
			TorrentUtils.addCreatedTorrent( this );
		}
		
		Map	root = serialiseToMap();
			
		try{
			return( BEncoder.encode( root ));
			
		}catch( IOException e ){

			throw( 	new TOTorrentException( 	
							"Failed to serialise torrent: " + Debug.getNestedExceptionMessage(e),
							TOTorrentException.RT_WRITE_FAILS ));
			
		}
	
public java.util.MapserialiseToMap()

		
			// protect against recursion when getting the hash 
		
		if ( created && !serialising ){
			
			try{
				serialising	= true;	// not thread safe but we can live without the hassle of using TLS or whatever
					
				TorrentUtils.addCreatedTorrent( this );
				
			}finally{
				
				serialising = false;
			}
		}
		
		Map	root = new HashMap();
		
		writeStringToMetaData( root, TK_ANNOUNCE, announce_url.toString());
		
		TOTorrentAnnounceURLSet[] sets = announce_group.getAnnounceURLSets();
		
		if (sets.length > 0 ){
			
			List	announce_list = new ArrayList();
			
			for (int i=0;i<sets.length;i++){
				
				TOTorrentAnnounceURLSet	set = sets[i];
				
				URL[]	urls = set.getAnnounceURLs();
				
				if ( urls.length == 0 ){
					
					continue;
				}
				
				List sub_list = new ArrayList();
				
				announce_list.add( sub_list );
				
				for (int j=0;j<urls.length;j++){
					
					sub_list.add( writeStringToMetaData( urls[j].toString())); 
				}
			}
			
			if ( announce_list.size() > 0 ){
				
				root.put( TK_ANNOUNCE_LIST, announce_list );
			}
		}
		
		if ( comment != null ){
			
			root.put( TK_COMMENT, comment );			
		}
		
		if ( creation_date != 0 ){
			
			root.put( TK_CREATION_DATE, new Long( creation_date ));
		}
		
		if ( created_by != null ){
			
			root.put( TK_CREATED_BY, created_by );						
		}
		
		Map info = new HashMap();
		
		root.put( TK_INFO, info );
		
		info.put( TK_PIECE_LENGTH, new Long( piece_length ));
		
		if ( pieces == null ){
			
			throw( new TOTorrentException( "Pieces is null", TOTorrentException.RT_WRITE_FAILS ));
		}
		
		byte[]	flat_pieces = new byte[pieces.length*20];
		
		for (int i=0;i<pieces.length;i++){
			
			System.arraycopy( pieces[i], 0, flat_pieces, i*20, 20 );
		}
		
		info.put( TK_PIECES, flat_pieces );
		
		info.put( TK_NAME, torrent_name );
		
		if ( torrent_name_utf8 != null ){
			
			info.put( TK_NAME_UTF8, torrent_name_utf8 );
		}
		
		if ( simple_torrent ){
		
			TOTorrentFile	file = files[0];
			
			info.put( TK_LENGTH, new Long( file.getLength()));
			
		}else{
	
			List	meta_files = new ArrayList();
		
			info.put( TK_FILES, meta_files );
		
			for (int i=0;i<files.length;i++){
				
				TOTorrentFileImpl	file	= files[i];
				
				Map	file_map = new HashMap();
		
				meta_files.add( file_map );
				
				file_map.put( TK_LENGTH, new Long( file.getLength()));
				
				List path = new ArrayList();
				
				file_map.put( TK_PATH, path );
				
				byte[][]	path_comps = file.getPathComponents();
				
				for (int j=0;j<path_comps.length;j++){
					
					path.add( path_comps[j]);
				}
				
				if ( file.isUTF8()){
					
					List utf8_path = new ArrayList();
					
					file_map.put( TK_PATH_UTF8, utf8_path );
										
					for (int j=0;j<path_comps.length;j++){
						
						utf8_path.add( path_comps[j]);
					}
				}
				
				Map file_additional_properties = file.getAdditionalProperties();
				
				Iterator prop_it = file_additional_properties.keySet().iterator();
				
				while( prop_it.hasNext()){
					
					String	key = (String)prop_it.next();
					
					file_map.put( key, file_additional_properties.get( key ));
				}
			}
		}
		
		Iterator info_it = additional_info_properties.keySet().iterator();
		
		while( info_it.hasNext()){
		
			String	key = (String)info_it.next();
			
			info.put( key, additional_info_properties.get( key ));	
		}
		
		Iterator it = additional_properties.keySet().iterator();
		
		while( it.hasNext()){
			
			String	key = (String)it.next();
			
			Object	value = additional_properties.get( key );
			
			if ( value != null ){
				
				root.put( key, value );
			}
		}
		
		return( root );
	
public voidserialiseToXMLFile(java.io.File file)

		if ( created ){
			
			TorrentUtils.addCreatedTorrent( this );
		}
		
		TOTorrentXMLSerialiser	serialiser = new TOTorrentXMLSerialiser( this );
		
		serialiser.serialiseToFile( file );
	
public voidsetAdditionalByteArrayProperty(java.lang.String name, byte[] value)

		additional_properties.put( name, value );
	
public voidsetAdditionalListProperty(java.lang.String name, java.util.List value)

		additional_properties.put( name, value );
	
public voidsetAdditionalLongProperty(java.lang.String name, java.lang.Long value)

		additional_properties.put( name, value );
	
public voidsetAdditionalMapProperty(java.lang.String name, java.util.Map value)

		additional_properties.put( name, value );
	
public voidsetAdditionalProperty(java.lang.String name, java.lang.Object value)

		if ( name instanceof String ){
			
			setAdditionalStringProperty(name,(String)value);
			
		}else{
		
			additional_properties.put( name, value );
		}
	
public voidsetAdditionalStringProperty(java.lang.String name, java.lang.String value)

		try{
		
			setAdditionalByteArrayProperty( name, writeStringToMetaData( value ));
			
		}catch( TOTorrentException e ){
			
				// hide encoding exceptions as default encoding must be available
			
			Debug.printStackTrace( e );
		}
	
public booleansetAnnounceURL(java.net.URL url)

		URL newURL = anonymityTransform( url );
		String s0 = (newURL == null) ? "" : newURL.toString();
		String s1 = (announce_url == null) ? "" : announce_url.toString();
		if (s0.equals(s1))
			return false;
		
		announce_url	= newURL;
		return true;
	
protected voidsetComment(byte[] _comment)

		comment = _comment;
	
public voidsetComment(java.lang.String _comment)

		try{
		
			byte[]	utf8_comment = _comment.getBytes( Constants.DEFAULT_ENCODING );
			
			setComment( utf8_comment );
			
			setAdditionalByteArrayProperty( TK_COMMENT_UTF8, utf8_comment );
			
		}catch( UnsupportedEncodingException e ){
			
			Debug.printStackTrace( e );
			
			comment = null;
		}
	
protected voidsetCreatedBy(byte[] _created_by)

		created_by	= _created_by;
	
protected voidsetCreatedBy(java.lang.String _created_by)

		try{
		
			setCreatedBy( _created_by.getBytes( Constants.DEFAULT_ENCODING ));
			
		}catch( UnsupportedEncodingException e ){
			
			Debug.printStackTrace( e );
			
			created_by = null;
		}	
	
public voidsetCreationDate(long _creation_date)

		creation_date 	= _creation_date;
	
protected voidsetFiles(TOTorrentFileImpl[] _files)

		files	= _files;
	
protected voidsetHashFromInfo(java.util.Map info)

	
		try{
			SHA1Hasher s = new SHA1Hasher();
				
			torrent_hash = s.calculateHash(BEncoder.encode(info));
	
			torrent_hash_wrapper = new HashWrapper( torrent_hash );
			
		}catch( Throwable e ){
				
			throw( new TOTorrentException( 	"Failed to calculate hash: " + Debug.getNestedExceptionMessage(e),
											TOTorrentException.RT_HASH_FAILS ));
		}
	
protected voidsetName(byte[] _name)

		torrent_name	= _name;
	
protected voidsetPieceLength(long _length)

		piece_length	= _length;
	
public voidsetPieces(byte[][] _pieces)

		pieces = _pieces;
	
public voidsetPrivate(boolean _private_torrent)

		additional_info_properties.put( TK_PRIVATE, new Long(_private_torrent?1:0));
		
			// update torrent hash
		
		torrent_hash	= null;
		
		getHash();
	
protected voidsetSimpleTorrent(boolean _simple_torrent)

		simple_torrent	= _simple_torrent;
	
protected voidwriteStringToMetaData(java.util.Map meta_data, java.lang.String name, java.lang.String value)

		meta_data.put( name, writeStringToMetaData( value ));	
	
protected byte[]writeStringToMetaData(java.lang.String value)

		try{
			
			return(	value.getBytes( Constants.DEFAULT_ENCODING ));
			
		}catch( UnsupportedEncodingException e ){
			
			throw( new TOTorrentException( 	"Unsupported encoding for '" + value + "'",
											TOTorrentException.RT_UNSUPPORTED_ENCODING));
		}