import releases
import settings
import talk
-import tracker
import wishlist
class Backend(object):
self.planet = planet.Planet(self)
self.releases = releases.Releases(self)
self.talk = talk.Talk(self)
- self.tracker = tracker.Tracker(self)
self.wishlist = wishlist.Wishlist(self)
def read_config(self, configfile):
return s
- @property
- def seeders(self):
- if not self.torrent_hash:
- return
-
- return self.backend.tracker.complete(self.torrent_hash)
-
- @property
- def peers(self):
- if not self.torrent_hash:
- return
-
- return self.backend.tracker.incomplete(self.torrent_hash)
-
class Release(Object):
def __init__(self, backend, id, data=None):
from __future__ import division
-import random
-
-from misc import Object
-
-def encode_hex(s):
- return s.encode("hex")
-
-def decode_hex(s):
- return s.decode("hex")
-
-
-class Tracker(Object):
- @property
- def tracker_id(self):
- return self.settings.get("tracker_id", "TheIPFireTorrentTracker")
-
- def _fuzzy_interval(self, interval, fuzz=120):
- return interval - random.randint(0, fuzz)
-
- @property
- def _interval(self):
- return self.settings.get_int("tracker_interval", 3600)
-
- @property
- def interval(self):
- return self._fuzzy_interval(self._interval)
-
- @property
- def min_interval(self):
- return self.settings.get_int("tracker_min_interval", self._interval // 2)
-
- @property
- def numwant(self):
- return self.settings.get_int("tracker_numwant", 50)
-
- def get_peers(self, info_hash, limit=None, random=True, no_peer_id=False, ipfamily=None):
- query = "SELECT * FROM tracker WHERE last_update >= NOW() - INTERVAL '%ss'"
- args = [self._interval,]
-
- if info_hash:
- query += " AND hash = %s"
- args.append(info_hash)
-
- if random:
- query += " ORDER BY RANDOM()"
-
- if limit:
- query += " LIMIT %s"
- args.append(limit)
-
- peers = []
- for row in self.db.query(query, *args):
- peer6 = None
- peer4 = None
-
- if row.address6 and row.port6:
- peer6 = {
- "ip" : row.address6,
- "port" : row.port6,
- }
-
- if row.address4 and row.port4:
- peer4 = {
- "ip" : row.address4,
- "port" : row.port4,
- }
-
- if not no_peer_id:
- if peer6:
- peer6["peer id"] = decode_hex(row.id)
-
- if peer4:
- peer4["peer id"] = decode_hex(row.id)
-
- if peer6:
- peers.append(peer6)
-
- if peer4:
- peers.append(peer4)
-
- return peers
-
- def cleanup_peers(self):
- """
- Remove all peers that have timed out.
- """
- self.db.execute("DELETE FROM tracker \
- WHERE last_update < NOW() - INTERVAL '%s s'", int(self._interval * 1.2))
-
- def update_peer(self, peer_id, info_hash, **kwargs):
- # Translate the location IP address
- address4 = kwargs.get("address4", None)
- if address4 and address4.startswith("172.28.1."):
- kwargs.update({
- "address4" : "178.63.73.246",
- })
-
- if self.peer_exists(peer_id, info_hash):
- self.__update_peer(peer_id, info_hash, **kwargs)
- else:
- self.__insert_peer(peer_id, info_hash, **kwargs)
-
- def complete(self, info_hash):
- ret = self.db.get("SELECT COUNT(*) AS c FROM tracker \
- WHERE hash = %s AND left_data = 0", info_hash)
-
- if ret:
- return ret.c
-
- def incomplete(self, info_hash):
- ret = self.db.get("SELECT COUNT(*) AS c FROM tracker \
- WHERE hash = %s AND left_data > 0", info_hash)
-
- if ret:
- return ret.c
-
- def handle_event(self, event, peer_id, info_hash, **kwargs):
- # stopped
- if event == "stopped":
- self.remove_peer(peer_id, info_hash)
-
- def peer_exists(self, peer_id, info_hash):
- ret = self.db.get("SELECT COUNT(*) AS c FROM tracker \
- WHERE id = %s AND hash = %s", peer_id, info_hash)
-
- if ret and ret.c > 0:
- return True
-
- return False
-
- def __insert_peer(self, peer_id, info_hash, address6=None, port6=None, address4=None, port4=None, left_data=0, **kwargs):
- self.db.execute("INSERT INTO tracker(id, hash, address6, port6, address4, port4, left_data) \
- VALUES(%s, %s, %s, %s, %s, %s, %s)", peer_id, info_hash, address6, port6, address4, port4, left_data)
-
- def __update_peer(self, peer_id, info_hash, address6=None, port6=None,
- address4=None, port4=None, downloaded=None, uploaded=None, left_data=None):
- query = "UPDATE tracker SET last_update = NOW()"
- args = []
-
- if address6:
- query += ", address6 = %s"
- args.append(address6)
-
- if port6:
- query += ", port6 = %s"
- args.append(port6)
-
- if address4:
- query += ", address4 = %s"
- args.append(address4)
-
- if port4:
- query += ", port4 = %s"
- args.append(port4)
-
- if downloaded:
- query += ", downloaded = %s"
- args.append(downloaded)
-
- if uploaded:
- query += ", uploaded = %s"
- args.append(uploaded)
-
- if left_data:
- query += ", left_data = %s"
- args.append(left_data)
-
- query += " WHERE id = %s AND hash = %s"
- args += [peer_id, info_hash]
-
- self.db.execute(query, *args)
-
- def remove_peer(self, peer_id, info_hash):
- self.db.execute("DELETE FROM tracker \
- WHERE id = %s AND hash = %s", peer_id, info_hash)
-
- def scrape(self, info_hashes):
- ret = {
- "files" : {},
- "flags" : {
- "min_request_interval" : self.interval,
- }
- }
-
- if info_hashes:
- for info_hash in info_hashes:
- ret["files"][info_hash] = {
- "complete" : self.complete(info_hash),
- "incomplete" : self.incomplete(info_hash),
- "downloaded" : 0,
- }
-
- return ret
-
-
##### This is borrowed from the bittorrent client libary #####
def decode_int(x, f):
+++ /dev/null
-#!/usr/bin/python
-
-import re
-import tornado.web
-
-from backend.tracker import bencode, bdecode, encode_hex, decode_hex
-from handlers_base import *
-
-
-class TrackerIndexHandler(BaseHandler):
- def get(self):
- releases = self.releases.get_all()
-
- limit = 5
- releases = releases[:limit]
-
- self.render("tracker-torrents.html", releases=releases)
-
-
-class TrackerDetailHandler(BaseHandler):
- def get(self, torrent_hash):
- file = self.releases.get_file_for_torrent_hash(torrent_hash)
-
- if not file:
- raise tornado.web.HTTPError(404, "Could not find torrent file for hash: %s" % torrent_hash)
-
- peers = self.tracker.get_peers(torrent_hash)
-
- self.render("tracker-torrent-detail.html", release=file.release,
- file=file, peers=peers)
-
-
-class TrackerDownloadHandler(BaseHandler):
- def get(self, torrent_hash):
- file = self.releases.get_file_for_torrent_hash(torrent_hash)
-
- if not file:
- raise tornado.web.HTTPError(404, "Could not find torrent file for hash: %s" % torrent_hash)
-
- # Redirect the user to the download redirector.
- self.redirect("http://downloads.ipfire.org/%s.torrent" % file.filename)
-
-
-class TrackerBaseHandler(BaseHandler):
- def get_hexencoded_argument(self, name, all=False):
- try:
- arguments = self.request.arguments[name]
- except KeyError:
- return None
-
- arguments_new = []
- for argument in arguments:
- arguments_new.append(encode_hex(argument))
-
- arguments = arguments_new
-
- if all:
- return arguments
-
- return arguments[0]
-
- def send_tracker_error(self, error_message):
- msg = bencode({"failure reason" : error_message })
- self.finish(msg)
-
-
-class TrackerAnnounceHandler(TrackerBaseHandler):
- def prepare(self):
- self.set_header("Content-Type", "text/plain")
-
- def get_ipv6_address(self, default_port):
- # Get the external IP address of the client.
- addr = self.get_remote_ip()
-
- if ":" in addr:
- return addr, default_port
-
- # IPv6
- ipv6 = self.get_argument("ipv6", None)
- if ipv6:
- port = default_port
-
- m = re.match("^\[(.*)\]\:(\d)$", ipv6)
- if m:
- ipv6, port = (m.group(1), m.group(2))
-
- return ipv6, port
-
- return None, None
-
- def get_ipv4_address(self, default_port):
- # Get the external IP address of the client.
- addr = self.get_remote_ip()
-
- if addr.startswith("172.28.1."):
- addr = "178.63.73.246"
-
- if not ":" in addr:
- return addr, default_port
-
- # IPv4
- ipv4 = self.get_argument("ipv4", None)
- if ipv4:
- return ipv4, default_port
-
- ip = self.get_argument("ip", None)
- if ip:
- return ip, default_port
-
- return None, None
-
- def get_port(self):
- # Get the port and check it for sanity
- port = self.get_argument("port", None)
-
- try:
- port = int(port)
-
- if port < 0 or port > 65535:
- raise ValueError
- except (TypeError, ValueError):
- port = None
-
- return port
-
- def get(self):
- # Get the info hash
- info_hash = self.get_hexencoded_argument("info_hash")
- if not info_hash:
- self.send_tracker_error("Your client forgot to send your torrent's info_hash")
- return
-
- # Get the peer id
- peer_id = self.get_hexencoded_argument("peer_id")
-
- # Get the port and check it for sanity
- port = self.get_port()
- if not port:
- self.send_tracker_error("Invalid port number or port number missing")
- return
-
- addr_ipv6, port_ipv6 = self.get_ipv6_address(port)
- addr_ipv4, port_ipv4 = self.get_ipv4_address(port)
-
- # Handle events
- event = self.get_argument("event", None)
- if event:
- if not event in ("started", "stopped", "completed"):
- self.send_tracker_error("Got unknown event")
- return
-
- self.tracker.handle_event(event, peer_id, info_hash)
-
- if event == "stopped":
- return
-
- peer_info = {
- "address6" : addr_ipv6,
- "port6" : port_ipv6,
- "address4" : addr_ipv4,
- "port4" : port_ipv4,
- "downloaded" : self.get_argument("downloaded", 0),
- "uploaded" : self.get_argument("uploaded", 0),
- "left_data" : self.get_argument("left", 0),
- }
-
- self.tracker.update_peer(peer_id, info_hash, **peer_info)
-
- no_peer_id = self.get_argument("no_peer_id", False)
- numwant = self.get_argument("numwant", self.tracker.numwant)
-
- peers = self.tracker.get_peers(info_hash, limit=numwant, no_peer_id=no_peer_id)
-
- response = bencode({
- "tracker id" : self.tracker.tracker_id,
- "interval" : self.tracker.interval,
- "min interval" : self.tracker.min_interval,
- "peers" : peers,
- "complete" : self.tracker.complete(info_hash),
- "incomplete" : self.tracker.incomplete(info_hash),
- })
- self.finish(response)
-
- def on_finish(self):
- """
- Cleanup after every request.
- """
- self.tracker.cleanup_peers()
-
-
-class TrackerScrapeHandler(TrackerBaseHandler):
- def get(self):
- info_hashes = self.get_hexencoded_argument("info_hash", all=True)
-
- response = self.tracker.scrape(info_hashes)
-
- self.finish(bencode(response))