xmlutils.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. # -*- coding: utf-8; indent-tabs-mode: nil; -*-
  2. #
  3. # This file is part of Radicale Server - Calendar Server
  4. # Copyright © 2008 The Radicale Team
  5. #
  6. # This library is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This library is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
  18. """
  19. XML and iCal requests manager
  20. Note that all these functions need to receive unicode objects for full
  21. iCal requests (PUT) and string objects with charset correctly defined
  22. in them for XML requests (all but PUT).
  23. """
  24. # TODO: Manage errors (see __init__)
  25. # TODO: Manage depth and calendars/collections (see main)
  26. import xml.etree.ElementTree as ET
  27. import config
  28. import ical
  29. # TODO: This is a well-known and accepted hack for ET
  30. for key,value in config.items("namespace"):
  31. ET._namespace_map[value] = key
  32. def _tag(shortName, local):
  33. """
  34. Get XML Clark notation {uri(shortName)}local
  35. """
  36. return "{%s}%s"%(config.get("namespace", shortName), local)
  37. def delete(obj, calendar, url):
  38. """
  39. Read and answer DELETE requests
  40. """
  41. # Read rfc4918-9.6 for info
  42. # Reading request
  43. calendar.remove(obj)
  44. # Writing answer
  45. multistatus = ET.Element(_tag("D", "multistatus"))
  46. response = ET.Element(_tag("D", "response"))
  47. multistatus.append(response)
  48. href = ET.Element(_tag("D", "href"))
  49. href.text = url
  50. response.append(href)
  51. status = ET.Element(_tag("D", "status"))
  52. status.text = config.get("status", "200")
  53. response.append(status)
  54. return ET.tostring(multistatus, config.get("encoding", "request"))
  55. def propfind(xmlRequest, calendar, url):
  56. """
  57. Read and answer PROPFIND requests
  58. """
  59. # Read rfc4918-9.1 for info
  60. # Reading request
  61. root = ET.fromstring(xmlRequest)
  62. propElement = root.find(_tag("D", "prop"))
  63. propList = propElement.getchildren()
  64. properties = [property.tag for property in propList]
  65. # Writing answer
  66. multistatus = ET.Element(_tag("D", "multistatus"))
  67. response = ET.Element(_tag("D", "response"))
  68. multistatus.append(response)
  69. href = ET.Element(_tag("D", "href"))
  70. href.text = url
  71. response.append(href)
  72. propstat = ET.Element(_tag("D", "propstat"))
  73. response.append(propstat)
  74. prop = ET.Element(_tag("D", "prop"))
  75. propstat.append(prop)
  76. if _tag("D", "resourcetype") in properties:
  77. resourcetype = ET.Element(_tag("D", "resourcetype"))
  78. resourcetype.append(ET.Element(_tag("D", "collection")))
  79. resourcetype.append(ET.Element(_tag("C", "calendar")))
  80. prop.append(resourcetype)
  81. if _tag("D", "owner") in properties:
  82. owner = ET.Element(_tag("D", "owner"))
  83. owner.text = calendar.owner
  84. prop.append(owner)
  85. if _tag("CS", "getctag") in properties:
  86. getctag = ET.Element(_tag("CS", "getctag"))
  87. getctag.text = calendar.ctag
  88. prop.append(getctag)
  89. status = ET.Element(_tag("D", "status"))
  90. status.text = config.get("status", "200")
  91. propstat.append(status)
  92. return ET.tostring(multistatus, config.get("encoding", "request"))
  93. def put(icalRequest, calendar, url, obj):
  94. """
  95. Read PUT requests
  96. """
  97. if obj:
  98. # PUT is modifying obj
  99. calendar.replace(obj, icalRequest)
  100. else:
  101. # PUT is adding a new object
  102. calendar.append(icalRequest)
  103. def report(xmlRequest, calendar, url):
  104. """
  105. Read and answer REPORT requests
  106. """
  107. # Read rfc3253-3.6 for info
  108. # Reading request
  109. root = ET.fromstring(xmlRequest)
  110. propElement = root.find(_tag("D", "prop"))
  111. propList = propElement.getchildren()
  112. properties = [property.tag for property in propList]
  113. filters = {}
  114. filterElement = root.find(_tag("C", "filter"))
  115. filterList = propElement.getchildren()
  116. # TODO: This should be recursive
  117. # TODO: Really manage filters (see ical)
  118. for filter in filterList:
  119. sub = filters[filter.get("name")] = {}
  120. for subfilter in filter.getchildren():
  121. sub[subfilter.get("name")] = {}
  122. if root.tag == _tag("C", "calendar-multiget"):
  123. # Read rfc4791-7.9 for info
  124. hreferences = [hrefElement.text for hrefElement in root.findall(_tag("D", "href"))]
  125. else:
  126. hreferences = [url]
  127. # Writing answer
  128. multistatus = ET.Element(_tag("D", "multistatus"))
  129. # TODO: WTF, sunbird needs one response by object,
  130. # is that really what is needed?
  131. # Read rfc4791-9.[6|10] for info
  132. for hreference in hreferences:
  133. headers = ical.headers(calendar.vcalendar())
  134. # TODO: Define timezones by obj
  135. timezones = ical.timezones(calendar.vcalendar())
  136. objects = []
  137. objects.extend(ical.events(calendar.vcalendar()))
  138. objects.extend(ical.todos(calendar.vcalendar()))
  139. for obj in objects:
  140. # TODO: Use the hreference to read data and create href.text
  141. # We assume here that hreference is url
  142. response = ET.Element(_tag("D", "response"))
  143. multistatus.append(response)
  144. href = ET.Element(_tag("D", "href"))
  145. href.text = url
  146. response.append(href)
  147. propstat = ET.Element(_tag("D", "propstat"))
  148. response.append(propstat)
  149. prop = ET.Element(_tag("D", "prop"))
  150. propstat.append(prop)
  151. if _tag("D", "getetag") in properties:
  152. # TODO: Can UID and ETAG be the same?
  153. getetag = ET.Element(_tag("D", "getetag"))
  154. getetag.text = obj.etag()
  155. prop.append(getetag)
  156. if _tag("C", "calendar-data") in properties:
  157. cdata = ET.Element(_tag("C", "calendar-data"))
  158. # TODO: Maybe assume that events and todos are not the same
  159. cdata.text = ical.writeCalendar(headers, timezones, [obj])
  160. prop.append(cdata)
  161. status = ET.Element(_tag("D", "status"))
  162. status.text = config.get("status", "200")
  163. propstat.append(status)
  164. return ET.tostring(multistatus, config.get("encoding", "request"))