Changeset View
Changeset View
Standalone View
Standalone View
pykolab/itip/__init__.py
import icalendar | import icalendar | ||||
import pykolab | import pykolab | ||||
import traceback | import traceback | ||||
import kolabformat | import kolabformat | ||||
import re | |||||
from pykolab.xml import to_dt | from pykolab.xml import to_dt | ||||
from pykolab.xml import event_from_ical | from pykolab.xml import event_from_ical | ||||
from pykolab.xml import todo_from_ical | from pykolab.xml import todo_from_ical | ||||
from pykolab.xml import participant_status_label | from pykolab.xml import participant_status_label | ||||
from pykolab.translate import _ | from pykolab.translate import _ | ||||
from tzlocal import windows_tz | |||||
log = pykolab.getLogger('pykolab.wallace') | log = pykolab.getLogger('pykolab.wallace') | ||||
def events_from_message(message, methods=None): | def events_from_message(message, methods=None): | ||||
return objects_from_message(message, ["VEVENT"], methods) | return objects_from_message(message, ["VEVENT"], methods) | ||||
def todos_from_message(message, methods=None): | def todos_from_message(message, methods=None): | ||||
return objects_from_message(message, ["VTODO"], methods) | return objects_from_message(message, ["VTODO"], methods) | ||||
def objects_from_message(message, objnames, methods=None): | def objects_from_message(message, objnames, methods=None): | ||||
""" | """ | ||||
Obtain the iTip payload from email.message <message> | Obtain the iTip payload from email.message <message> | ||||
""" | """ | ||||
# Placeholder for any itip_objects found in the message. | # Placeholder for any itip_objects found in the message. | ||||
itip_objects = [] | itip_objects = [] | ||||
seen_uids = [] | seen_uids = [] | ||||
Show All 16 Lines | for part in message.walk(): | ||||
log.info(_("Method %r not really interesting for us.") % (part.get_param('method'))) | log.info(_("Method %r not really interesting for us.") % (part.get_param('method'))) | ||||
continue | continue | ||||
# Get the itip_payload | # Get the itip_payload | ||||
itip_payload = part.get_payload(decode=True) | itip_payload = part.get_payload(decode=True) | ||||
log.debug(_("Raw iTip payload (%r): %r") % (part.get_param('charset'), itip_payload), level=9) | log.debug(_("Raw iTip payload (%r): %r") % (part.get_param('charset'), itip_payload), level=9) | ||||
# Convert unsupported timezones, etc. | |||||
itip_payload = _convert_itip_payload(itip_payload) | |||||
# Python iCalendar prior to 3.0 uses "from_string". | # Python iCalendar prior to 3.0 uses "from_string". | ||||
if hasattr(icalendar.Calendar, 'from_ical'): | if hasattr(icalendar.Calendar, 'from_ical'): | ||||
cal = icalendar.Calendar.from_ical(itip_payload) | cal = icalendar.Calendar.from_ical(itip_payload) | ||||
elif hasattr(icalendar.Calendar, 'from_string'): | elif hasattr(icalendar.Calendar, 'from_string'): | ||||
cal = icalendar.Calendar.from_string(itip_payload) | cal = icalendar.Calendar.from_string(itip_payload) | ||||
# If we can't read it, we're out | # If we can't read it, we're out | ||||
else: | else: | ||||
▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | def objects_from_message(message, objnames, methods=None): | ||||
# end for part in message.walk() | # end for part in message.walk() | ||||
if not len(itip_objects) and not message.is_multipart(): | if not len(itip_objects) and not message.is_multipart(): | ||||
log.debug(_("Message is not an iTip message (non-multipart message)"), level=5) | log.debug(_("Message is not an iTip message (non-multipart message)"), level=5) | ||||
return itip_objects | return itip_objects | ||||
def check_event_conflict(kolab_event, itip_event): | def check_event_conflict(kolab_event, itip_event): | ||||
""" | """ | ||||
Determine whether the given kolab event conflicts with the given itip event | Determine whether the given kolab event conflicts with the given itip event | ||||
""" | """ | ||||
conflict = False | conflict = False | ||||
# don't consider conflict with myself | # don't consider conflict with myself | ||||
if kolab_event.uid == itip_event['uid']: | if kolab_event.uid == itip_event['uid']: | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | while not conflict and _es is not None: | ||||
elif _es is None and not kolab_event.is_recurring() and len(kolab_event.get_exceptions()) > _ei: | elif _es is None and not kolab_event.is_recurring() and len(kolab_event.get_exceptions()) > _ei: | ||||
_ev = kolab_event.get_exceptions()[_ei] | _ev = kolab_event.get_exceptions()[_ei] | ||||
_es = to_dt(_ev.get_start()) | _es = to_dt(_ev.get_start()) | ||||
_ee = to_dt(_ev.get_end()) | _ee = to_dt(_ev.get_end()) | ||||
_ei += 1 | _ei += 1 | ||||
return conflict | return conflict | ||||
def _is_transparent(event): | def _is_transparent(event): | ||||
return event.get_transparency() or event.get_status() == kolabformat.StatusCancelled | return event.get_transparency() or event.get_status() == kolabformat.StatusCancelled | ||||
def _convert_itip_payload(itip): | |||||
matchlist = re.findall("^((DTSTART|DTEND|DUE|EXDATE|COMPLETED)[:;][^\n]+)$", itip, re.MULTILINE) | |||||
for match in matchlist: | |||||
match = match[0] | |||||
search = re.search(";TZID=([^:;]+)", match) | |||||
if search: | |||||
tzorig = tzdest = search.group(1).replace('"', '') | |||||
# timezone in Olson-database format, nothing to convert | |||||
if re.match("[a-zA-Z]+/[a-zA-Z0-9_+-]+", tzorig): | |||||
continue | |||||
# convert timezone from windows format to Olson | |||||
if tzorig in windows_tz.win_tz: | |||||
tzdest = windows_tz.win_tz[tzorig] | |||||
# @TODO: Should be prefer server time if it has the same offset? | |||||
# replace old with new timezone name | |||||
if tzorig != tzdest: | |||||
replace = match.replace(search.group(0), ";TZID=" + tzdest) | |||||
itip = itip.replace("\n" + match, "\n" + replace) | |||||
return itip | |||||
def check_date_conflict(_es, _ee, _is, _ie): | def check_date_conflict(_es, _ee, _is, _ie): | ||||
""" | """ | ||||
Check the given event start/end dates for conflicts | Check the given event start/end dates for conflicts | ||||
""" | """ | ||||
conflict = False | conflict = False | ||||
# TODO: add margin for all-day dates (+13h; -12h) | # TODO: add margin for all-day dates (+13h; -12h) | ||||
if _es < _is: | if _es < _is: | ||||
if _es <= _ie: | if _es <= _ie: | ||||
if _ee <= _is: | if _ee <= _is: | ||||
conflict = False | conflict = False | ||||
else: | else: | ||||
conflict = True | conflict = True | ||||
else: | else: | ||||
conflict = True | conflict = True | ||||
elif _es == _is: | elif _es == _is: | ||||
conflict = True | conflict = True | ||||
else: # _es > _is | else: # _es > _is | ||||
if _es < _ie: | if _es < _ie: | ||||
conflict = True | conflict = True | ||||
else: | else: | ||||
conflict = False | conflict = False | ||||
return conflict | return conflict | ||||
def send_reply(from_address, itip_events, response_text, subject=None): | def send_reply(from_address, itip_events, response_text, subject=None): | ||||
""" | """ | ||||
Send the given iCal events as a valid iTip REPLY to the organizer. | Send the given iCal events as a valid iTip REPLY to the organizer. | ||||
""" | """ | ||||
import smtplib | import smtplib | ||||
▲ Show 20 Lines • Show All 86 Lines • Show Last 20 Lines |