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