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