mirror of https://gitlab.com/bashrc2/epicyon
caldav report implementation
parent
ef8c7a5eb9
commit
26610e4555
12
daemon.py
12
daemon.py
|
@ -16906,7 +16906,8 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
nickname, self.server.domain,
|
||||
depth, propfind_xml,
|
||||
self.server.http_prefix,
|
||||
self.server.system_language)
|
||||
self.server.system_language,
|
||||
self.server.recent_dav_etags)
|
||||
elif endpoint_type == 'report':
|
||||
curr_etag = None
|
||||
if self.headers.get('ETag'):
|
||||
|
@ -16920,6 +16921,7 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
self.server.person_cache,
|
||||
self.server.http_prefix,
|
||||
curr_etag,
|
||||
self.server.recent_dav_etags,
|
||||
self.server.domain_full)
|
||||
elif endpoint_type == 'delete':
|
||||
response_str = \
|
||||
|
@ -16933,7 +16935,10 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
self._404()
|
||||
return
|
||||
if response_str == 'Not modified':
|
||||
return self._304()
|
||||
if endpoint_type == 'put':
|
||||
return self._200()
|
||||
else:
|
||||
return self._304()
|
||||
elif response_str.startswith('ETag:') and endpoint_type == 'put':
|
||||
response_etag = response_str.split('ETag:', 1)[1]
|
||||
self._201(response_etag)
|
||||
|
@ -19282,6 +19287,9 @@ def run_daemon(dyslexic_font: bool,
|
|||
default_reply_interval_hrs = 9999999
|
||||
httpd.default_reply_interval_hrs = default_reply_interval_hrs
|
||||
|
||||
# recent caldav etags for each account
|
||||
httpd.recent_dav_etags = {}
|
||||
|
||||
httpd.key_shortcuts = {}
|
||||
load_access_keys_for_accounts(base_dir, httpd.key_shortcuts,
|
||||
httpd.access_keys)
|
||||
|
|
323
happening.py
323
happening.py
|
@ -27,6 +27,31 @@ from filters import is_filtered
|
|||
from context import get_individual_post_context
|
||||
|
||||
|
||||
def _dav_date_from_string(timestamp: str) -> str:
|
||||
"""Returns a datetime from a caldav date
|
||||
"""
|
||||
timestamp_year = timestamp[:4]
|
||||
timestamp_month = timestamp[4:][:2]
|
||||
timestamp_day = timestamp[6:][:2]
|
||||
timestamp_hour = timestamp[9:][:2]
|
||||
timestamp_min = timestamp[11:][:2]
|
||||
timestamp_sec = timestamp[13:][:2]
|
||||
|
||||
if not timestamp_year.isdigit() or \
|
||||
not timestamp_month.isdigit() or \
|
||||
not timestamp_day.isdigit() or \
|
||||
not timestamp_hour.isdigit() or \
|
||||
not timestamp_min.isdigit() or \
|
||||
not timestamp_sec.isdigit():
|
||||
return None
|
||||
if int(timestamp_year) < 2020 or int(timestamp_year) > 2100:
|
||||
return None
|
||||
published = \
|
||||
timestamp_year + '-' + timestamp_month + '-' + timestamp_day + 'T' + \
|
||||
timestamp_hour + ':' + timestamp_min + ':' + timestamp_sec + 'Z'
|
||||
return published
|
||||
|
||||
|
||||
def _valid_uuid(test_uuid: str, version: int):
|
||||
"""Check if uuid_to_test is a valid UUID
|
||||
"""
|
||||
|
@ -784,66 +809,15 @@ def _dav_store_event(base_dir: str, nickname: str, domain: str,
|
|||
return False
|
||||
|
||||
# convert to the expected time format
|
||||
timestamp_year = timestamp[:4]
|
||||
timestamp_month = timestamp[4:][:2]
|
||||
timestamp_day = timestamp[6:][:2]
|
||||
timestamp_hour = timestamp[9:][:2]
|
||||
timestamp_min = timestamp[11:][:2]
|
||||
timestamp_sec = timestamp[13:][:2]
|
||||
|
||||
if not timestamp_year.isdigit() or \
|
||||
not timestamp_month.isdigit() or \
|
||||
not timestamp_day.isdigit() or \
|
||||
not timestamp_hour.isdigit() or \
|
||||
not timestamp_min.isdigit() or \
|
||||
not timestamp_sec.isdigit():
|
||||
published = _dav_date_from_string(timestamp)
|
||||
if not published:
|
||||
return False
|
||||
if int(timestamp_year) < 2020 or int(timestamp_year) > 2100:
|
||||
start_time = _dav_date_from_string(start_time)
|
||||
if not start_time:
|
||||
return False
|
||||
published = \
|
||||
timestamp_year + '-' + timestamp_month + '-' + timestamp_day + 'T' + \
|
||||
timestamp_hour + ':' + timestamp_min + ':' + timestamp_sec + 'Z'
|
||||
|
||||
start_time_year = start_time[:4]
|
||||
start_time_month = start_time[4:][:2]
|
||||
start_time_day = start_time[6:][:2]
|
||||
start_time_hour = start_time[9:][:2]
|
||||
start_time_min = start_time[11:][:2]
|
||||
start_time_sec = start_time[13:][:2]
|
||||
|
||||
if not start_time_year.isdigit() or \
|
||||
not start_time_month.isdigit() or \
|
||||
not start_time_day.isdigit() or \
|
||||
not start_time_hour.isdigit() or \
|
||||
not start_time_min.isdigit() or \
|
||||
not start_time_sec.isdigit():
|
||||
end_time = _dav_date_from_string(end_time)
|
||||
if not end_time:
|
||||
return False
|
||||
if int(start_time_year) < 2020 or int(start_time_year) > 2100:
|
||||
return False
|
||||
start_time = \
|
||||
start_time_year + '-' + start_time_month + '-' + \
|
||||
start_time_day + 'T' + \
|
||||
start_time_hour + ':' + start_time_min + ':' + start_time_sec + 'Z'
|
||||
|
||||
end_time_year = end_time[:4]
|
||||
end_time_month = end_time[4:][:2]
|
||||
end_time_day = end_time[6:][:2]
|
||||
end_time_hour = end_time[9:][:2]
|
||||
end_time_min = end_time[11:][:2]
|
||||
end_time_sec = end_time[13:][:2]
|
||||
|
||||
if not end_time_year.isdigit() or \
|
||||
not end_time_month.isdigit() or \
|
||||
not end_time_day.isdigit() or \
|
||||
not end_time_hour.isdigit() or \
|
||||
not end_time_min.isdigit() or \
|
||||
not end_time_sec.isdigit():
|
||||
return False
|
||||
if int(end_time_year) < 2020 or int(end_time_year) > 2100:
|
||||
return False
|
||||
end_time = \
|
||||
end_time_year + '-' + end_time_month + '-' + end_time_day + 'T' + \
|
||||
end_time_hour + ':' + end_time_min + ':' + end_time_sec + 'Z'
|
||||
|
||||
post_id = ''
|
||||
post_context = get_individual_post_context()
|
||||
|
@ -932,9 +906,26 @@ def _dav_store_event(base_dir: str, nickname: str, domain: str,
|
|||
return True
|
||||
|
||||
|
||||
def _dav_update_recent_etags(etag: str, nickname: str,
|
||||
recent_dav_etags: {}) -> None:
|
||||
"""Updates the recent etags for each account
|
||||
"""
|
||||
# update the recent caldav etags for each account
|
||||
if not recent_dav_etags.get(nickname):
|
||||
recent_dav_etags[nickname] = [etag]
|
||||
else:
|
||||
# only keep a limited number of recent etags
|
||||
while len(recent_dav_etags[nickname]) > 32:
|
||||
recent_dav_etags[nickname].pop(0)
|
||||
# append the etag to the recent list
|
||||
if etag not in recent_dav_etags[nickname]:
|
||||
recent_dav_etags[nickname].append(etag)
|
||||
|
||||
|
||||
def dav_put_response(base_dir: str, nickname: str, domain: str,
|
||||
depth: int, xml_str: str, http_prefix: str,
|
||||
system_language: str) -> str:
|
||||
system_language: str,
|
||||
recent_dav_etags: {}) -> str:
|
||||
"""Returns the response to caldav PUT
|
||||
"""
|
||||
if '\n' not in xml_str:
|
||||
|
@ -946,6 +937,11 @@ def dav_put_response(base_dir: str, nickname: str, domain: str,
|
|||
'END:VEVENT' not in xml_str:
|
||||
return None
|
||||
|
||||
etag = md5(xml_str).hexdigest()
|
||||
if recent_dav_etags.get(nickname):
|
||||
if etag in recent_dav_etags[nickname]:
|
||||
return 'Not modified'
|
||||
|
||||
stored_count = 0
|
||||
reading_event = False
|
||||
lines_list = xml_str.split('\n')
|
||||
|
@ -968,13 +964,14 @@ def dav_put_response(base_dir: str, nickname: str, domain: str,
|
|||
event_list.append(line)
|
||||
if stored_count == 0:
|
||||
return None
|
||||
return 'ETag:' + md5(xml_str).hexdigest()
|
||||
_dav_update_recent_etags(etag, nickname, recent_dav_etags)
|
||||
return 'ETag:' + etag
|
||||
|
||||
|
||||
def dav_report_response(base_dir: str, nickname: str, domain: str,
|
||||
depth: int, xml_str: str,
|
||||
person_cache: {}, http_prefix: str,
|
||||
curr_etag: str,
|
||||
curr_etag: str, recent_dav_etags: {},
|
||||
domain_full: str) -> str:
|
||||
"""Returns the response to caldav REPORT
|
||||
"""
|
||||
|
@ -983,42 +980,200 @@ def dav_report_response(base_dir: str, nickname: str, domain: str,
|
|||
if '<c:calendar-multiget' not in xml_str or \
|
||||
'</c:calendar-multiget>' not in xml_str:
|
||||
return None
|
||||
|
||||
if curr_etag:
|
||||
if recent_dav_etags.get(nickname):
|
||||
if curr_etag in recent_dav_etags[nickname]:
|
||||
return "Not modified"
|
||||
|
||||
xml_str_lower = xml_str.lower()
|
||||
query_start_time = None
|
||||
query_end_time = None
|
||||
if ':time-range' in xml_str_lower:
|
||||
time_range_str = xml_str_lower.split(':time-range')[1]
|
||||
if 'start=' in time_range_str and 'end=' in time_range_str:
|
||||
start_time_str = time_range_str.split('start=')[1]
|
||||
if start_time_str.startswith("'"):
|
||||
query_start_time_str = start_time_str.split("'")[1]
|
||||
query_start_time = _dav_date_from_string(query_start_time_str)
|
||||
elif start_time_str.startswith('"'):
|
||||
query_start_time_str = start_time_str.split('"')[1]
|
||||
query_start_time = _dav_date_from_string(query_start_time_str)
|
||||
|
||||
end_time_str = time_range_str.split('end=')[1]
|
||||
if end_time_str.startswith("'"):
|
||||
query_end_time_str = end_time_str.split("'")[1]
|
||||
query_end_time = _dav_date_from_string(query_end_time_str)
|
||||
elif end_time_str.startswith('"'):
|
||||
query_end_time_str = end_time_str.split('"')[1]
|
||||
query_end_time = _dav_date_from_string(query_end_time_str)
|
||||
|
||||
ical_events = None
|
||||
etag = None
|
||||
events_href = ''
|
||||
responses = ''
|
||||
search_date = datetime.now()
|
||||
if query_start_time and query_end_time:
|
||||
query_start_year = int(query_start_time.split('-')[0])
|
||||
query_start_month = int(query_start_time.split('-')[1])
|
||||
query_start_day = query_start_time.split('-')[2]
|
||||
query_start_day = int(query_start_day.split('T')[0])
|
||||
query_end_year = int(query_end_time.split('-')[0])
|
||||
query_end_month = int(query_end_time.split('-')[1])
|
||||
query_end_day = query_end_time.split('-')[2]
|
||||
query_end_day = int(query_end_day.split('T')[0])
|
||||
if query_start_year == query_end_year and \
|
||||
query_start_month == query_end_month:
|
||||
if query_start_day == query_end_day:
|
||||
# calendar for one day
|
||||
search_date = \
|
||||
datetime.datetime(year=query_start_year,
|
||||
month=query_start_month,
|
||||
day=query_start_day)
|
||||
ical_events = \
|
||||
get_todays_events_icalendar(base_dir, nickname, domain,
|
||||
search_date.year,
|
||||
search_date.month,
|
||||
search_date.day, person_cache,
|
||||
http_prefix)
|
||||
events_href = \
|
||||
http_prefix + '://' + domain_full + '/users/' + \
|
||||
nickname + '/calendar?year=' + \
|
||||
str(search_date.year) + '?month=' + \
|
||||
str(search_date.month) + '?day=' + str(search_date.day)
|
||||
if ical_events:
|
||||
etag = md5(ical_events).hexdigest()
|
||||
responses = \
|
||||
' <d:response>\n' + \
|
||||
' <d:href>' + events_href + '</d:href>\n' + \
|
||||
' <d:propstat>\n' + \
|
||||
' <d:prop>\n' + \
|
||||
' <d:getetag>"' + \
|
||||
etag + '"</d:getetag>\n' + \
|
||||
' <c:calendar-data>' + ical_events + \
|
||||
' </c:calendar-data>\n' + \
|
||||
' </d:prop>\n' + \
|
||||
' <d:status>HTTP/1.1 200 OK' + \
|
||||
'</d:status>\n' + \
|
||||
' </d:propstat>\n' + \
|
||||
' </d:response>\n'
|
||||
elif query_start_day == 1 and query_start_day >= 28:
|
||||
# calendar for a month
|
||||
ical_events = \
|
||||
get_month_events_icalendar(base_dir, nickname, domain,
|
||||
query_start_year,
|
||||
query_start_month,
|
||||
person_cache,
|
||||
http_prefix)
|
||||
events_href = \
|
||||
http_prefix + '://' + domain_full + '/users/' + \
|
||||
nickname + '/calendar?year=' + \
|
||||
str(query_start_year) + '?month=' + \
|
||||
str(query_start_month)
|
||||
if ical_events:
|
||||
etag = md5(ical_events).hexdigest()
|
||||
responses = \
|
||||
' <d:response>\n' + \
|
||||
' <d:href>' + events_href + '</d:href>\n' + \
|
||||
' <d:propstat>\n' + \
|
||||
' <d:prop>\n' + \
|
||||
' <d:getetag>"' + \
|
||||
etag + '"</d:getetag>\n' + \
|
||||
' <c:calendar-data>' + ical_events + \
|
||||
' </c:calendar-data>\n' + \
|
||||
' </d:prop>\n' + \
|
||||
' <d:status>HTTP/1.1 200 OK' + \
|
||||
'</d:status>\n' + \
|
||||
' </d:propstat>\n' + \
|
||||
' </d:response>\n'
|
||||
if not responses:
|
||||
all_events = ''
|
||||
for year in range(query_start_year, query_end_year+1):
|
||||
if query_start_year == query_end_year:
|
||||
start_month_number = query_start_month
|
||||
end_month_number = query_end_month
|
||||
elif year == query_end_year:
|
||||
start_month_number = 1
|
||||
end_month_number = query_end_month
|
||||
elif year == query_start_year:
|
||||
start_month_number = query_start_month
|
||||
end_month_number = 12
|
||||
else:
|
||||
start_month_number = 1
|
||||
end_month_number = 12
|
||||
for month in range(start_month_number, end_month_number+1):
|
||||
ical_events = \
|
||||
get_month_events_icalendar(base_dir,
|
||||
nickname, domain,
|
||||
year, month,
|
||||
person_cache,
|
||||
http_prefix)
|
||||
events_href = \
|
||||
http_prefix + '://' + domain_full + '/users/' + \
|
||||
nickname + '/calendar?year=' + \
|
||||
str(year) + '?month=' + \
|
||||
str(month)
|
||||
if ical_events:
|
||||
all_events += ical_events
|
||||
responses += \
|
||||
' <d:response>\n' + \
|
||||
' <d:href>' + events_href + \
|
||||
'</d:href>\n' + \
|
||||
' <d:propstat>\n' + \
|
||||
' <d:prop>\n' + \
|
||||
' <d:getetag>"' + \
|
||||
etag + '"</d:getetag>\n' + \
|
||||
' <c:calendar-data>' + \
|
||||
ical_events + \
|
||||
' </c:calendar-data>\n' + \
|
||||
' </d:prop>\n' + \
|
||||
' <d:status>HTTP/1.1 200 OK' + \
|
||||
'</d:status>\n' + \
|
||||
' </d:propstat>\n' + \
|
||||
' </d:response>\n'
|
||||
etag = md5(all_events).hexdigest()
|
||||
|
||||
# today's calendar events
|
||||
now = datetime.now()
|
||||
ical_events = \
|
||||
get_todays_events_icalendar(base_dir, nickname, domain,
|
||||
now.year, now.month,
|
||||
now.day, person_cache,
|
||||
http_prefix)
|
||||
if not ical_events:
|
||||
ical_events = \
|
||||
get_todays_events_icalendar(base_dir, nickname, domain,
|
||||
search_date.year, search_date.month,
|
||||
search_date.day, person_cache,
|
||||
http_prefix)
|
||||
events_href = \
|
||||
http_prefix + '://' + domain_full + '/users/' + \
|
||||
nickname + '/calendar?year=' + \
|
||||
str(search_date.year) + '?month=' + \
|
||||
str(search_date.month) + '?day=' + str(search_date.day)
|
||||
if ical_events:
|
||||
etag = md5(ical_events).hexdigest()
|
||||
responses = \
|
||||
' <d:response>\n' + \
|
||||
' <d:href>' + events_href + '</d:href>\n' + \
|
||||
' <d:propstat>\n' + \
|
||||
' <d:prop>\n' + \
|
||||
' <d:getetag>"' + etag + '"</d:getetag>\n' + \
|
||||
' <c:calendar-data>' + ical_events + \
|
||||
' </c:calendar-data>\n' + \
|
||||
' </d:prop>\n' + \
|
||||
' <d:status>HTTP/1.1 200 OK</d:status>\n' + \
|
||||
' </d:propstat>\n' + \
|
||||
' </d:response>\n'
|
||||
|
||||
if not ical_events or not etag:
|
||||
return None
|
||||
if 'VEVENT' not in ical_events:
|
||||
return None
|
||||
|
||||
etag = md5(ical_events).hexdigest()
|
||||
if etag == curr_etag:
|
||||
return "Not modified"
|
||||
events_href = \
|
||||
http_prefix + '://' + domain_full + '/users/' + \
|
||||
nickname + '/calendar?year=' + \
|
||||
str(now.year) + '?month=' + str(now.month) + '?day=' + str(now.day)
|
||||
response_str = \
|
||||
'<?xml version="1.0" encoding="utf-8" ?>\n' + \
|
||||
'<d:multistatus xmlns:d="DAV:" ' + \
|
||||
'xmlns:cs="http://calendarserver.org/ns/">\n' + \
|
||||
' <d:response>\n' + \
|
||||
' <d:href>' + events_href + '</d:href>\n' + \
|
||||
' <d:propstat>\n' + \
|
||||
' <d:prop>\n' + \
|
||||
' <d:getetag>"' + etag + '"</d:getetag>\n' + \
|
||||
' <c:calendar-data>' + ical_events + \
|
||||
' </c:calendar-data>\n' + \
|
||||
' </d:prop>\n' + \
|
||||
' <d:status>HTTP/1.1 200 OK</d:status>\n' + \
|
||||
' </d:propstat>\n' + \
|
||||
' </d:response>\n' + \
|
||||
' <d:response>\n' + \
|
||||
'</d:multistatus>'
|
||||
responses + '</d:multistatus>'
|
||||
|
||||
_dav_update_recent_etags(etag, nickname, recent_dav_etags)
|
||||
return response_str
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue