get.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. # This file is part of Radicale Server - Calendar Server
  2. # Copyright © 2008 Nicolas Kandel
  3. # Copyright © 2008 Pascal Halter
  4. # Copyright © 2008-2017 Guillaume Ayoub
  5. # Copyright © 2017-2018 Unrud<unrud@outlook.com>
  6. #
  7. # This library is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This library is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
  19. """
  20. Radicale WSGI application.
  21. Can be used with an external WSGI server or the built-in server.
  22. """
  23. import posixpath
  24. from http import client
  25. from urllib.parse import quote
  26. from radicale import httputils, pathutils, storage, xmlutils
  27. from radicale.log import logger
  28. def propose_filename(collection):
  29. """Propose a filename for a collection."""
  30. tag = collection.get_meta("tag")
  31. if tag == "VADDRESSBOOK":
  32. fallback_title = "Address book"
  33. suffix = ".vcf"
  34. elif tag == "VCALENDAR":
  35. fallback_title = "Calendar"
  36. suffix = ".ics"
  37. else:
  38. fallback_title = posixpath.basename(collection.path)
  39. suffix = ""
  40. title = collection.get_meta("D:displayname") or fallback_title
  41. if title and not title.lower().endswith(suffix.lower()):
  42. title += suffix
  43. return title
  44. class ApplicationGetMixin:
  45. def _content_disposition_attachement(self, filename):
  46. value = "attachement"
  47. try:
  48. encoded_filename = quote(filename, encoding=self.encoding)
  49. except UnicodeEncodeError as e:
  50. logger.warning("Failed to encode filename: %r", filename,
  51. exc_info=True)
  52. encoded_filename = ""
  53. if encoded_filename:
  54. value += "; filename*=%s''%s" % (self.encoding, encoded_filename)
  55. return value
  56. def do_GET(self, environ, base_prefix, path, user):
  57. """Manage GET request."""
  58. # Redirect to .web if the root URL is requested
  59. if not pathutils.strip_path(path):
  60. web_path = ".web"
  61. if not environ.get("PATH_INFO"):
  62. web_path = posixpath.join(posixpath.basename(base_prefix),
  63. web_path)
  64. return (client.FOUND,
  65. {"Location": web_path, "Content-Type": "text/plain"},
  66. "Redirected to %s" % web_path)
  67. # Dispatch .web URL to web module
  68. if path == "/.web" or path.startswith("/.web/"):
  69. return self.Web.get(environ, base_prefix, path, user)
  70. if not self.access(user, path, "r"):
  71. return httputils.NOT_ALLOWED
  72. with self.Collection.acquire_lock("r", user):
  73. item = next(self.Collection.discover(path), None)
  74. if not item:
  75. return httputils.NOT_FOUND
  76. if not self.access(user, path, "r", item):
  77. return httputils.NOT_ALLOWED
  78. if isinstance(item, storage.BaseCollection):
  79. tag = item.get_meta("tag")
  80. if not tag:
  81. return httputils.DIRECTORY_LISTING
  82. content_type = xmlutils.MIMETYPES[tag]
  83. content_disposition = self._content_disposition_attachement(
  84. propose_filename(item))
  85. else:
  86. content_type = xmlutils.OBJECT_MIMETYPES[item.name]
  87. content_disposition = ""
  88. headers = {
  89. "Content-Type": content_type,
  90. "Last-Modified": item.last_modified,
  91. "ETag": item.etag}
  92. if content_disposition:
  93. headers["Content-Disposition"] = content_disposition
  94. answer = item.serialize()
  95. return client.OK, headers, answer