/* * bitlet - Simple bittorrent library * Copyright (C) 2008 Alessandro Bahgat Shehata, Daniele Castagna * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * This is just a little class that lets you read and write bencoded files. * It uses List, Map, Long, and ByteBuffer in memory to represents data * */ package org.bitlet.wetorrent.bencode; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; public class Bencode { private Object rootElement = null; /** * This creates and parse a bencoded InputStream */ public Bencode(InputStream is) throws IOException { if (!is.markSupported()) { throw new IOException("is.markSupported should be true"); } rootElement = parse(is); } /** * This creates a new instance of Bencode class */ public Bencode() { } /** * This method prints the bencoded file on the OutputStream os */ public void print(OutputStream os) throws IOException { print(rootElement, os); } private void print(Object object, OutputStream os) throws IOException { if (object instanceof Long) { os.write('i'); os.write(((Long) object).toString().getBytes()); os.write('e'); } if (object instanceof ByteBuffer) { byte[] byteString = ((ByteBuffer) object).array(); os.write(Integer.toString(byteString.length).getBytes()); os.write(':'); for (int i = 0; i < byteString.length; i++) { os.write(byteString[i]); } } else if (object instanceof List) { List list = (List) object; os.write('l'); for (Object elem : list) { print(elem, os); } os.write('e'); } else if (object instanceof Map) { Map map = (Map) object; os.write('d'); SortedMap<ByteBuffer, Object> sortedMap = new TreeMap<ByteBuffer, Object>(new DictionaryComparator()); // sortedMap.putAll(map); for (Object elem : map.entrySet()) { Map.Entry entry = (Map.Entry) elem; sortedMap.put((ByteBuffer) entry.getKey(), entry.getValue()); } for (Object elem : sortedMap.entrySet()) { Map.Entry entry = (Map.Entry) elem; print(entry.getKey(), os); print(entry.getValue(), os); } os.write('e'); } } private Object parse(InputStream is) throws IOException { is.mark(0); int readChar = is.read(); switch (readChar) { case 'i': return parseInteger(is); case 'l': return parseList(is); case 'd': return parseDictionary(is); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': is.reset(); return parseByteString(is); default: throw new IOException("Problem parsing bencoded file"); } } public Object getRootElement() { return rootElement; } public void setRootElement(Object rootElement) { this.rootElement = rootElement; } private Long parseInteger(InputStream is) throws IOException { int readChar = is.read(); StringBuffer buff = new StringBuffer(); do { if (readChar < 0) { throw new IOException("Unexpected EOF found"); } buff.append((char) readChar); readChar = is.read(); } while (readChar != 'e'); // System.out.println("Loaded int: " + buff); return Long.parseLong(buff.toString()); } private List<Object> parseList(InputStream is) throws IOException { List<Object> list = new LinkedList<Object>(); is.mark(0); int readChar = is.read(); while (readChar != 'e') { if (readChar < 0) { throw new IOException("Unexpected EOF found"); } is.reset(); list.add(parse(is)); is.mark(0); readChar = is.read(); } return list; } private SortedMap parseDictionary(InputStream is) throws IOException { SortedMap<ByteBuffer, Object> map = new TreeMap<ByteBuffer, Object>(new DictionaryComparator()); is.mark(0); int readChar = is.read(); while (readChar != 'e') { if (readChar < 0) { throw new IOException("Unexpected EOF found"); } is.reset(); map.put(parseByteString(is), parse(is)); is.mark(0); readChar = is.read(); } return map; } private ByteBuffer parseByteString(InputStream is) throws IOException { int readChar = is.read(); StringBuffer buff = new StringBuffer(); do { if (readChar < 0) { throw new IOException("Unexpected EOF found"); } buff.append((char) readChar); readChar = is.read(); } while (readChar != ':'); Integer length = Integer.parseInt(buff.toString()); byte[] byteString = new byte[length]; for (int i = 0; i < byteString.length; i++) { byteString[i] = (byte) is.read(); // System.out.println("Loaded string: " + new String(byteString)); } return ByteBuffer.wrap(byteString); } }