]>
git.ipfire.org Git - people/shoehn/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
=60):
26 return interval
+ random
.randint(-fuzz
, fuzz
)
30 return self
.settings
.get_int("tracker_interval", 3600)
33 def min_interval(self
):
34 interval
= self
.settings
.get_int("tracker_min_interval", self
.interval
// 2)
36 return self
._fuzzy
_interval
(interval
)
40 return self
.settings
.get_int("tracker_numwant", 50)
42 def get_peers(self
, info_hash
, limit
=None, random
=True, no_peer_id
=False, ipfamily
=None):
43 query
= "SELECT * FROM tracker WHERE last_update >= NOW() - INTERVAL '%ss'"
44 args
= [self
.interval
,]
47 query
+= " AND hash = %s"
48 args
.append(info_hash
)
51 query
+= " ORDER BY RANDOM()"
58 for row
in self
.db
.query(query
, *args
):
62 if row
.address6
and row
.port6
:
68 if row
.address4
and row
.port4
:
76 peer6
["peer id"] = row
.id
79 peer4
["peer id"] = row
.id
89 def cleanup_peers(self
):
91 Remove all peers that have timed out.
93 self
.db
.execute("DELETE FROM tracker \
94 WHERE last_update < NOW() - INTERVAL '%ss'", self
.interval
+ 600)
96 def update_peer(self
, peer_id
, info_hash
, address6
=None, port6
=None,
97 address4
=None, port4
=None, downloaded
=None, uploaded
=None, left_data
=None):
98 if address4
and address4
.startswith("172.28.1."):
99 address
= "178.63.73.246"
101 query
= "UPDATE tracker SET last_update = NOW()"
105 query
+= ", address6 = %s"
106 args
.append(address6
)
109 query
+= ", port6 = %s"
113 query
+= ", address4 = %s"
114 args
.append(address4
)
117 query
+= ", port4 = %s"
121 query
+= ", downloaded = %s"
122 args
.append(downloaded
)
125 query
+= ", uploaded = %s"
126 args
.append(uploaded
)
129 query
+= ", left_data = %s"
130 args
.append(left_data
)
132 query
+= " WHERE id = %s AND hash = %s"
133 args
+= [peer_id
, info_hash
]
135 self
.db
.execute(query
, *args
)
137 def complete(self
, info_hash
):
138 ret
= self
.db
.get("SELECT COUNT(*) AS c FROM tracker \
139 WHERE hash = %s AND left_data = 0", info_hash
)
144 def incomplete(self
, info_hash
):
145 ret
= self
.db
.get("SELECT COUNT(*) AS c FROM tracker \
146 WHERE hash = %s AND left_data > 0", info_hash
)
151 def handle_event(self
, event
, peer_id
, info_hash
, **kwargs
):
153 if event
== "started":
154 self
.insert_peer(peer_id
, info_hash
, **kwargs
)
157 elif event
== "stopped":
158 self
.remove_peer(peer_id
, info_hash
)
160 def peer_exists(self
, peer_id
, info_hash
):
161 ret
= self
.db
.get("SELECT COUNT(*) AS c FROM tracker \
162 WHERE id = %s AND hash = %s", peer_id
, info_hash
)
164 if ret
and ret
.c
> 0:
169 def insert_peer(self
, peer_id
, info_hash
, address6
=None, port6
=None, address4
=None, port4
=None):
170 exists
= self
.peer_exists(peer_id
, info_hash
)
174 self
.db
.execute("INSERT INTO tracker(id, hash, address6, port6, address4, port4) \
175 VALUES(%s, %s, %s, %s, %s, %s)", peer_id
, info_hash
, address6
, port6
, address4
, port4
)
177 def remove_peer(self
, peer_id
, info_hash
):
178 self
.db
.execute("DELETE FROM tracker \
179 WHERE id = %s AND hash = %s", peer_id
, info_hash
)
181 def scrape(self
, info_hashes
):
185 "min_request_interval" : self
.interval
,
190 for info_hash
in info_hashes
:
191 ret
["files"][info_hash
] = {
192 "complete" : self
.complete(info_hash
),
193 "incomplete" : self
.incomplete(info_hash
),
200 ##### This is borrowed from the bittorrent client libary #####
202 def decode_int(x
, f
):
204 newf
= x
.index('e', f
)
209 elif x
[f
] == '0' and newf
!= f
+1:
213 def decode_string(x
, f
):
214 colon
= x
.index(':', f
)
216 if x
[f
] == '0' and colon
!= f
+1:
219 return (x
[colon
:colon
+n
], colon
+n
)
221 def decode_list(x
, f
):
224 v
, f
= decode_func
[x
[f
]](x
, f
)
228 def decode_dict(x
, f
):
231 k
, f
= decode_string(x
, f
)
232 r
[k
], f
= decode_func
[x
[f
]](x
, f
)
236 decode_func
['l'] = decode_list
237 decode_func
['d'] = decode_dict
238 decode_func
['i'] = decode_int
239 decode_func
['0'] = decode_string
240 decode_func
['1'] = decode_string
241 decode_func
['2'] = decode_string
242 decode_func
['3'] = decode_string
243 decode_func
['4'] = decode_string
244 decode_func
['5'] = decode_string
245 decode_func
['6'] = decode_string
246 decode_func
['7'] = decode_string
247 decode_func
['8'] = decode_string
248 decode_func
['9'] = decode_string
252 r
, l
= decode_func
[x
[0]](x
, 0)
253 except (IndexError, KeyError, ValueError):
254 raise Exception("not a valid bencoded string")
256 raise Exception("invalid bencoded value (data after valid prefix)")
259 from types
import StringType
, IntType
, LongType
, DictType
, ListType
, TupleType
262 class Bencached(object):
264 __slots__
= ['bencoded']
266 def __init__(self
, s
):
269 def encode_bencached(x
,r
):
272 def encode_int(x
, r
):
273 r
.extend(('i', str(x
), 'e'))
275 def encode_bool(x
, r
):
281 def encode_string(x
, r
):
282 r
.extend((str(len(x
)), ':', x
))
284 def encode_list(x
, r
):
287 encode_func
[type(i
)](i
, r
)
290 def encode_dict(x
,r
):
295 r
.extend((str(len(k
)), ':', k
))
296 encode_func
[type(v
)](v
, r
)
300 encode_func
[Bencached
] = encode_bencached
301 encode_func
[IntType
] = encode_int
302 encode_func
[LongType
] = encode_int
303 encode_func
[StringType
] = encode_string
304 encode_func
[ListType
] = encode_list
305 encode_func
[TupleType
] = encode_list
306 encode_func
[DictType
] = encode_dict
309 from types
import BooleanType
310 encode_func
[BooleanType
] = encode_bool
316 encode_func
[type(x
)](x
, r
)