]> git.ipfire.org Git - ipfire.org.git/blame - www/webapp/datastore/tracker.py
Create a global webapp database connection and create a config class.
[ipfire.org.git] / www / webapp / datastore / tracker.py
CommitLineData
43d991f6
MT
1#!/usr/bin/python
2
3import time
4
43d991f6
MT
5def decode_hex(s):
6 ret = []
7 for c in s:
8 for i in range(256):
9 if not c == chr(i):
10 continue
11
12 ret.append("%0x" % i)
13
14 return "".join(ret)
15
16class Tracker(object):
17 id = "The IPFire Torrent Tracker"
18
19 # Intervals
20 interval = 60*60
21 min_interval = 30*60
22
23 numwant = 50
24
feb02477
MT
25 def __init__(self, application):
26 self.application = application
27
28 @property
29 def db(self):
30 return self.application.db.tracker
43d991f6 31
e2afbd6a 32 def _fetch(self, hash, limit=None, random=False, completed=False, no_peer_id=False):
43d991f6
MT
33 query = "SELECT * FROM peers WHERE last_update >= %d" % self.since
34
35 if hash:
36 query += " AND hash = '%s'" % hash
37
38 if completed:
39 query += " AND left_data = 0"
40
41 if random:
42 query += " ORDER BY RAND()"
43
44 if limit:
45 query += " LIMIT %s" % limit
46
47 peers = []
48 for peer in self.db.query(query):
49 if not peer.ip or not peer.port:
50 continue
51
e2afbd6a 52 peer_dict = {
43d991f6
MT
53 "ip" : str(peer.ip),
54 "port" : int(peer.port),
e2afbd6a
MT
55 }
56
57 if not no_peer_id:
58 peer_dict["peer id"] = str(peer.id),
59
60 peers.append(peer_dict)
43d991f6
MT
61
62 return peers
63
64 def get_peers(self, hash, **kwargs):
65 return self._fetch(hash, **kwargs)
66
67 def get_seeds(self, hash, **kwargs):
68 kwargs.update({"completed" : True})
69 return self._fetch(hash, **kwargs)
70
71 def complete(self, hash):
72 return len(self.get_seeds(hash))
73
74 def incomplete(self, hash):
75 return len(self.get_peers(hash))
76
77 def event_started(self, hash, peer_id):
78 # Damn, mysql does not support INSERT IF NOT EXISTS...
79 if not self.db.query("SELECT id FROM peers WHERE hash = '%s' AND peer_id = '%s'" % (hash, peer_id)):
80 self.db.execute("INSERT INTO peers(hash, peer_id) VALUES('%s', '%s')" % (hash, peer_id))
81
82 if not hash in [h["hash"] for h in self.hashes]:
83 self.db.execute("INSERT INTO hashes(hash) VALUES('%s')" % hash)
84
85 def event_stopped(self, hash, peer_id):
86 self.db.execute("DELETE FROM peers WHERE hash = '%s' AND peer_id = '%s'" % (hash, peer_id))
87
88 def event_completed(self, hash, peer_id):
89 self.db.execute("UPDATE hashes SET completed=completed+1 WHERE hash = '%s'" % hash)
90
e2afbd6a
MT
91 def scrape(self, hashes=[]):
92 ret = {}
93 for hash in self.db.query("SELECT hash, completed FROM hashes"):
94 hash, completed = hash.hash, hash.completed
95
96 if hashes and hash not in hashes:
97 continue
98
99 ret[hash] = {
100 "complete" : self.complete(hash),
101 "downloaded" : completed or 0,
102 "incomplete" : self.incomplete(hash),
103 }
104
105 return ret
106
43d991f6
MT
107 def update(self, hash, id, ip=None, port=None, downloaded=None, uploaded=None, left=None):
108 args = [ "last_update = '%s'" % self.now ]
109
110 if ip:
111 args.append("ip='%s'" % ip)
112
113 if port:
114 args.append("port='%s'" % port)
115
116 if downloaded:
117 args.append("downloaded='%s'" % downloaded)
118
119 if uploaded:
120 args.append("uploaded='%s'" % uploaded)
121
122 if left:
123 args.append("left_data='%s'" % left)
124
125 if not args:
126 return
127
128 query = "UPDATE peers SET " + ", ".join(args) + \
129 " WHERE hash = '%s' AND peer_id = '%s'" % (hash, id)
130
131 self.db.execute(query)
132
133 @property
134 def hashes(self):
135 return self.db.query("SELECT * FROM hashes");
136
137 @property
138 def now(self):
139 return int(time.time())
140
141 @property
142 def since(self):
143 return int(time.time() - self.interval)
144
145
43d991f6
MT
146##### This is borrowed from the bittorrent client libary #####
147
148def decode_int(x, f):
149 f += 1
150 newf = x.index('e', f)
151 n = int(x[f:newf])
152 if x[f] == '-':
153 if x[f + 1] == '0':
154 raise ValueError
155 elif x[f] == '0' and newf != f+1:
156 raise ValueError
157 return (n, newf+1)
158
159def decode_string(x, f):
160 colon = x.index(':', f)
161 n = int(x[f:colon])
162 if x[f] == '0' and colon != f+1:
163 raise ValueError
164 colon += 1
165 return (x[colon:colon+n], colon+n)
166
167def decode_list(x, f):
168 r, f = [], f+1
169 while x[f] != 'e':
170 v, f = decode_func[x[f]](x, f)
171 r.append(v)
172 return (r, f + 1)
173
174def decode_dict(x, f):
175 r, f = {}, f+1
176 while x[f] != 'e':
177 k, f = decode_string(x, f)
178 r[k], f = decode_func[x[f]](x, f)
179 return (r, f + 1)
180
181decode_func = {}
182decode_func['l'] = decode_list
183decode_func['d'] = decode_dict
184decode_func['i'] = decode_int
185decode_func['0'] = decode_string
186decode_func['1'] = decode_string
187decode_func['2'] = decode_string
188decode_func['3'] = decode_string
189decode_func['4'] = decode_string
190decode_func['5'] = decode_string
191decode_func['6'] = decode_string
192decode_func['7'] = decode_string
193decode_func['8'] = decode_string
194decode_func['9'] = decode_string
195
196def bdecode(x):
197 try:
198 r, l = decode_func[x[0]](x, 0)
199 except (IndexError, KeyError, ValueError):
200 raise Exception("not a valid bencoded string")
201 if l != len(x):
202 raise Exception("invalid bencoded value (data after valid prefix)")
203 return r
204
205from types import StringType, IntType, LongType, DictType, ListType, TupleType
206
207
208class Bencached(object):
209
210 __slots__ = ['bencoded']
211
212 def __init__(self, s):
213 self.bencoded = s
214
215def encode_bencached(x,r):
216 r.append(x.bencoded)
217
218def encode_int(x, r):
219 r.extend(('i', str(x), 'e'))
220
221def encode_bool(x, r):
222 if x:
223 encode_int(1, r)
224 else:
225 encode_int(0, r)
226
227def encode_string(x, r):
228 r.extend((str(len(x)), ':', x))
229
230def encode_list(x, r):
231 r.append('l')
232 for i in x:
233 encode_func[type(i)](i, r)
234 r.append('e')
235
236def encode_dict(x,r):
237 r.append('d')
238 ilist = x.items()
239 ilist.sort()
240 for k, v in ilist:
241 r.extend((str(len(k)), ':', k))
242 encode_func[type(v)](v, r)
243 r.append('e')
244
245encode_func = {}
246encode_func[Bencached] = encode_bencached
247encode_func[IntType] = encode_int
248encode_func[LongType] = encode_int
249encode_func[StringType] = encode_string
250encode_func[ListType] = encode_list
251encode_func[TupleType] = encode_list
252encode_func[DictType] = encode_dict
253
254try:
255 from types import BooleanType
256 encode_func[BooleanType] = encode_bool
257except ImportError:
258 pass
259
260def bencode(x):
261 r = []
262 encode_func[type(x)](x, r)
263 return ''.join(r)