1 #!/usr/bin/env python 2 3 from email import message_from_file 4 from vCalendar import parse, iterwrite 5 import imip_store 6 import sys 7 8 try: 9 from cStringIO import StringIO 10 except ImportError: 11 from StringIO import StringIO 12 13 # Postfix exit codes. 14 15 EX_USAGE = 64 16 EX_DATAERR = 65 17 EX_NOINPUT = 66 18 EX_NOUSER = 67 19 EX_NOHOST = 68 20 EX_UNAVAILABLE = 69 21 EX_SOFTWARE = 70 22 EX_OSERR = 71 23 EX_OSFILE = 72 24 EX_CANTCREAT = 73 25 EX_IOERR = 74 26 EX_TEMPFAIL = 75 27 EX_PROTOCOL = 76 28 EX_NOPERM = 77 29 EX_CONFIG = 78 30 31 # Permitted iTIP content types. 32 33 itip_content_types = [ 34 "text/calendar", # from RFC 6047 35 "text/x-vcalendar", "application/ics", # other possibilities 36 ] 37 38 def process(f, args): 39 msg = message_from_file(f) 40 print >>open("/tmp/imip.txt", "a"), "----" 41 print >>open("/tmp/imip.txt", "a"), args 42 print >>open("/tmp/imip.txt", "a"), "----" 43 print >>open("/tmp/imip.txt", "a"), msg.as_string() 44 print >>open("/tmp/imip.txt", "a") 45 46 # Handle messages with iTIP parts. 47 48 for part in msg.walk(): 49 if part.get_content_type() in itip_content_types and \ 50 part.get_param("method"): 51 52 handle_itip_part(part) 53 54 def get_itip_elements(elements): 55 d = {} 56 for name, attr, value in elements: 57 if isinstance(value, list): 58 d[name] = attr, get_itip_elements(value) 59 else: 60 if not d.has_key(name): 61 d[name] = [] 62 d[name].append((attr, value)) 63 return d 64 65 def get_attr_value(d, name, single=True): 66 if d.has_key(name): 67 values = d[name] 68 if isinstance(values, tuple): 69 return values 70 elif single and len(values) == 1: 71 return values[0] 72 else: 73 return values 74 else: 75 return None 76 77 def get_value(d, name, single=True): 78 if d.has_key(name): 79 values = d[name] 80 if isinstance(values, tuple): 81 return values[1] 82 elif single and len(values) == 1: 83 return values[0][1] 84 else: 85 return map(lambda x: x[1], values) 86 else: 87 return None 88 89 def handle_itip_part(part): 90 method = part.get_param("method") 91 92 f = StringIO(part.get_payload(decode=True)) 93 doctype, attrs, elements = parse(f, encoding=part.get_content_charset()) 94 95 if doctype == "VCALENDAR": 96 itip = get_itip_elements(elements) 97 98 if get_value(itip, "METHOD") == method: 99 100 for name, cls in handlers: 101 details = get_value(itip, name) 102 103 if details: 104 print >>open("/tmp/imip.txt", "a"), details 105 handler = cls(details) 106 print >>open("/tmp/imip.txt", "a"), "Handling", method, "with", handler, "->", methods[method](handler) 107 methods[method](handler)() 108 109 class Handler: 110 def __init__(self, details): 111 self.details = details 112 self.uid = get_value(details, "UID") 113 self.sequence = get_value(details, "SEQUENCE") 114 self.store = imip_store.FileStore() 115 116 def publish(self): 117 pass 118 119 def get_attr_value(self, name, single=True): 120 return get_attr_value(self.details, name, single) 121 122 def get_value(self, name, single=True): 123 return get_value(self.details, name, single) 124 125 class Event(Handler): 126 def add(self): 127 pass 128 129 def cancel(self): 130 pass 131 132 def counter(self): 133 pass 134 135 def declinecounter(self): 136 pass 137 138 def refresh(self): 139 pass 140 141 def reply(self): 142 pass 143 144 def request(self): 145 pass 146 147 class Freebusy(Handler): 148 def reply(self): 149 attendees = self.get_value("ATTENDEE", False) 150 organiser = self.get_value("ORGANIZER") 151 if not attendees and not organiser: 152 return 153 154 # Store the reply details for the attendee. 155 156 def request(self): 157 attendees = self.get_attr_value("ATTENDEE", False) 158 organiser = self.get_attr_value("ORGANIZER") 159 160 if not attendees and not organiser: 161 return 162 163 organiser_attr, organiser = organiser 164 165 # Get the details for the attendee. 166 167 out = open("/tmp/imip.txt", "a") 168 try: 169 w = iterwrite(out) 170 171 calendar = [] 172 cwrite = calendar.append 173 cwrite(("METHOD", {}, "REPLY")) 174 cwrite(("VERSION", {}, "2.0")) 175 176 for attendee_attr, attendee in attendees: 177 freebusy = self.store.get_freebusy(attendee) 178 if freebusy: 179 record = [] 180 rwrite = record.append 181 rwrite(("ORGANIZER", organiser_attr, organiser)) 182 rwrite(("ATTENDEE", attendee_attr, attendee)) 183 rwrite(("UID", {}, self.uid)) 184 185 for start, end in freebusy: 186 rwrite(("FREEBUSY", {}, [start, end])) 187 188 cwrite(("VFREEBUSY", {}, record)) 189 190 # Send a reply with the information. 191 192 w.write("VCALENDAR", {}, calendar) 193 194 finally: 195 out.close() 196 197 class Journal(Handler): 198 def add(self): 199 pass 200 201 def cancel(self): 202 pass 203 204 class Todo(Handler): 205 def add(self): 206 pass 207 208 def cancel(self): 209 pass 210 211 def counter(self): 212 pass 213 214 def declinecounter(self): 215 pass 216 217 def refresh(self): 218 pass 219 220 def reply(self): 221 pass 222 223 def request(self): 224 pass 225 226 # Handler registry. 227 228 handlers = [ 229 ("VFREEBUSY", Freebusy), 230 ("VEVENT", Event), 231 ("VTODO", Todo), 232 ("VJOURNAL", Journal), 233 ] 234 235 methods = { 236 "ADD" : lambda handler: handler.add, 237 "CANCEL" : lambda handler: handler.cancel, 238 "COUNTER" : lambda handler: handler.counter, 239 "DECLINECOUNTER" : lambda handler: handler.declinecounter, 240 "PUBLISH" : lambda handler: handler.publish, 241 "REFRESH" : lambda handler: handler.refresh, 242 "REPLY" : lambda handler: handler.reply, 243 "REQUEST" : lambda handler: handler.request, 244 } 245 246 if __name__ == "__main__": 247 process(sys.stdin, sys.argv[1:]) 248 249 # vim: tabstop=4 expandtab shiftwidth=4