/******************************************************************************* * Copyright (c) 2011 Ziver Koc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. ******************************************************************************/ package zutil.parser; import zutil.parser.DataNode.DataType; import zutil.struct.MutableInt; /** * http://wiki.theory.org/BitTorrentSpecification * @author Ziver * */ public class BEncodedParser { /** * Returns the representation of the data in the BEncoded string * * @param data The data to be decoded * @return */ public static DataNode read(String data){ return decode_BEncoded(new MutableInt(), new StringBuilder(data)); } /** * Returns the representation of the data in the BEncoded string * * @param data The data to be decoded * @param index The index in data to start from * @return */ private static DataNode decode_BEncoded(MutableInt index, StringBuilder data){ String tmp; char c = ' '; switch (data.charAt(index.i)) { /** * Integers are prefixed with an i and terminated by an e. For * example, 123 would bEcode to i123e, -3272002 would bEncode to * i-3272002e. */ case 'i': //System.out.println("Found Integer at "+index); index.i++; tmp = data.substring(index.i, data.indexOf("e")); index.i += tmp.length() + 1; //System.out.println(tmp); return new DataNode( new Long(tmp)); /** * Lists are prefixed with a l and terminated by an e. The list * should contain a series of bEncoded elements. For example, the * list of strings ["Monduna", "Bit", "Torrents"] would bEncode to * l7:Monduna3:Bit8:Torrentse. The list [1, "Monduna", 3, ["Sub", "List"]] * would bEncode to li1e7:Mondunai3el3:Sub4:Listee */ case 'l': //System.out.println("Found List at "+index); index.i++; DataNode list = new DataNode( DataType.List ); c = data.charAt(index.i); while(c != 'e'){ list.add( decode_BEncoded(index, data) ); c = data.charAt(index.i); } index.i++; //MultiPrintStream.out.dump(list); if(list.size() == 1) return list.get(0); else return list; /** * Dictionaries are prefixed with a d and terminated by an e. They * are similar to list, except that items are in key value pairs. The * dictionary {"key":"value", "Monduna":"com", "bit":"Torrents", "number":7} * would bEncode to d3:key5:value7:Monduna3:com3:bit:8:Torrents6:numberi7ee */ case 'd': //System.out.println("Found Dictionary at "+index); index.i++; DataNode map = new DataNode( DataType.Map ); c = data.charAt(index.i); while(c != 'e'){ DataNode tmp2 = decode_BEncoded(index, data); map.set(tmp2.getString(), decode_BEncoded(index, data)); c = data.charAt(index.i); } index.i++; //MultiPrintStream.out.dump(map); return map; /** * Strings are prefixed with their length followed by a colon. * For example, "Monduna" would bEncode to 7:Monduna and "BitTorrents" * would bEncode to 11:BitTorrents. */ default: //System.out.println("Found String at "+index); tmp = data.substring(index.i, data.indexOf(":")); int length = Integer.parseInt(tmp); index.i += tmp.length() + 1; String ret = data.substring(index.i, length); index.i += length; //System.out.println(data.substring(i, i+length)); return new DataNode( ret ); } } }