1 #!/usr/bin/env python 2 3 """ 4 Handlers for a resource. 5 """ 6 7 from imiptools.content import Handler, format_datetime, get_address, get_uri, to_part 8 from imiptools.handlers.common import CommonFreebusy 9 from vCalendar import to_node 10 11 class ResourceHandler(Handler): 12 13 "Handling mechanisms specific to resources." 14 15 def _record_and_respond(self, handle_for_attendee): 16 17 oa = self.require_organiser_and_attendees() 18 if not oa: 19 return None 20 21 organiser_item, attendees = oa 22 23 # Validate the organiser, ignoring spoofed requests. 24 25 if not self.validate_identities([organiser_item]): 26 return None 27 28 # Process each attendee separately. 29 30 calendar = [] 31 32 for attendee, attendee_attr in attendees.items(): 33 34 # Check for event using UID. 35 36 if not self.have_new_object(attendee, "VEVENT"): 37 continue 38 39 # Collect response objects produced when handling the request. 40 41 response = handle_for_attendee(attendee, attendee_attr) 42 if response: 43 calendar.append(response) 44 45 return calendar 46 47 def _schedule_for_attendee(self, attendee, attendee_attr): 48 49 # If newer than any old version, discard old details from the 50 # free/busy record and check for suitability. 51 52 periods = self.get_periods() 53 freebusy = self.store.get_freebusy(attendee) or [] 54 scheduled = self.can_schedule(freebusy, periods) 55 56 attendee_attr["PARTSTAT"] = scheduled and "ACCEPTED" or "DECLINED" 57 if self.messenger and self.messenger.sender != get_address(attendee): 58 attendee_attr["SENT-BY"] = get_uri(self.messenger.sender) 59 60 # Make a version of the request with just this attendee. 61 62 self.details["ATTENDEE"] = [(attendee, attendee_attr)] 63 64 event = to_node({"VEVENT" : [(self.details, {})]}) 65 self.store.set_event(attendee, self.uid, event) 66 67 # Only update free/busy details if the event is scheduled. 68 69 if scheduled: 70 self.update_freebusy(freebusy, attendee, periods) 71 else: 72 self.remove_from_freebusy(freebusy, attendee) 73 74 if self.publisher: 75 self.publisher.set_freebusy(attendee, freebusy) 76 77 return event 78 79 def _cancel_for_attendee(self, attendee, attendee_attr): 80 81 freebusy = self.store.get_freebusy(attendee) or [] 82 self.remove_from_freebusy(freebusy, attendee) 83 84 if self.publisher: 85 self.publisher.set_freebusy(attendee, freebusy) 86 87 return None 88 89 class Event(ResourceHandler): 90 91 "An event handler." 92 93 def add(self): 94 pass 95 96 def cancel(self): 97 98 "Cancel attendance for attendees." 99 100 self._record_and_respond(self._cancel_for_attendee) 101 return None 102 103 def counter(self): 104 105 "Since this handler does not send requests, it will not handle replies." 106 107 pass 108 109 def declinecounter(self): 110 111 """ 112 Since this handler does not send counter proposals, it will not handle 113 replies to such proposals. 114 """ 115 116 pass 117 118 def publish(self): 119 pass 120 121 def refresh(self): 122 pass 123 124 def reply(self): 125 126 "Since this handler does not send requests, it will not handle replies." 127 128 pass 129 130 def request(self): 131 132 """ 133 Respond to a request by preparing a reply containing accept/decline 134 information for each indicated attendee. 135 136 No support for countering requests is implemented. 137 """ 138 139 response = self._record_and_respond(self._schedule_for_attendee) 140 if response: 141 return "REPLY", to_part("REPLY", response) 142 else: 143 return None 144 145 class Freebusy(ResourceHandler, CommonFreebusy): 146 147 "A free/busy handler." 148 149 def publish(self): 150 pass 151 152 def reply(self): 153 154 "Since this handler does not send requests, it will not handle replies." 155 156 pass 157 158 # request provided by CommonFreeBusy.request 159 160 class Journal(ResourceHandler): 161 162 "A journal entry handler." 163 164 def add(self): 165 pass 166 167 def cancel(self): 168 pass 169 170 def publish(self): 171 pass 172 173 class Todo(ResourceHandler): 174 175 "A to-do item handler." 176 177 def add(self): 178 pass 179 180 def cancel(self): 181 pass 182 183 def counter(self): 184 185 "Since this handler does not send requests, it will not handle replies." 186 187 pass 188 189 def declinecounter(self): 190 191 """ 192 Since this handler does not send counter proposals, it will not handle 193 replies to such proposals. 194 """ 195 196 pass 197 198 def publish(self): 199 pass 200 201 def refresh(self): 202 pass 203 204 def reply(self): 205 206 "Since this handler does not send requests, it will not handle replies." 207 208 pass 209 210 def request(self): 211 pass 212 213 # Handler registry. 214 215 handlers = [ 216 ("VFREEBUSY", Freebusy), 217 ("VEVENT", Event), 218 ("VTODO", Todo), 219 ("VJOURNAL", Journal), 220 ] 221 222 # vim: tabstop=4 expandtab shiftwidth=4