/* * File : TOTorrentXMLDeserialiser.java * Created : 14-Oct-2003 * By : parg * * Azureus - a Java Bittorrent client * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details ( see the LICENSE file ). * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.gudy.azureus2.core3.torrent.impl; /** * @author parg * */ import java.util.*; import java.io.*; import java.net.*; import org.gudy.azureus2.core3.xml.simpleparser.*; import org.gudy.azureus2.core3.torrent.*; import org.gudy.azureus2.core3.util.*; import org.gudy.azureus2.plugins.utils.xml.simpleparser.SimpleXMLParserDocument; import org.gudy.azureus2.plugins.utils.xml.simpleparser.SimpleXMLParserDocumentAttribute; import org.gudy.azureus2.plugins.utils.xml.simpleparser.SimpleXMLParserDocumentException; import org.gudy.azureus2.plugins.utils.xml.simpleparser.SimpleXMLParserDocumentNode; public class TOTorrentXMLDeserialiser { public TOTorrentXMLDeserialiser() { } public TOTorrent deserialise( File file ) throws TOTorrentException { try{ SimpleXMLParserDocument doc = SimpleXMLParserDocumentFactory.create( file ); TOTorrent res = decodeRoot( doc ); return( res ); }catch( SimpleXMLParserDocumentException e ){ throw( new TOTorrentException( "XML Parse Fails: " + e.getMessage(), TOTorrentException.RT_DECODE_FAILS )); } } protected TOTorrent decodeRoot( SimpleXMLParserDocument doc ) throws TOTorrentException { String root_name = doc.getName(); if ( root_name.equalsIgnoreCase("TORRENT")){ TOTorrentImpl torrent = new TOTorrentImpl(); SimpleXMLParserDocumentNode[] kids = doc.getChildren(); URL announce_url = null; byte[] torrent_hash = null; byte[] torrent_hash_override = null; for (int i=0;i<kids.length;i++){ SimpleXMLParserDocumentNode kid = kids[i]; String name = kid.getName(); if ( name.equalsIgnoreCase( "ANNOUNCE_URL")){ try{ announce_url = new URL(kid.getValue()); }catch( MalformedURLException e ){ throw( new TOTorrentException( "ANNOUNCE_URL malformed", TOTorrentException.RT_DECODE_FAILS)); } }else if ( name.equalsIgnoreCase( "ANNOUNCE_LIST")){ SimpleXMLParserDocumentNode[] set_nodes = kid.getChildren(); TOTorrentAnnounceURLGroup group = torrent.getAnnounceURLGroup(); TOTorrentAnnounceURLSet[] sets = new TOTorrentAnnounceURLSet[set_nodes.length]; for (int j=0;j<sets.length;j++){ SimpleXMLParserDocumentNode[] url_nodes = set_nodes[j].getChildren(); URL[] urls = new URL[url_nodes.length]; for (int k=0;k<urls.length;k++){ try{ urls[k] = new URL(url_nodes[k].getValue()); }catch( MalformedURLException e ){ throw( new TOTorrentException( "ANNOUNCE_LIST malformed", TOTorrentException.RT_DECODE_FAILS)); } } sets[j] = group.createAnnounceURLSet( urls ); } group.setAnnounceURLSets( sets ); }else if ( name.equalsIgnoreCase( "COMMENT")){ torrent.setComment( readLocalisableString( kid )); }else if ( name.equalsIgnoreCase( "CREATED_BY")){ torrent.setCreatedBy( readLocalisableString(kid)); }else if ( name.equalsIgnoreCase( "CREATION_DATE")){ torrent.setCreationDate( readGenericLong( kid ).longValue()); }else if ( name.equalsIgnoreCase( "TORRENT_HASH")){ torrent_hash = readGenericBytes( kid ); }else if ( name.equalsIgnoreCase( "TORRENT_HASH_OVERRIDE")){ torrent_hash_override = readGenericBytes( kid ); }else if ( name.equalsIgnoreCase( "INFO" )){ decodeInfo( kid, torrent ); }else{ mapEntry entry = readGenericMapEntry( kid ); torrent.addAdditionalProperty( entry.name, entry.value ); } } if ( announce_url == null ){ throw( new TOTorrentException( "ANNOUNCE_URL missing", TOTorrentException.RT_DECODE_FAILS)); } torrent.setAnnounceURL( announce_url ); if ( torrent_hash_override != null ){ try{ torrent.setHashOverride( torrent_hash_override ); }catch( Throwable e ){ Debug.printStackTrace( e ); } } if ( torrent_hash != null ){ if ( !Arrays.equals( torrent.getHash(), torrent_hash )){ throw( new TOTorrentException( "Hash differs - declared TORRENT_HASH and computed hash differ. If this really is the intent (unlikely) then remove the TORRENT_HASH element", TOTorrentException.RT_DECODE_FAILS)); } } return( torrent ); }else{ throw( new TOTorrentException( "Invalid root element", TOTorrentException.RT_DECODE_FAILS)); } } protected void decodeInfo( SimpleXMLParserDocumentNode doc, TOTorrentImpl torrent ) throws TOTorrentException { SimpleXMLParserDocumentNode[] kids = doc.getChildren(); byte[] torrent_name = null; long torrent_length = 0; SimpleXMLParserDocumentNode[] file_nodes = null; for (int i=0;i<kids.length;i++){ SimpleXMLParserDocumentNode kid = kids[i]; String name = kid.getName(); if ( name.equalsIgnoreCase( "PIECE_LENGTH")){ torrent.setPieceLength( readGenericLong( kid ).longValue()); }else if ( name.equalsIgnoreCase( "LENGTH")){ torrent.setSimpleTorrent( true ); torrent_length = readGenericLong( kid ).longValue(); }else if ( name.equalsIgnoreCase( "NAME")){ torrent.setName( readLocalisableString( kid )); }else if ( name.equalsIgnoreCase( "FILES" )){ torrent.setSimpleTorrent( false ); file_nodes = kid.getChildren(); }else if ( name.equalsIgnoreCase( "PIECES" )){ SimpleXMLParserDocumentNode[] piece_nodes = kid.getChildren(); byte[][] pieces = new byte[piece_nodes.length][]; for (int j=0;j<pieces.length;j++){ pieces[j] = readGenericBytes( piece_nodes[j] ); } torrent.setPieces( pieces ); }else{ mapEntry entry = readGenericMapEntry( kid ); torrent.addAdditionalInfoProperty( entry.name, entry.value ); } } if ( torrent.isSimpleTorrent()){ torrent.setFiles( new TOTorrentFileImpl[]{ new TOTorrentFileImpl( torrent, 0, 0, torrent_length, new byte[][]{ torrent.getName()})}); }else{ if ( file_nodes == null ){ throw( new TOTorrentException( "FILES element missing", TOTorrentException.RT_DECODE_FAILS)); } TOTorrentFileImpl[] files = new TOTorrentFileImpl[ file_nodes.length ]; long offset = 0; for (int j=0;j<files.length;j++){ SimpleXMLParserDocumentNode file_node = file_nodes[j]; SimpleXMLParserDocumentNode[] file_entries = file_node.getChildren(); long file_length = 0; boolean length_entry_found = false; byte[][] path_comps = null; Vector additional_props = new Vector(); for ( int k=0;k<file_entries.length;k++){ SimpleXMLParserDocumentNode file_entry = file_entries[k]; String entry_name = file_entry.getName(); if ( entry_name.equalsIgnoreCase( "LENGTH" )){ file_length = readGenericLong(file_entry).longValue(); length_entry_found = true; }else if ( entry_name.equalsIgnoreCase( "PATH" )){ SimpleXMLParserDocumentNode[] path_nodes = file_entry.getChildren(); path_comps = new byte[path_nodes.length][]; for (int n=0;n<path_nodes.length;n++){ path_comps[n] = readLocalisableString( path_nodes[n] ); } }else{ additional_props.addElement( readGenericMapEntry( file_entry )); } } if ( (!length_entry_found) || path_comps == null ){ throw( new TOTorrentException( "FILE element invalid (file length = " + file_length + ")", TOTorrentException.RT_DECODE_FAILS)); } files[j] = new TOTorrentFileImpl( torrent, j, offset, file_length, path_comps ); offset += file_length; for (int k=0;k<additional_props.size();k++){ mapEntry entry = (mapEntry)additional_props.elementAt(k); files[j].setAdditionalProperty( entry.name, entry.value ); } } torrent.setFiles( files ); } } protected mapEntry readGenericMapEntry( SimpleXMLParserDocumentNode node ) throws TOTorrentException { if ( !node.getName().equalsIgnoreCase("KEY")){ throw( new TOTorrentException( "Additional property invalid, must be KEY node", TOTorrentException.RT_DECODE_FAILS)); } String name = node.getAttribute("name").getValue(); SimpleXMLParserDocumentNode[] kids = node.getChildren(); if ( kids.length != 1 ){ throw( new TOTorrentException( "Additional property invalid, KEY must have one child", TOTorrentException.RT_DECODE_FAILS)); } String type = kids[0].getName(); Object value = readGenericValue( kids[0] ); return( new mapEntry( name, value )); } protected Object readGenericValue( SimpleXMLParserDocumentNode node ) throws TOTorrentException { String name = node.getName(); if ( name.equalsIgnoreCase( "BYTES")){ return( readGenericBytes( node )); }else if ( name.equalsIgnoreCase( "LONG" )){ return( readGenericLong( node )); }else if ( name.equalsIgnoreCase( "LIST" )){ return( readGenericList( node )); }else if ( name.equalsIgnoreCase( "MAP" )){ return( readGenericMap( node )); }else{ throw( new TOTorrentException( "Additional property invalid, sub-key '" + name + "' not recognised", TOTorrentException.RT_DECODE_FAILS)); } } protected byte[] readGenericBytes( SimpleXMLParserDocumentNode node ) throws TOTorrentException { String value = node.getValue(); byte[] res = new byte[value.length()/2]; for (int i=0;i<res.length;i++){ res[i] = (byte)Integer.parseInt( value.substring(i*2,i*2+2), 16 ); } return( res ); /* try{ return( URLDecoder.decode( node.getValue(), Constants.BYTE_ENCODING ).getBytes( Constants.BYTE_ENCODING )); }catch( UnsupportedEncodingException e ){ throw( new TOTorrentException( "bytes invalid - unsupported encoding", TOTorrentException.RT_DECODE_FAILS)); } */ } protected Long readGenericLong( SimpleXMLParserDocumentNode node ) throws TOTorrentException { String value = node.getValue(); try{ return( new Long( value )); }catch( Throwable e ){ throw( new TOTorrentException( "long value invalid for '" + node.getName() + "'", TOTorrentException.RT_DECODE_FAILS)); } } protected Map readGenericMap( SimpleXMLParserDocumentNode node ) throws TOTorrentException { Map res = new HashMap(); SimpleXMLParserDocumentNode[] kids = node.getChildren(); for (int i=0;i<kids.length;i++){ mapEntry entry = readGenericMapEntry( kids[i] ); res.put( entry.name, entry.value ); } return( res ); } protected byte[] readLocalisableString( SimpleXMLParserDocumentNode kid ) throws TOTorrentException { SimpleXMLParserDocumentAttribute attr = kid.getAttribute("encoding"); if ( attr == null || attr.getValue().equalsIgnoreCase("bytes")){ return( readGenericBytes( kid )); } try{ return( kid.getValue().getBytes( Constants.DEFAULT_ENCODING )); }catch( UnsupportedEncodingException e ){ throw( new TOTorrentException( "bytes invalid - unsupported encoding", TOTorrentException.RT_DECODE_FAILS)); } } protected List readGenericList( SimpleXMLParserDocumentNode node ) throws TOTorrentException { List res = new ArrayList(); SimpleXMLParserDocumentNode[] kids = node.getChildren(); for (int i=0;i<kids.length;i++){ res.add( readGenericValue( kids[i])); } return( res ); } protected class mapEntry { String name; Object value; mapEntry( String _name, Object _value ) { name = _name; value = _value; } } }