]> git.ipfire.org Git - people/shoehn/ipfire.org.git/blobdiff - webapp/backend/tracker.py
Major update of the webapp.
[people/shoehn/ipfire.org.git] / webapp / backend / tracker.py
index 4880bee4398833462c34e8b88abfe87e66c413f2..24a763b22937a98de76a70584afbc3f2ea47bd48 100644 (file)
@@ -1,13 +1,10 @@
 #!/usr/bin/python
 
-import os
-import random
-import time
+from __future__ import division
 
-import releases
+import random
 
-from databases import Databases, Row
-from misc import Singleton
+from misc import Object
 
 def decode_hex(s):
        ret = []
@@ -20,150 +17,183 @@ def decode_hex(s):
 
        return "".join(ret)
 
-class Tracker(object):
-       id = "TheIPFireTorrentTracker"
-
-       # Intervals # XXX needs to be in Settings
-       _interval = 60*60
-       _min_interval = 30*60
+class Tracker(Object):
+       @property
+       def tracker_id(self):
+               return self.settings.get("tracker_id", "TheIPFireTorrentTracker")
 
-       random_interval = -60, 60
+       def _fuzzy_interval(self, interval, fuzz=60):
+               return interval + random.randint(-fuzz, fuzz)
 
-       numwant = 50
+       @property
+       def interval(self):
+               return self.settings.get_int("tracker_interval", 3600)
 
        @property
-       def db(self):
-               return Databases().webapp
+       def min_interval(self):
+               interval = self.settings.get_int("tracker_min_interval", self.interval // 2)
 
-       def _fetch(self, hash, limit=None, random=False, completed=False, no_peer_id=False):
-               query = "SELECT * FROM tracker_peers WHERE last_update >= %d" % self.since
+               return self._fuzzy_interval(interval)
+
+       @property
+       def numwant(self):
+               return self.settings.get_int("tracker_numwant", 50)
 
-               if hash:
-                       query += " AND hash = '%s'" % hash
+       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 completed:
-                       query += " AND left_data = 0"
-               else:
-                       query += " AND left_data != 0"
+               if info_hash:
+                       query += " AND hash = %s"
+                       args.append(info_hash)
 
                if random:
-                       query += " ORDER BY RAND()"
+                       query += " ORDER BY RANDOM()"
 
                if limit:
-                       query += " LIMIT %s" % limit
+                       query += " LIMIT %s"
+                       args.append(limit)
 
                peers = []
-               for peer in self.db.query(query):
-                       if not peer.ip or not peer.port:
-                               continue
-
-                       peer_dict = {
-                               "ip" : str(peer.ip),
-                               "port" : int(peer.port),
-                       }
+               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:
-                               peer_dict["peer id"] = str(peer.id),
+                               if peer6:
+                                       peer6["peer id"] = row.id
 
-                       peers.append(peer_dict)
+                               if peer4:
+                                       peer6["peer id"] = row.id
 
-               return peers
+                       if peer6:
+                               peers.append(peer6)
 
-       def get_peers(self, hash, **kwargs):
-               return self._fetch(hash, **kwargs)
+                       if peer4:
+                               peers.append(peer4)
 
-       def get_seeds(self, hash, **kwargs):
-               kwargs.update({"completed" : True})
-               return self._fetch(hash, **kwargs)
+               return peers
 
-       def complete(self, hash):
-               return len(self.get_seeds(hash))
+       def cleanup_peers(self):
+               """
+                       Remove all peers that have timed out.
+               """
+               self.db.execute("DELETE FROM tracker \
+                       WHERE last_update < NOW() - INTERVAL '%ss'", self.interval)
 
-       def incomplete(self, hash):
-               return len(self.get_peers(hash))
+       def update_peer(self, peer_id, info_hash, address6=None, port6=None,
+                       address4=None, port4=None, downloaded=None, uploaded=None, left_data=None):
+               if address4 and address4.startswith("172.28.1."):
+                       address = "178.63.73.246"
 
-       def event_started(self, hash, peer_id):
-               # Damn, mysql does not support INSERT IF NOT EXISTS...
-               if not self.db.query("SELECT id FROM tracker_peers WHERE hash = '%s' AND peer_id = '%s'" % (hash, peer_id)):
-                       self.db.execute("INSERT INTO tracker_peers(hash, peer_id) VALUES('%s', '%s')" % (hash, peer_id))
+               query = "UPDATE tracker SET last_update = NOW()"
+               args = []
 
-               if not hash in self.hashes:
-                       self.db.execute("INSERT INTO tracker_hashes(hash) VALUES('%s')" % hash)
+               if address6:
+                       query += ", address6 = %s"
+                       args.append(address6)
 
-       def event_stopped(self, hash, peer_id):
-               self.db.execute("DELETE FROM tracker_peers WHERE hash = '%s' AND peer_id = '%s'" % (hash, peer_id))
+               if port6:
+                       query += ", port6 = %s"
+                       args.append(port6)
 
-       def event_completed(self, hash, peer_id):
-               self.db.execute("UPDATE tracker_hashes SET completed=completed+1 WHERE hash = '%s'" % hash)
+               if address4:
+                       query += ", address4 = %s"
+                       args.append(address4)
 
-       def scrape(self, hashes=[]):
-               ret = {}
-               for hash in self.db.query("SELECT hash, completed FROM tracker_hashes"):
-                       hash, completed = hash.hash, hash.completed
+               if port4:
+                       query += ", port4 = %s"
+                       args.append(port4)
 
-                       if hashes and hash not in hashes:
-                               continue
+               if downloaded:
+                       query += ", downloaded = %s"
+                       args.append(downloaded)
 
-                       ret[hash] = {
-                               "complete" : self.complete(hash),
-                               "downloaded" : completed or 0,
-                               "incomplete" : self.incomplete(hash),
-                       }
+               if uploaded:
+                       query += ", uploaded = %s"
+                       args.append(uploaded)
 
-               return ret
+               if left_data:
+                       query += ", left_data = %s"
+                       args.append(left_data)
 
-       def update(self, hash, id, ip=None, port=None, downloaded=None, uploaded=None, left=None):
-               args = [ "last_update = '%s'" % self.now ]
+               query += " WHERE id = %s AND hash = %s"
+               args += [peer_id, info_hash]
 
-               if ip:
-                       if ip.startswith("172.28.1."):
-                               ip = "178.63.73.246"
+               self.db.execute(query, *args)
 
-                       args.append("ip='%s'" % ip)
+       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 port:
-                       args.append("port='%s'" % port)
+               if ret:
+                       return ret.c
 
-               if downloaded:
-                       args.append("downloaded='%s'" % downloaded)
+       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 uploaded:
-                       args.append("uploaded='%s'" % uploaded)
+               if ret:
+                       return ret.c
 
-               if left:
-                       args.append("left_data='%s'" % left)
+       def handle_event(self, event, peer_id, info_hash, **kwargs):
+               # started
+               if event == "started":
+                       self.insert_peer(peer_id, info_hash, **kwargs)
 
-               if not args:
-                       return
+               # stopped
+               elif event == "stopped":
+                       self.remove_peer(peer_id, info_hash)
 
-               query = "UPDATE tracker_peers SET " + ", ".join(args) + \
-                       " WHERE hash = '%s' AND peer_id = '%s'" % (hash, id)
+       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)
 
