imip-agent

imiptools/handlers/person.py

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