/*
* TrackerInfo - Holds information returned by a tracker, mainly the peer list.
* Copyright (C) 2003 Mark J. Wielaard
*
* This file is part of Snark.
*
* 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, or (at your option) any later version.
*
* 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.
*
* 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.klomp.snark;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.klomp.snark.bencode.BDecoder;
import org.klomp.snark.bencode.BEValue;
import org.klomp.snark.bencode.InvalidBEncodingException;
public class TrackerInfo
{
private final String failure_reason;
private final int interval;
private final Set peers;
public TrackerInfo (InputStream in, byte[] my_id, MetaInfo metainfo)
throws IOException
{
this(new BDecoder(in), my_id, metainfo);
}
public TrackerInfo (BDecoder be, byte[] my_id, MetaInfo metainfo)
throws IOException
{
this(be.bdecodeMap().getMap(), my_id, metainfo);
}
public TrackerInfo (Map m, byte[] my_id, MetaInfo metainfo)
throws IOException
{
BEValue reason = (BEValue)m.get("failure reason");
if (reason != null) {
failure_reason = reason.getString();
interval = -1;
peers = null;
} else {
failure_reason = null;
BEValue beInterval = (BEValue)m.get("interval");
if (beInterval == null) {
throw new InvalidBEncodingException("No interval given");
} else {
interval = beInterval.getInt();
}
BEValue bePeers = (BEValue)m.get("peers");
if (bePeers == null) {
throw new InvalidBEncodingException("No peer list");
} else {
Set<Peer> peers;
try {
peers = getPeers(bePeers.getList(), my_id, metainfo);
}
catch(InvalidBEncodingException e) {
// The encoded part is not a list, maybe it's binary [LIMA].
peers = getPeers(bePeers.getBytes(), my_id, metainfo);
}
this.peers = peers;
}
}
}
public static Set getPeers (InputStream in, byte[] my_id, MetaInfo metainfo)
throws IOException
{
return getPeers(new BDecoder(in), my_id, metainfo);
}
public static Set getPeers (BDecoder be, byte[] my_id, MetaInfo metainfo)
throws IOException
{
return getPeers(be.bdecodeList().getList(), my_id, metainfo);
}
public static Set<Peer> getPeers (List l, byte[] my_id, MetaInfo metainfo)
throws IOException
{
Set<Peer> peers = new HashSet<Peer>(l.size());
Iterator it = l.iterator();
while (it.hasNext()) {
PeerID peerID = new PeerID(((BEValue)it.next()).getMap());
peers.add(new Peer(peerID, my_id, metainfo));
}
return peers;
}
/**
* Decodes a binary encoded peer list from the tracker [LIMA].
*/
public static Set<Peer> getPeers(byte[] binaryPeers, byte[] myId, MetaInfo metainfo) throws
IOException {
Set<Peer> peers = new HashSet<Peer>();
if(binaryPeers.length % 6 != 0)
// Encoded string consisting of multiple of six.
throw new InvalidBEncodingException("Invalid binary encoding of peers. " +
"Length of string " + binaryPeers.length);
for(int i = 0; i < binaryPeers.length; i = i + 6) {
byte[] ip = new byte[4];
System.arraycopy(binaryPeers, i, ip, 0, ip.length);
byte[] port = new byte[2];
System.arraycopy(binaryPeers, i + ip.length, port, 0, port.length);
//System.out.println((ip[0] & 0xff) + "." + (ip[1] & 0xff) + "." +
// (ip[2] & 0xff) + "." + (ip[3] & 0xff));
//String ipString = "";
//for(int j = 0; j < ip.length; j++) {
// ipString += (int) (ip[j] & 0xff); // Magic
// if(j < ip.length - 1)
// ipString += ".";
//}
DataInputStream in = new DataInputStream(new ByteArrayInputStream(port));
int portInt = (int) (in.readShort() & 0xffff); // 2x Magic
//System.out.println(in.readShort() & 0xffff);
PeerID id = new PeerID(null, InetAddress.getByAddress(ip), portInt);
peers.add(new Peer(id, myId, metainfo));
//System.out.println(ipString + ":" + portInt);
}
return peers;
}
public Set getPeers ()
{
return peers;
}
public String getFailureReason ()
{
return failure_reason;
}
public int getInterval ()
{
return interval;
}
@Override
public String toString ()
{
if (failure_reason != null) {
return "TrackerInfo[FAILED: " + failure_reason + "]";
} else {
return "TrackerInfo[interval=" + interval + ", peers=" + peers
+ "]";
}
}
}