diff options
Diffstat (limited to 'wallace/module_resources.py')
-rw-r--r-- | wallace/module_resources.py | 190 |
1 files changed, 9 insertions, 181 deletions
diff --git a/wallace/module_resources.py b/wallace/module_resources.py index 303252b..f398120 100644 --- a/wallace/module_resources.py +++ b/wallace/module_resources.py @@ -40,9 +40,10 @@ import kolabformat from pykolab.auth import Auth from pykolab.conf import Conf from pykolab.imap import IMAP -from pykolab.xml import event_from_ical from pykolab.xml import event_from_string from pykolab.xml import to_dt +from pykolab.itip import events_from_message +from pykolab.itip import check_event_conflict from pykolab.translate import _ log = pykolab.getLogger('pykolab.wallace') @@ -150,7 +151,7 @@ def execute(*args, **kw): # An iTip message may contain multiple events. Later on, test if the message # is an iTip message by checking the length of this list. try: - itip_events = itip_events_from_message(message) + itip_events = events_from_message(message, ['REQUEST', 'CANCEL']) except Exception, e: log.error(_("Failed to parse iTip events from message: %r" % (e))) itip_events = [] @@ -473,33 +474,11 @@ def read_resource_calendar(resource_rec, itip_events): event = pykolab.xml.event_from_string(payload) for itip in itip_events: - _es = to_dt(event.get_start()) - _ee = to_dt(event.get_end()) - - conflict = False - - # naive loops to check for collisions in (recurring) events - # TODO: compare recurrence rules directly (e.g. matching time slot or weekday or monthday) - while not conflict and _es is not None: - _is = to_dt(itip['start']) - _ie = to_dt(itip['end']) - - while not conflict and _is is not None: - log.debug("* Comparing event dates at %s/%s with %s/%s" % (_es, _ee, _is, _ie), level=9) - conflict = check_date_conflict(_es, _ee, _is, _ie) - _is = to_dt(itip['xml'].get_next_occurence(_is)) if event.is_recurring() else None - _ie = to_dt(itip['xml'].get_occurence_end_date(_is)) - - _es = to_dt(event.get_next_occurence(_es)) if event.is_recurring() else None - _ee = to_dt(event.get_occurence_end_date(_es)) + conflict = check_event_conflict(event, itip) if event.get_uid() == itip['uid']: resource_rec['existing_events'].append(itip['uid']) - # don't register conflict for updates - if itip['sequence'] > 0 and itip['sequence'] >= event.get_sequence(): - conflict = False - if conflict: log.info( _("Event %r conflicts with event %r") % ( @@ -513,29 +492,6 @@ def read_resource_calendar(resource_rec, itip_events): return num_messages -def check_date_conflict(_es, _ee, _is, _ie): - conflict = False - - # TODO: add margin for all-day dates (+13h; -12h) - - if _es < _is: - if _es <= _ie: - if _ee <= _is: - conflict = False - else: - conflict = True - else: - conflict = True - elif _es == _is: - conflict = True - else: # _es > _is - if _es <= _ie: - conflict = True - else: - conflict = False - - return conflict - def accept_reservation_request(itip_event, resource, delegator=None): """ @@ -617,118 +573,6 @@ def delete_resource_event(uid, resource): imap.imap.m.expunge() -def itip_events_from_message(message): - """ - Obtain the iTip payload from email.message <message> - """ - # Placeholder for any itip_events found in the message. - itip_events = [] - seen_uids = [] - - # iTip methods we are actually interested in. Other methods will be ignored. - itip_methods = [ "REQUEST", "CANCEL" ] - - # Are all iTip messages multipart? No! RFC 6047, section 2.4 states "A - # MIME body part containing content information that conforms to this - # document MUST have (...)" but does not state whether an iTip message must - # therefore also be multipart. - - # Check each part - for part in message.walk(): - - # The iTip part MUST be Content-Type: text/calendar (RFC 6047, section 2.4) - # But in real word, other mime-types are used as well - if part.get_content_type() in [ "text/calendar", "text/x-vcalendar", "application/ics" ]: - if not str(part.get_param('method')).upper() in itip_methods: - log.error(_("Method %r not really interesting for us.") % (part.get_param('method'))) - continue - - # Get the itip_payload - itip_payload = part.get_payload(decode=True) - - log.debug(_("Raw iTip payload: %s") % (itip_payload), level=9) - - # Python iCalendar prior to 3.0 uses "from_string". - if hasattr(icalendar.Calendar, 'from_ical'): - cal = icalendar.Calendar.from_ical(itip_payload) - elif hasattr(icalendar.Calendar, 'from_string'): - cal = icalendar.Calendar.from_string(itip_payload) - - # If we can't read it, we're out - else: - log.error(_("Could not read iTip from message.")) - return [] - - for c in cal.walk(): - if c.name == "VEVENT": - itip = {} - - if c['uid'] in seen_uids: - log.debug(_("Duplicate iTip event: %s") % (c['uid']), level=9) - continue - - # From the event, take the following properties: - # - # - method - # - uid - # - sequence - # - start - # - end (if any) - # - duration (if any) - # - organizer - # - attendees (if any) - # - resources (if any) - # - - itip['uid'] = str(c['uid']) - itip['method'] = str(cal['method']).upper() - itip['sequence'] = int(c['sequence']) if c.has_key('sequence') else 0 - - if c.has_key('dtstart'): - itip['start'] = c['dtstart'].dt - else: - log.error(_("iTip event without a start")) - continue - - if c.has_key('dtend'): - itip['end'] = c['dtend'].dt - - if c.has_key('duration'): - itip['duration'] = c['duration'].dt - itip['end'] = itip['start'] + c['duration'].dt - - itip['organizer'] = c['organizer'] - - itip['attendees'] = c['attendee'] - - if c.has_key('resources'): - itip['resources'] = c['resources'] - - itip['raw'] = itip_payload - - try: - itip['xml'] = event_from_ical(c.to_ical()) - except Exception, e: - log.error("event_from_ical() exception: %r" % (e)) - continue - - itip_events.append(itip) - - seen_uids.append(c['uid']) - - # end if c.name == "VEVENT" - - # end for c in cal.walk() - - # end if part.get_content_type() == "text/calendar" - - # end for part in message.walk() - - if not len(itip_events) and not message.is_multipart(): - log.debug(_("Message is not an iTip message (non-multipart message)"), level=5) - - return itip_events - def reject(filepath): new_filepath = os.path.join( mybasepath, @@ -986,12 +830,6 @@ def send_response(from_address, itip_events, owner=None): resource, this will send an additional DELEGATED response message. """ - import smtplib - smtp = smtplib.SMTP("localhost", 10027) - - if conf.debuglevel > 8: - smtp.set_debuglevel(True) - if isinstance(itip_events, dict): itip_events = [ itip_events ] @@ -1000,6 +838,7 @@ def send_response(from_address, itip_events, owner=None): participant_status = itip_event['xml'].get_ical_attendee_participant_status(attendee) message_text = reservation_response_text(participant_status, owner) + subject_template = _("Reservation Request for %(summary)s was %(status)s") if participant_status == "DELEGATED": # Extra actions to take @@ -1007,32 +846,21 @@ def send_response(from_address, itip_events, owner=None): delegatee = [a for a in itip_event['xml'].get_attendees() if from_address in [b.email() for b in a.get_delegated_from()]][0] delegatee_status = itip_event['xml'].get_ical_attendee_participant_status(delegatee) - message = itip_event['xml'].to_message_itip(delegatee.get_email(), - method="REPLY", - participant_status=delegatee_status, - message_text=reservation_response_text(delegatee_status, owner) - ) - smtp.sendmail(message['From'], message['To'], message.as_string()) + pykolab.itip.send_reply(delegatee.get_email(), itip_event, reservation_response_text(delegatee_status, owner), + subject=subject_template) # restore list of attendees after to_message_itip() itip_event['xml']._attendees = [ delegator, delegatee ] itip_event['xml'].event.setAttendees(itip_event['xml']._attendees) - participant_status = "DELEGATED" message_text = _(""" *** This is an automated response, please do not reply! *** Your reservation was delegated to "%s" which is available for the requested time. """) % (delegatee.get_name()) - message = itip_event['xml'].to_message_itip(from_address, - method="REPLY", - participant_status=participant_status, - message_text=message_text - ) - smtp.sendmail(message['From'], message['To'], message.as_string()) - - smtp.quit() + pykolab.itip.send_reply(from_address, itip_event, message_text, + subject=subject_template) def reservation_response_text(status, owner): |