imip-agent

imiptools/handlers/person.py

130:4a7f257f3846
2014-12-10 Paul Boddie Leave non-mailto URIs unprefixed; ensure SENT-BY properties are URIs.
     1 #!/usr/bin/env python     2      3 """     4 Handlers for a person for whom scheduling is performed.     5 """     6      7 from email.mime.text import MIMEText     8 from imiptools.config import MANAGER_PATH, MANAGER_URL     9 from imiptools.content import Handler, get_address, get_uri, to_part, uri_dict, uri_items    10 from imiptools.handlers.common import CommonFreebusy    11 from socket import gethostname    12 from vCalendar import to_node    13     14 def get_manager_url():    15     url_base = MANAGER_URL or "http://%s/" % gethostname()    16     return "%s/%s" % (url_base.rstrip("/"), MANAGER_PATH.lstrip("/"))    17     18 class PersonHandler(Handler):    19     20     "Handling mechanisms specific to people."    21     22     def _record_and_deliver(self, objtype, from_organiser=True, queue=False):    23     24         oa = self.require_organiser_and_attendees(from_organiser)    25         if not oa:    26             return False    27     28         (organiser, organiser_attr), attendees = organiser_item, attendees = oa    29     30         # Validate the organiser or attendee, ignoring spoofed requests.    31     32         if not self.validate_identities(from_organiser and [organiser_item] or attendees.items()):    33             return False    34     35         # Handle notifications and invitations.    36     37         if from_organiser:    38     39             # Process each attendee separately.    40     41             for attendee, attendee_attr in attendees.items():    42     43                 if not self.have_new_object(attendee, objtype):    44                     continue    45     46                 # Store the object and queue any request.    47     48                 self.store.set_event(attendee, self.uid, to_node(    49                     {objtype : [(self.details, {})]}    50                     ))    51     52                 if queue:    53                     self.store.queue_request(attendee, self.uid)    54     55         # As organiser, update attendance.    56     57         else:    58             obj = self.get_object(organiser, objtype)    59     60             if obj and self.have_new_object(organiser, objtype, obj):    61     62                 # Get attendee details in a usable form.    63     64                 attendee_map = uri_dict(self.get_value_map("ATTENDEE"))    65     66                 for attendee, attendee_attr in attendees.items():    67     68                     # Update attendance in the loaded object.    69     70                     attendee_map[attendee] = attendee_attr    71     72                 # Set the new details and store the object.    73     74                 obj["ATTENDEE"] = attendee_map.items()    75     76                 self.store.set_event(organiser, self.uid, to_node(    77                     {objtype : [(obj, {})]}    78                     ))    79     80         return True    81     82     def _record_freebusy(self, from_organiser=True):    83     84         "Record free/busy information for the received information."    85     86         freebusy = []    87     88         for value in self.get_values("FREEBUSY") or []:    89             if not isinstance(value, list):    90                 value = [value]    91             for v in value:    92                 try:    93                     start, end = v.split("/", 1)    94                     freebusy.append((start, end))    95                 except ValueError:    96                     pass    97     98         for sender, sender_attr in uri_items(self.get_items(from_organiser and "ORGANIZER" or "ATTENDEE")):    99             for recipient in self.recipients:   100                 self.store.set_freebusy_for_other(get_uri(recipient), freebusy, sender)   101    102     def reply(self):   103    104         "Wrap any valid message and pass it on to the recipient."   105    106         attendee = get_address(self.get_value("ATTENDEE"))   107         if attendee:   108             return "REPLY", MIMEText("A reply has been received from %s." % attendee)   109    110 class Event(PersonHandler):   111    112     "An event handler."   113    114     def add(self):   115    116         # NOTE: Queue a suggested modification to any active event.   117    118         # The message is now wrapped and passed on to the recipient.   119    120         return "ADD", MIMEText("An addition to an event has been received.")   121    122     def cancel(self):   123    124         # NOTE: Queue a suggested modification to any active event.   125    126         # The message is now wrapped and passed on to the recipient.   127    128         return "CANCEL", MIMEText("A cancellation has been received.")   129    130     def counter(self):   131    132         # NOTE: Queue a suggested modification to any active event.   133    134         # The message is now wrapped and passed on to the recipient.   135    136         return "COUNTER", MIMEText("A counter proposal has been received.")   137    138     def declinecounter(self):   139    140         # NOTE: Queue a suggested modification to any active event.   141    142         # The message is now wrapped and passed on to the recipient.   143    144         return "DECLINECOUNTER", MIMEText("A declining counter proposal has been received.")   145    146     def publish(self):   147    148         # NOTE: Register details of any relevant event.   149    150         # The message is now wrapped and passed on to the recipient.   151    152         return "PUBLISH", MIMEText("Details of an event have been received.")   153    154     def refresh(self):   155    156         # NOTE: Update details of any active event.   157    158         # The message is now wrapped and passed on to the recipient.   159    160         return "REFRESH", MIMEText("An event update has been received.")   161    162     def reply(self):   163    164         "Record replies and notify the recipient."   165    166         self._record_and_deliver("VEVENT", from_organiser=False, queue=False)   167         return PersonHandler.reply(self)   168    169     def request(self):   170    171         "Hold requests and notify the recipient."   172    173         self._record_and_deliver("VEVENT", from_organiser=True, queue=True)   174    175         # The message is now wrapped and passed on to the recipient.   176    177         url = "%s/%s" % (get_manager_url().rstrip("/"), self.uid)   178         return "REQUEST", MIMEText("A request has been queued and can be viewed here: %s" % url)   179    180 class Freebusy(PersonHandler, CommonFreebusy):   181    182     "A free/busy handler."   183    184     def publish(self):   185    186         "Register free/busy information."   187    188         self._record_freebusy(from_organiser=True)   189    190         # The message is now wrapped and passed on to the recipient.   191    192         return "PUBLISH", MIMEText("Details of a contact's availability have been received.")   193    194     def reply(self):   195    196         "Record replies and notify the recipient."   197    198         self._record_freebusy(from_organiser=False)   199         return PersonHandler.reply(self)   200    201     def request(self):   202    203         """   204         Respond to a request by preparing a reply containing free/busy   205         information for each indicated attendee.   206         """   207    208         # NOTE: This should be subject to policy/preferences.   209    210         return CommonFreebusy.request(self)   211    212 class Journal(PersonHandler):   213    214     "A journal entry handler."   215    216     def add(self):   217    218         # NOTE: Queue a suggested modification to any active entry.   219    220         # The message is now wrapped and passed on to the recipient.   221    222         return "ADD", MIMEText("An addition to a journal entry has been received.")   223    224     def cancel(self):   225    226         # NOTE: Queue a suggested modification to any active entry.   227    228         # The message is now wrapped and passed on to the recipient.   229    230         return "CANCEL", MIMEText("A cancellation has been received.")   231    232     def publish(self):   233    234         # NOTE: Register details of any relevant entry.   235    236         # The message is now wrapped and passed on to the recipient.   237    238         return "PUBLISH", MIMEText("Details of a journal entry have been received.")   239    240 class Todo(PersonHandler):   241    242     "A to-do item handler."   243    244     def add(self):   245    246         # NOTE: Queue a suggested modification to any active item.   247    248         # The message is now wrapped and passed on to the recipient.   249    250         return "ADD", MIMEText("An addition to an item has been received.")   251    252     def cancel(self):   253    254         # NOTE: Queue a suggested modification to any active item.   255    256         # The message is now wrapped and passed on to the recipient.   257    258         return "CANCEL", MIMEText("A cancellation has been received.")   259    260     def counter(self):   261    262         # NOTE: Queue a suggested modification to any active item.   263    264         # The message is now wrapped and passed on to the recipient.   265    266         return "COUNTER", MIMEText("A counter proposal has been received.")   267    268     def declinecounter(self):   269    270         # NOTE: Queue a suggested modification to any active item.   271    272         # The message is now wrapped and passed on to the recipient.   273    274         return "DECLINECOUNTER", MIMEText("A declining counter proposal has been received.")   275    276     def publish(self):   277    278         # NOTE: Register details of any relevant item.   279    280         # The message is now wrapped and passed on to the recipient.   281    282         return "PUBLISH", MIMEText("Details of an item have been received.")   283    284     def refresh(self):   285    286         # NOTE: Update details of any active item.   287    288         # The message is now wrapped and passed on to the recipient.   289    290         return "REFRESH", MIMEText("An item update has been received.")   291    292     def reply(self):   293    294         "Record replies and notify the recipient."   295    296         self._record_and_deliver("VTODO", from_organiser=False, queue=False)   297         return PersonHandler.reply(self)   298    299     def request(self):   300    301         "Hold requests and notify the recipient."   302    303         self._record_and_deliver("VTODO", from_organiser=True, queue=True)   304    305         # The message is now wrapped and passed on to the recipient.   306    307         return "REQUEST", MIMEText("A request has been queued.")   308    309 # Handler registry.   310    311 handlers = [   312     ("VFREEBUSY",   Freebusy),   313     ("VEVENT",      Event),   314     ("VTODO",       Todo),   315     ("VJOURNAL",    Journal),   316     ]   317    318 # vim: tabstop=4 expandtab shiftwidth=4