-               self.db.execute(query)
+               if ret and ret.c > 0:
+                       return True
 
-       @property
-       def hashes(self):
-               hashes = []
-               for h in self.db.query("SELECT hash FROM tracker_hashes"):
-                       hashes.append(h["hash"].lower())
+               return False
 
-               return hashes
+       def insert_peer(self, peer_id, info_hash, address6=None, port6=None, address4=None, port4=None):
+               exists = self.peer_exists(peer_id, info_hash)
+               if exists:
+                       return
 
-       @property
-       def now(self):
-               return int(time.time())
+               self.db.execute("INSERT INTO tracker(id, hash, address6, port6, address4, port4) \
+                       VALUES(%s, %s, %s, %s, %s, %s)", peer_id, info_hash, address6, port6, address4, port4)
 
-       @property
-       def since(self):
-               return int(time.time() - self.interval)
+       def remove_peer(self, peer_id, info_hash):
+               self.db.execute("DELETE FROM tracker \
+                       WHERE id = %s AND hash = %s", peer_id, info_hash)
 
-       @property
-       def interval(self):
-               return self._interval + random.randint(*self.random_interval)
+       def scrape(self, info_hashes):
+               ret = {
+                       "files" : {},
+                       "flags" : {
+                               "min_request_interval" : self.interval,
+                       }
+               }
 
-       @property
-       def min_interval(self):
-               return self._min_interval + random.randint(*self.random_interval)
+               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 #####