]> git.ipfire.org Git - ipfire.org.git/blame - src/web/base.py
talk: Drop old FreeSWITCH integration
[ipfire.org.git] / src / web / base.py
CommitLineData
940227cb
MT
1#!/usr/bin/python
2
cc3b928d 3import datetime
66862195 4import dateutil.parser
cfe7d74c 5import functools
11347e46 6import http.client
a69e87a1 7import ipaddress
47ed77ed 8import logging
940227cb
MT
9import time
10import tornado.locale
11import tornado.web
12
f110a9ff 13from ..decorators import *
a95c2f97 14from .. import util
60024cc8 15
372ef119
MT
16class ratelimit(object):
17 def __init__(self, minutes=15, requests=180):
18 self.minutes = minutes
19 self.requests = requests
20
21 def __call__(self, method):
22 @functools.wraps(method)
23 def wrapper(handler, *args, **kwargs):
24 # Pass the request to the rate limiter and get a request object
25 req = handler.backend.ratelimiter.handle_request(handler.request,
26 handler, minutes=self.minutes, limit=self.requests)
27
28 # If the rate limit has been reached, we won't allow
29 # processing the request and therefore send HTTP error code 429.
30 if req.is_ratelimited():
31 raise tornado.web.HTTPError(429, "Rate limit exceeded")
32
33 return method(handler, *args, **kwargs)
34
35 return wrapper
36
cfe7d74c 37
940227cb 38class BaseHandler(tornado.web.RequestHandler):
90199689
MT
39 def prepare(self):
40 # Mark this as private when someone is logged in
41 if self.current_user:
42 self.set_header("Cache-Control", "private")
43
44 # Always send Vary: Cookie
45 self.set_header("Vary", "Cookie")
46
f6ed3d4d
MT
47 def set_expires(self, seconds):
48 # For HTTP/1.1
b592d7c8 49 self.add_header("Cache-Control", "max-age=%s, must-revalidate" % seconds)
f6ed3d4d
MT
50
51 # For HTTP/1.0
52 expires = datetime.datetime.utcnow() + datetime.timedelta(seconds=seconds)
90199689 53 self.set_header("Expires", expires)
f6ed3d4d 54
37ed7c3c
MT
55 def write_error(self, status_code, **kwargs):
56 # Translate code into message
57 try:
11347e46 58 message = http.client.responses[status_code]
37ed7c3c
MT
59 except KeyError:
60 message = None
61
62 self.render("error.html", status_code=status_code, message=message, **kwargs)
63
4ca1a601
MT
64 @property
65 def hostname(self):
242d11d5
MT
66 # Return hostname in production
67 if self.request.host.endswith("ipfire.org"):
68 return self.request.host
69
4ca1a601 70 # Remove the development prefix
242d11d5
MT
71 subdomain, delimier, domain = self.request.host.partition(".")
72 if subdomain:
73 return "%s.ipfire.org" % subdomain
74
75 # Return whatever it is
76 return self.request.host
4ca1a601 77
bff08cb0
MT
78 def get_template_namespace(self):
79 ns = tornado.web.RequestHandler.get_template_namespace(self)
80
911064cf 81 now = datetime.date.today()
cc3b928d 82
bff08cb0 83 ns.update({
6c13ca2d 84 "backend" : self.backend,
5613b94b 85 "debug" : self.application.settings.get("debug", False),
a95c2f97
MT
86 "format_size" : util.format_size,
87 "format_time" : util.format_time,
4ca1a601 88 "hostname" : self.hostname,
911064cf
MT
89 "now" : now,
90 "year" : now.year,
bff08cb0 91 })
940227cb 92
bff08cb0 93 return ns
940227cb 94
9068dba1
MT
95 def get_remote_ip(self):
96 # Fix for clients behind a proxy that sends "X-Forwarded-For".
66862195 97 remote_ips = self.request.remote_ip.split(", ")
494d80e6 98
66862195
MT
99 for remote_ip in remote_ips:
100 try:
a69e87a1 101 addr = ipaddress.ip_address(remote_ip)
66862195
MT
102 except ValueError:
103 # Skip invalid IP addresses.
104 continue
9068dba1 105
66862195
MT
106 # Check if the given IP address is from a
107 # private network.
108 if addr.is_private:
109 continue
9068dba1 110
66862195 111 return remote_ip
9068dba1 112
494d80e6
MT
113 # Return the last IP if nothing else worked
114 return remote_ips.pop()
115
cfe7d74c 116 @lazy_property
440aba92 117 def current_address(self):
cfe7d74c
MT
118 address = self.get_remote_ip()
119
120 if address:
440aba92 121 return util.Address(self.backend, address)
cfe7d74c 122
f110a9ff
MT
123 @lazy_property
124 def current_country_code(self):
440aba92
MT
125 if self.current_address:
126 return self.current_address.country_code
9068dba1 127
bd2723d4
MT
128 def get_argument_int(self, *args, **kwargs):
129 arg = self.get_argument(*args, **kwargs)
130
131 if arg is None or arg == "":
132 return
133
134 try:
135 return int(arg)
136 except ValueError:
a5f94966
MT
137 raise tornado.web.HTTPError(400, "Could not convert integer: %s" % arg)
138
139 def get_argument_float(self, *args, **kwargs):
140 arg = self.get_argument(*args, **kwargs)
141
142 if arg is None or arg == "":
143 return
144
145 try:
146 return float(arg)
147 except ValueError:
148 raise tornado.web.HTTPError(400, "Could not convert float: %s" % arg)
bd2723d4 149
66862195
MT
150 def get_argument_date(self, arg, *args, **kwargs):
151 value = self.get_argument(arg, *args, **kwargs)
152 if value is None:
153 return
154
155 try:
156 return dateutil.parser.parse(value)
157 except ValueError:
158 raise tornado.web.HTTPError(400)
159
5cc10421
MT
160 def get_file(self, name):
161 try:
162 file = self.request.files[name][0]
163
164 return file["filename"], file["body"], file["content_type"]
165 except KeyError:
166 return None
167
66862195
MT
168 # Login stuff
169
170 def get_current_user(self):
171 session_id = self.get_cookie("session_id")
172 if not session_id:
173 return
174
175 # Get account from the session object
176 account = self.backend.accounts.get_by_session(session_id, self.request.host)
177
178 # If the account was not found or the session was not valid
179 # any more, we will remove the cookie.
180 if not account:
181 self.clear_cookie("session_id")
182
183 return account
184
a6dc0bad
MT
185 @property
186 def backend(self):
187 return self.application.backend
188
9068dba1
MT
189 @property
190 def db(self):
191 return self.backend.db
192
940227cb
MT
193 @property
194 def accounts(self):
a6dc0bad 195 return self.backend.accounts
940227cb
MT
196
197 @property
9068dba1
MT
198 def downloads(self):
199 return self.backend.downloads
200
66862195
MT
201 @property
202 def fireinfo(self):
203 return self.backend.fireinfo
204
9068dba1
MT
205 @property
206 def iuse(self):
207 return self.backend.iuse
940227cb 208
e28b082e
MT
209 @property
210 def memcached(self):
211 return self.backend.memcache
212
940227cb
MT
213 @property
214 def mirrors(self):
9068dba1
MT
215 return self.backend.mirrors
216
217 @property
218 def netboot(self):
219 return self.backend.netboot
940227cb 220
940227cb
MT
221 @property
222 def releases(self):
9068dba1 223 return self.backend.releases
940227cb 224
66862195 225
689effd0
MT
226class APIHandler(BaseHandler):
227 def check_xsrf_cookie(self):
228 """
229 Do nothing here, because we cannot verify the XSRF token
230 """
231 pass
232
233
3403dc5e
MT
234class NotFoundHandler(BaseHandler):
235 def prepare(self):
236 # Raises 404 as soon as it is called
237 raise tornado.web.HTTPError(404)
238
3403dc5e 239
37ed7c3c
MT
240class ErrorHandler(BaseHandler):
241 """
242 Raises any error we want
243 """
244 def get(self, code):
245 try:
246 code = int(code)
247 except:
248 raise tornado.web.HTTPError(400)
249
250 raise tornado.web.HTTPError(code)