]> git.ipfire.org Git - people/shoehn/ipfire.org.git/blob - www/webapp/handlers.py
Merge remote branch 'origin/tracker'
[people/shoehn/ipfire.org.git] / www / webapp / handlers.py
1 #!/usr/bin/python
2
3 import datetime
4 import httplib
5 import mimetypes
6 import operator
7 import os
8 import simplejson
9 import stat
10 import sqlite3
11 import time
12 import urlparse
13
14 import tornado.httpclient
15 import tornado.locale
16 import tornado.web
17
18 from banners import banners
19 from helpers import size, Item
20 from info import info
21 from mirrors import mirrors
22 from news import news
23 from releases import releases
24 from torrent import tracker, bencode, bdecode, decode_hex
25
26 import builds
27 import menu
28 #import uriel
29
30 class BaseHandler(tornado.web.RequestHandler):
31 def get_user_locale(self):
32 uri = self.request.uri.split("/")
33 if len(uri) > 1:
34 for lang in tornado.locale.get_supported_locales(None):
35 if lang[:2] == uri[1]:
36 return tornado.locale.get(lang)
37
38 @property
39 def render_args(self):
40 return {
41 "banner" : banners.get(),
42 "lang" : self.locale.code[:2],
43 "langs" : [l[:2] for l in tornado.locale.get_supported_locales(None)],
44 "lang_link" : self.lang_link,
45 "link" : self.link,
46 "title" : "no title given",
47 "server" : self.request.host.replace("ipfire", "<span>ipfire</span>"),
48 "uri" : self.request.uri,
49 "year" : time.strftime("%Y"),
50 }
51
52 def render(self, *args, **kwargs):
53 nargs = self.render_args
54 nargs.update(kwargs)
55 nargs["hostname"] = self.request.host
56 tornado.web.RequestHandler.render(self, *args, **nargs)
57
58 def link(self, s):
59 return "/%s/%s" % (self.locale.code[:2], s)
60
61 def lang_link(self, lang):
62 return "/%s/%s" % (lang, self.request.uri[4:])
63
64 def get_error_html(self, status_code, **kwargs):
65 if status_code in (404, 500):
66 render_args = self.render_args
67 render_args.update({
68 "code" : status_code,
69 "exception" : kwargs.get("exception", None),
70 "message" : httplib.responses[status_code],
71 })
72 return self.render_string("error-%s.html" % status_code, **render_args)
73 else:
74 return tornado.web.RequestHandler.get_error_html(self, status_code, **kwargs)
75
76 @property
77 def hash_db(self):
78 return self.application.hash_db
79
80 class MainHandler(BaseHandler):
81 def get(self):
82 lang = self.locale.code[:2]
83 self.redirect("/%s/index" % (lang))
84
85
86 class DownloadHandler(BaseHandler):
87 def get(self):
88 self.render("downloads.html", release=releases.latest)
89
90
91 class DownloadAllHandler(BaseHandler):
92 def get(self):
93 self.render("downloads-all.html", releases=releases)
94
95
96 class DownloadDevelopmentHandler(BaseHandler):
97 def get(self):
98 self.render("downloads-development.html", releases=releases)
99
100
101 class DownloadTorrentHandler(BaseHandler):
102 tracker_url = "http://tracker.ipfire.org:6969/stats?format=txt&mode=tpbs"
103
104 @tornado.web.asynchronous
105 def get(self):
106 http = tornado.httpclient.AsyncHTTPClient()
107 http.fetch(self.tracker_url, callback=self.async_callback(self.on_response))
108
109 def on_response(self, response):
110 torrents = releases.torrents
111 hashes = {}
112 if response.code == 200:
113 for line in response.body.split("\n"):
114 if not line: continue
115 hash, seeds, peers = line.split(":")
116 hash.lower()
117 hashes[hash] = {
118 "peers" : peers,
119 "seeds" : seeds,
120 }
121
122 self.render("downloads-torrents.html",
123 hashes=hashes,
124 releases=torrents,
125 request_time=response.request_time,
126 tracker=urlparse.urlparse(response.request.url).netloc)
127
128
129 class DownloadMirrorHandler(BaseHandler):
130 def get(self):
131 self.render("downloads-mirrors.html", mirrors=mirrors)
132
133
134 class StaticHandler(BaseHandler):
135 @property
136 def static_path(self):
137 return os.path.join(self.application.settings["template_path"], "static")
138
139 @property
140 def static_files(self):
141 ret = []
142 for filename in os.listdir(self.static_path):
143 if filename.endswith(".html"):
144 ret.append(filename)
145 return ret
146
147 def get(self, name=None):
148 name = "%s.html" % name
149
150 if not name in self.static_files:
151 raise tornado.web.HTTPError(404)
152
153 self.render("static/%s" % name)
154
155
156 class IndexHandler(BaseHandler):
157 def get(self):
158 self.render("index.html", news=news)
159
160
161 class NewsHandler(BaseHandler):
162 def get(self):
163 self.render("news.html", news=news)
164
165
166 class BuildHandler(BaseHandler):
167 def prepare(self):
168 self.builds = {
169 "<12h" : [],
170 ">12h" : [],
171 ">24h" : [],
172 }
173
174 for build in builds.find():
175 if (time.time() - float(build.get("date"))) < 12*60*60:
176 self.builds["<12h"].append(build)
177 elif (time.time() - float(build.get("date"))) < 24*60*60:
178 self.builds[">12h"].append(build)
179 else:
180 self.builds[">24h"].append(build)
181
182 for l in self.builds.values():
183 l.sort()
184
185 def get(self):
186 self.render("builds.html", builds=self.builds)
187
188
189 class UrielBaseHandler(BaseHandler):
190 #db = uriel.Database()
191 pass
192
193 class UrielHandler(UrielBaseHandler):
194 def get(self):
195 pass
196
197
198 class SourceHandler(BaseHandler):
199 def get(self):
200 source_path = "/srv/sources"
201 fileobjects = []
202
203 for dir, subdirs, files in os.walk(source_path):
204 if not files:
205 continue
206 for file in files:
207 if file in [f["name"] for f in fileobjects]:
208 continue
209
210 hash = self.hash_db.get_hash(os.path.join(dir, file))
211
212 if not hash:
213 hash = "0000000000000000000000000000000000000000"
214
215 fileobjects.append({
216 "dir" : dir[len(source_path)+1:],
217 "name" : file,
218 "hash" : hash,
219 "size" : size(os.path.getsize(os.path.join(source_path, dir, file))),
220 })
221
222 fileobjects.sort(key=operator.itemgetter("name"))
223
224 self.render("sources.html", files=fileobjects)
225
226
227 class SourceDownloadHandler(BaseHandler):
228 def head(self, path):
229 self.get(path, include_body=False)
230
231 def get(self, path, include_body=True):
232 source_path = "/srv/sources"
233
234 path = os.path.abspath(os.path.join(source_path, path[1:]))
235
236 if not path.startswith(source_path):
237 raise tornado.web.HTTPError(403)
238 if not os.path.exists(path):
239 raise tornado.web.HTTPError(404)
240
241 stat_result = os.stat(path)
242 modified = datetime.datetime.fromtimestamp(stat_result[stat.ST_MTIME])
243
244 self.set_header("Last-Modified", modified)
245 self.set_header("Content-Length", stat_result[stat.ST_SIZE])
246
247 mime_type, encoding = mimetypes.guess_type(path)
248 if mime_type:
249 self.set_header("Content-Type", mime_type)
250
251 hash = self.hash_db.get_hash(path)
252 if hash:
253 self.set_header("X-Hash-Sha1", "%s" % hash)
254
255 if not include_body:
256 return
257 file = open(path, "r")
258 try:
259 self.write(file.read())
260 finally:
261 file.close()
262
263
264 class DownloadFileHandler(BaseHandler):
265 def get(self, path):
266 for mirror in mirrors.with_file(path):
267 if not mirror.reachable:
268 continue
269
270 self.redirect(mirror.url + path)
271 return
272
273 raise tornado.web.HTTPError(404)
274
275 def get_error_html(self, status_code, **kwargs):
276 return tornado.web.RequestHandler.get_error_html(self, status_code, **kwargs)
277
278
279 class RSSHandler(BaseHandler):
280 def get(self, lang):
281 items = []
282 for item in news.get(15):
283 item = Item(**item.args.copy())
284 for attr in ("subject", "content"):
285 if type(item[attr]) == type({}):
286 item[attr] = item[attr][lang]
287 items.append(item)
288
289 self.set_header("Content-Type", "application/rss+xml")
290 self.render("rss.xml", items=items, lang=lang)
291
292
293 class TrackerBaseHandler(tornado.web.RequestHandler):
294 def get_hexencoded_argument(self, name, all=False):
295 try:
296 arguments = self.request.arguments[name]
297 except KeyError:
298 return None
299
300 arguments_new = []
301 for argument in arguments:
302 arguments_new.append(decode_hex(argument))
303
304 arguments = arguments_new
305
306 if all:
307 return arguments
308
309 return arguments[0]
310
311 def send_tracker_error(self, error_message):
312 self.write(bencode({"failure reason" : error_message }))
313 self.finish()
314
315 class TrackerAnnounceHandler(TrackerBaseHandler):
316 def get(self):
317 self.set_header("Content-Type", "text/plain")
318
319 info_hash = self.get_hexencoded_argument("info_hash")
320 if not info_hash:
321 self.send_tracker_error("Your client forgot to send your torrent's info_hash.")
322 return
323
324 peer = {
325 "id" : self.get_hexencoded_argument("peer_id"),
326 "ip" : self.get_argument("ip", None),
327 "port" : self.get_argument("port", None),
328 "downloaded" : self.get_argument("downloaded", 0),
329 "uploaded" : self.get_argument("uploaded", 0),
330 "left" : self.get_argument("left", 0),
331 }
332
333 event = self.get_argument("event", "")
334 if not event in ("started", "stopped", "completed", ""):
335 self.send_tracker_error("Got unknown event")
336 return
337
338 if peer["ip"]:
339 if peer["ip"].startswith("10.") or \
340 peer["ip"].startswith("172.") or \
341 peer["ip"].startswith("192.168."):
342 peer["ip"] = self.request.remote_ip
343
344 if peer["port"]:
345 peer["port"] = int(peer["port"])
346
347 if peer["port"] < 0 or peer["port"] > 65535:
348 self.send_tracker_error("Port number is not in valid range")
349 return
350
351 eventhandlers = {
352 "started" : tracker.event_started,
353 "stopped" : tracker.event_stopped,
354 "completed" : tracker.event_completed,
355 }
356
357 if event:
358 eventhandlers[event](info_hash, peer["id"])
359
360 tracker.update(hash=info_hash, **peer)
361
362 no_peer_id = self.get_argument("no_peer_id", False)
363 numwant = self.get_argument("numwant", tracker.numwant)
364
365 self.write(bencode({
366 "tracker id" : tracker.id,
367 "interval" : tracker.interval,
368 "min interval" : tracker.min_interval,
369 "peers" : tracker.get_peers(info_hash, limit=numwant,
370 random=True, no_peer_id=no_peer_id),
371 "complete" : tracker.complete(info_hash),
372 "incomplete" : tracker.incomplete(info_hash),
373 }))
374 self.finish()
375
376
377 class TrackerScrapeHandler(TrackerBaseHandler):
378 def get(self):
379 info_hashes = self.get_hexencoded_argument("info_hash", all=True)
380
381 self.write(bencode(tracker.scrape(hashes=info_hashes)))
382 self.finish()