]>
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
=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 peer6
["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
)
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
,
189 for info_hash
in info_hashes
:
190 ret
["files"][info_hash
] = {
191 "complete" : self
.complete(info_hash
),
192 "incomplete" : self
.incomplete(info_hash
),
199 ##### This is borrowed from the bittorrent client libary #####
201 def decode_int(x
, f
):
203 newf
= x
.index('e', f
)
208 elif x
[f
] == '0' and newf
!= f
+1:
212 def decode_string(x
, f
):
213 colon
= x
.index(':', f
)
215 if x
[f
] == '0' and colon
!= f
+1:
218 return (x
[colon
:colon
+n
], colon
+n
)
220 def decode_list(x
, f
):
223 v
, f
= decode_func
[x
[f
]](x
, f
)
227 def decode_dict(x
, f
):
230 k
, f
= decode_string(x
, f
)
231 r
[k
], f
= decode_func
[x
[f
]](x
, f
)
235 decode_func
['l'] = decode_list
236 decode_func
['d'] = decode_dict
237 decode_func
['i'] = decode_int
238 decode_func
['0'] = decode_string
239 decode_func
['1'] = decode_string
240 decode_func
['2'] = decode_string
241 decode_func
['3'] = decode_string
242 decode_func
['4'] = decode_string
243 decode_func
['5'] = decode_string
244 decode_func
['6'] = decode_string
245 decode_func
['7'] = decode_string
246 decode_func
['8'] = decode_string
247 decode_func
['9'] = decode_string
251 r
, l
= decode_func
[x
[0]](x
, 0)
252 except (IndexError, KeyError, ValueError):
253 raise Exception("not a valid bencoded string")
255 raise Exception("invalid bencoded value (data after valid prefix)")
258 from types
import StringType
, IntType
, LongType
, DictType
, ListType
, TupleType
261 class Bencached(object):
263 __slots__
= ['bencoded']
265 def __init__(self
, s
):
268 def encode_bencached(x
,r
):
271 def encode_int(x
, r
):
272 r
.extend(('i', str(x
), 'e'))
274 def encode_bool(x
, r
):
280 def encode_string(x
, r
):
281 r
.extend((str(len(x
)), ':', x
))
283 def encode_list(x
, r
):
286 encode_func
[type(i
)](i
, r
)
289 def encode_dict(x
,r
):
294 r
.extend((str(len(k
)), ':', k
))
295 encode_func
[type(v
)](v
, r
)
299 encode_func
[Bencached
] = encode_bencached
300 encode_func
[IntType
] = encode_int
301 encode_func
[LongType
] = encode_int
302 encode_func
[StringType
] = encode_string
303 encode_func
[ListType
] = encode_list
304 encode_func
[TupleType
] = encode_list
305 encode_func
[DictType
] = encode_dict
308 from types
import BooleanType
309 encode_func
[BooleanType
] = encode_bool
315 encode_func
[type(x
)](x
, r
)