1.1 --- a/imiptools/content.py Sun Oct 01 23:09:50 2017 +0200
1.2 +++ b/imiptools/content.py Mon Oct 02 19:01:22 2017 +0200
1.3 @@ -29,6 +29,76 @@
1.4
1.5 IMIP_COUNTER_AS_REQUEST = settings["IMIP_COUNTER_AS_REQUEST"]
1.6
1.7 +# Permitted iTIP content types.
1.8 +
1.9 +itip_content_types = [
1.10 + "text/calendar", # from RFC 6047
1.11 + "text/x-vcalendar", "application/ics", # other possibilities
1.12 + ]
1.13 +
1.14 +def have_itip_part(part):
1.15 +
1.16 + "Return whether 'part' provides iTIP content."
1.17 +
1.18 + return part.get_content_type() in itip_content_types and \
1.19 + part.get_param("method")
1.20 +
1.21 +def is_returned_message(message):
1.22 +
1.23 + """
1.24 + Return whether 'message' contains a returned message that should not be
1.25 + handled because it may have originated from the current recipient.
1.26 + """
1.27 +
1.28 + for part in message.walk():
1.29 + if part.get_content_type() == "message/delivery-status":
1.30 + return True
1.31 + return False
1.32 +
1.33 +def consistent_methods(itip_method, part_method):
1.34 +
1.35 + """
1.36 + Return whether 'itip_method' (from content) and 'part_method' (from
1.37 + metadata) are consistent.
1.38 + """
1.39 +
1.40 + return itip_method == part_method or \
1.41 + IMIP_COUNTER_AS_REQUEST and itip_method == "COUNTER" and \
1.42 + part_method == "REQUEST"
1.43 +
1.44 +def parse_itip_part(part):
1.45 +
1.46 + """
1.47 + Parse the given message 'part' and return a dictionary mapping calendar
1.48 + object type names to lists of fragments, along with the method employed by
1.49 + the parsed calendar object.
1.50 +
1.51 + If no iTIP content is found, None is returned.
1.52 + """
1.53 +
1.54 + # Decode the data and parse it.
1.55 +
1.56 + f = StringIO(part.get_payload(decode=True))
1.57 + itip = parse_object(f, part.get_content_charset(), "VCALENDAR")
1.58 +
1.59 + # Ignore the part if not a calendar object.
1.60 +
1.61 + if not itip:
1.62 + return None
1.63 +
1.64 + # Require consistency between declared and employed methods.
1.65 +
1.66 + itip_method = get_value(itip, "METHOD")
1.67 + method = part.get_param("method")
1.68 + method = method and method.upper()
1.69 +
1.70 + if not consistent_methods(itip_method, method):
1.71 + return None
1.72 +
1.73 + # Assert the object's method as the definitive one.
1.74 +
1.75 + return itip, itip_method
1.76 +
1.77 def handle_itip_part(part, handlers):
1.78
1.79 """
1.80 @@ -38,59 +108,40 @@
1.81 (outgoing-recipients, message-part).
1.82 """
1.83
1.84 - method = part.get_param("method")
1.85 - method = method and method.upper()
1.86 -
1.87 - # Decode the data and parse it.
1.88 -
1.89 - f = StringIO(part.get_payload(decode=True))
1.90 -
1.91 - itip = parse_object(f, part.get_content_charset(), "VCALENDAR")
1.92 -
1.93 - # Ignore the part if not a calendar object.
1.94 -
1.95 - if not itip:
1.96 + itip_details = parse_itip_part(part)
1.97 + if not itip_details:
1.98 return
1.99
1.100 - # Require consistency between declared and employed methods.
1.101 -
1.102 - itip_method = get_value(itip, "METHOD")
1.103 + itip, method = itip_details
1.104
1.105 - if itip_method == method or \
1.106 - IMIP_COUNTER_AS_REQUEST and itip_method == "COUNTER" and method == "REQUEST":
1.107 + # Look for different kinds of sections.
1.108
1.109 - # Assert the object's method as the definitive one.
1.110 + all_results = []
1.111
1.112 - method = itip_method
1.113 + for name, items in itip.items():
1.114
1.115 - # Look for different kinds of sections.
1.116 + # Get a handler for the given section.
1.117
1.118 - all_results = []
1.119 -
1.120 - for name, items in itip.items():
1.121 -
1.122 - # Get a handler for the given section.
1.123 + handler = handlers.get(name)
1.124 + if not handler:
1.125 + continue
1.126
1.127 - handler = handlers.get(name)
1.128 - if not handler:
1.129 - continue
1.130 + for item in items:
1.131
1.132 - for item in items:
1.133 + # Dispatch to a handler and obtain any response.
1.134
1.135 - # Dispatch to a handler and obtain any response.
1.136 + handler.set_object(Object({name : item}))
1.137 + handler.set_identity(method)
1.138
1.139 - handler.set_object(Object({name : item}))
1.140 - handler.set_identity(method)
1.141 + if handler.is_usable(method):
1.142
1.143 - if handler.is_usable(method):
1.144 + # Perform the method in a critical section.
1.145
1.146 - # Perform the method in a critical section.
1.147 -
1.148 - handler.acquire_lock()
1.149 - try:
1.150 - methods[method](handler)()
1.151 - finally:
1.152 - handler.release_lock()
1.153 + handler.acquire_lock()
1.154 + try:
1.155 + methods[method](handler)()
1.156 + finally:
1.157 + handler.release_lock()
1.158
1.159 # Handler registry.
1.160