1 #!/usr/bin/env python 2 3 from datetime import datetime 4 from os.path import abspath, commonprefix, exists, join, split 5 from os import makedirs 6 from vCalendar import iterwrite 7 8 # The location of the stored calendar information. 9 10 STORE_DIR = "/var/lib/imip-agent/store" 11 12 # The location of published static free/busy information. 13 14 PUBLISH_DIR = "/var/www/imip-agent/static" 15 16 def check_dir(base, dir): 17 return commonprefix([base, abspath(dir)]) == base 18 19 def make_calendar(fragment, method=None): 20 return ("VCALENDAR", {}, 21 (method and [("METHOD", {}, method)] or []) + 22 [("VERSION", {}, "2.0")] + 23 fragment 24 ) 25 26 def to_stream(out, fragment, encoding="utf-8"): 27 iterwrite(out, encoding=encoding).append(fragment) 28 29 class FileBase: 30 31 "Basic filesystem operations." 32 33 def __init__(self, store_dir=STORE_DIR): 34 self.store_dir = store_dir 35 if not exists(self.store_dir): 36 makedirs(self.store_dir) 37 38 def get_file_object(self, base, *parts): 39 pathname = join(base, *parts) 40 return check_dir(base, pathname) and pathname or None 41 42 def get_object_in_store(self, *parts): 43 44 """ 45 Return the name of any valid object stored within a hierarchy specified 46 by the given 'parts'. 47 """ 48 49 parent = expected = self.store_dir 50 51 for part in parts: 52 filename = self.get_file_object(expected, part) 53 if not filename: 54 return False 55 parent = expected 56 expected = filename 57 58 if not exists(parent): 59 makedirs(parent) 60 61 return filename 62 63 class FileStore(FileBase): 64 65 "A file store of tabular free/busy data and objects." 66 67 def get_event(self, user, uid): 68 69 "Get the event for the given 'user' with the given 'uid'." 70 71 filename = self.get_object_in_store(user, uid) 72 if not filename or not exists(filename): 73 return None 74 75 return exists(filename) and open(filename) or None 76 77 def set_event(self, user, uid, node): 78 79 "Set an event for 'user' having the given 'uid' and 'node'." 80 81 filename = self.get_object_in_store(user, uid) 82 if not filename: 83 return False 84 85 f = open(filename, "w") 86 try: 87 to_stream(f, node) 88 finally: 89 f.close() 90 91 return True 92 93 def get_freebusy(self, user): 94 95 "Get free/busy details for the given 'user'." 96 97 filename = self.get_object_in_store(user, "freebusy") 98 if not filename or not exists(filename): 99 return None 100 101 f = open(filename) 102 try: 103 l = [] 104 for line in f.readlines(): 105 l.append(tuple(line.strip().split("\t"))) 106 return l 107 finally: 108 f.close() 109 110 def set_freebusy(self, user, freebusy): 111 112 "For the given 'user', set 'freebusy' details." 113 114 filename = self.get_object_in_store(user, "freebusy") 115 if not filename: 116 return False 117 118 f = open(filename, "w") 119 try: 120 for item in freebusy: 121 f.write("\t".join(item) + "\n") 122 finally: 123 f.close() 124 125 return True 126 127 def get_requests(self, user): 128 129 "Get requests for the given 'user'." 130 131 filename = self.get_object_in_store(user, "requests") 132 if not filename: 133 return None 134 135 f = open(filename) 136 try: 137 return [line.strip() for line in f.readlines()] 138 finally: 139 f.close() 140 141 def set_requests(self, user, requests): 142 143 "For the given 'user', set the list of queued 'requests'." 144 145 filename = self.get_object_in_store(user, "requests") 146 if not filename: 147 return False 148 149 f = open(filename, "w") 150 try: 151 for request in requests: 152 print >>f, request 153 finally: 154 f.close() 155 156 return True 157 158 def set_request(self, user, request): 159 160 "For the given 'user', set the queued 'request'." 161 162 filename = self.get_object_in_store(user, "requests") 163 if not filename: 164 return False 165 166 f = open(filename, "a") 167 try: 168 print >>f, request 169 finally: 170 f.close() 171 172 return True 173 174 def queue_request(self, user, uid): 175 176 "Queue a request for 'user' having the given 'uid'." 177 178 requests = self.get_requests(user) 179 180 if uid not in requests: 181 return self.set_request(user, uid) 182 183 return False 184 185 class FilePublisher(FileBase): 186 187 "A publisher of objects." 188 189 def __init__(self, store_dir=PUBLISH_DIR): 190 FileBase.__init__(self, store_dir) 191 192 def set_freebusy(self, user, freebusy): 193 194 "For the given 'user', set 'freebusy' details." 195 196 filename = self.get_object_in_store(user, "freebusy") 197 if not filename: 198 return False 199 200 record = [] 201 rwrite = record.append 202 203 rwrite(("ORGANIZER", {}, user)) 204 rwrite(("UID", {}, user)) 205 rwrite(("DTSTAMP", {}, datetime.utcnow().strftime("%Y%m%dT%H%M%SZ"))) 206 207 for start, end, uid in freebusy: 208 rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([start, end]))) 209 210 f = open(filename, "w") 211 try: 212 to_stream(f, make_calendar([("VFREEBUSY", {}, record)], "PUBLISH")) 213 finally: 214 f.close() 215 216 return True 217 218 # vim: tabstop=4 expandtab shiftwidth=4