1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
# -*- coding: utf-8 -*-
import json
import logging
import os
import re
try:
from functools import lru_cache
except ImportError:
from ..plugins.lru_cache import lru_cache
import cherrypy
from cherrypy.lib.static import serve_file
from .. import mgr
from . import BaseController, Endpoint, Proxy, Router, UIRouter
logger = logging.getLogger("controllers.home")
class LanguageMixin(object):
def __init__(self):
try:
self.LANGUAGES = {
f
for f in os.listdir(mgr.get_frontend_path())
if os.path.isdir(os.path.join(mgr.get_frontend_path(), f))
}
except FileNotFoundError:
logger.exception("Build directory missing")
self.LANGUAGES = {}
self.LANGUAGES_PATH_MAP = {
f.lower(): {
'lang': f,
'path': os.path.join(mgr.get_frontend_path(), f)
}
for f in self.LANGUAGES
}
# pre-populating with the primary language subtag.
for lang in list(self.LANGUAGES_PATH_MAP.keys()):
if '-' in lang:
self.LANGUAGES_PATH_MAP[lang.split('-')[0]] = {
'lang': self.LANGUAGES_PATH_MAP[lang]['lang'],
'path': self.LANGUAGES_PATH_MAP[lang]['path']
}
with open(os.path.normpath("{}/../package.json".format(mgr.get_frontend_path())),
"r") as f:
config = json.load(f)
self.DEFAULT_LANGUAGE = config['config']['locale']
self.DEFAULT_LANGUAGE_PATH = os.path.join(mgr.get_frontend_path(),
self.DEFAULT_LANGUAGE)
super().__init__()
@Router("/", secure=False)
class HomeController(BaseController, LanguageMixin):
LANG_TAG_SEQ_RE = re.compile(r'\s*([^,]+)\s*,?\s*')
LANG_TAG_RE = re.compile(
r'^(?P<locale>[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*|\*)(;q=(?P<weight>[01]\.\d{0,3}))?$')
MAX_ACCEPTED_LANGS = 10
@lru_cache()
def _parse_accept_language(self, accept_lang_header):
result = []
for i, m in enumerate(self.LANG_TAG_SEQ_RE.finditer(accept_lang_header)):
if i >= self.MAX_ACCEPTED_LANGS:
logger.debug("reached max accepted languages, skipping remaining")
break
tag_match = self.LANG_TAG_RE.match(m.group(1))
if tag_match is None:
raise cherrypy.HTTPError(400, "Malformed 'Accept-Language' header")
locale = tag_match.group('locale').lower()
weight = tag_match.group('weight')
if weight:
try:
ratio = float(weight)
except ValueError:
raise cherrypy.HTTPError(400, "Malformed 'Accept-Language' header")
else:
ratio = 1.0
result.append((locale, ratio))
result.sort(key=lambda l: l[0])
result.sort(key=lambda l: l[1], reverse=True)
logger.debug("language preference: %s", result)
return [r[0] for r in result]
def _language_dir(self, langs):
for lang in langs:
if lang in self.LANGUAGES_PATH_MAP:
logger.debug("found directory for language '%s'", lang)
cherrypy.response.headers[
'Content-Language'] = self.LANGUAGES_PATH_MAP[lang]['lang']
return self.LANGUAGES_PATH_MAP[lang]['path']
logger.debug("Languages '%s' not available, falling back to %s",
langs, self.DEFAULT_LANGUAGE)
cherrypy.response.headers['Content-Language'] = self.DEFAULT_LANGUAGE
return self.DEFAULT_LANGUAGE_PATH
@Proxy()
def __call__(self, path, **params):
if not path:
path = "index.html"
if 'cd-lang' in cherrypy.request.cookie:
langs = [cherrypy.request.cookie['cd-lang'].value.lower()]
logger.debug("frontend language from cookie: %s", langs)
else:
if 'Accept-Language' in cherrypy.request.headers:
accept_lang_header = cherrypy.request.headers['Accept-Language']
langs = self._parse_accept_language(accept_lang_header)
else:
langs = [self.DEFAULT_LANGUAGE.lower()]
logger.debug("frontend language from headers: %s", langs)
base_dir = self._language_dir(langs)
full_path = os.path.join(base_dir, path)
# Block uplevel attacks
if not os.path.normpath(full_path).startswith(os.path.normpath(base_dir)):
raise cherrypy.HTTPError(403) # Forbidden
logger.debug("serving static content: %s", full_path)
if 'Vary' in cherrypy.response.headers:
cherrypy.response.headers['Vary'] = "{}, Accept-Language"
else:
cherrypy.response.headers['Vary'] = "Accept-Language"
cherrypy.response.headers['Cache-control'] = "no-cache"
return serve_file(full_path)
@UIRouter("/langs", secure=False)
class LangsController(BaseController, LanguageMixin):
@Endpoint('GET')
def __call__(self):
return list(self.LANGUAGES)
@UIRouter("/login", secure=False)
class LoginController(BaseController):
@Endpoint('GET', 'custom_banner')
def __call__(self):
return mgr.get_store('custom_login_banner')
|