# HG changeset patch # User Paul Boddie # Date 1422141528 -3600 # Node ID 11308c50b0557cec0b708fc5ff40dca9fc756633 # Parent 28ca1a52d37966f844bd25394196385d8839899c Added the recording of other parties' free/busy information when receiving messages from them. Consolidated sender/recipient filtering for organiser and attendee identities. Fixed PARTSTAT inspection in new object detection when multiple attendees are present. diff -r 28ca1a52d379 -r 11308c50b055 imiptools/content.py --- a/imiptools/content.py Sat Jan 24 23:55:24 2015 +0100 +++ b/imiptools/content.py Sun Jan 25 00:18:48 2015 +0100 @@ -180,6 +180,28 @@ remove_period(freebusy, uid) store.set_freebusy(attendee, freebusy) +def remove_from_freebusy_for_other(freebusy, user, other, uid, store): + + """ + For the given 'user', remove for the 'other' party periods from 'freebusy' + that are associated with 'uid' in the 'store'. + """ + + remove_period(freebusy, uid) + store.set_freebusy_for_other(user, freebusy, other) + +def _update_freebusy(freebusy, periods, transp, uid): + + """ + Update the free/busy details with the given 'periods', 'transp' setting and + 'uid'. + """ + + remove_period(freebusy, uid) + + for start, end in periods: + insert_period(freebusy, (start, end, uid, transp)) + def update_freebusy(freebusy, attendee, periods, transp, uid, store): """ @@ -187,12 +209,18 @@ 'periods', 'transp' setting and 'uid' in the 'store'. """ - remove_period(freebusy, uid) + _update_freebusy(freebusy, periods, transp, uid) + store.set_freebusy(attendee, freebusy) + +def update_freebusy_for_other(freebusy, user, other, periods, transp, uid, store): - for start, end in periods: - insert_period(freebusy, (start, end, uid, transp)) + """ + For the given 'user', update the free/busy details of 'other' with the given + 'periods', 'transp' setting and 'uid' in the 'store'. + """ - store.set_freebusy(attendee, freebusy) + _update_freebusy(freebusy, periods, transp, uid) + store.set_freebusy_for_other(user, freebusy, other) def can_schedule(freebusy, periods, uid): @@ -358,25 +386,53 @@ def remove_from_freebusy(self, freebusy, attendee): remove_from_freebusy(freebusy, attendee, self.uid, self.store) + def remove_from_freebusy_for_other(self, freebusy, user, other): + remove_from_freebusy_for_other(freebusy, user, other, self.uid, self.store) + def update_freebusy(self, freebusy, attendee, periods): return update_freebusy(freebusy, attendee, periods, self.get_value("TRANSP"), self.uid, self.store) + def update_freebusy_for_other(self, freebusy, user, other, periods): + return update_freebusy_for_other(freebusy, user, other, periods, self.get_value("TRANSP"), self.uid, self.store) + def can_schedule(self, freebusy, periods): return can_schedule(freebusy, periods, self.uid) - def filter_by_senders(self, values): - addresses = map(get_address, values) + def filter_by_senders(self, mapping): + + """ + Return a list of items from 'mapping' filtered using sender information. + """ + if self.senders: - return self.senders.intersection(addresses) - else: - return addresses + + # Get a mapping from senders to identities. + + identities = self.get_sender_identities(mapping) + + # Find the senders that are valid. + + senders = map(get_address, identities) + valid = self.senders.intersection(senders) - def filter_by_recipients(self, values): - addresses = map(get_address, values) + # Return the true identities. + + return [identities[get_uri(address)] for address in valid] + else: + return mapping + + def filter_by_recipients(self, mapping): + + """ + Return a list of items from 'mapping' filtered using recipient + information. + """ + if self.recipients: - return self.recipients.intersection(addresses) + addresses = map(get_address, mapping) + return map(get_uri, self.recipients.intersection(addresses)) else: - return addresses + return mapping def require_organiser_and_attendees(self, from_organiser=True): @@ -388,39 +444,46 @@ """ attendee_map = uri_dict(self.get_value_map("ATTENDEE")) - organiser = uri_item(self.get_item("ORGANIZER")) + organiser_item = uri_item(self.get_item("ORGANIZER")) - # Only provide details for recipients who are also attendees. + # Only provide details for attendees who sent/receive the message. - filter_fn = from_organiser and self.filter_by_recipients or self.filter_by_senders + attendee_filter_fn = from_organiser and self.filter_by_recipients or self.filter_by_senders attendees = {} - for attendee in map(get_uri, filter_fn(attendee_map)): + for attendee in attendee_filter_fn(attendee_map): attendees[attendee] = attendee_map[attendee] - if not attendees or not organiser: + if not attendees or not organiser_item: + return None + + # Only provide details for an organiser who sent/receives the message. + + organiser_filter_fn = from_organiser and self.filter_by_senders or self.filter_by_recipients + + if not organiser_filter_fn(dict([organiser_item])): return None - return organiser, attendees + return organiser_item, attendees - def validate_identities(self, items): + def get_sender_identities(self, mapping): """ - Validate the 'items' against the known senders, obtaining sent-by - addresses from attributes provided by the items. + Return a mapping from actual senders to the identities for which they + have provided data, extracting this information from the given + 'mapping'. """ - # Reject organisers that do not match any senders. + senders = {} - identities = [] - - for value, attr in items: - identities.append(value) + for value, attr in mapping.items(): sent_by = attr.get("SENT-BY") if sent_by: - identities.append(get_uri(sent_by)) + senders[get_uri(sent_by)] = value + else: + senders[value] = value - return self.filter_by_senders(identities) + return senders def get_object(self, user, objtype): @@ -452,8 +515,18 @@ # NOTE: and make it invalid. Thus, attendance information may also be # NOTE: checked. - _attendee, old_attr = get_item(obj, "ATTENDEE") - _attendee, new_attr = self.get_item("ATTENDEE") + for _attendee, old_attr in get_items(obj, "ATTENDEE"): + if _attendee == attendee: + break + else: + return False + + for _attendee, new_attr in self.get_items("ATTENDEE"): + if _attendee == attendee: + break + else: + return False + old_partstat = old_attr.get("PARTSTAT") new_partstat = new_attr.get("PARTSTAT") diff -r 28ca1a52d379 -r 11308c50b055 imiptools/handlers/common.py --- a/imiptools/handlers/common.py Sat Jan 24 23:55:24 2015 +0100 +++ b/imiptools/handlers/common.py Sun Jan 25 00:18:48 2015 +0100 @@ -38,11 +38,6 @@ (organiser, organiser_attr), attendees = organiser_item, attendees = oa - # Validate the organiser, ignoring spoofed requests. - - if not self.validate_identities([organiser_item]): - return None - # Construct an appropriate fragment. calendar = [] diff -r 28ca1a52d379 -r 11308c50b055 imiptools/handlers/person.py --- a/imiptools/handlers/person.py Sat Jan 24 23:55:24 2015 +0100 +++ b/imiptools/handlers/person.py Sun Jan 25 00:18:48 2015 +0100 @@ -45,11 +45,6 @@ (organiser, organiser_attr), attendees = organiser_item, attendees = oa - # Validate the organiser or attendee, ignoring spoofed requests. - - if not self.validate_identities(from_organiser and [organiser_item] or attendees.items()): - return False - # Handle notifications and invitations. if from_organiser: @@ -61,6 +56,16 @@ if not self.have_new_object(attendee, objtype): continue + # Record other party free/busy information. + + if organiser != attendee: + freebusy = self.store.get_freebusy_for_other(attendee, organiser) + + if organiser_attr.get("PARTSTAT") != "DECLINED": + self.update_freebusy_for_other(freebusy, attendee, organiser, self.get_periods()) + else: + self.remove_from_freebusy_for_other(freebusy, attendee, organiser) + # Store the object and queue any request. self.store.set_event(attendee, self.uid, to_node( @@ -89,7 +94,15 @@ attendee_map[attendee] = attendee_attr - # NOTE: Record other attendee free/busy information. + # Record other party free/busy information. + + if organiser != attendee: + freebusy = self.store.get_freebusy_for_other(organiser, attendee) + + if attendee_attr.get("PARTSTAT") != "DECLINED": + self.update_freebusy_for_other(freebusy, organiser, attendee, self.get_periods()) + else: + self.remove_from_freebusy_for_other(freebusy, organiser, attendee) # Set the new details and store the object. diff -r 28ca1a52d379 -r 11308c50b055 imiptools/handlers/person_outgoing.py --- a/imiptools/handlers/person_outgoing.py Sat Jan 24 23:55:24 2015 +0100 +++ b/imiptools/handlers/person_outgoing.py Sun Jan 25 00:18:48 2015 +0100 @@ -63,8 +63,6 @@ self.store.dequeue_request(identity, self.uid) - # NOTE: Record other attendee free/busy information. - # Update free/busy information. if update_freebusy: diff -r 28ca1a52d379 -r 11308c50b055 imiptools/handlers/resource.py --- a/imiptools/handlers/resource.py Sat Jan 24 23:55:24 2015 +0100 +++ b/imiptools/handlers/resource.py Sun Jan 25 00:18:48 2015 +0100 @@ -36,11 +36,6 @@ organiser_item, attendees = oa - # Validate the organiser, ignoring spoofed requests. - - if not self.validate_identities([organiser_item]): - return None - # Process each attendee separately. calendar = []