]>
Commit | Line | Data |
---|---|---|
81675874 | 1 | #!/usr/bin/python |
2 | ||
6b952290 | 3 | import datetime |
81675874 | 4 | import httplib |
6b952290 | 5 | import mimetypes |
e2b0b0e4 | 6 | import operator |
81675874 | 7 | import os |
d0d074e0 | 8 | import re |
81675874 | 9 | import simplejson |
6b952290 | 10 | import stat |
e2b0b0e4 | 11 | import sqlite3 |
81675874 | 12 | import time |
d0d074e0 | 13 | import unicodedata |
81675874 | 14 | import urlparse |
15 | ||
d0d074e0 | 16 | import tornado.database |
81675874 | 17 | import tornado.httpclient |
18 | import tornado.locale | |
19 | import tornado.web | |
20 | ||
21 | from banners import banners | |
e48d9ae6 | 22 | from helpers import size, Item |
d979645d | 23 | from info import info |
3add293a | 24 | from mirrors import mirrors |
81675874 | 25 | from news import news |
26 | from releases import releases | |
43d991f6 | 27 | from torrent import tracker, bencode, bdecode, decode_hex |
81675874 | 28 | |
29 | import builds | |
5a1018ab | 30 | import menu |
81675874 | 31 | import cluster |
d0d074e0 | 32 | import markdown |
81675874 | 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) | |
53f812d2 | 60 | nargs["hostname"] = self.request.host |
81675874 | 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 | ||
4cb3de1c MT |
81 | @property |
82 | def hash_db(self): | |
83 | return self.application.hash_db | |
81675874 | 84 | |
d0d074e0 MT |
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 | ||
81675874 | 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 | ||
3add293a MT |
143 | class DownloadMirrorHandler(BaseHandler): |
144 | def get(self): | |
145 | self.render("downloads-mirrors.html", mirrors=mirrors) | |
146 | ||
147 | ||
81675874 | 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 | ||
81675874 | 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(): | |
2e4c4605 | 189 | if (time.time() - float(build.get("date"))) < 12*60*60: |
81675874 | 190 | self.builds["<12h"].append(build) |
2e4c4605 | 191 | elif (time.time() - float(build.get("date"))) < 24*60*60: |
81675874 | 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 | ||
e2b0b0e4 | 212 | class SourceHandler(BaseHandler): |
e2b0b0e4 MT |
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 | ||
8ef0879a | 224 | hash = self.hash_db.get_hash(os.path.join(dir, file)) |
e2b0b0e4 | 225 | |
4cb3de1c | 226 | if not hash: |
e2b0b0e4 MT |
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 | ||
e2b0b0e4 MT |
236 | fileobjects.sort(key=operator.itemgetter("name")) |
237 | ||
238 | self.render("sources.html", files=fileobjects) | |
6b952290 MT |
239 | |
240 | ||
241 | class SourceDownloadHandler(BaseHandler): | |
6b952290 MT |
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 | ||
4cb3de1c | 265 | hash = self.hash_db.get_hash(path) |
6b952290 MT |
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() | |
3add293a MT |
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) | |
e48d9ae6 MT |
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) | |
43d991f6 MT |
305 | |
306 | ||
01f86827 | 307 | <<<<<<< HEAD |
43d991f6 | 308 | class TrackerBaseHandler(tornado.web.RequestHandler): |
e2afbd6a | 309 | def get_hexencoded_argument(self, name, all=False): |
43d991f6 | 310 | try: |
e2afbd6a | 311 | arguments = self.request.arguments[name] |
43d991f6 MT |
312 | except KeyError: |
313 | return None | |
314 | ||
e2afbd6a MT |
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] | |
43d991f6 MT |
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 | ||
43d991f6 MT |
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 | ||
e2afbd6a | 377 | no_peer_id = self.get_argument("no_peer_id", False) |
43d991f6 MT |
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, | |
e2afbd6a MT |
384 | "peers" : tracker.get_peers(info_hash, limit=numwant, |
385 | random=True, no_peer_id=no_peer_id), | |
43d991f6 MT |
386 | "complete" : tracker.complete(info_hash), |
387 | "incomplete" : tracker.incomplete(info_hash), | |
388 | })) | |
389 | self.finish() | |
e2afbd6a MT |
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() | |
01f86827 | 398 | ======= |
d0d074e0 MT |
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) | |
01f86827 | 448 | >>>>>>> planet |