Mercurial > libervia-web
comparison twisted/plugins/libervia_server.py @ 1124:28e3eb3bb217
files reorganisation and installation rework:
- files have been reorganised to follow other SàT projects and usual Python organisation (no more "/src" directory)
- VERSION file is now used, as for other SàT projects
- replace the overcomplicated setup.py be a more sane one. Pyjamas part is not compiled anymore by setup.py, it must be done separatly
- removed check for data_dir if it's empty
- installation tested working in virtual env
- libervia launching script is now in bin/libervia
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 25 Aug 2018 17:59:48 +0200 |
parents | src/twisted/plugins/libervia_server.py@d2036b2db6be |
children | 56ace2d45783 |
comparison
equal
deleted
inserted
replaced
1123:63a4b8fe9782 | 1124:28e3eb3bb217 |
---|---|
1 #!/usr/bin/python | |
2 # -*- coding: utf-8 -*- | |
3 | |
4 # Libervia: a Salut à Toi frontend | |
5 # Copyright (C) 2013-2018 Jérôme Poisson <goffi@goffi.org> | |
6 # Copyright (C) 2013-2016 Adrien Cossa <souliane@mailoo.org> | |
7 # Copyright (C) 2013 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> | |
8 | |
9 # This program is free software: you can redistribute it and/or modify | |
10 # it under the terms of the GNU Affero General Public License as published by | |
11 # the Free Software Foundation, either version 3 of the License, or | |
12 # (at your option) any later version. | |
13 | |
14 # This program is distributed in the hope that it will be useful, | |
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 # GNU Affero General Public License for more details. | |
18 | |
19 # You should have received a copy of the GNU Affero General Public License | |
20 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
21 | |
22 from twisted.internet import defer | |
23 | |
24 if defer.Deferred.debug: | |
25 # if we are in debug mode, we want to use ipdb instead of pdb | |
26 try: | |
27 import ipdb | |
28 import pdb | |
29 | |
30 pdb.set_trace = ipdb.set_trace | |
31 pdb.post_mortem = ipdb.post_mortem | |
32 except ImportError: | |
33 pass | |
34 | |
35 import os.path | |
36 import libervia | |
37 import sat | |
38 | |
39 from libervia.server.constants import Const as C | |
40 | |
41 from sat.core.i18n import _ | |
42 from sat.tools import config | |
43 | |
44 from zope.interface import implements | |
45 | |
46 from twisted.python import usage | |
47 from twisted.plugin import IPlugin | |
48 from twisted.application.service import IServiceMaker | |
49 import ConfigParser | |
50 | |
51 | |
52 CONFIG_SECTION = C.APP_NAME.lower() | |
53 if libervia.__version__ != sat.__version__: | |
54 import sys | |
55 | |
56 sys.stderr.write( | |
57 u"""sat module version ({sat_version}) and {current_app} version ({current_version}) mismatch | |
58 | |
59 sat module is located at {sat_path} | |
60 libervia module is located at {libervia_path} | |
61 | |
62 Please be sure to have the same version running | |
63 """.format( | |
64 sat_version=sat.__version__, | |
65 current_app=C.APP_NAME, | |
66 current_version=libervia.__version__, | |
67 sat_path=os.path.dirname(sat.__file__), | |
68 libervia_path=os.path.dirname(libervia.__file__), | |
69 ).encode( | |
70 "utf-8" | |
71 ) | |
72 ) | |
73 sys.stderr.flush() | |
74 # we call os._exit to avoid help to be printed by twisted | |
75 import os | |
76 | |
77 os._exit(1) | |
78 | |
79 | |
80 def coerceConnectionType(value): # called from Libervia.OPT_PARAMETERS | |
81 allowed_values = ("http", "https", "both") | |
82 if value not in allowed_values: | |
83 raise ValueError( | |
84 "%(given)s not in %(expected)s" | |
85 % {"given": value, "expected": str(allowed_values)} | |
86 ) | |
87 return value | |
88 | |
89 | |
90 def coerceDataDir(value): # called from Libervia.OPT_PARAMETERS | |
91 if not value: | |
92 # we ignore missing values | |
93 return u'' | |
94 if isinstance(value, unicode): | |
95 # XXX: if value comes from sat.conf, it's unicode, | |
96 # and we need byte str here (for twisted) | |
97 value = value.encode("utf-8") | |
98 value = value.encode("utf-8") | |
99 html = os.path.join(value, C.HTML_DIR) | |
100 if not os.path.isfile(os.path.join(html, C.LIBERVIA_MAIN_PAGE)): | |
101 raise ValueError( | |
102 "%s is not a Libervia's browser HTML directory" % os.path.realpath(html) | |
103 ) | |
104 themes_dir = os.path.join(value, C.THEMES_DIR) | |
105 if not os.path.isfile(os.path.join(themes_dir, "default/styles/blog.css")): | |
106 raise ValueError( | |
107 "%s is not a Libervia's server data directory" % os.path.realpath(themes_dir) | |
108 ) | |
109 return value | |
110 | |
111 | |
112 def coerceBool(value): | |
113 return C.bool(value) | |
114 | |
115 | |
116 def coerceUnicode(value): | |
117 # XXX: we use this method to check which value to convert to Unicode | |
118 # but we don't do the conversion here as Twisted expect str | |
119 return value | |
120 | |
121 | |
122 DATA_DIR_DEFAULT = '' | |
123 # options which are in sat.conf and on command line, | |
124 # see https://twistedmatrix.com/documents/current/api/twisted.python.usage.Options.html | |
125 OPT_PARAMETERS_BOTH = [['connection_type', 't', 'https', _(u"'http', 'https' or 'both' " | |
126 "(to launch both servers).").encode('utf-8'), | |
127 coerceConnectionType], | |
128 ['port', 'p', 8080, | |
129 _(u'The port number to listen HTTP on.').encode('utf-8'), int], | |
130 ['port_https', 's', 8443, | |
131 _(u'The port number to listen HTTPS on.').encode('utf-8'), int], | |
132 ['port_https_ext', 'e', 0, _(u'The external port number used for ' | |
133 u'HTTPS (0 means port_https value).').encode('utf-8'), int], | |
134 ['tls_private_key', '', '', _(u'TLS certificate private key (PEM ' | |
135 u'format)').encode('utf-8'), coerceUnicode], | |
136 ['tls_certificate', 'c', 'libervia.pem', _(u'TLS public ' | |
137 u'certificate or private key and public certificate combined ' | |
138 u'(PEM format)').encode('utf-8'), coerceUnicode], | |
139 ['tls_chain', '', '', _(u'TLS certificate intermediate chain (PEM ' | |
140 u'format)').encode('utf-8'), coerceUnicode], | |
141 ['redirect_to_https', 'r', True, _(u'Automatically redirect from ' | |
142 u'HTTP to HTTPS.').encode('utf-8'), coerceBool], | |
143 ['security_warning', 'w', True, _(u'Warn user that he is about to ' | |
144 u'connect on HTTP.').encode('utf-8'), coerceBool], | |
145 ['passphrase', 'k', '', (_(u"Passphrase for the SàT profile " | |
146 u"named '%s'") % C.SERVICE_PROFILE).encode('utf-8'), | |
147 coerceUnicode], | |
148 ['data_dir', 'd', DATA_DIR_DEFAULT, _(u'Data directory for ' | |
149 u'Libervia legacy').encode('utf-8'), coerceDataDir], | |
150 ['allow_registration', '', True, _(u'Allow user to register new ' | |
151 u'account').encode('utf-8'), coerceBool], | |
152 ['base_url_ext', '', '', | |
153 _(u'The external URL to use as base URL').encode('utf-8'), | |
154 coerceUnicode], | |
155 ] | |
156 # Options which are in sat.conf only | |
157 OPT_PARAMETERS_CFG = [ | |
158 ["empty_password_allowed_warning_dangerous_list", None, "", None], | |
159 ["url_redirections_profile", None, "", None], | |
160 ["url_redirections_dict", None, {}, None], | |
161 ["menu_json", None, C.DEFAULT_MENU, None], | |
162 ["tickets_trackers_json", None, None, None], | |
163 ["mr_handlers_json", None, None, None], | |
164 ] | |
165 | |
166 | |
167 def initialise(options): | |
168 """Method to initialise global modules""" | |
169 from twisted.internet import gireactor | |
170 gireactor.install() | |
171 | |
172 # XXX: We need to configure logs before any log method is used, | |
173 # so here is the best place. | |
174 from sat.core import log_config | |
175 | |
176 log_config.satConfigure(C.LOG_BACKEND_TWISTED, C, backend_data=options) | |
177 from libervia.server import server | |
178 | |
179 # we can't import this file from libervia.server.server because it's not a true module | |
180 # (there is no __init__.py file, as required by twistd plugin system), so we set the | |
181 # global values from here | |
182 server.DATA_DIR_DEFAULT = DATA_DIR_DEFAULT | |
183 server.OPT_PARAMETERS_BOTH = OPT_PARAMETERS_BOTH | |
184 server.OPT_PARAMETERS_CFG = OPT_PARAMETERS_CFG | |
185 server.coerceDataDir = coerceDataDir | |
186 | |
187 | |
188 class Options(usage.Options): | |
189 # optArgs is not really useful in our case, we need more than a flag | |
190 optParameters = OPT_PARAMETERS_BOTH | |
191 | |
192 def __init__(self): | |
193 """Read SàT configuration file in order to overwrite the hard-coded default values | |
194 | |
195 Priority for the usage of the values is (from lowest to highest): | |
196 - hard-coded default values | |
197 - values from SàT configuration files | |
198 - values passed on the command line | |
199 """ | |
200 # If we do it the reading later: after the command line options have been parsed, | |
201 # there's no good way to know | |
202 # if the options values are the hard-coded ones or if they have been passed | |
203 # on the command line. | |
204 | |
205 # FIXME: must be refactored + code can be factorised with backend | |
206 config_parser = ConfigParser.SafeConfigParser() | |
207 config_parser.read(C.CONFIG_FILES) | |
208 self.handleDeprecated(config_parser) | |
209 for param in self.optParameters + OPT_PARAMETERS_CFG: | |
210 name = param[0] | |
211 try: | |
212 value = config.getConfig(config_parser, CONFIG_SECTION, name, Exception) | |
213 if isinstance(value, unicode): | |
214 value = value.encode("utf-8") | |
215 try: | |
216 param[2] = param[4](value) | |
217 except IndexError: # the coerce method is optional | |
218 param[2] = value | |
219 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): | |
220 pass | |
221 usage.Options.__init__(self) | |
222 for opt_data in OPT_PARAMETERS_CFG: | |
223 self[opt_data[0]] = opt_data[2] | |
224 | |
225 def handleDeprecated(self, config_parser): | |
226 """display warning and/or change option when a deprecated option if found | |
227 | |
228 param config_parser(ConfigParser): read ConfigParser instance for sat.conf | |
229 """ | |
230 replacements = (("ssl_certificate", "tls_certificate"),) | |
231 for old, new in replacements: | |
232 try: | |
233 value = config.getConfig(config_parser, CONFIG_SECTION, old, Exception) | |
234 except (ConfigParser.NoSectionError, ConfigParser.NoOptionError): | |
235 pass | |
236 else: | |
237 print(u"\n/!\\ Use of {old} is deprecated, please use {new} instead\n" | |
238 .format(old=old, new=new)) | |
239 config_parser.set(CONFIG_SECTION, new, value) | |
240 | |
241 | |
242 class LiberviaMaker(object): | |
243 implements(IServiceMaker, IPlugin) | |
244 | |
245 tapname = C.APP_NAME_FILE | |
246 description = _(u"The web frontend of Salut à Toi") | |
247 options = Options | |
248 | |
249 def makeService(self, options): | |
250 for opt in OPT_PARAMETERS_BOTH: | |
251 # FIXME: that's a ugly way to get unicode in Libervia | |
252 # from command line or sat.conf | |
253 # we should move to argparse and handle options this properly | |
254 try: | |
255 coerce_cb = opt[4] | |
256 except IndexError: | |
257 continue | |
258 if coerce_cb == coerceUnicode: | |
259 options[opt[0]] = options[opt[0]].decode("utf-8") | |
260 initialise(options.parent) | |
261 from libervia.server import server | |
262 | |
263 return server.Libervia(options) | |
264 | |
265 | |
266 # affectation to some variable is necessary for twisted introspection to work | |
267 serviceMaker = LiberviaMaker() |