]>
git.ipfire.org Git - ipfire.org.git/blob - webapp/backend/tracker.py
80f38c0204d68b72d6fe7bcfbdf579da85f56f5c
3 from __future__
import division
7 from misc
import Object
10 return s
.encode("hex")
13 return s
.decode("hex")
16 class Tracker(Object
):
19 return self
.settings
.get("tracker_id", "TheIPFireTorrentTracker")
21 def _fuzzy_interval(self
, interval
, fuzz
=120):
22 return interval
- random
.randint(0, fuzz
)
26 return self
.settings
.get_int("tracker_interval", 3600)
30 return self
._fuzzy
_interval
(self
._interval
)
33 def min_interval(self
):
34 return self
.settings
.get_int("tracker_min_interval", self
._interval
// 2)
38 return self
.settings
.get_int("tracker_numwant", 50)
40 def get_peers(self
, info_hash
, limit
=None, random
=True, no_peer_id
=False, ipfamily
=None):
41 query
= "SELECT * FROM tracker WHERE last_update >= NOW() - INTERVAL '%ss'"
42 args
= [self
._interval
,]
45 query
+= " AND hash = %s"
46 args
.append(info_hash
)
49 query
+= " ORDER BY RANDOM()"
56 for row
in self
.db
.query(query
, *args
):
60 if row
.address6
and row
.port6
:
66 if row
.address4
and row
.port4
:
74 peer6
["peer id"] = decode_hex(row
.id)
77 peer4
["peer id"] = decode_hex(row
.id)
87 def cleanup_peers(self
):
89 Remove all peers that have timed out.
91 self
.db
.execute("DELETE FROM tracker \
92 WHERE last_update < NOW() - INTERVAL '%s s'", int(self
._interval
* 1.2))
94 def update_peer(self
, peer_id
, info_hash
, **kwargs
):
95 # Translate the location IP address
96 address4
= kwargs
.get("address4", None)
97 if address4
and address4
.startswith("172.28.1."):
99 "address4" : "178.63.73.246",
102 if self
.peer_exists(peer_id
, info_hash
):
103 self
.__update
_peer
(peer_id
, info_hash
, **kwargs
)
105 self
.__insert
_peer
(peer_id
, info_hash
, **kwargs
)
107 def complete(self
, info_hash
):
108 ret
= self
.db
.get("SELECT COUNT(*) AS c FROM tracker \
109 WHERE hash = %s AND left_data = 0", info_hash
)
114 def incomplete(self
, info_hash
):
115 ret
= self
.db
.get("SELECT COUNT(*) AS c FROM tracker \
116 WHERE hash = %s AND left_data > 0", info_hash
)
121 def handle_event(self
, event
, peer_id
, info_hash
, **kwargs
):
123 if event
== "stopped":
124 self
.remove_peer(peer_id
, info_hash
)
126 def peer_exists(self
, peer_id
, info_hash
):
127 ret
= self
.db
.get("SELECT COUNT(*) AS c FROM tracker \
128 WHERE id = %s AND hash = %s", peer_id
, info_hash
)
130 if ret
and ret
.c
> 0:
135 def __insert_peer(self
, peer_id
, info_hash
, address6
=None, port6
=None, address4
=None, port4
=None, left_data
=0, **kwargs
):
136 self
.db
.execute("INSERT INTO tracker(id, hash, address6, port6, address4, port4, left_data) \
137 VALUES(%s, %s, %s, %s, %s, %s, %s)", peer_id
, info_hash
, address6
, port6
, address4
, port4
, left_data
)
139 def __update_peer(self
, peer_id
, info_hash
, address6
=None, port6
=None,
140 address4
=None, port4
=None, downloaded
=None, uploaded
=None, left_data
=None):
141 query
= "UPDATE tracker SET last_update = NOW()"
145 query
+= ", address6 = %s"
146 args
.append(address6
)
149 query
+= ", port6 = %s"
153 query
+= ", address4 = %s"
154 args
.append(address4
)
157 query
+= ", port4 = %s"
161 query
+= ", downloaded = %s"
162 args
.append(downloaded
)
165 query
+= ", uploaded = %s"
166 args
.append(uploaded
)
169 query
+= ", left_data = %s"
170 args
.append(left_data
)
172 query
+= " WHERE id = %s AND hash = %s"
173 args
+= [peer_id
, info_hash
]
175 self
.db
.execute(query
, *args
)
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
)