get.py 3.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  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. import posixpath
  20. from http import client
  21. from urllib.parse import quote
  22. from radicale import httputils, pathutils, storage, xmlutils
  23. from radicale.log import logger
  24. def propose_filename(collection):
  25. """Propose a filename for a collection."""
  26. tag = collection.get_meta("tag")
  27. if tag == "VADDRESSBOOK":
  28. fallback_title = "Address book"
  29. suffix = ".vcf"
  30. elif tag == "VCALENDAR":
  31. fallback_title = "Calendar"
  32. suffix = ".ics"
  33. else:
  34. fallback_title = posixpath.basename(collection.path)
  35. suffix = ""
  36. title = collection.get_meta("D:displayname") or fallback_title
  37. if title and not title.lower().endswith(suffix.lower()):
  38. title += suffix
  39. return title
  40. class ApplicationGetMixin:
  41. def _content_disposition_attachement(self, filename):
  42. value = "attachement"
  43. try:
  44. encoded_filename = quote(filename, encoding=self.encoding)
  45. except UnicodeEncodeError:
  46. logger.warning("Failed to encode filename: %r", filename,
  47. exc_info=True)
  48. encoded_filename = ""
  49. if encoded_filename:
  50. value += "; filename*=%s''%s" % (self.encoding, encoded_filename)
  51. return value
  52. def do_GET(self, environ, base_prefix, path, user):
  53. """Manage GET request."""
  54. # Redirect to .web if the root URL is requested
  55. if not pathutils.strip_path(path):
  56. web_path = ".web"
  57. if not environ.get("PATH_INFO"):
  58. web_path = posixpath.join(posixpath.basename(base_prefix),
  59. web_path)
  60. return (client.FOUND,
  61. {"Location": web_path, "Content-Type": "text/plain"},
  62. "Redirected to %s" % web_path)
  63. # Dispatch .web URL to web module
  64. if path == "/.web" or path.startswith("/.web/"):
  65. return self.Web.get(environ, base_prefix, path, user)
  66. if not self.access(user, path, "r"):
  67. return httputils.NOT_ALLOWED
  68. with self.storage.acquire_lock("r", user):
  69. item = next(self.storage.discover(path), None)
  70. if not item:
  71. return httputils.NOT_FOUND
  72. if not self.access(user, path, "r", item):
  73. return httputils.NOT_ALLOWED
  74. if isinstance(item, storage.BaseCollection):
  75. tag = item.get_meta("tag")
  76. if not tag:
  77. return httputils.DIRECTORY_LISTING
  78. content_type = xmlutils.MIMETYPES[tag]
  79. content_disposition = self._content_disposition_attachement(
  80. propose_filename(item))
  81. else:
  82. content_type = xmlutils.OBJECT_MIMETYPES[item.name]
  83. content_disposition = ""
  84. headers = {
  85. "Content-Type": content_type,
  86. "Last-Modified": item.last_modified,
  87. "ETag": item.etag}
  88. if content_disposition:
  89. headers["Content-Disposition"] = content_disposition
  90. answer = item.serialize()
  91. return client.OK, headers, answer