Mercurial > libervia-web
comparison libervia/server/server.py @ 1509:106bae41f5c8
massive refactoring from camelCase -> snake_case. See backend commit log for more details
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 08 Apr 2023 13:44:11 +0200 |
parents | ce879da7fcf7 |
children | 65e063657597 |
comparison
equal
deleted
inserted
replaced
1508:ec3ad9abf9f9 | 1509:106bae41f5c8 |
---|---|
38 from sat.tools.common import data_format | 38 from sat.tools.common import data_format |
39 from sat.tools.common import tls | 39 from sat.tools.common import tls |
40 from sat.tools.common.utils import OrderedSet, recursive_update | 40 from sat.tools.common.utils import OrderedSet, recursive_update |
41 from sat_frontends.bridge.bridge_frontend import BridgeException | 41 from sat_frontends.bridge.bridge_frontend import BridgeException |
42 from sat_frontends.bridge.dbus_bridge import ( | 42 from sat_frontends.bridge.dbus_bridge import ( |
43 Bridge, | 43 bridge, |
44 BridgeExceptionNoService, | 44 BridgeExceptionNoService, |
45 const_TIMEOUT as BRIDGE_TIMEOUT, | 45 const_TIMEOUT as BRIDGE_TIMEOUT, |
46 ) | 46 ) |
47 from twisted.application import service | 47 from twisted.application import service |
48 from twisted.internet import defer, inotify, reactor | 48 from twisted.internet import defer, inotify, reactor |
96 if self._notifier == None: | 96 if self._notifier == None: |
97 notifier = self.__class__._notifier = inotify.INotify() | 97 notifier = self.__class__._notifier = inotify.INotify() |
98 notifier.startReading() | 98 notifier.startReading() |
99 return self._notifier | 99 return self._notifier |
100 | 100 |
101 def _checkCallback(self, dir_path, callback, recursive): | 101 def _check_callback(self, dir_path, callback, recursive): |
102 # Twisted doesn't add callback if a watcher was already set on a path | 102 # Twisted doesn't add callback if a watcher was already set on a path |
103 # but in dev mode Libervia watches whole sites + internal path can be watched | 103 # but in dev mode Libervia watches whole sites + internal path can be watched |
104 # by tasks, so several callbacks must be called on some paths. | 104 # by tasks, so several callbacks must be called on some paths. |
105 # This method check that the new callback is indeed present in the desired path | 105 # This method check that the new callback is indeed present in the desired path |
106 # and add it otherwise. | 106 # and add it otherwise. |
107 # FIXME: this should probably be fixed upstream | 107 # FIXME: this should probably be fixed upstream |
108 if recursive: | 108 if recursive: |
109 for child in dir_path.walk(): | 109 for child in dir_path.walk(): |
110 if child.isdir(): | 110 if child.isdir(): |
111 self._checkCallback(child, callback, recursive=False) | 111 self._check_callback(child, callback, recursive=False) |
112 else: | 112 else: |
113 watch_id = self.notifier._isWatched(dir_path) | 113 watch_id = self.notifier._isWatched(dir_path) |
114 if watch_id is None: | 114 if watch_id is None: |
115 log.warning( | 115 log.warning( |
116 f"There is no watch ID for path {dir_path}, this should not happen" | 116 f"There is no watch ID for path {dir_path}, this should not happen" |
118 else: | 118 else: |
119 watch_point = self.notifier._watchpoints[watch_id] | 119 watch_point = self.notifier._watchpoints[watch_id] |
120 if callback not in watch_point.callbacks: | 120 if callback not in watch_point.callbacks: |
121 watch_point.callbacks.append(callback) | 121 watch_point.callbacks.append(callback) |
122 | 122 |
123 def watchDir(self, dir_path, callback, mask=DEFAULT_MASK, auto_add=False, | 123 def watch_dir(self, dir_path, callback, mask=DEFAULT_MASK, auto_add=False, |
124 recursive=False, **kwargs): | 124 recursive=False, **kwargs): |
125 dir_path = str(dir_path) | 125 dir_path = str(dir_path) |
126 log.info(_("Watching directory {dir_path}").format(dir_path=dir_path)) | 126 log.info(_("Watching directory {dir_path}").format(dir_path=dir_path)) |
127 wrapped_callback = lambda __, filepath, mask: callback( | 127 wrapped_callback = lambda __, filepath, mask: callback( |
128 self.host, filepath, inotify.humanReadableMask(mask), **kwargs) | 128 self.host, filepath, inotify.humanReadableMask(mask), **kwargs) |
129 callbacks = [wrapped_callback] | 129 callbacks = [wrapped_callback] |
130 dir_path = filepath.FilePath(dir_path) | 130 dir_path = filepath.FilePath(dir_path) |
131 self.notifier.watch( | 131 self.notifier.watch( |
132 dir_path, mask=mask, autoAdd=auto_add, recursive=recursive, | 132 dir_path, mask=mask, autoAdd=auto_add, recursive=recursive, |
133 callbacks=callbacks) | 133 callbacks=callbacks) |
134 self._checkCallback(dir_path, wrapped_callback, recursive) | 134 self._check_callback(dir_path, wrapped_callback, recursive) |
135 | 135 |
136 | 136 |
137 class WebSession(server.Session): | 137 class WebSession(server.Session): |
138 sessionTimeout = C.SESSION_TIMEOUT | 138 sessionTimeout = C.SESSION_TIMEOUT |
139 | 139 |
191 ProtectedFile.__init__(self, *args, **kwargs) | 191 ProtectedFile.__init__(self, *args, **kwargs) |
192 self.host = host | 192 self.host = host |
193 self.host_name = host_name | 193 self.host_name = host_name |
194 self.site_name = site_name | 194 self.site_name = site_name |
195 self.site_path = Path(site_path) | 195 self.site_path = Path(site_path) |
196 self.default_theme = self.getConfig('theme') | 196 self.default_theme = self.config_get('theme') |
197 if self.default_theme is None: | 197 if self.default_theme is None: |
198 if not host_name: | 198 if not host_name: |
199 # FIXME: we use bulma theme by default for main site for now | 199 # FIXME: we use bulma theme by default for main site for now |
200 # as the development is focusing on this one, and default theme may | 200 # as the development is focusing on this one, and default theme may |
201 # be broken | 201 # be broken |
205 self.site_themes = set() | 205 self.site_themes = set() |
206 self.named_pages = {} | 206 self.named_pages = {} |
207 self.browser_modules = {} | 207 self.browser_modules = {} |
208 # template dynamic data used in all pages | 208 # template dynamic data used in all pages |
209 self.dyn_data_common = {"scripts": OrderedSet()} | 209 self.dyn_data_common = {"scripts": OrderedSet()} |
210 for theme, data in host.renderer.getThemesData(site_name).items(): | 210 for theme, data in host.renderer.get_themes_data(site_name).items(): |
211 # we check themes for browser metadata, and merge them here if found | 211 # we check themes for browser metadata, and merge them here if found |
212 self.site_themes.add(theme) | 212 self.site_themes.add(theme) |
213 browser_meta = data.get('browser_meta') | 213 browser_meta = data.get('browser_meta') |
214 if browser_meta is not None: | 214 if browser_meta is not None: |
215 log.debug(f"merging browser metadata from theme {theme}: {browser_meta}") | 215 log.debug(f"merging browser metadata from theme {theme}: {browser_meta}") |
236 self.pages_redirects = {} | 236 self.pages_redirects = {} |
237 self.cached_urls = {} | 237 self.cached_urls = {} |
238 self.main_menu = None | 238 self.main_menu = None |
239 # map Libervia application names => data | 239 # map Libervia application names => data |
240 self.libervia_apps = {} | 240 self.libervia_apps = {} |
241 self.build_path = host.getBuildPath(site_name) | 241 self.build_path = host.get_build_path(site_name) |
242 self.build_path.mkdir(parents=True, exist_ok=True) | 242 self.build_path.mkdir(parents=True, exist_ok=True) |
243 self.dev_build_path = host.getBuildPath(site_name, dev=True) | 243 self.dev_build_path = host.get_build_path(site_name, dev=True) |
244 self.dev_build_path.mkdir(parents=True, exist_ok=True) | 244 self.dev_build_path.mkdir(parents=True, exist_ok=True) |
245 self.putChild( | 245 self.putChild( |
246 C.BUILD_DIR.encode(), | 246 C.BUILD_DIR.encode(), |
247 ProtectedFile( | 247 ProtectedFile( |
248 self.build_path, | 248 self.build_path, |
254 f"Root resource for {self.host_name or 'default host'} using " | 254 f"Root resource for {self.host_name or 'default host'} using " |
255 f"{self.site_name or 'default site'} at {self.site_path} and deserving " | 255 f"{self.site_name or 'default site'} at {self.site_path} and deserving " |
256 f"files at {self.path}" | 256 f"files at {self.path}" |
257 ) | 257 ) |
258 | 258 |
259 def getConfig(self, key, default=None, value_type=None): | 259 def config_get(self, key, default=None, value_type=None): |
260 """Retrieve configuration for this site | 260 """Retrieve configuration for this site |
261 | 261 |
262 params are the same as for [Libervia.getConfig] | 262 params are the same as for [Libervia.config_get] |
263 """ | 263 """ |
264 return self.host.getConfig(self, key, default, value_type) | 264 return self.host.config_get(self, key, default, value_type) |
265 | 265 |
266 def getFrontURL(self, theme): | 266 def get_front_url(self, theme): |
267 return Path( | 267 return Path( |
268 '/', | 268 '/', |
269 C.TPL_RESOURCE, | 269 C.TPL_RESOURCE, |
270 self.site_name or C.SITE_NAME_DEFAULT, | 270 self.site_name or C.SITE_NAME_DEFAULT, |
271 C.TEMPLATE_TPL_DIR, | 271 C.TEMPLATE_TPL_DIR, |
272 theme) | 272 theme) |
273 | 273 |
274 def addResourceToPath(self, path: str, resource: web_resource.Resource) -> None: | 274 def add_resource_to_path(self, path: str, resource: web_resource.Resource) -> None: |
275 """Add a resource to the given path | 275 """Add a resource to the given path |
276 | 276 |
277 A "NoResource" will be used for all intermediate segments | 277 A "NoResource" will be used for all intermediate segments |
278 """ | 278 """ |
279 segments, __, last_segment = path.rpartition("/") | 279 segments, __, last_segment = path.rpartition("/") |
301 if extra is None: | 301 if extra is None: |
302 extra = {} | 302 extra = {} |
303 log.info(_( | 303 log.info(_( |
304 "starting application {app_name}").format(app_name=app_name)) | 304 "starting application {app_name}").format(app_name=app_name)) |
305 app_data = data_format.deserialise( | 305 app_data = data_format.deserialise( |
306 await self.host.bridgeCall( | 306 await self.host.bridge_call( |
307 "applicationStart", app_name, data_format.serialise(extra) | 307 "application_start", app_name, data_format.serialise(extra) |
308 ) | 308 ) |
309 ) | 309 ) |
310 if app_data.get("started", False): | 310 if app_data.get("started", False): |
311 log.debug(f"application {app_name!r} is already started or starting") | 311 log.debug(f"application {app_name!r} is already started or starting") |
312 # we do not await on purpose, the workflow should not be blocking at this | 312 # we do not await on purpose, the workflow should not be blocking at this |
320 self, | 320 self, |
321 app_name: str, | 321 app_name: str, |
322 instance_id: str | 322 instance_id: str |
323 ) -> None: | 323 ) -> None: |
324 exposed_data = self.libervia_apps[app_name] = data_format.deserialise( | 324 exposed_data = self.libervia_apps[app_name] = data_format.deserialise( |
325 await self.host.bridgeCall("applicationExposedGet", app_name, "", "") | 325 await self.host.bridge_call("application_exposed_get", app_name, "", "") |
326 ) | 326 ) |
327 | 327 |
328 try: | 328 try: |
329 web_port = int(exposed_data['ports']['web'].split(':')[1]) | 329 web_port = int(exposed_data['ports']['web'].split(':')[1]) |
330 except (KeyError, ValueError): | 330 except (KeyError, ValueError): |
348 res = proxy.SatReverseProxyResource( | 348 res = proxy.SatReverseProxyResource( |
349 "localhost", | 349 "localhost", |
350 web_port, | 350 web_port, |
351 url_prefix.encode() | 351 url_prefix.encode() |
352 ) | 352 ) |
353 self.addResourceToPath(url_prefix, res) | 353 self.add_resource_to_path(url_prefix, res) |
354 log.info( | 354 log.info( |
355 f"Resource for app {app_name!r} (instance {instance_id!r}) has been added" | 355 f"Resource for app {app_name!r} (instance {instance_id!r}) has been added" |
356 ) | 356 ) |
357 | 357 |
358 async def _initRedirections(self, options): | 358 async def _init_redirections(self, options): |
359 url_redirections = options["url_redirections_dict"] | 359 url_redirections = options["url_redirections_dict"] |
360 | 360 |
361 url_redirections = url_redirections.get(self.site_name, {}) | 361 url_redirections = url_redirections.get(self.site_name, {}) |
362 | 362 |
363 ## redirections | 363 ## redirections |
408 ) | 408 ) |
409 continue | 409 continue |
410 new["path_args"] = [quote(a) for a in new["path_args"]] | 410 new["path_args"] = [quote(a) for a in new["path_args"]] |
411 # we keep an inversed dict of page redirection | 411 # we keep an inversed dict of page redirection |
412 # (page/path_args => redirecting URL) | 412 # (page/path_args => redirecting URL) |
413 # so getURL can return the redirecting URL if the same arguments | 413 # so get_url can return the redirecting URL if the same arguments |
414 # are used # making the URL consistent | 414 # are used # making the URL consistent |
415 args_hash = tuple(new["path_args"]) | 415 args_hash = tuple(new["path_args"]) |
416 self.pages_redirects.setdefault(new_data["page"], {}).setdefault( | 416 self.pages_redirects.setdefault(new_data["page"], {}).setdefault( |
417 args_hash, | 417 args_hash, |
418 old | 418 old |
445 _("redirected url must start with '/', got {value}. Ignoring") | 445 _("redirected url must start with '/', got {value}. Ignoring") |
446 .format(value=old) | 446 .format(value=old) |
447 ) | 447 ) |
448 continue | 448 continue |
449 else: | 449 else: |
450 old = self._normalizeURL(old) | 450 old = self._normalize_url(old) |
451 | 451 |
452 if isinstance(new, dict): | 452 if isinstance(new, dict): |
453 # dict are handled differently, they contain data | 453 # dict are handled differently, they contain data |
454 # which ared use dynamically when the request is done | 454 # which ared use dynamically when the request is done |
455 self.redirections.setdefault(old, new) | 455 self.redirections.setdefault(old, new) |
460 name=new["page"] | 460 name=new["page"] |
461 ) | 461 ) |
462 ) | 462 ) |
463 else: | 463 else: |
464 if new["type"] == "page": | 464 if new["type"] == "page": |
465 page = self.getPageByName(new["page"]) | 465 page = self.get_page_by_name(new["page"]) |
466 url = page.getURL(*new.get("path_args", [])) | 466 url = page.get_url(*new.get("path_args", [])) |
467 self.inv_redirections[url] = old | 467 self.inv_redirections[url] = old |
468 continue | 468 continue |
469 | 469 |
470 # at this point we have a redirection URL in new, we can parse it | 470 # at this point we have a redirection URL in new, we can parse it |
471 new_url = urllib.parse.urlsplit(new) | 471 new_url = urllib.parse.urlsplit(new) |
472 | 472 |
473 # we handle the known URL schemes | 473 # we handle the known URL schemes |
474 if new_url.scheme == "xmpp": | 474 if new_url.scheme == "xmpp": |
475 location = self.getPagePathFromURI(new) | 475 location = self.get_page_path_from_uri(new) |
476 if location is None: | 476 if location is None: |
477 log.warning( | 477 log.warning( |
478 _("ignoring redirection, no page found to handle this URI: " | 478 _("ignoring redirection, no page found to handle this URI: " |
479 "{uri}").format(uri=new)) | 479 "{uri}").format(uri=new)) |
480 continue | 480 continue |
481 request_data = self._getRequestData(location) | 481 request_data = self._get_request_data(location) |
482 self.inv_redirections[location] = old | 482 self.inv_redirections[location] = old |
483 | 483 |
484 elif new_url.scheme in ("", "http", "https"): | 484 elif new_url.scheme in ("", "http", "https"): |
485 # direct redirection | 485 # direct redirection |
486 if new_url.netloc: | 486 if new_url.netloc: |
489 "url_redirections_dict, it is not possible to redirect to an " | 489 "url_redirections_dict, it is not possible to redirect to an " |
490 "external website".format(netloc=new_url.netloc)) | 490 "external website".format(netloc=new_url.netloc)) |
491 location = urllib.parse.urlunsplit( | 491 location = urllib.parse.urlunsplit( |
492 ("", "", new_url.path, new_url.query, new_url.fragment) | 492 ("", "", new_url.path, new_url.query, new_url.fragment) |
493 ) | 493 ) |
494 request_data = self._getRequestData(location) | 494 request_data = self._get_request_data(location) |
495 self.inv_redirections[location] = old | 495 self.inv_redirections[location] = old |
496 | 496 |
497 elif new_url.scheme == "file": | 497 elif new_url.scheme == "file": |
498 # file or directory | 498 # file or directory |
499 if new_url.netloc: | 499 if new_url.netloc: |
510 # for file redirection, we directly put child here | 510 # for file redirection, we directly put child here |
511 resource_class = ( | 511 resource_class = ( |
512 ProtectedFile if new_data.get("protected", True) else static.File | 512 ProtectedFile if new_data.get("protected", True) else static.File |
513 ) | 513 ) |
514 res = resource_class(path, defaultType="application/octet-stream") | 514 res = resource_class(path, defaultType="application/octet-stream") |
515 self.addResourceToPath(old, res) | 515 self.add_resource_to_path(old, res) |
516 log.info("[{host_name}] Added redirection from /{old} to file system " | 516 log.info("[{host_name}] Added redirection from /{old} to file system " |
517 "path {path}".format(host_name=self.host_name, | 517 "path {path}".format(host_name=self.host_name, |
518 old=old, | 518 old=old, |
519 path=path)) | 519 path=path)) |
520 | 520 |
552 res = proxy.SatReverseProxyResource( | 552 res = proxy.SatReverseProxyResource( |
553 host, | 553 host, |
554 port, | 554 port, |
555 url_prefix.encode(), | 555 url_prefix.encode(), |
556 ) | 556 ) |
557 self.addResourceToPath(old, res) | 557 self.add_resource_to_path(old, res) |
558 log.info( | 558 log.info( |
559 f"[{self.host_name}] Added redirection from /{old} to reverse proxy " | 559 f"[{self.host_name}] Added redirection from /{old} to reverse proxy " |
560 f"{new_url.netloc} with URL prefix {url_prefix}/" | 560 f"{new_url.netloc} with URL prefix {url_prefix}/" |
561 ) | 561 ) |
562 | 562 |
575 .format(host_name=self.host_name, | 575 .format(host_name=self.host_name, |
576 uri=request_data[1])) | 576 uri=request_data[1])) |
577 | 577 |
578 # the default root URL, if not redirected | 578 # the default root URL, if not redirected |
579 if not "" in self.redirections: | 579 if not "" in self.redirections: |
580 self.redirections[""] = self._getRequestData(C.LIBERVIA_PAGE_START) | 580 self.redirections[""] = self._get_request_data(C.LIBERVIA_PAGE_START) |
581 | 581 |
582 async def _setMenu(self, menus): | 582 async def _set_menu(self, menus): |
583 menus = menus.get(self.site_name, []) | 583 menus = menus.get(self.site_name, []) |
584 main_menu = [] | 584 main_menu = [] |
585 for menu in menus: | 585 for menu in menus: |
586 if not menu: | 586 if not menu: |
587 msg = _("menu item can't be empty") | 587 msg = _("menu item can't be empty") |
621 f"Application {app_name} added to menu of {self.site_name}" | 621 f"Application {app_name} added to menu of {self.site_name}" |
622 ) | 622 ) |
623 else: | 623 else: |
624 page_name = menu | 624 page_name = menu |
625 try: | 625 try: |
626 url = self.getPageByName(page_name).url | 626 url = self.get_page_by_name(page_name).url |
627 except KeyError as e: | 627 except KeyError as e: |
628 log_msg = _("Can'find a named page ({msg}), please check " | 628 log_msg = _("Can'find a named page ({msg}), please check " |
629 "menu_json in configuration.").format(msg=e.args[0]) | 629 "menu_json in configuration.").format(msg=e.args[0]) |
630 log.error(log_msg) | 630 log.error(log_msg) |
631 raise exceptions.ConfigError(log_msg) | 631 raise exceptions.ConfigError(log_msg) |
632 main_menu.append((page_name, url)) | 632 main_menu.append((page_name, url)) |
633 self.main_menu = main_menu | 633 self.main_menu = main_menu |
634 | 634 |
635 def _normalizeURL(self, url, lower=True): | 635 def _normalize_url(self, url, lower=True): |
636 """Return URL normalized for self.redirections dict | 636 """Return URL normalized for self.redirections dict |
637 | 637 |
638 @param url(unicode): URL to normalize | 638 @param url(unicode): URL to normalize |
639 @param lower(bool): lower case of url if True | 639 @param lower(bool): lower case of url if True |
640 @return (str): normalized URL | 640 @return (str): normalized URL |
641 """ | 641 """ |
642 if lower: | 642 if lower: |
643 url = url.lower() | 643 url = url.lower() |
644 return "/".join((p for p in url.split("/") if p)) | 644 return "/".join((p for p in url.split("/") if p)) |
645 | 645 |
646 def _getRequestData(self, uri): | 646 def _get_request_data(self, uri): |
647 """Return data needed to redirect request | 647 """Return data needed to redirect request |
648 | 648 |
649 @param url(unicode): destination url | 649 @param url(unicode): destination url |
650 @return (tuple(list[str], str, str, dict): tuple with | 650 @return (tuple(list[str], str, str, dict): tuple with |
651 splitted path as in Request.postpath | 651 splitted path as in Request.postpath |
666 args = urllib.parse.parse_qs(argstring, True) | 666 args = urllib.parse.parse_qs(argstring, True) |
667 | 667 |
668 # XXX: splitted path case must not be changed, as it may be significant | 668 # XXX: splitted path case must not be changed, as it may be significant |
669 # (e.g. for blog items) | 669 # (e.g. for blog items) |
670 return ( | 670 return ( |
671 self._normalizeURL(path, lower=False).split("/"), | 671 self._normalize_url(path, lower=False).split("/"), |
672 uri, | 672 uri, |
673 path, | 673 path, |
674 args, | 674 args, |
675 ) | 675 ) |
676 | 676 |
677 def _redirect(self, request, request_data): | 677 def _redirect(self, request, request_data): |
678 """Redirect an URL by rewritting request | 678 """Redirect an URL by rewritting request |
679 | 679 |
680 this is *NOT* a HTTP redirection, but equivalent to URL rewritting | 680 this is *NOT* a HTTP redirection, but equivalent to URL rewritting |
681 @param request(web.http.request): original request | 681 @param request(web.http.request): original request |
682 @param request_data(tuple): data returned by self._getRequestData | 682 @param request_data(tuple): data returned by self._get_request_data |
683 @return (web_resource.Resource): resource to use | 683 @return (web_resource.Resource): resource to use |
684 """ | 684 """ |
685 # recursion check | 685 # recursion check |
686 try: | 686 try: |
687 request._redirected | 687 request._redirected |
700 request._redirected = True # here to avoid recursive redirections | 700 request._redirected = True # here to avoid recursive redirections |
701 | 701 |
702 if isinstance(request_data, dict): | 702 if isinstance(request_data, dict): |
703 if request_data["type"] == "page": | 703 if request_data["type"] == "page": |
704 try: | 704 try: |
705 page = self.getPageByName(request_data["page"]) | 705 page = self.get_page_by_name(request_data["page"]) |
706 except KeyError: | 706 except KeyError: |
707 log.error( | 707 log.error( |
708 _( | 708 _( |
709 'Can\'t find page named "{name}" requested in redirection' | 709 'Can\'t find page named "{name}" requested in redirection' |
710 ).format(name=request_data["page"]) | 710 ).format(name=request_data["page"]) |
738 request.args.update(args) | 738 request.args.update(args) |
739 | 739 |
740 # we start again to look for a child with the new url | 740 # we start again to look for a child with the new url |
741 return self.getChildWithDefault(path_list[0], request) | 741 return self.getChildWithDefault(path_list[0], request) |
742 | 742 |
743 def getPageByName(self, name): | 743 def get_page_by_name(self, name): |
744 """Retrieve page instance from its name | 744 """Retrieve page instance from its name |
745 | 745 |
746 @param name(unicode): name of the page | 746 @param name(unicode): name of the page |
747 @return (LiberviaPage): page instance | 747 @return (LiberviaPage): page instance |
748 @raise KeyError: the page doesn't exist | 748 @raise KeyError: the page doesn't exist |
749 """ | 749 """ |
750 return self.named_pages[name] | 750 return self.named_pages[name] |
751 | 751 |
752 def getPagePathFromURI(self, uri): | 752 def get_page_path_from_uri(self, uri): |
753 """Retrieve page URL from xmpp: URI | 753 """Retrieve page URL from xmpp: URI |
754 | 754 |
755 @param uri(unicode): URI with a xmpp: scheme | 755 @param uri(unicode): URI with a xmpp: scheme |
756 @return (unicode,None): absolute path (starting from root "/") to page handling | 756 @return (unicode,None): absolute path (starting from root "/") to page handling |
757 the URI. | 757 the URI. |
758 None is returned if no page has been registered for this URI | 758 None is returned if no page has been registered for this URI |
759 """ | 759 """ |
760 uri_data = common_uri.parseXMPPUri(uri) | 760 uri_data = common_uri.parse_xmpp_uri(uri) |
761 try: | 761 try: |
762 page, cb = self.uri_callbacks[uri_data["type"], uri_data["sub_type"]] | 762 page, cb = self.uri_callbacks[uri_data["type"], uri_data["sub_type"]] |
763 except KeyError: | 763 except KeyError: |
764 url = None | 764 url = None |
765 else: | 765 else: |
821 f.childNotFound = self.childNotFound | 821 f.childNotFound = self.childNotFound |
822 return f | 822 return f |
823 | 823 |
824 | 824 |
825 class WaitingRequests(dict): | 825 class WaitingRequests(dict): |
826 def setRequest(self, request, profile, register_with_ext_jid=False): | 826 def set_request(self, request, profile, register_with_ext_jid=False): |
827 """Add the given profile to the waiting list. | 827 """Add the given profile to the waiting list. |
828 | 828 |
829 @param request (server.Request): the connection request | 829 @param request (server.Request): the connection request |
830 @param profile (str): %(doc_profile)s | 830 @param profile (str): %(doc_profile)s |
831 @param register_with_ext_jid (bool): True if we will try to register the | 831 @param register_with_ext_jid (bool): True if we will try to register the |
832 profile with an external XMPP account credentials | 832 profile with an external XMPP account credentials |
833 """ | 833 """ |
834 dc = reactor.callLater(BRIDGE_TIMEOUT, self.purgeRequest, profile) | 834 dc = reactor.callLater(BRIDGE_TIMEOUT, self.purge_request, profile) |
835 self[profile] = (request, dc, register_with_ext_jid) | 835 self[profile] = (request, dc, register_with_ext_jid) |
836 | 836 |
837 def purgeRequest(self, profile): | 837 def purge_request(self, profile): |
838 """Remove the given profile from the waiting list. | 838 """Remove the given profile from the waiting list. |
839 | 839 |
840 @param profile (str): %(doc_profile)s | 840 @param profile (str): %(doc_profile)s |
841 """ | 841 """ |
842 try: | 842 try: |
845 return | 845 return |
846 if dc.active(): | 846 if dc.active(): |
847 dc.cancel() | 847 dc.cancel() |
848 del self[profile] | 848 del self[profile] |
849 | 849 |
850 def getRequest(self, profile): | 850 def get_request(self, profile): |
851 """Get the waiting request for the given profile. | 851 """Get the waiting request for the given profile. |
852 | 852 |
853 @param profile (str): %(doc_profile)s | 853 @param profile (str): %(doc_profile)s |
854 @return: the waiting request or None | 854 @return: the waiting request or None |
855 """ | 855 """ |
856 return self[profile][0] if profile in self else None | 856 return self[profile][0] if profile in self else None |
857 | 857 |
858 def getRegisterWithExtJid(self, profile): | 858 def get_register_with_ext_jid(self, profile): |
859 """Get the value of the register_with_ext_jid parameter. | 859 """Get the value of the register_with_ext_jid parameter. |
860 | 860 |
861 @param profile (str): %(doc_profile)s | 861 @param profile (str): %(doc_profile)s |
862 @return: bool or None | 862 @return: bool or None |
863 """ | 863 """ |
899 self.prof_connected = set() # Profiles connected | 899 self.prof_connected = set() # Profiles connected |
900 self.ns_map = {} # map of short name to namespaces | 900 self.ns_map = {} # map of short name to namespaces |
901 | 901 |
902 ## bridge ## | 902 ## bridge ## |
903 self._bridge_retry = self.options['bridge-retries'] | 903 self._bridge_retry = self.options['bridge-retries'] |
904 self.bridge = Bridge() | 904 self.bridge = bridge() |
905 self.bridge.bridgeConnect(callback=self._bridgeCb, errback=self._bridgeEb) | 905 self.bridge.bridge_connect(callback=self._bridge_cb, errback=self._bridge_eb) |
906 | 906 |
907 ## libervia app callbacks ## | 907 ## libervia app callbacks ## |
908 # mapping instance id to the callback to call on "started" signal | 908 # mapping instance id to the callback to call on "started" signal |
909 self.apps_cb: Dict[str, Callable] = {} | 909 self.apps_cb: Dict[str, Callable] = {} |
910 | 910 |
925 | 925 |
926 @property | 926 @property |
927 def main_conf(self): | 927 def main_conf(self): |
928 """SafeConfigParser instance opened on configuration file (sat.conf)""" | 928 """SafeConfigParser instance opened on configuration file (sat.conf)""" |
929 if self._main_conf is None: | 929 if self._main_conf is None: |
930 self._main_conf = config.parseMainConf(log_filenames=True) | 930 self._main_conf = config.parse_main_conf(log_filenames=True) |
931 return self._main_conf | 931 return self._main_conf |
932 | 932 |
933 def getConfig(self, site_root_res, key, default=None, value_type=None): | 933 def config_get(self, site_root_res, key, default=None, value_type=None): |
934 """Retrieve configuration associated to a site | 934 """Retrieve configuration associated to a site |
935 | 935 |
936 Section is automatically set to site name | 936 Section is automatically set to site name |
937 @param site_root_res(LiberviaRootResource): resource of the site in use | 937 @param site_root_res(LiberviaRootResource): resource of the site in use |
938 @param key(unicode): key to use | 938 @param key(unicode): key to use |
939 @param default: value to use if not found (see [config.getConfig]) | 939 @param default: value to use if not found (see [config.config_get]) |
940 @param value_type(unicode, None): filter to use on value | 940 @param value_type(unicode, None): filter to use on value |
941 Note that filters are already automatically used when the key finish | 941 Note that filters are already automatically used when the key finish |
942 by a well known suffix ("_path", "_list", "_dict", or "_json") | 942 by a well known suffix ("_path", "_list", "_dict", or "_json") |
943 None to use no filter, else can be: | 943 None to use no filter, else can be: |
944 - "path": a path is expected, will be normalized and expanded | 944 - "path": a path is expected, will be normalized and expanded |
945 | 945 |
946 """ | 946 """ |
947 section = site_root_res.site_name.lower().strip() or C.CONFIG_SECTION | 947 section = site_root_res.site_name.lower().strip() or C.CONFIG_SECTION |
948 value = config.getConfig(self.main_conf, section, key, default=default) | 948 value = config.config_get(self.main_conf, section, key, default=default) |
949 if value_type is not None: | 949 if value_type is not None: |
950 if value_type == 'path': | 950 if value_type == 'path': |
951 v_filter = lambda v: os.path.abspath(os.path.expanduser(v)) | 951 v_filter = lambda v: os.path.abspath(os.path.expanduser(v)) |
952 else: | 952 else: |
953 raise ValueError("unknown value type {value_type}".format( | 953 raise ValueError("unknown value type {value_type}".format( |
958 value = {k:v_filter(v) for k,v in list(value.items())} | 958 value = {k:v_filter(v) for k,v in list(value.items())} |
959 elif value is not None: | 959 elif value is not None: |
960 value = v_filter(value) | 960 value = v_filter(value) |
961 return value | 961 return value |
962 | 962 |
963 def _namespacesGetCb(self, ns_map): | 963 def _namespaces_get_cb(self, ns_map): |
964 self.ns_map = {str(k): str(v) for k,v in ns_map.items()} | 964 self.ns_map = {str(k): str(v) for k,v in ns_map.items()} |
965 | 965 |
966 def _namespacesGetEb(self, failure_): | 966 def _namespaces_get_eb(self, failure_): |
967 log.error(_("Can't get namespaces map: {msg}").format(msg=failure_)) | 967 log.error(_("Can't get namespaces map: {msg}").format(msg=failure_)) |
968 | 968 |
969 @template.contextfilter | 969 @template.contextfilter |
970 def _front_url_filter(self, ctx, relative_url): | 970 def _front_url_filter(self, ctx, relative_url): |
971 template_data = ctx['template_data'] | 971 template_data = ctx['template_data'] |
972 return os.path.join( | 972 return os.path.join( |
973 '/', C.TPL_RESOURCE, template_data.site or C.SITE_NAME_DEFAULT, | 973 '/', C.TPL_RESOURCE, template_data.site or C.SITE_NAME_DEFAULT, |
974 C.TEMPLATE_TPL_DIR, template_data.theme, relative_url) | 974 C.TEMPLATE_TPL_DIR, template_data.theme, relative_url) |
975 | 975 |
976 def _moveFirstLevelToDict(self, options, key, keys_to_keep): | 976 def _move_first_level_to_dict(self, options, key, keys_to_keep): |
977 """Read a config option and put value at first level into u'' dict | 977 """Read a config option and put value at first level into u'' dict |
978 | 978 |
979 This is useful to put values for Libervia official site directly in dictionary, | 979 This is useful to put values for Libervia official site directly in dictionary, |
980 and to use site_name as keys when external sites are used. | 980 and to use site_name as keys when external sites are used. |
981 options will be modified in place | 981 options will be modified in place |
999 for key in to_delete: | 999 for key in to_delete: |
1000 del conf[key] | 1000 del conf[key] |
1001 if default_dict: | 1001 if default_dict: |
1002 conf[''] = default_dict | 1002 conf[''] = default_dict |
1003 | 1003 |
1004 async def checkAndConnectServiceProfile(self): | 1004 async def check_and_connect_service_profile(self): |
1005 passphrase = self.options["passphrase"] | 1005 passphrase = self.options["passphrase"] |
1006 if not passphrase: | 1006 if not passphrase: |
1007 raise SysExit( | 1007 raise SysExit( |
1008 C.EXIT_BAD_ARG, | 1008 C.EXIT_BAD_ARG, |
1009 _("No passphrase set for service profile, please check installation " | 1009 _("No passphrase set for service profile, please check installation " |
1010 "documentation.") | 1010 "documentation.") |
1011 ) | 1011 ) |
1012 try: | 1012 try: |
1013 s_prof_connected = await self.bridgeCall("isConnected", C.SERVICE_PROFILE) | 1013 s_prof_connected = await self.bridge_call("is_connected", C.SERVICE_PROFILE) |
1014 except BridgeException as e: | 1014 except BridgeException as e: |
1015 if e.classname == "ProfileUnknownError": | 1015 if e.classname == "ProfileUnknownError": |
1016 log.info("Service profile doesn't exist, creating it.") | 1016 log.info("Service profile doesn't exist, creating it.") |
1017 try: | 1017 try: |
1018 xmpp_domain = await self.bridgeCall("getConfig", "", "xmpp_domain") | 1018 xmpp_domain = await self.bridge_call("config_get", "", "xmpp_domain") |
1019 xmpp_domain = xmpp_domain.strip() | 1019 xmpp_domain = xmpp_domain.strip() |
1020 if not xmpp_domain: | 1020 if not xmpp_domain: |
1021 raise SysExit( | 1021 raise SysExit( |
1022 C.EXIT_BAD_ARG, | 1022 C.EXIT_BAD_ARG, |
1023 _('"xmpp_domain" must be set to create new accounts, please ' | 1023 _('"xmpp_domain" must be set to create new accounts, please ' |
1024 'check documentation') | 1024 'check documentation') |
1025 ) | 1025 ) |
1026 service_profile_jid_s = f"{C.SERVICE_PROFILE}@{xmpp_domain}" | 1026 service_profile_jid_s = f"{C.SERVICE_PROFILE}@{xmpp_domain}" |
1027 await self.bridgeCall( | 1027 await self.bridge_call( |
1028 "inBandAccountNew", | 1028 "in_band_account_new", |
1029 service_profile_jid_s, | 1029 service_profile_jid_s, |
1030 passphrase, | 1030 passphrase, |
1031 "", | 1031 "", |
1032 xmpp_domain, | 1032 xmpp_domain, |
1033 0, | 1033 0, |
1080 C.EXIT_BRIDGE_ERROR, | 1080 C.EXIT_BRIDGE_ERROR, |
1081 _("Can't create service profile XMPP account, you'll have " | 1081 _("Can't create service profile XMPP account, you'll have " |
1082 "do to it manually: {reason}").format(reason=e.message) | 1082 "do to it manually: {reason}").format(reason=e.message) |
1083 ) | 1083 ) |
1084 try: | 1084 try: |
1085 await self.bridgeCall("profileCreate", C.SERVICE_PROFILE, passphrase) | 1085 await self.bridge_call("profile_create", C.SERVICE_PROFILE, passphrase) |
1086 await self.bridgeCall( | 1086 await self.bridge_call( |
1087 "profileStartSession", passphrase, C.SERVICE_PROFILE) | 1087 "profile_start_session", passphrase, C.SERVICE_PROFILE) |
1088 await self.bridgeCall( | 1088 await self.bridge_call( |
1089 "setParam", "JabberID", service_profile_jid_s, "Connection", -1, | 1089 "param_set", "JabberID", service_profile_jid_s, "Connection", -1, |
1090 C.SERVICE_PROFILE) | 1090 C.SERVICE_PROFILE) |
1091 await self.bridgeCall( | 1091 await self.bridge_call( |
1092 "setParam", "Password", passphrase, "Connection", -1, | 1092 "param_set", "Password", passphrase, "Connection", -1, |
1093 C.SERVICE_PROFILE) | 1093 C.SERVICE_PROFILE) |
1094 except BridgeException as e: | 1094 except BridgeException as e: |
1095 raise SysExit( | 1095 raise SysExit( |
1096 C.EXIT_BRIDGE_ERROR, | 1096 C.EXIT_BRIDGE_ERROR, |
1097 _("Can't create service profile XMPP account, you'll have " | 1097 _("Can't create service profile XMPP account, you'll have " |
1102 else: | 1102 else: |
1103 raise SysExit(C.EXIT_BRIDGE_ERROR, e.message) | 1103 raise SysExit(C.EXIT_BRIDGE_ERROR, e.message) |
1104 | 1104 |
1105 if not s_prof_connected: | 1105 if not s_prof_connected: |
1106 try: | 1106 try: |
1107 await self.bridgeCall( | 1107 await self.bridge_call( |
1108 "connect", | 1108 "connect", |
1109 C.SERVICE_PROFILE, | 1109 C.SERVICE_PROFILE, |
1110 passphrase, | 1110 passphrase, |
1111 {}, | 1111 {}, |
1112 ) | 1112 ) |
1114 raise SysExit( | 1114 raise SysExit( |
1115 C.EXIT_BRIDGE_ERROR, | 1115 C.EXIT_BRIDGE_ERROR, |
1116 _("Connection of service profile failed: {reason}").format(reason=e) | 1116 _("Connection of service profile failed: {reason}").format(reason=e) |
1117 ) | 1117 ) |
1118 | 1118 |
1119 async def backendReady(self): | 1119 async def backend_ready(self): |
1120 log.info(f"Libervia Web v{self.full_version}") | 1120 log.info(f"Libervia Web v{self.full_version}") |
1121 | 1121 |
1122 # settings | 1122 # settings |
1123 if self.options['dev-mode']: | 1123 if self.options['dev-mode']: |
1124 log.info(_("Developer mode activated")) | 1124 log.info(_("Developer mode activated")) |
1125 self.media_dir = await self.bridgeCall("getConfig", "", "media_dir") | 1125 self.media_dir = await self.bridge_call("config_get", "", "media_dir") |
1126 self.local_dir = await self.bridgeCall("getConfig", "", "local_dir") | 1126 self.local_dir = await self.bridge_call("config_get", "", "local_dir") |
1127 self.cache_root_dir = os.path.join(self.local_dir, C.CACHE_DIR) | 1127 self.cache_root_dir = os.path.join(self.local_dir, C.CACHE_DIR) |
1128 self.renderer = template.Renderer(self, self._front_url_filter) | 1128 self.renderer = template.Renderer(self, self._front_url_filter) |
1129 sites_names = list(self.renderer.sites_paths.keys()) | 1129 sites_names = list(self.renderer.sites_paths.keys()) |
1130 | 1130 |
1131 self._moveFirstLevelToDict(self.options, "url_redirections_dict", sites_names) | 1131 self._move_first_level_to_dict(self.options, "url_redirections_dict", sites_names) |
1132 self._moveFirstLevelToDict(self.options, "menu_json", sites_names) | 1132 self._move_first_level_to_dict(self.options, "menu_json", sites_names) |
1133 self._moveFirstLevelToDict(self.options, "menu_extra_json", sites_names) | 1133 self._move_first_level_to_dict(self.options, "menu_extra_json", sites_names) |
1134 menu = self.options["menu_json"] | 1134 menu = self.options["menu_json"] |
1135 if not '' in menu: | 1135 if not '' in menu: |
1136 menu[''] = C.DEFAULT_MENU | 1136 menu[''] = C.DEFAULT_MENU |
1137 for site, value in self.options["menu_extra_json"].items(): | 1137 for site, value in self.options["menu_extra_json"].items(): |
1138 menu[site].extend(value) | 1138 menu[site].extend(value) |
1139 | 1139 |
1140 # service profile | 1140 # service profile |
1141 if not self.options['build-only']: | 1141 if not self.options['build-only']: |
1142 await self.checkAndConnectServiceProfile() | 1142 await self.check_and_connect_service_profile() |
1143 | 1143 |
1144 # restricted bridge, the one used by browser code | 1144 # restricted bridge, the one used by browser code |
1145 self.restricted_bridge = RestrictedBridge(self) | 1145 self.restricted_bridge = RestrictedBridge(self) |
1146 | 1146 |
1147 # we create virtual hosts and import Libervia pages into them | 1147 # we create virtual hosts and import Libervia pages into them |
1151 root_path = default_site_path / C.TEMPLATE_STATIC_DIR | 1151 root_path = default_site_path / C.TEMPLATE_STATIC_DIR |
1152 self.sat_root = default_root = LiberviaRootResource( | 1152 self.sat_root = default_root = LiberviaRootResource( |
1153 host=self, host_name='', site_name='', | 1153 host=self, host_name='', site_name='', |
1154 site_path=default_site_path, path=root_path) | 1154 site_path=default_site_path, path=root_path) |
1155 if self.options['dev-mode']: | 1155 if self.options['dev-mode']: |
1156 self.files_watcher.watchDir( | 1156 self.files_watcher.watch_dir( |
1157 default_site_path, auto_add=True, recursive=True, | 1157 default_site_path, auto_add=True, recursive=True, |
1158 callback=LiberviaPage.onFileChange, site_root=self.sat_root, | 1158 callback=LiberviaPage.on_file_change, site_root=self.sat_root, |
1159 site_path=default_site_path) | 1159 site_path=default_site_path) |
1160 LiberviaPage.importPages(self, self.sat_root) | 1160 LiberviaPage.import_pages(self, self.sat_root) |
1161 tasks_manager = TasksManager(self, self.sat_root) | 1161 tasks_manager = TasksManager(self, self.sat_root) |
1162 await tasks_manager.parseTasks() | 1162 await tasks_manager.parse_tasks() |
1163 await tasks_manager.runTasks() | 1163 await tasks_manager.run_tasks() |
1164 # FIXME: handle _setMenu in a more generic way, taking care of external sites | 1164 # FIXME: handle _set_menu in a more generic way, taking care of external sites |
1165 await self.sat_root._setMenu(self.options["menu_json"]) | 1165 await self.sat_root._set_menu(self.options["menu_json"]) |
1166 self.vhost_root.default = default_root | 1166 self.vhost_root.default = default_root |
1167 existing_vhosts = {b'': default_root} | 1167 existing_vhosts = {b'': default_root} |
1168 | 1168 |
1169 for host_name, site_name in self.options["vhosts_dict"].items(): | 1169 for host_name, site_name in self.options["vhosts_dict"].items(): |
1170 if site_name == C.SITE_NAME_DEFAULT: | 1170 if site_name == C.SITE_NAME_DEFAULT: |
1197 path=root_path) | 1197 path=root_path) |
1198 | 1198 |
1199 existing_vhosts[encoded_site_name] = res | 1199 existing_vhosts[encoded_site_name] = res |
1200 | 1200 |
1201 if self.options['dev-mode']: | 1201 if self.options['dev-mode']: |
1202 self.files_watcher.watchDir( | 1202 self.files_watcher.watch_dir( |
1203 site_path, auto_add=True, recursive=True, | 1203 site_path, auto_add=True, recursive=True, |
1204 callback=LiberviaPage.onFileChange, site_root=res, | 1204 callback=LiberviaPage.on_file_change, site_root=res, |
1205 site_path=site_path) | 1205 site_path=site_path) |
1206 | 1206 |
1207 LiberviaPage.importPages(self, res) | 1207 LiberviaPage.import_pages(self, res) |
1208 # FIXME: default pages are accessible if not overriden by external website | 1208 # FIXME: default pages are accessible if not overriden by external website |
1209 # while necessary for login or re-using existing pages | 1209 # while necessary for login or re-using existing pages |
1210 # we may want to disable access to the page by direct URL | 1210 # we may want to disable access to the page by direct URL |
1211 # (e.g. /blog disabled except if called by external site) | 1211 # (e.g. /blog disabled except if called by external site) |
1212 LiberviaPage.importPages(self, res, root_path=default_site_path) | 1212 LiberviaPage.import_pages(self, res, root_path=default_site_path) |
1213 tasks_manager = TasksManager(self, res) | 1213 tasks_manager = TasksManager(self, res) |
1214 await tasks_manager.parseTasks() | 1214 await tasks_manager.parse_tasks() |
1215 await tasks_manager.runTasks() | 1215 await tasks_manager.run_tasks() |
1216 await res._setMenu(self.options["menu_json"]) | 1216 await res._set_menu(self.options["menu_json"]) |
1217 | 1217 |
1218 self.vhost_root.addHost(host_name.encode('utf-8'), res) | 1218 self.vhost_root.addHost(host_name.encode('utf-8'), res) |
1219 | 1219 |
1220 templates_res = web_resource.Resource() | 1220 templates_res = web_resource.Resource() |
1221 self.putChildAll(C.TPL_RESOURCE.encode('utf-8'), templates_res) | 1221 self.put_child_all(C.TPL_RESOURCE.encode('utf-8'), templates_res) |
1222 for site_name, site_path in self.renderer.sites_paths.items(): | 1222 for site_name, site_path in self.renderer.sites_paths.items(): |
1223 templates_res.putChild(site_name.encode() or C.SITE_NAME_DEFAULT.encode(), | 1223 templates_res.putChild(site_name.encode() or C.SITE_NAME_DEFAULT.encode(), |
1224 static.File(site_path)) | 1224 static.File(site_path)) |
1225 | 1225 |
1226 d = self.bridgeCall("namespacesGet") | 1226 d = self.bridge_call("namespaces_get") |
1227 d.addCallback(self._namespacesGetCb) | 1227 d.addCallback(self._namespaces_get_cb) |
1228 d.addErrback(self._namespacesGetEb) | 1228 d.addErrback(self._namespaces_get_eb) |
1229 | 1229 |
1230 # websocket | 1230 # websocket |
1231 if self.options["connection_type"] in ("https", "both"): | 1231 if self.options["connection_type"] in ("https", "both"): |
1232 wss = websockets.LiberviaPageWSProtocol.getResource(secure=True) | 1232 wss = websockets.LiberviaPageWSProtocol.get_resource(secure=True) |
1233 self.putChildAll(b'wss', wss) | 1233 self.put_child_all(b'wss', wss) |
1234 if self.options["connection_type"] in ("http", "both"): | 1234 if self.options["connection_type"] in ("http", "both"): |
1235 ws = websockets.LiberviaPageWSProtocol.getResource(secure=False) | 1235 ws = websockets.LiberviaPageWSProtocol.get_resource(secure=False) |
1236 self.putChildAll(b'ws', ws) | 1236 self.put_child_all(b'ws', ws) |
1237 | 1237 |
1238 ## following signal is needed for cache handling in Libervia pages | 1238 ## following signal is needed for cache handling in Libervia pages |
1239 self.bridge.register_signal( | 1239 self.bridge.register_signal( |
1240 "psEventRaw", partial(LiberviaPage.onNodeEvent, self), "plugin" | 1240 "ps_event_raw", partial(LiberviaPage.on_node_event, self), "plugin" |
1241 ) | 1241 ) |
1242 self.bridge.register_signal( | 1242 self.bridge.register_signal( |
1243 "messageNew", partial(self.on_signal, "messageNew") | 1243 "message_new", partial(self.on_signal, "message_new") |
1244 ) | 1244 ) |
1245 | 1245 |
1246 # libervia applications handling | 1246 # libervia applications handling |
1247 self.bridge.register_signal( | 1247 self.bridge.register_signal( |
1248 "application_started", self.application_started_handler, "plugin" | 1248 "application_started", self.application_started_handler, "plugin" |
1251 "application_error", self.application_error_handler, "plugin" | 1251 "application_error", self.application_error_handler, "plugin" |
1252 ) | 1252 ) |
1253 | 1253 |
1254 # Progress handling | 1254 # Progress handling |
1255 self.bridge.register_signal( | 1255 self.bridge.register_signal( |
1256 "progressStarted", partial(ProgressHandler._signal, "started") | 1256 "progress_started", partial(ProgressHandler._signal, "started") |
1257 ) | 1257 ) |
1258 self.bridge.register_signal( | 1258 self.bridge.register_signal( |
1259 "progressFinished", partial(ProgressHandler._signal, "finished") | 1259 "progress_finished", partial(ProgressHandler._signal, "finished") |
1260 ) | 1260 ) |
1261 self.bridge.register_signal( | 1261 self.bridge.register_signal( |
1262 "progressError", partial(ProgressHandler._signal, "error") | 1262 "progress_error", partial(ProgressHandler._signal, "error") |
1263 ) | 1263 ) |
1264 | 1264 |
1265 # media dirs | 1265 # media dirs |
1266 # FIXME: get rid of dirname and "/" in C.XXX_DIR | 1266 # FIXME: get rid of dirname and "/" in C.XXX_DIR |
1267 self.putChildAll(os.path.dirname(C.MEDIA_DIR).encode('utf-8'), | 1267 self.put_child_all(os.path.dirname(C.MEDIA_DIR).encode('utf-8'), |
1268 ProtectedFile(self.media_dir)) | 1268 ProtectedFile(self.media_dir)) |
1269 | 1269 |
1270 self.cache_resource = web_resource.NoResource() | 1270 self.cache_resource = web_resource.NoResource() |
1271 self.putChildAll(C.CACHE_DIR.encode('utf-8'), self.cache_resource) | 1271 self.put_child_all(C.CACHE_DIR.encode('utf-8'), self.cache_resource) |
1272 self.cache_resource.putChild( | 1272 self.cache_resource.putChild( |
1273 b"common", ProtectedFile(str(self.cache_root_dir / Path("common")))) | 1273 b"common", ProtectedFile(str(self.cache_root_dir / Path("common")))) |
1274 | 1274 |
1275 # redirections | 1275 # redirections |
1276 for root in self.roots: | 1276 for root in self.roots: |
1277 await root._initRedirections(self.options) | 1277 await root._init_redirections(self.options) |
1278 | 1278 |
1279 # no need to keep url_redirections_dict, it will not be used anymore | 1279 # no need to keep url_redirections_dict, it will not be used anymore |
1280 del self.options["url_redirections_dict"] | 1280 del self.options["url_redirections_dict"] |
1281 | 1281 |
1282 server.Request.defaultContentType = "text/html; charset=utf-8" | 1282 server.Request.defaultContentType = "text/html; charset=utf-8" |
1284 self.vhost_root, [server.GzipEncoderFactory()] | 1284 self.vhost_root, [server.GzipEncoderFactory()] |
1285 ) | 1285 ) |
1286 self.site = server.Site(wrapped) | 1286 self.site = server.Site(wrapped) |
1287 self.site.sessionFactory = WebSession | 1287 self.site.sessionFactory = WebSession |
1288 | 1288 |
1289 def _bridgeCb(self): | 1289 def _bridge_cb(self): |
1290 del self._bridge_retry | 1290 del self._bridge_retry |
1291 self.bridge.getReady( | 1291 self.bridge.ready_get( |
1292 lambda: self.initialised.callback(None), | 1292 lambda: self.initialised.callback(None), |
1293 lambda failure: self.initialised.errback(Exception(failure)), | 1293 lambda failure: self.initialised.errback(Exception(failure)), |
1294 ) | 1294 ) |
1295 self.initialised.addCallback(lambda __: defer.ensureDeferred(self.backendReady())) | 1295 self.initialised.addCallback(lambda __: defer.ensureDeferred(self.backend_ready())) |
1296 | 1296 |
1297 def _bridgeEb(self, failure_): | 1297 def _bridge_eb(self, failure_): |
1298 if isinstance(failure_, BridgeExceptionNoService): | 1298 if isinstance(failure_, BridgeExceptionNoService): |
1299 if self._bridge_retry: | 1299 if self._bridge_retry: |
1300 if self._bridge_retry < 0: | 1300 if self._bridge_retry < 0: |
1301 print(_("Can't connect to bridge, will retry indefinitely. " | 1301 print(_("Can't connect to bridge, will retry indefinitely. " |
1302 "Next try in 1s.")) | 1302 "Next try in 1s.")) |
1307 "Can't connect to bridge, will retry in 1 s ({retries_left} " | 1307 "Can't connect to bridge, will retry in 1 s ({retries_left} " |
1308 "trie(s) left)." | 1308 "trie(s) left)." |
1309 ).format(retries_left=self._bridge_retry) | 1309 ).format(retries_left=self._bridge_retry) |
1310 ) | 1310 ) |
1311 time.sleep(1) | 1311 time.sleep(1) |
1312 self.bridge.bridgeConnect(callback=self._bridgeCb, errback=self._bridgeEb) | 1312 self.bridge.bridge_connect(callback=self._bridge_cb, errback=self._bridge_eb) |
1313 return | 1313 return |
1314 | 1314 |
1315 print("Can't connect to SàT backend, are you sure it's launched ?") | 1315 print("Can't connect to SàT backend, are you sure it's launched ?") |
1316 else: | 1316 else: |
1317 log.error("Can't connect to bridge: {}".format(failure)) | 1317 log.error("Can't connect to bridge: {}".format(failure)) |
1330 # we are in debug version, we add extra data | 1330 # we are in debug version, we add extra data |
1331 try: | 1331 try: |
1332 return self._version_cache | 1332 return self._version_cache |
1333 except AttributeError: | 1333 except AttributeError: |
1334 self._version_cache = "{} ({})".format( | 1334 self._version_cache = "{} ({})".format( |
1335 version, utils.getRepositoryData(libervia) | 1335 version, utils.get_repository_data(libervia) |
1336 ) | 1336 ) |
1337 return self._version_cache | 1337 return self._version_cache |
1338 else: | 1338 else: |
1339 return version | 1339 return version |
1340 | 1340 |
1341 def bridgeCall(self, method_name, *args, **kwargs): | 1341 def bridge_call(self, method_name, *args, **kwargs): |
1342 """Call an asynchronous bridge method and return a deferred | 1342 """Call an asynchronous bridge method and return a deferred |
1343 | 1343 |
1344 @param method_name: name of the method as a unicode | 1344 @param method_name: name of the method as a unicode |
1345 @return: a deferred which trigger the result | 1345 @return: a deferred which trigger the result |
1346 | 1346 |
1405 @return: a constant indicating the state: | 1405 @return: a constant indicating the state: |
1406 - C.PROFILE_LOGGED | 1406 - C.PROFILE_LOGGED |
1407 - C.PROFILE_LOGGED_EXT_JID | 1407 - C.PROFILE_LOGGED_EXT_JID |
1408 @raise exceptions.ConflictError: session is already active | 1408 @raise exceptions.ConflictError: session is already active |
1409 """ | 1409 """ |
1410 register_with_ext_jid = self.waiting_profiles.getRegisterWithExtJid(profile) | 1410 register_with_ext_jid = self.waiting_profiles.get_register_with_ext_jid(profile) |
1411 self.waiting_profiles.purgeRequest(profile) | 1411 self.waiting_profiles.purge_request(profile) |
1412 session = request.getSession() | 1412 session = request.getSession() |
1413 web_session = session_iface.IWebSession(session) | 1413 web_session = session_iface.IWebSession(session) |
1414 if web_session.profile: | 1414 if web_session.profile: |
1415 log.error(_("/!\\ Session has already a profile, this should NEVER happen!")) | 1415 log.error(_("/!\\ Session has already a profile, this should NEVER happen!")) |
1416 raise failure.Failure(exceptions.ConflictError("Already active")) | 1416 raise failure.Failure(exceptions.ConflictError("Already active")) |
1418 # XXX: we force string because python D-Bus has its own string type (dbus.String) | 1418 # XXX: we force string because python D-Bus has its own string type (dbus.String) |
1419 # which may cause trouble when exposing it to scripts | 1419 # which may cause trouble when exposing it to scripts |
1420 web_session.profile = str(profile) | 1420 web_session.profile = str(profile) |
1421 self.prof_connected.add(profile) | 1421 self.prof_connected.add(profile) |
1422 cache_dir = os.path.join( | 1422 cache_dir = os.path.join( |
1423 self.cache_root_dir, "profiles", regex.pathEscape(profile) | 1423 self.cache_root_dir, "profiles", regex.path_escape(profile) |
1424 ) | 1424 ) |
1425 # FIXME: would be better to have a global /cache URL which redirect to | 1425 # FIXME: would be better to have a global /cache URL which redirect to |
1426 # profile's cache directory, without uuid | 1426 # profile's cache directory, without uuid |
1427 self.cache_resource.putChild(web_session.uuid.encode('utf-8'), | 1427 self.cache_resource.putChild(web_session.uuid.encode('utf-8'), |
1428 ProtectedFile(cache_dir)) | 1428 ProtectedFile(cache_dir)) |
1440 ) | 1440 ) |
1441 web_session.on_expire() | 1441 web_session.on_expire() |
1442 if web_session.ws_socket is not None: | 1442 if web_session.ws_socket is not None: |
1443 web_session.ws_socket.close() | 1443 web_session.ws_socket.close() |
1444 # and now we disconnect the profile | 1444 # and now we disconnect the profile |
1445 self.bridgeCall("disconnect", profile) | 1445 self.bridge_call("disconnect", profile) |
1446 | 1446 |
1447 session.notifyOnExpire(on_expire) | 1447 session.notifyOnExpire(on_expire) |
1448 | 1448 |
1449 # FIXME: those session infos should be returned by connect or isConnected | 1449 # FIXME: those session infos should be returned by connect or is_connected |
1450 infos = await self.bridgeCall("sessionInfosGet", profile) | 1450 infos = await self.bridge_call("session_infos_get", profile) |
1451 web_session.jid = jid.JID(infos["jid"]) | 1451 web_session.jid = jid.JID(infos["jid"]) |
1452 own_bare_jid_s = web_session.jid.userhost() | 1452 own_bare_jid_s = web_session.jid.userhost() |
1453 own_id_raw = await self.bridgeCall( | 1453 own_id_raw = await self.bridge_call( |
1454 "identityGet", own_bare_jid_s, [], True, profile) | 1454 "identity_get", own_bare_jid_s, [], True, profile) |
1455 web_session.identities[own_bare_jid_s] = data_format.deserialise(own_id_raw) | 1455 web_session.identities[own_bare_jid_s] = data_format.deserialise(own_id_raw) |
1456 web_session.backend_started = int(infos["started"]) | 1456 web_session.backend_started = int(infos["started"]) |
1457 | 1457 |
1458 state = C.PROFILE_LOGGED_EXT_JID if register_with_ext_jid else C.PROFILE_LOGGED | 1458 state = C.PROFILE_LOGGED_EXT_JID if register_with_ext_jid else C.PROFILE_LOGGED |
1459 return state | 1459 return state |
1474 @raise exceptions.DataError: invalid login | 1474 @raise exceptions.DataError: invalid login |
1475 @raise exceptions.ProfileUnknownError: this login doesn't exist | 1475 @raise exceptions.ProfileUnknownError: this login doesn't exist |
1476 @raise exceptions.PermissionError: a login is not accepted (e.g. empty password | 1476 @raise exceptions.PermissionError: a login is not accepted (e.g. empty password |
1477 not allowed) | 1477 not allowed) |
1478 @raise exceptions.NotReady: a profile connection is already waiting | 1478 @raise exceptions.NotReady: a profile connection is already waiting |
1479 @raise exceptions.TimeoutError: didn't received and answer from Bridge | 1479 @raise exceptions.TimeoutError: didn't received and answer from bridge |
1480 @raise exceptions.InternalError: unknown error | 1480 @raise exceptions.InternalError: unknown error |
1481 @raise ValueError(C.PROFILE_AUTH_ERROR): invalid login and/or password | 1481 @raise ValueError(C.PROFILE_AUTH_ERROR): invalid login and/or password |
1482 @raise ValueError(C.XMPP_AUTH_ERROR): invalid XMPP account password | 1482 @raise ValueError(C.XMPP_AUTH_ERROR): invalid XMPP account password |
1483 """ | 1483 """ |
1484 | 1484 |
1497 login_jid = jid.JID(login) | 1497 login_jid = jid.JID(login) |
1498 except (RuntimeError, jid.InvalidFormat, AttributeError): | 1498 except (RuntimeError, jid.InvalidFormat, AttributeError): |
1499 raise failure.Failure(exceptions.DataError("No profile_key allowed")) | 1499 raise failure.Failure(exceptions.DataError("No profile_key allowed")) |
1500 | 1500 |
1501 # FIXME: should it be cached? | 1501 # FIXME: should it be cached? |
1502 new_account_domain = yield self.bridgeCall("getNewAccountDomain") | 1502 new_account_domain = yield self.bridge_call("account_domain_new_get") |
1503 | 1503 |
1504 if login_jid.host == new_account_domain: | 1504 if login_jid.host == new_account_domain: |
1505 # redirect "user@libervia.org" to the "user" profile | 1505 # redirect "user@libervia.org" to the "user" profile |
1506 login = login_jid.user | 1506 login = login_jid.user |
1507 login_jid = None | 1507 login_jid = None |
1508 else: | 1508 else: |
1509 login_jid = None | 1509 login_jid = None |
1510 | 1510 |
1511 try: | 1511 try: |
1512 profile = yield self.bridgeCall("profileNameGet", login) | 1512 profile = yield self.bridge_call("profile_name_get", login) |
1513 except Exception: # XXX: ProfileUnknownError wouldn't work, it's encapsulated | 1513 except Exception: # XXX: ProfileUnknownError wouldn't work, it's encapsulated |
1514 # FIXME: find a better way to handle bridge errors | 1514 # FIXME: find a better way to handle bridge errors |
1515 if ( | 1515 if ( |
1516 login_jid is not None and login_jid.user | 1516 login_jid is not None and login_jid.user |
1517 ): # try to create a new sat profile using the XMPP credentials | 1517 ): # try to create a new sat profile using the XMPP credentials |
1523 exceptions.DataError( | 1523 exceptions.DataError( |
1524 "JID login while registration is not allowed" | 1524 "JID login while registration is not allowed" |
1525 ) | 1525 ) |
1526 ) | 1526 ) |
1527 profile = login # FIXME: what if there is a resource? | 1527 profile = login # FIXME: what if there is a resource? |
1528 connect_method = "asyncConnectWithXMPPCredentials" | 1528 connect_method = "credentials_xmpp_connect" |
1529 register_with_ext_jid = True | 1529 register_with_ext_jid = True |
1530 else: # non existing username | 1530 else: # non existing username |
1531 raise failure.Failure(exceptions.ProfileUnknownError()) | 1531 raise failure.Failure(exceptions.ProfileUnknownError()) |
1532 else: | 1532 else: |
1533 if profile != login or ( | 1533 if profile != login or ( |
1549 # it's a different profile, we need to disconnect it | 1549 # it's a different profile, we need to disconnect it |
1550 log.warning(_( | 1550 log.warning(_( |
1551 "{new_profile} requested login, but {old_profile} was already " | 1551 "{new_profile} requested login, but {old_profile} was already " |
1552 "connected, disconnecting {old_profile}").format( | 1552 "connected, disconnecting {old_profile}").format( |
1553 old_profile=web_session.profile, new_profile=profile)) | 1553 old_profile=web_session.profile, new_profile=profile)) |
1554 self.purgeSession(request) | 1554 self.purge_session(request) |
1555 | 1555 |
1556 if self.waiting_profiles.getRequest(profile): | 1556 if self.waiting_profiles.get_request(profile): |
1557 # FIXME: check if and when this can happen | 1557 # FIXME: check if and when this can happen |
1558 raise failure.Failure(exceptions.NotReady("Already waiting")) | 1558 raise failure.Failure(exceptions.NotReady("Already waiting")) |
1559 | 1559 |
1560 self.waiting_profiles.setRequest(request, profile, register_with_ext_jid) | 1560 self.waiting_profiles.set_request(request, profile, register_with_ext_jid) |
1561 try: | 1561 try: |
1562 connected = yield self.bridgeCall(connect_method, profile, password) | 1562 connected = yield self.bridge_call(connect_method, profile, password) |
1563 except Exception as failure_: | 1563 except Exception as failure_: |
1564 fault = getattr(failure_, 'classname', None) | 1564 fault = getattr(failure_, 'classname', None) |
1565 self.waiting_profiles.purgeRequest(profile) | 1565 self.waiting_profiles.purge_request(profile) |
1566 if fault in ("PasswordError", "ProfileUnknownError"): | 1566 if fault in ("PasswordError", "ProfileUnknownError"): |
1567 log.info("Profile {profile} doesn't exist or the submitted password is " | 1567 log.info("Profile {profile} doesn't exist or the submitted password is " |
1568 "wrong".format( profile=profile)) | 1568 "wrong".format( profile=profile)) |
1569 raise failure.Failure(ValueError(C.PROFILE_AUTH_ERROR)) | 1569 raise failure.Failure(ValueError(C.PROFILE_AUTH_ERROR)) |
1570 elif fault == "SASLAuthError": | 1570 elif fault == "SASLAuthError": |
1609 # no, we have to create it | 1609 # no, we have to create it |
1610 | 1610 |
1611 state = yield defer.ensureDeferred(self._logged(profile, request)) | 1611 state = yield defer.ensureDeferred(self._logged(profile, request)) |
1612 defer.returnValue(state) | 1612 defer.returnValue(state) |
1613 | 1613 |
1614 def registerNewAccount(self, request, login, password, email): | 1614 def register_new_account(self, request, login, password, email): |
1615 """Create a new account, or return error | 1615 """Create a new account, or return error |
1616 @param request(server.Request): request linked to the session | 1616 @param request(server.Request): request linked to the session |
1617 @param login(unicode): new account requested login | 1617 @param login(unicode): new account requested login |
1618 @param email(unicode): new account email | 1618 @param email(unicode): new account email |
1619 @param password(unicode): new account password | 1619 @param password(unicode): new account password |
1641 return C.INVALID_INPUT | 1641 return C.INVALID_INPUT |
1642 | 1642 |
1643 def registered(result): | 1643 def registered(result): |
1644 return C.REGISTRATION_SUCCEED | 1644 return C.REGISTRATION_SUCCEED |
1645 | 1645 |
1646 def registeringError(failure_): | 1646 def registering_error(failure_): |
1647 # FIXME: better error handling for bridge error is needed | 1647 # FIXME: better error handling for bridge error is needed |
1648 status = failure_.value.fullname.split('.')[-1] | 1648 status = failure_.value.fullname.split('.')[-1] |
1649 if status == "ConflictError": | 1649 if status == "ConflictError": |
1650 return C.ALREADY_EXISTS | 1650 return C.ALREADY_EXISTS |
1651 elif status == "InvalidCertificate": | 1651 elif status == "InvalidCertificate": |
1658 status=status, traceback=failure_.value.message | 1658 status=status, traceback=failure_.value.message |
1659 ) | 1659 ) |
1660 ) | 1660 ) |
1661 return status | 1661 return status |
1662 | 1662 |
1663 d = self.bridgeCall("registerSatAccount", email, password, login) | 1663 d = self.bridge_call("libervia_account_register", email, password, login) |
1664 d.addCallback(registered) | 1664 d.addCallback(registered) |
1665 d.addErrback(registeringError) | 1665 d.addErrback(registering_error) |
1666 return d | 1666 return d |
1667 | 1667 |
1668 def addCleanup(self, callback, *args, **kwargs): | 1668 def addCleanup(self, callback, *args, **kwargs): |
1669 """Add cleaning method to call when service is stopped | 1669 """Add cleaning method to call when service is stopped |
1670 | 1670 |
1672 @param callback: callable to call on service stop | 1672 @param callback: callable to call on service stop |
1673 @param *args: list of arguments of the callback | 1673 @param *args: list of arguments of the callback |
1674 @param **kwargs: list of keyword arguments of the callback""" | 1674 @param **kwargs: list of keyword arguments of the callback""" |
1675 self._cleanup.insert(0, (callback, args, kwargs)) | 1675 self._cleanup.insert(0, (callback, args, kwargs)) |
1676 | 1676 |
1677 def initEb(self, failure): | 1677 def init_eb(self, failure): |
1678 from twisted.application import app | 1678 from twisted.application import app |
1679 if failure.check(SysExit): | 1679 if failure.check(SysExit): |
1680 if failure.value.message: | 1680 if failure.value.message: |
1681 log.error(failure.value.message) | 1681 log.error(failure.value.message) |
1682 app._exitCode = failure.value.exit_code | 1682 app._exitCode = failure.value.exit_code |
1685 log.error(_("Init error: {msg}").format(msg=failure)) | 1685 log.error(_("Init error: {msg}").format(msg=failure)) |
1686 app._exitCode = C.EXIT_INTERNAL_ERROR | 1686 app._exitCode = C.EXIT_INTERNAL_ERROR |
1687 reactor.stop() | 1687 reactor.stop() |
1688 return failure | 1688 return failure |
1689 | 1689 |
1690 def _buildOnlyCb(self, __): | 1690 def _build_only_cb(self, __): |
1691 log.info(_("Stopping here due to --build-only flag")) | 1691 log.info(_("Stopping here due to --build-only flag")) |
1692 self.stop() | 1692 self.stop() |
1693 | 1693 |
1694 def startService(self): | 1694 def startService(self): |
1695 """Connect the profile for Libervia and start the HTTP(S) server(s)""" | 1695 """Connect the profile for Libervia and start the HTTP(S) server(s)""" |
1696 self._init() | 1696 self._init() |
1697 if self.options['build-only']: | 1697 if self.options['build-only']: |
1698 self.initialised.addCallback(self._buildOnlyCb) | 1698 self.initialised.addCallback(self._build_only_cb) |
1699 else: | 1699 else: |
1700 self.initialised.addCallback(self._startService) | 1700 self.initialised.addCallback(self._start_service) |
1701 self.initialised.addErrback(self.initEb) | 1701 self.initialised.addErrback(self.init_eb) |
1702 | 1702 |
1703 ## URLs ## | 1703 ## URLs ## |
1704 | 1704 |
1705 def putChildSAT(self, path, resource): | 1705 def put_child_sat(self, path, resource): |
1706 """Add a child to the sat resource""" | 1706 """Add a child to the sat resource""" |
1707 if not isinstance(path, bytes): | 1707 if not isinstance(path, bytes): |
1708 raise ValueError("path must be specified in bytes") | 1708 raise ValueError("path must be specified in bytes") |
1709 self.sat_root.putChild(path, resource) | 1709 self.sat_root.putChild(path, resource) |
1710 | 1710 |
1711 def putChildAll(self, path, resource): | 1711 def put_child_all(self, path, resource): |
1712 """Add a child to all vhost root resources""" | 1712 """Add a child to all vhost root resources""" |
1713 if not isinstance(path, bytes): | 1713 if not isinstance(path, bytes): |
1714 raise ValueError("path must be specified in bytes") | 1714 raise ValueError("path must be specified in bytes") |
1715 # we wrap before calling putChild, to avoid having useless multiple instances | 1715 # we wrap before calling putChild, to avoid having useless multiple instances |
1716 # of the resource | 1716 # of the resource |
1718 wrapped_res = web_resource.EncodingResourceWrapper( | 1718 wrapped_res = web_resource.EncodingResourceWrapper( |
1719 resource, [server.GzipEncoderFactory()]) | 1719 resource, [server.GzipEncoderFactory()]) |
1720 for root in self.roots: | 1720 for root in self.roots: |
1721 root.putChild(path, wrapped_res) | 1721 root.putChild(path, wrapped_res) |
1722 | 1722 |
1723 def getBuildPath(self, site_name: str, dev: bool=False) -> Path: | 1723 def get_build_path(self, site_name: str, dev: bool=False) -> Path: |
1724 """Generate build path for a given site name | 1724 """Generate build path for a given site name |
1725 | 1725 |
1726 @param site_name: name of the site | 1726 @param site_name: name of the site |
1727 @param dev: return dev build dir if True, production one otherwise | 1727 @param dev: return dev build dir if True, production one otherwise |
1728 dev build dir is used for installing dependencies needed temporarily (e.g. | 1728 dev build dir is used for installing dependencies needed temporarily (e.g. |
1730 HTTP server, where final files are downloaded. | 1730 HTTP server, where final files are downloaded. |
1731 @return: path to the build directory | 1731 @return: path to the build directory |
1732 """ | 1732 """ |
1733 sub_dir = C.DEV_BUILD_DIR if dev else C.PRODUCTION_BUILD_DIR | 1733 sub_dir = C.DEV_BUILD_DIR if dev else C.PRODUCTION_BUILD_DIR |
1734 build_path_elts = [ | 1734 build_path_elts = [ |
1735 config.getConfig(self.main_conf, "", "local_dir"), | 1735 config.config_get(self.main_conf, "", "local_dir"), |
1736 C.CACHE_DIR, | 1736 C.CACHE_DIR, |
1737 C.LIBERVIA_CACHE, | 1737 C.LIBERVIA_CACHE, |
1738 sub_dir, | 1738 sub_dir, |
1739 regex.pathEscape(site_name or C.SITE_NAME_DEFAULT)] | 1739 regex.path_escape(site_name or C.SITE_NAME_DEFAULT)] |
1740 build_path = Path("/".join(build_path_elts)) | 1740 build_path = Path("/".join(build_path_elts)) |
1741 return build_path.expanduser().resolve() | 1741 return build_path.expanduser().resolve() |
1742 | 1742 |
1743 def getExtBaseURLData(self, request): | 1743 def get_ext_base_url_data(self, request): |
1744 """Retrieve external base URL Data | 1744 """Retrieve external base URL Data |
1745 | 1745 |
1746 this method try to retrieve the base URL found by external user | 1746 this method try to retrieve the base URL found by external user |
1747 It does by checking in this order: | 1747 It does by checking in this order: |
1748 - base_url_ext option from configuration | 1748 - base_url_ext option from configuration |
1786 ext_data.path or "/", | 1786 ext_data.path or "/", |
1787 "", | 1787 "", |
1788 "", | 1788 "", |
1789 ) | 1789 ) |
1790 | 1790 |
1791 def getExtBaseURL( | 1791 def get_ext_base_url( |
1792 self, | 1792 self, |
1793 request: server.Request, | 1793 request: server.Request, |
1794 path: str = "", | 1794 path: str = "", |
1795 query: str = "", | 1795 query: str = "", |
1796 fragment: str = "", | 1796 fragment: str = "", |
1805 @param query: same as for urlsplit.urlsplit | 1805 @param query: same as for urlsplit.urlsplit |
1806 @param fragment: same as for urlsplit.urlsplit | 1806 @param fragment: same as for urlsplit.urlsplit |
1807 @param scheme: if not None, will override scheme from base URL | 1807 @param scheme: if not None, will override scheme from base URL |
1808 @return: external URL | 1808 @return: external URL |
1809 """ | 1809 """ |
1810 split_result = self.getExtBaseURLData(request) | 1810 split_result = self.get_ext_base_url_data(request) |
1811 return urllib.parse.urlunsplit( | 1811 return urllib.parse.urlunsplit( |
1812 ( | 1812 ( |
1813 split_result.scheme if scheme is None else scheme, | 1813 split_result.scheme if scheme is None else scheme, |
1814 split_result.netloc, | 1814 split_result.netloc, |
1815 os.path.join(split_result.path, path), | 1815 os.path.join(split_result.path, path), |
1816 query, | 1816 query, |
1817 fragment, | 1817 fragment, |
1818 ) | 1818 ) |
1819 ) | 1819 ) |
1820 | 1820 |
1821 def checkRedirection(self, vhost_root: LiberviaRootResource, url_path: str) -> str: | 1821 def check_redirection(self, vhost_root: LiberviaRootResource, url_path: str) -> str: |
1822 """check is a part of the URL prefix is redirected then replace it | 1822 """check is a part of the URL prefix is redirected then replace it |
1823 | 1823 |
1824 @param vhost_root: root of this virtual host | 1824 @param vhost_root: root of this virtual host |
1825 @param url_path: path of the url to check | 1825 @param url_path: path of the url to check |
1826 @return: possibly redirected URL which should link to the same location | 1826 @return: possibly redirected URL which should link to the same location |
1836 ) | 1836 ) |
1837 return url_path | 1837 return url_path |
1838 | 1838 |
1839 ## Sessions ## | 1839 ## Sessions ## |
1840 | 1840 |
1841 def purgeSession(self, request): | 1841 def purge_session(self, request): |
1842 """helper method to purge a session during request handling""" | 1842 """helper method to purge a session during request handling""" |
1843 session = request.session | 1843 session = request.session |
1844 if session is not None: | 1844 if session is not None: |
1845 log.debug(_("session purge")) | 1845 log.debug(_("session purge")) |
1846 web_session = self.getSessionData(request, session_iface.IWebSession) | 1846 web_session = self.get_session_data(request, session_iface.IWebSession) |
1847 socket = web_session.ws_socket | 1847 socket = web_session.ws_socket |
1848 if socket is not None: | 1848 if socket is not None: |
1849 socket.close() | 1849 socket.close() |
1850 session.ws_socket = None | 1850 session.ws_socket = None |
1851 session.expire() | 1851 session.expire() |
1852 # FIXME: not clean but it seems that it's the best way to reset | 1852 # FIXME: not clean but it seems that it's the best way to reset |
1853 # session during request handling | 1853 # session during request handling |
1854 request._secureSession = request._insecureSession = None | 1854 request._secureSession = request._insecureSession = None |
1855 | 1855 |
1856 def getSessionData(self, request, *args): | 1856 def get_session_data(self, request, *args): |
1857 """helper method to retrieve session data | 1857 """helper method to retrieve session data |
1858 | 1858 |
1859 @param request(server.Request): request linked to the session | 1859 @param request(server.Request): request linked to the session |
1860 @param *args(zope.interface.Interface): interface of the session to get | 1860 @param *args(zope.interface.Interface): interface of the session to get |
1861 @return (iterator(data)): requested session data | 1861 @return (iterator(data)): requested session data |
1865 return args[0](session) | 1865 return args[0](session) |
1866 else: | 1866 else: |
1867 return (iface(session) for iface in args) | 1867 return (iface(session) for iface in args) |
1868 | 1868 |
1869 @defer.inlineCallbacks | 1869 @defer.inlineCallbacks |
1870 def getAffiliation(self, request, service, node): | 1870 def get_affiliation(self, request, service, node): |
1871 """retrieve pubsub node affiliation for current user | 1871 """retrieve pubsub node affiliation for current user |
1872 | 1872 |
1873 use cache first, and request pubsub service if not cache is found | 1873 use cache first, and request pubsub service if not cache is found |
1874 @param request(server.Request): request linked to the session | 1874 @param request(server.Request): request linked to the session |
1875 @param service(jid.JID): pubsub service | 1875 @param service(jid.JID): pubsub service |
1876 @param node(unicode): pubsub node | 1876 @param node(unicode): pubsub node |
1877 @return (unicode): affiliation | 1877 @return (unicode): affiliation |
1878 """ | 1878 """ |
1879 web_session = self.getSessionData(request, session_iface.IWebSession) | 1879 web_session = self.get_session_data(request, session_iface.IWebSession) |
1880 if web_session.profile is None: | 1880 if web_session.profile is None: |
1881 raise exceptions.InternalError("profile must be set to use this method") | 1881 raise exceptions.InternalError("profile must be set to use this method") |
1882 affiliation = web_session.getAffiliation(service, node) | 1882 affiliation = web_session.get_affiliation(service, node) |
1883 if affiliation is not None: | 1883 if affiliation is not None: |
1884 defer.returnValue(affiliation) | 1884 defer.returnValue(affiliation) |
1885 else: | 1885 else: |
1886 try: | 1886 try: |
1887 affiliations = yield self.bridgeCall( | 1887 affiliations = yield self.bridge_call( |
1888 "psAffiliationsGet", service.full(), node, web_session.profile | 1888 "ps_affiliations_get", service.full(), node, web_session.profile |
1889 ) | 1889 ) |
1890 except Exception as e: | 1890 except Exception as e: |
1891 log.warning( | 1891 log.warning( |
1892 "Can't retrieve affiliation for {service}/{node}: {reason}".format( | 1892 "Can't retrieve affiliation for {service}/{node}: {reason}".format( |
1893 service=service, node=node, reason=e | 1893 service=service, node=node, reason=e |
1897 else: | 1897 else: |
1898 try: | 1898 try: |
1899 affiliation = affiliations[node] | 1899 affiliation = affiliations[node] |
1900 except KeyError: | 1900 except KeyError: |
1901 affiliation = "" | 1901 affiliation = "" |
1902 web_session.setAffiliation(service, node, affiliation) | 1902 web_session.set_affiliation(service, node, affiliation) |
1903 defer.returnValue(affiliation) | 1903 defer.returnValue(affiliation) |
1904 | 1904 |
1905 ## Websocket (dynamic pages) ## | 1905 ## Websocket (dynamic pages) ## |
1906 | 1906 |
1907 def get_websocket_url(self, request): | 1907 def get_websocket_url(self, request): |
1908 base_url_split = self.getExtBaseURLData(request) | 1908 base_url_split = self.get_ext_base_url_data(request) |
1909 if base_url_split.scheme.endswith("s"): | 1909 if base_url_split.scheme.endswith("s"): |
1910 scheme = "wss" | 1910 scheme = "wss" |
1911 else: | 1911 else: |
1912 scheme = "ws" | 1912 scheme = "ws" |
1913 | 1913 |
1914 return self.getExtBaseURL(request, path=scheme, scheme=scheme) | 1914 return self.get_ext_base_url(request, path=scheme, scheme=scheme) |
1915 | 1915 |
1916 | 1916 |
1917 ## Various utils ## | 1917 ## Various utils ## |
1918 | 1918 |
1919 def getHTTPDate(self, timestamp=None): | 1919 def get_http_date(self, timestamp=None): |
1920 now = time.gmtime(timestamp) | 1920 now = time.gmtime(timestamp) |
1921 fmt_date = "{day_name}, %d {month_name} %Y %H:%M:%S GMT".format( | 1921 fmt_date = "{day_name}, %d {month_name} %Y %H:%M:%S GMT".format( |
1922 day_name=C.HTTP_DAYS[now.tm_wday], month_name=C.HTTP_MONTH[now.tm_mon - 1] | 1922 day_name=C.HTTP_DAYS[now.tm_wday], month_name=C.HTTP_MONTH[now.tm_mon - 1] |
1923 ) | 1923 ) |
1924 return time.strftime(fmt_date, now) | 1924 return time.strftime(fmt_date, now) |
1925 | 1925 |
1926 ## service management ## | 1926 ## service management ## |
1927 | 1927 |
1928 def _startService(self, __=None): | 1928 def _start_service(self, __=None): |
1929 """Actually start the HTTP(S) server(s) after the profile for Libervia is connected. | 1929 """Actually start the HTTP(S) server(s) after the profile for Libervia is connected. |
1930 | 1930 |
1931 @raise ImportError: OpenSSL is not available | 1931 @raise ImportError: OpenSSL is not available |
1932 @raise IOError: the certificate file doesn't exist | 1932 @raise IOError: the certificate file doesn't exist |
1933 @raise OpenSSL.crypto.Error: the certificate file is invalid | 1933 @raise OpenSSL.crypto.Error: the certificate file is invalid |
1934 """ | 1934 """ |
1935 # now that we have service profile connected, we add resource for its cache | 1935 # now that we have service profile connected, we add resource for its cache |
1936 service_path = regex.pathEscape(C.SERVICE_PROFILE) | 1936 service_path = regex.path_escape(C.SERVICE_PROFILE) |
1937 cache_dir = os.path.join(self.cache_root_dir, "profiles", service_path) | 1937 cache_dir = os.path.join(self.cache_root_dir, "profiles", service_path) |
1938 self.cache_resource.putChild(service_path.encode('utf-8'), | 1938 self.cache_resource.putChild(service_path.encode('utf-8'), |
1939 ProtectedFile(cache_dir)) | 1939 ProtectedFile(cache_dir)) |
1940 self.service_cache_url = "/" + os.path.join(C.CACHE_DIR, service_path) | 1940 self.service_cache_url = "/" + os.path.join(C.CACHE_DIR, service_path) |
1941 session_iface.WebSession.service_cache_url = self.service_cache_url | 1941 session_iface.WebSession.service_cache_url = self.service_cache_url |
1942 | 1942 |
1943 if self.options["connection_type"] in ("https", "both"): | 1943 if self.options["connection_type"] in ("https", "both"): |
1944 try: | 1944 try: |
1945 tls.TLSOptionsCheck(self.options) | 1945 tls.tls_options_check(self.options) |
1946 context_factory = tls.getTLSContextFactory(self.options) | 1946 context_factory = tls.get_tls_context_factory(self.options) |
1947 except exceptions.ConfigError as e: | 1947 except exceptions.ConfigError as e: |
1948 log.warning( | 1948 log.warning( |
1949 f"There is a problem in TLS settings in your configuration file: {e}") | 1949 f"There is a problem in TLS settings in your configuration file: {e}") |
1950 self.quit(2) | 1950 self.quit(2) |
1951 except exceptions.DataError as e: | 1951 except exceptions.DataError as e: |
1973 def stopService(self): | 1973 def stopService(self): |
1974 log.info(_("launching cleaning methods")) | 1974 log.info(_("launching cleaning methods")) |
1975 for callback, args, kwargs in self._cleanup: | 1975 for callback, args, kwargs in self._cleanup: |
1976 callback(*args, **kwargs) | 1976 callback(*args, **kwargs) |
1977 try: | 1977 try: |
1978 yield self.bridgeCall("disconnect", C.SERVICE_PROFILE) | 1978 yield self.bridge_call("disconnect", C.SERVICE_PROFILE) |
1979 except Exception: | 1979 except Exception: |
1980 log.warning("Can't disconnect service profile") | 1980 log.warning("Can't disconnect service profile") |
1981 | 1981 |
1982 def run(self): | 1982 def run(self): |
1983 reactor.run() | 1983 reactor.run() |