]>
git.ipfire.org Git - ipfire.org.git/blob - webapp/backend/tracker.py
3 from __future__
import division
7 from misc
import Object
16 ret
.append("%02x" % i
)
20 class Tracker(Object
):
23 return self
.settings
.get("tracker_id", "TheIPFireTorrentTracker")
25 def _fuzzy_interval(self
, interval
, fuzz
=120):
26 return interval
- random
.randint(0, fuzz
)
30 return self
.settings
.get_int("tracker_interval", 3600)
34 return self
._fuzzy
_interval
(self
._interval
)
37 def min_interval(self
):
38 return self
.settings
.get_int("tracker_min_interval", self
._interval
// 2)
42 return self
.settings
.get_int("tracker_numwant", 50)
44 def get_peers(self
, info_hash
, limit
=None, random
=True, no_peer_id
=False, ipfamily
=None):
45 query
= "SELECT * FROM tracker WHERE last_update >= NOW() - INTERVAL '%ss'"
46 args
= [self
._interval
,]
49 query
+= " AND hash = %s"
50 args
.append(info_hash
)
53 query
+= " ORDER BY RANDOM()"
60 for row
in self
.db
.query(query
, *args
):
64 if row
.address6
and row
.port6
:
70 if row
.address4
and row
.port4
:
78 peer6
["peer id"] = row
.id
81 peer4
["peer id"] = row
.id
91 def cleanup_peers(self
):
93 Remove all peers that have timed out.
95 self
.db
.execute("DELETE FROM tracker \
96 WHERE last_update < NOW() - INTERVAL '%s s'", int(self
._interval
* 1.1))
98 def update_peer(self
, peer_id
, info_hash
, address6
=None, port6
=None,
99 address4
=None, port4
=None, downloaded
=None, uploaded
=None, left_data
=None):
100 if address4
and address4
.startswith("172.28.1."):
101 address
= "178.63.73.246"
103 query
= "UPDATE tracker SET last_update = NOW()"
107 query
+= ", address6 = %s"
108 args
.append(address6
)
111 query
+= ", port6 = %s"
115 query
+= ", address4 = %s"
116 args
.append(address4
)
119 query
+= ", port4 = %s"
123 query
+= ", downloaded = %s"
124 args
.append(downloaded
)
127 query
+= ", uploaded = %s"
128 args
.append(uploaded
)
131 query
+= ", left_data = %s"
132 args
.append(left_data
)
134 query
+= " WHERE id = %s AND hash = %s"
135 args
+= [peer_id
, info_hash
]
137 self
.db
.execute(query
, *args
)
139 def complete(self
, info_hash
):
140 ret
= self
.db
.get("SELECT COUNT(*) AS c FROM tracker \
141 WHERE hash = %s AND left_data = 0", info_hash
)
146 def incomplete(self
, info_hash
):
147 ret
= self
.db
.get("SELECT COUNT(*) AS c FROM tracker \
148 WHERE hash = %s AND left_data > 0", info_hash
)
153 def handle_event(self
, event
, peer_id
, info_hash
, **kwargs
):
155 if event
== "started":
156 self
.insert_peer(peer_id
, info_hash
, **kwargs
)
159 elif event
== "stopped":
160 self
.remove_peer(peer_id
, info_hash
)
162 def peer_exists(self
, peer_id
, info_hash
):
163 ret
= self
.db
.get("SELECT COUNT(*) AS c FROM tracker \
164 WHERE id = %s AND hash = %s", peer_id
, info_hash
)
166 if ret
and ret
.c
> 0:
171 def insert_peer(self
, peer_id
, info_hash
, address6
=None, port6
=None, address4
=None, port4
=None):
172 exists
= self
.peer_exists(peer_id
, info_hash
)
176 self
.db
.execute("INSERT INTO tracker(id, hash, address6, port6, address4, port4) \
177 VALUES(%s, %s, %s, %s, %s, %s)", peer_id
, info_hash
, address6
, port6
, address4
, port4
)
179 def remove_peer(self
, peer_id
, info_hash
):
180 self
.db
.execute("DELETE FROM tracker \
181 WHERE id = %s AND hash = %s", peer_id
, info_hash
)
183 def scrape(self
, info_hashes
):
187 "min_request_interval" : self
.interval
,
192 for info_hash
in info_hashes
:
193 ret
["files"][info_hash
] = {
194 "complete" : self
.complete(info_hash
),
195 "incomplete" : self
.incomplete(info_hash
),
202 ##### This is borrowed from the bittorrent client libary #####
204 def decode_int(x
, f
):
206 newf
= x
.index('e', f
)
211 elif x
[f
] == '0' and newf
!= f
+1:
215 def decode_string(x
, f
):
216 colon
= x
.index(':', f
)
218 if x
[f
] == '0' and colon
!= f
+1:
221 return (x
[colon
:colon
+n
], colon
+n
)
223 def decode_list(x
, f
):
226 v
, f
= decode_func
[x
[f
]](x
, f
)
230 def decode_dict(x
, f
):
233 k
, f
= decode_string(x
, f
)
234 r
[k
], f
= decode_func
[x
[f
]](x
, f
)
238 decode_func
['l'] = decode_list
239 decode_func
['d'] = decode_dict
240 decode_func
['i'] = decode_int
241 decode_func
['0'] = decode_string
242 decode_func
['1'] = decode_string
243 decode_func
['2'] = decode_string
244 decode_func
['3'] = decode_string
245 decode_func
['4'] = decode_string
246 decode_func
['5'] = decode_string
247 decode_func
['6'] = decode_string
248 decode_func
['7'] = decode_string
249 decode_func
['8'] = decode_string
250 decode_func
['9'] = decode_string
254 r
, l
= decode_func
[x
[0]](x
, 0)
255 except (IndexError, KeyError, ValueError):
256 raise Exception("not a valid bencoded string")
258 raise Exception("invalid bencoded value (data after valid prefix)")
261 from types
import StringType
, IntType
, LongType
, DictType
, ListType
, TupleType
264 class Bencached(object):
266 __slots__
= ['bencoded']
268 def __init__(self
, s
):
271 def encode_bencached(x
,r
):
274 def encode_int(x
, r
):
275 r
.extend(('i', str(x
), 'e'))
277 def encode_bool(x
, r
):
283 def encode_string(x
, r
):
284 r
.extend((str(len(x
)), ':', x
))
286 def encode_list(x
, r
):
289 encode_func
[type(i
)](i
, r
)
292 def encode_dict(x
,r
):
297 r
.extend((str(len(k
)), ':', k
))
298 encode_func
[type(v
)](v
, r
)
302 encode_func
[Bencached
] = encode_bencached
303 encode_func
[IntType
] = encode_int
304 encode_func
[LongType
] = encode_int
305 encode_func
[StringType
] = encode_string
306 encode_func
[ListType
] = encode_list
307 encode_func
[TupleType
] = encode_list
308 encode_func
[DictType
] = encode_dict
311 from types
import BooleanType
312 encode_func
[BooleanType
] = encode_bool
318 encode_func
[type(x
)](x
, r
)