Changeset View
Changeset View
Standalone View
Standalone View
wallace/module_resources.py
Show First 20 Lines • Show All 164 Lines • ▼ Show 20 Lines | def execute(*args, **kw): | ||||
# parse full message | # parse full message | ||||
message = Parser().parse(open(filepath, 'r')) | message = Parser().parse(open(filepath, 'r')) | ||||
# invalid message, skip | # invalid message, skip | ||||
if not message.get('X-Kolab-To'): | if not message.get('X-Kolab-To'): | ||||
return filepath | return filepath | ||||
recipients = [address for displayname,address in getaddresses(message.get_all('X-Kolab-To'))] | recipients = [address for displayname,address in getaddresses(message.get_all('X-Kolab-To'))] | ||||
sender_email = [address for displayname,address in getaddresses(message.get_all('X-Kolab-From'))][0] | |||||
sender_email = [ | |||||
address for displayname,address in getaddresses(message.get_all('X-Kolab-From')) | |||||
][0] | |||||
any_itips = False | any_itips = False | ||||
any_resources = False | any_resources = False | ||||
possibly_any_resources = False | possibly_any_resources = False | ||||
reference_uid = None | reference_uid = None | ||||
# An iTip message may contain multiple events. Later on, test if the message | # An iTip message may contain multiple events. Later on, test if the message | ||||
# is an iTip message by checking the length of this list. | # is an iTip message by checking the length of this list. | ||||
try: | try: | ||||
itip_events = events_from_message(message, ['REQUEST', 'REPLY', 'CANCEL']) | itip_events = events_from_message(message, ['REQUEST', 'REPLY', 'CANCEL']) | ||||
except Exception, e: | except Exception, errmsg: | ||||
log.error(_("Failed to parse iTip events from message: %r" % (e))) | log.error(_("Failed to parse iTip events from message: %r" % (errmsg))) | ||||
itip_events = [] | itip_events = [] | ||||
if not len(itip_events) > 0: | if not len(itip_events) > 0: | ||||
log.info( | log.info( | ||||
_("Message is not an iTip message or does not contain any " + \ | _("Message is not an iTip message or does not contain any " + \ | ||||
"(valid) iTip.") | "(valid) iTip.") | ||||
) | ) | ||||
Show All 27 Lines | if possibly_any_resources: | ||||
continue | continue | ||||
if not len(resource_record_from_email_address(recipient)) == 0: | if not len(resource_record_from_email_address(recipient)) == 0: | ||||
resource_recipient = recipient | resource_recipient = recipient | ||||
any_resources = True | any_resources = True | ||||
if any_resources: | if any_resources: | ||||
if not any_itips: | if not any_itips: | ||||
log.debug(_("Not an iTip message, but sent to resource nonetheless. Reject message"), level=5) | log.debug( | ||||
_("Not an iTip message, but sent to resource nonetheless. Reject message"), | |||||
level=5 | |||||
) | |||||
reject(filepath) | reject(filepath) | ||||
return False | return False | ||||
else: | else: | ||||
# Continue. Resources and iTips. We like. | # Continue. Resources and iTips. We like. | ||||
pass | pass | ||||
else: | else: | ||||
if not any_itips: | if not any_itips: | ||||
log.debug(_("No itips, no resources, pass along %r") % (filepath), level=5) | log.debug(_("No itips, no resources, pass along %r") % (filepath), level=5) | ||||
return filepath | return filepath | ||||
else: | else: | ||||
log.debug(_("iTips, but no resources, pass along %r") % (filepath), level=5) | log.debug(_("iTips, but no resources, pass along %r") % (filepath), level=5) | ||||
return filepath | return filepath | ||||
# A simple list of merely resource entry IDs that hold any relevance to the | # A simple list of merely resource entry IDs that hold any relevance to the | ||||
# iTip events | # iTip events | ||||
resource_dns = resource_records_from_itip_events(itip_events, resource_recipient) | resource_dns = resource_records_from_itip_events(itip_events, resource_recipient) | ||||
# check if resource attendees match the envelope recipient | # check if resource attendees match the envelope recipient | ||||
if len(resource_dns) == 0: | if len(resource_dns) == 0: | ||||
log.info(_("No resource attendees matching envelope recipient %s, Reject message") % (resource_recipient)) | log.info( | ||||
_("No resource attendees matching envelope recipient %s, Reject message") % ( | |||||
resource_recipient | |||||
) | |||||
) | |||||
log.debug("%r" % (itip_events), level=8) | log.debug("%r" % (itip_events), level=8) | ||||
reject(filepath) | reject(filepath) | ||||
return False | return False | ||||
# Get the resource details, which includes details on the IMAP folder | # Get the resource details, which includes details on the IMAP folder | ||||
# This may append resource collection members to recource_dns | # This may append resource collection members to recource_dns | ||||
resources = get_resource_records(resource_dns) | resources = get_resource_records(resource_dns) | ||||
log.debug(_("Resources: %r; %r") % (resource_dns, resources), level=8) | log.debug(_("Resources: %r; %r") % (resource_dns, resources), level=8) | ||||
imap.connect() | imap.connect() | ||||
done = False | done = False | ||||
receiving_resource = resources[resource_dns[0]] | receiving_resource = resources[resource_dns[0]] | ||||
for itip_event in itip_events: | for itip_event in itip_events: | ||||
if itip_event['method'] == 'REPLY': | if itip_event['method'] == 'REPLY': | ||||
done = True | done = True | ||||
# find initial reservation referenced by the reply | # find initial reservation referenced by the reply | ||||
if reference_uid: | if reference_uid: | ||||
(event, master) = find_existing_event(reference_uid, itip_event['recurrence-id'], receiving_resource) | (event, master) = find_existing_event( | ||||
log.debug(_("iTip REPLY to %r, %r; matches %r") % (reference_uid, itip_event['recurrence-id'], type(event)), level=8) | reference_uid, | ||||
itip_event['recurrence-id'], | |||||
receiving_resource | |||||
) | |||||
log.debug( | |||||
_("iTip REPLY to %r, %r; matches %r") % ( | |||||
reference_uid, | |||||
itip_event['recurrence-id'], | |||||
type(event) | |||||
), | |||||
level=8 | |||||
) | |||||
if event: | if event: | ||||
try: | try: | ||||
sender_attendee = itip_event['xml'].get_attendee_by_email(sender_email) | sender_attendee = itip_event['xml'].get_attendee_by_email(sender_email) | ||||
owner_reply = sender_attendee.get_participant_status() | owner_reply = sender_attendee.get_participant_status() | ||||
log.debug(_("Sender Attendee: %r => %r") % (sender_attendee, owner_reply), level=8) | log.debug( | ||||
_("Sender Attendee: %r => %r") % (sender_attendee, owner_reply), | |||||
level=8 | |||||
) | |||||
except Exception, e: | except Exception, e: | ||||
log.error(_("Could not find envelope sender attendee: %r") % (e)) | log.error(_("Could not find envelope sender attendee: %r") % (e)) | ||||
continue | continue | ||||
# compare sequence number to avoid outdated replies | # compare sequence number to avoid outdated replies | ||||
if not itip_event['sequence'] == event.get_sequence(): | if not itip_event['sequence'] == event.get_sequence(): | ||||
log.info(_("The iTip reply sequence (%r) doesn't match the referred event version (%r). Ignoring.") % ( | log.info( | ||||
itip_event['sequence'], event.get_sequence() | _("The iTip reply sequence (%r) doesn't match the referred event version (%r). Ignoring.") % ( | ||||
)) | itip_event['sequence'], | ||||
event.get_sequence() | |||||
) | |||||
) | |||||
continue | continue | ||||
# forward owner response comment | # forward owner response comment | ||||
comment = itip_event['xml'].get_comment() | comment = itip_event['xml'].get_comment() | ||||
if comment: | if comment: | ||||
event.set_comment(str(comment)) | event.set_comment(str(comment)) | ||||
_itip_event = dict(xml=event, uid=event.get_uid(), _master=master) | _itip_event = dict(xml=event, uid=event.get_uid(), _master=master) | ||||
_itip_event['recurrence-id'] = event.get_recurrence_id() | _itip_event['recurrence-id'] = event.get_recurrence_id() | ||||
if owner_reply == kolabformat.PartAccepted: | if owner_reply == kolabformat.PartAccepted: | ||||
event.set_status(kolabformat.StatusConfirmed) | event.set_status(kolabformat.StatusConfirmed) | ||||
accept_reservation_request(_itip_event, receiving_resource, confirmed=True) | accept_reservation_request(_itip_event, receiving_resource, confirmed=True) | ||||
elif owner_reply == kolabformat.PartDeclined: | elif owner_reply == kolabformat.PartDeclined: | ||||
decline_reservation_request(_itip_event, receiving_resource) | decline_reservation_request(_itip_event, receiving_resource) | ||||
else: | else: | ||||
log.info(_("Invalid response (%r) received from resource owner for event %r") % ( | log.info(_("Invalid response (%r) received from resource owner for event %r") % ( | ||||
sender_attendee.get_participant_status(True), reference_uid | sender_attendee.get_participant_status(True), reference_uid | ||||
)) | )) | ||||
else: | else: | ||||
log.info(_("Event referenced by this REPLY (%r) not found in resource calendar") % (reference_uid)) | log.info( | ||||
_("Event referenced by this REPLY (%r) not found in resource calendar") % ( | |||||
reference_uid | |||||
) | |||||
) | |||||
else: | else: | ||||
log.info(_("No event reference found in this REPLY. Ignoring.")) | log.info(_("No event reference found in this REPLY. Ignoring.")) | ||||
# exit for-loop | # exit for-loop | ||||
break | break | ||||
# else: | # else: | ||||
try: | try: | ||||
receiving_attendee = itip_event['xml'].get_attendee_by_email(receiving_resource['mail']) | receiving_attendee = itip_event['xml'].get_attendee_by_email( | ||||
log.debug(_("Receiving Resource: %r; %r") % (receiving_resource, receiving_attendee), level=8) | receiving_resource['mail'] | ||||
) | |||||
log.debug( | |||||
_("Receiving Resource: %r; %r") % (receiving_resource, receiving_attendee), | |||||
level=8 | |||||
) | |||||
except Exception, e: | except Exception, e: | ||||
log.error(_("Could not find envelope attendee: %r") % (e)) | log.error(_("Could not find envelope attendee: %r") % (e)) | ||||
continue | continue | ||||
# ignore updates and cancellations to resource collections who already delegated the event | # ignore updates and cancellations to resource collections who already delegated the event | ||||
if len(receiving_attendee.get_delegated_to()) > 0 or receiving_attendee.get_role() == kolabformat.NonParticipant: | _radt = receiving_attendee.get_delegated_to() | ||||
_rar = receiving_attendee.get_role() | |||||
if len(_radt) > 0 or _rar == kolabformat.NonParticipant: | |||||
done = True | done = True | ||||
log.debug(_("Recipient %r is non-participant, ignoring message") % (receiving_resource['mail']), level=8) | |||||
log.debug( | |||||
_("Recipient %r is non-participant, ignoring message") % ( | |||||
receiving_resource['mail'] | |||||
), | |||||
level=8 | |||||
) | |||||
# process CANCEL messages | # process CANCEL messages | ||||
if not done and itip_event['method'] == "CANCEL": | if not done and itip_event['method'] == "CANCEL": | ||||
for resource in resource_dns: | for resource in resource_dns: | ||||
if resources[resource]['mail'] in [a.get_email() for a in itip_event['xml'].get_attendees()] \ | r_emails = [a.get_email() for a in itip_event['xml'].get_attendees()] | ||||
and resources[resource].has_key('kolabtargetfolder'): | _resource = resources[resource] | ||||
(event, master) = find_existing_event(itip_event['uid'], itip_event['recurrence-id'], resources[resource]) | |||||
if _resource['mail'] in r_emails and 'kolabtargetfolder' in _resource: | |||||
(event, master) = find_existing_event( | |||||
itip_event['uid'], | |||||
itip_event['recurrence-id'], | |||||
_resource | |||||
) | |||||
if not event: | |||||
continue | |||||
# remove entire event | # remove entire event | ||||
if event and master is None: | if master is None: | ||||
log.debug(_("Cancellation for entire event %r: deleting") % (itip_event['uid']), level=8) | log.debug( | ||||
delete_resource_event(itip_event['uid'], resources[resource], event._msguid) | _("Cancellation for entire event %r: deleting") % (itip_event['uid']), | ||||
level=8 | |||||
) | |||||
delete_resource_event( | |||||
itip_event['uid'], | |||||
resources[resource], | |||||
event._msguid | |||||
) | |||||
# just cancel one single occurrence: add exception with status=cancelled | # just cancel one single occurrence: add exception with status=cancelled | ||||
elif master is not None: | else: | ||||
log.debug(_("Cancellation for a single occurrence %r of %r: updating...") % (itip_event['recurrence-id'], itip_event['uid']), level=8) | log.debug( | ||||
_("Cancellation for a single occurrence %r of %r: updating...") % ( | |||||
itip_event['recurrence-id'], | |||||
itip_event['uid'] | |||||
), | |||||
level=8 | |||||
) | |||||
event.set_status('CANCELLED') | event.set_status('CANCELLED') | ||||
event.set_transparency(True) | event.set_transparency(True) | ||||
_itip_event = dict(xml=event, uid=event.get_uid(), _master=master) | _itip_event = dict(xml=event, uid=event.get_uid(), _master=master) | ||||
_itip_event['recurrence-id'] = event.get_recurrence_id() | _itip_event['recurrence-id'] = event.get_recurrence_id() | ||||
save_resource_event(_itip_event, resources[resource]) | save_resource_event(_itip_event, resources[resource]) | ||||
done = True | done = True | ||||
if done: | if done: | ||||
os.unlink(filepath) | os.unlink(filepath) | ||||
cleanup() | cleanup() | ||||
return | return | ||||
# do the magic for the receiving attendee | # do the magic for the receiving attendee | ||||
(available_resource, itip_event) = check_availability(itip_events, resource_dns, resources, receiving_attendee) | (available_resource, itip_event) = check_availability( | ||||
itip_events, | |||||
resource_dns, | |||||
resources, | |||||
receiving_attendee | |||||
) | |||||
_reject = False | _reject = False | ||||
resource = None | resource = None | ||||
original_resource = None | original_resource = None | ||||
# accept reservation | # accept reservation | ||||
if available_resource is not None: | if available_resource is not None: | ||||
if available_resource['mail'] in [a.get_email() for a in itip_event['xml'].get_attendees()]: | atts = [a.get_email() for a in itip_event['xml'].get_attendees()] | ||||
if available_resource['mail'] in atts: | |||||
# check if reservation was delegated | # check if reservation was delegated | ||||
if available_resource['mail'] != receiving_resource['mail'] and receiving_attendee.get_participant_status() == kolabformat.PartDelegated: | if available_resource['mail'] != receiving_resource['mail']: | ||||
if receiving_attendee.get_participant_status() == kolabformat.PartDelegated: | |||||
original_resource = receiving_resource | original_resource = receiving_resource | ||||
resource = available_resource | resource = available_resource | ||||
else: | else: | ||||
# This must have been a resource collection originally. | # This must have been a resource collection originally. | ||||
# We have inserted the reference to the original resource | # We have inserted the reference to the original resource | ||||
# record in 'memberof'. | # record in 'memberof'. | ||||
if available_resource.has_key('memberof'): | if available_resource.has_key('memberof'): | ||||
original_resource = resources[available_resource['memberof']] | original_resource = resources[available_resource['memberof']] | ||||
if original_resource['mail'] in [a.get_email() for a in itip_event['xml'].get_attendees()]: | atts = [a.get_email() for a in itip_event['xml'].get_attendees()] | ||||
if original_resource['mail'] in atts: | |||||
# | # | ||||
# Delegate: | # Delegate: | ||||
# - delegator: the original resource collection | # - delegator: the original resource collection | ||||
# - delegatee: the target resource | # - delegatee: the target resource | ||||
# | # | ||||
itip_event['xml'].delegate(original_resource['mail'], available_resource['mail'], available_resource['cn']) | itip_event['xml'].delegate( | ||||
original_resource['mail'], | |||||
available_resource['mail'], | |||||
available_resource['cn'] | |||||
) | |||||
# set delegator to NON-PARTICIPANT and RSVP=FALSE | # set delegator to NON-PARTICIPANT and RSVP=FALSE | ||||
delegator = itip_event['xml'].get_attendee_by_email(original_resource['mail']) | delegator = itip_event['xml'].get_attendee_by_email(original_resource['mail']) | ||||
delegator.set_role(kolabformat.NonParticipant) | delegator.set_role(kolabformat.NonParticipant) | ||||
delegator.set_rsvp(False) | delegator.set_rsvp(False) | ||||
log.debug(_("Delegate invitation for resource collection %r to %r") % (original_resource['mail'], available_resource['mail']), level=8) | log.debug( | ||||
_("Delegate invitation for resource collection %r to %r") % ( | |||||
original_resource['mail'], | |||||
available_resource['mail'] | |||||
), | |||||
level=8 | |||||
) | |||||
resource = available_resource | resource = available_resource | ||||
# Look for ACT_REJECT policy | # Look for ACT_REJECT policy | ||||
if resource is not None: | if resource is not None: | ||||
invitationpolicy = get_resource_invitationpolicy(resource) | invitationpolicy = get_resource_invitationpolicy(resource) | ||||
log.debug(_("Apply invitation policies %r") % (invitationpolicy), level=8) | log.debug(_("Apply invitation policies %r") % (invitationpolicy), level=8) | ||||
if invitationpolicy is not None: | if invitationpolicy is not None: | ||||
for policy in invitationpolicy: | for policy in invitationpolicy: | ||||
if policy & ACT_REJECT: | if policy & ACT_REJECT: | ||||
_reject = True | _reject = True | ||||
break | break | ||||
if resource is not None and not _reject: | if resource is not None and not _reject: | ||||
log.debug(_("Accept invitation for individual resource %r / %r") % (resource['dn'], resource['mail']), level=8) | log.debug( | ||||
accept_reservation_request(itip_event, resource, original_resource, False, invitationpolicy) | _("Accept invitation for individual resource %r / %r") % ( | ||||
resource['dn'], | |||||
resource['mail'] | |||||
), | |||||
level=8 | |||||
) | |||||
accept_reservation_request( | |||||
itip_event, | |||||
resource, | |||||
original_resource, | |||||
False, | |||||
invitationpolicy | |||||
) | |||||
else: | else: | ||||
resource = resources[resource_dns[0]] # this is the receiving resource record | resource = resources[resource_dns[0]] # this is the receiving resource record | ||||
log.debug(_("Decline invitation for individual resource %r / %r") % (resource['dn'], resource['mail']), level=8) | log.debug( | ||||
_("Decline invitation for individual resource %r / %r") % ( | |||||
resource['dn'], | |||||
resource['mail'] | |||||
), | |||||
level=8 | |||||
) | |||||
decline_reservation_request(itip_event, resource) | decline_reservation_request(itip_event, resource) | ||||
cleanup() | cleanup() | ||||
os.unlink(filepath) | os.unlink(filepath) | ||||
def heartbeat(lastrun): | def heartbeat(lastrun): | ||||
▲ Show 20 Lines • Show All 117 Lines • ▼ Show 20 Lines | for resource in resources.keys(): | ||||
# sets the 'conflicting' flag and adds a list of conflicting events found | # sets the 'conflicting' flag and adds a list of conflicting events found | ||||
try: | try: | ||||
num_messages += read_resource_calendar(resources[resource], itip_events) | num_messages += read_resource_calendar(resources[resource], itip_events) | ||||
except Exception, e: | except Exception, e: | ||||
log.error(_("Failed to read resource calendar for %r: %r") % (resource, e)) | log.error(_("Failed to read resource calendar for %r: %r") % (resource, e)) | ||||
end = time.time() | end = time.time() | ||||
log.debug(_("start: %r, end: %r, total: %r, messages: %d") % (start, end, (end-start), num_messages), level=8) | log.debug(_("start: %r, end: %r, total: %r, messages: %d") % (start, end, (end-start), num_messages), level=8) | ||||
Lint: PEP8 E226: missing whitespace around arithmetic operator | |||||
# For each resource (collections are first!) | # For each resource (collections are first!) | ||||
# check conflicts and either accept or decline the reservation request | # check conflicts and either accept or decline the reservation request | ||||
for resource in resource_dns: | for resource in resource_dns: | ||||
log.debug(_("Polling for resource %r") % (resource), level=8) | log.debug(_("Polling for resource %r") % (resource), level=8) | ||||
if not resources.has_key(resource): | if not resources.has_key(resource): | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | for resource in resource_dns: | ||||
else: | else: | ||||
# This must have been a resource collection originally. | # This must have been a resource collection originally. | ||||
# We have inserted the reference to the original resource | # We have inserted the reference to the original resource | ||||
# record in 'memberof'. | # record in 'memberof'. | ||||
if resources[resource].has_key('memberof'): | if resources[resource].has_key('memberof'): | ||||
original_resource = resources[resources[resource]['memberof']] | original_resource = resources[resources[resource]['memberof']] | ||||
# Randomly select a target resource from the resource collection. | # Randomly select a target resource from the resource collection. | ||||
available_resource = resources[original_resource['uniquemember'][random.randint(0,(len(original_resource['uniquemember'])-1))]] | available_resource = resources[original_resource['uniquemember'][random.randint(0,(len(original_resource['uniquemember'])-1))]] | ||||
Lint: PEP8 E226 missing whitespace around arithmetic operator Lint: PEP8 E226: missing whitespace around arithmetic operator | |||||
done = True | done = True | ||||
if done: | if done: | ||||
break | break | ||||
# end for resource in resource_dns: | # end for resource in resource_dns: | ||||
return (available_resource, itip_event) | return (available_resource, itip_event) | ||||
▲ Show 20 Lines • Show All 803 Lines • Show Last 20 Lines |
missing whitespace around arithmetic operator