internal.py 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. # This file is part of Radicale Server - Calendar Server
  2. # Copyright © 2017-2018 Unrud<unrud@outlook.com>
  3. #
  4. # This library is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This library is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
  16. import os
  17. import time
  18. from http import client
  19. import pkg_resources
  20. from radicale import httputils, pathutils, web
  21. from radicale.log import logger
  22. import posixpath # isort:skip
  23. MIMETYPES = {
  24. ".css": "text/css",
  25. ".eot": "application/vnd.ms-fontobject",
  26. ".gif": "image/gif",
  27. ".html": "text/html",
  28. ".js": "application/javascript",
  29. ".manifest": "text/cache-manifest",
  30. ".png": "image/png",
  31. ".svg": "image/svg+xml",
  32. ".ttf": "application/font-sfnt",
  33. ".txt": "text/plain",
  34. ".woff": "application/font-woff",
  35. ".woff2": "font/woff2",
  36. ".xml": "text/xml"}
  37. FALLBACK_MIMETYPE = "application/octet-stream"
  38. class Web(web.BaseWeb):
  39. def __init__(self, configuration):
  40. super().__init__(configuration)
  41. self.folder = pkg_resources.resource_filename(__name__,
  42. "internal_data")
  43. def get(self, environ, base_prefix, path, user):
  44. assert path == "/.web" or path.startswith("/.web/")
  45. assert pathutils.sanitize_path(path) == path
  46. try:
  47. filesystem_path = pathutils.path_to_filesystem(
  48. self.folder, path[len("/.web"):].strip("/"))
  49. except ValueError as e:
  50. logger.debug("Web content with unsafe path %r requested: %s",
  51. path, e, exc_info=True)
  52. return httputils.NOT_FOUND
  53. if os.path.isdir(filesystem_path) and not path.endswith("/"):
  54. location = posixpath.basename(path) + "/"
  55. return (client.FOUND,
  56. {"Location": location, "Content-Type": "text/plain"},
  57. "Redirected to %s" % location)
  58. if os.path.isdir(filesystem_path):
  59. filesystem_path = os.path.join(filesystem_path, "index.html")
  60. if not os.path.isfile(filesystem_path):
  61. return httputils.NOT_FOUND
  62. content_type = MIMETYPES.get(
  63. os.path.splitext(filesystem_path)[1].lower(), FALLBACK_MIMETYPE)
  64. with open(filesystem_path, "rb") as f:
  65. answer = f.read()
  66. last_modified = time.strftime(
  67. "%a, %d %b %Y %H:%M:%S GMT",
  68. time.gmtime(os.fstat(f.fileno()).st_mtime))
  69. headers = {
  70. "Content-Type": content_type,
  71. "Last-Modified": last_modified}
  72. return client.OK, headers, answer