]>
Commit | Line | Data |
---|---|---|
940227cb MT |
1 | #!/usr/bin/python |
2 | ||
9068dba1 | 3 | import re |
940227cb MT |
4 | import tornado.web |
5 | ||
f1349ebc | 6 | from backend.tracker import bencode, bdecode, encode_hex, decode_hex |
940227cb MT |
7 | from handlers_base import * |
8 | ||
9 | ||
10 | class TrackerIndexHandler(BaseHandler): | |
11 | def get(self): | |
938d083d | 12 | releases = self.releases.get_all() |
940227cb | 13 | |
938d083d MT |
14 | limit = 5 |
15 | releases = releases[:limit] | |
940227cb | 16 | |
938d083d | 17 | self.render("tracker-torrents.html", releases=releases) |
940227cb MT |
18 | |
19 | ||
20 | class TrackerDetailHandler(BaseHandler): | |
938d083d MT |
21 | def get(self, torrent_hash): |
22 | file = self.releases.get_file_for_torrent_hash(torrent_hash) | |
940227cb | 23 | |
938d083d MT |
24 | if not file: |
25 | raise tornado.web.HTTPError(404, "Could not find torrent file for hash: %s" % torrent_hash) | |
940227cb | 26 | |
938d083d MT |
27 | peers = self.tracker.get_peers(torrent_hash) |
28 | seeds = self.tracker.get_seeds(torrent_hash) | |
940227cb | 29 | |
938d083d MT |
30 | self.render("tracker-torrent-detail.html", release=file.release, |
31 | file=file, peers=peers, seeds=seeds) | |
940227cb MT |
32 | |
33 | ||
fadcfd00 MT |
34 | class TrackerDownloadHandler(BaseHandler): |
35 | def get(self, torrent_hash): | |
938d083d | 36 | file = self.releases.get_file_for_torrent_hash(torrent_hash) |
fadcfd00 MT |
37 | |
38 | if not file: | |
938d083d | 39 | raise tornado.web.HTTPError(404, "Could not find torrent file for hash: %s" % torrent_hash) |
fadcfd00 MT |
40 | |
41 | # Redirect the user to the download redirector. | |
938d083d | 42 | self.redirect("http://downloads.ipfire.org/%s.torrent" % file.filename) |
fadcfd00 MT |
43 | |
44 | ||
9068dba1 | 45 | class TrackerBaseHandler(BaseHandler): |
940227cb MT |
46 | def get_hexencoded_argument(self, name, all=False): |
47 | try: | |
48 | arguments = self.request.arguments[name] | |
49 | except KeyError: | |
50 | return None | |
51 | ||
52 | arguments_new = [] | |
53 | for argument in arguments: | |
f1349ebc | 54 | arguments_new.append(encode_hex(argument)) |
940227cb MT |
55 | |
56 | arguments = arguments_new | |
57 | ||
58 | if all: | |
59 | return arguments | |
60 | ||
61 | return arguments[0] | |
62 | ||
63 | def send_tracker_error(self, error_message): | |
9068dba1 MT |
64 | msg = bencode({"failure reason" : error_message }) |
65 | self.finish(msg) | |
940227cb MT |
66 | |
67 | ||
68 | class TrackerAnnounceHandler(TrackerBaseHandler): | |
9068dba1 | 69 | def prepare(self): |
940227cb MT |
70 | self.set_header("Content-Type", "text/plain") |
71 | ||
9068dba1 MT |
72 | def get_ipv6_address(self, default_port): |
73 | # Get the external IP address of the client. | |
74 | addr = self.get_remote_ip() | |
75 | ||
76 | if ":" in addr: | |
77 | return addr, default_port | |
78 | ||
79 | # IPv6 | |
80 | ipv6 = self.get_argument("ipv6", None) | |
81 | if ipv6: | |
82 | port = default_port | |
83 | ||
84 | m = re.match("^\[(.*)\]\:(\d)$", ipv6) | |
85 | if m: | |
86 | ipv6, port = (m.group(1), m.group(2)) | |
87 | ||
88 | return ipv6, port | |
89 | ||
90 | return None, None | |
91 | ||
92 | def get_ipv4_address(self, default_port): | |
93 | # Get the external IP address of the client. | |
94 | addr = self.get_remote_ip() | |
95 | ||
ea324f48 MT |
96 | if addr.startswith("172.28.1."): |
97 | addr = "178.63.73.246" | |
98 | ||
9068dba1 MT |
99 | if not ":" in addr: |
100 | return addr, default_port | |
101 | ||
102 | # IPv4 | |
103 | ipv4 = self.get_argument("ipv4", None) | |
104 | if ipv4: | |
105 | return ipv4, default_port | |
106 | ||
107 | ip = self.get_argument("ip", None) | |
108 | if ip: | |
109 | return ip, default_port | |
110 | ||
111 | return None, None | |
112 | ||
113 | def get_port(self): | |
114 | # Get the port and check it for sanity | |
115 | port = self.get_argument("port", None) | |
116 | ||
117 | try: | |
118 | port = int(port) | |
119 | ||
120 | if port < 0 or port > 65535: | |
121 | raise ValueError | |
122 | except (TypeError, ValueError): | |
123 | port = None | |
124 | ||
125 | return port | |
126 | ||
127 | def get(self): | |
128 | # Get the info hash | |
940227cb MT |
129 | info_hash = self.get_hexencoded_argument("info_hash") |
130 | if not info_hash: | |
9068dba1 | 131 | self.send_tracker_error("Your client forgot to send your torrent's info_hash") |
940227cb MT |
132 | return |
133 | ||
9068dba1 MT |
134 | # Get the peer id |
135 | peer_id = self.get_hexencoded_argument("peer_id") | |
f611ae02 | 136 | |
9068dba1 MT |
137 | # Get the port and check it for sanity |
138 | port = self.get_port() | |
139 | if not port: | |
140 | self.send_tracker_error("Invalid port number or port number missing") | |
940227cb MT |
141 | return |
142 | ||
9068dba1 MT |
143 | addr_ipv6, port_ipv6 = self.get_ipv6_address(port) |
144 | addr_ipv4, port_ipv4 = self.get_ipv4_address(port) | |
940227cb | 145 | |
9068dba1 MT |
146 | # Handle events |
147 | event = self.get_argument("event", None) | |
148 | if event: | |
149 | if not event in ("started", "stopped", "completed"): | |
150 | self.send_tracker_error("Got unknown event") | |
940227cb MT |
151 | return |
152 | ||
08f85138 | 153 | self.tracker.handle_event(event, peer_id, info_hash) |
940227cb | 154 | |
c300b349 MT |
155 | if event == "stopped": |
156 | return | |
157 | ||
9068dba1 MT |
158 | peer_info = { |
159 | "address6" : addr_ipv6, | |
160 | "port6" : port_ipv6, | |
161 | "address4" : addr_ipv4, | |
162 | "port4" : port_ipv4, | |
163 | "downloaded" : self.get_argument("downloaded", 0), | |
164 | "uploaded" : self.get_argument("uploaded", 0), | |
165 | "left_data" : self.get_argument("left", 0), | |
166 | } | |
940227cb | 167 | |
9068dba1 | 168 | self.tracker.update_peer(peer_id, info_hash, **peer_info) |
940227cb MT |
169 | |
170 | no_peer_id = self.get_argument("no_peer_id", False) | |
171 | numwant = self.get_argument("numwant", self.tracker.numwant) | |
172 | ||
9068dba1 MT |
173 | peers = self.tracker.get_peers(info_hash, limit=numwant, no_peer_id=no_peer_id) |
174 | ||
175 | response = bencode({ | |
176 | "tracker id" : self.tracker.tracker_id, | |
177 | "interval" : self.tracker.interval, | |
940227cb | 178 | "min interval" : self.tracker.min_interval, |
9068dba1 MT |
179 | "peers" : peers, |
180 | "complete" : self.tracker.complete(info_hash), | |
181 | "incomplete" : self.tracker.incomplete(info_hash), | |
182 | }) | |
183 | self.finish(response) | |
184 | ||
185 | def on_finish(self): | |
186 | """ | |
187 | Cleanup after every request. | |
188 | """ | |
189 | self.tracker.cleanup_peers() | |
940227cb MT |
190 | |
191 | ||
192 | class TrackerScrapeHandler(TrackerBaseHandler): | |
193 | def get(self): | |
194 | info_hashes = self.get_hexencoded_argument("info_hash", all=True) | |
195 | ||
9068dba1 MT |
196 | response = self.tracker.scrape(info_hashes) |
197 | ||
198 | self.finish(bencode(response)) |