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