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 107 f = open(filename) 108 try: 109 l = [] 110 for line in f.readlines(): 111 l.append(tuple(line.strip().split("\t"))) 112 return l 113 finally: 114 f.close() 115 116 def set_freebusy(self, user, freebusy): 117 118 "For the given 'user', set 'freebusy' details." 119 120 filename = self.get_object_in_store(user, "freebusy") 121 if not filename: 122 return False 123 124 f = open(filename, "w") 125 try: 126 for item in freebusy: 127 f.write("\t".join(item) + "\n") 128 finally: 129 f.close() 130 fix_permissions(filename) 131 132 return True 133 134 def set_freebusy_for_other(self, user, freebusy, other): 135 136 "For the given 'user', set 'freebusy' details for the 'other' user." 137 138 filename = self.get_object_in_store(user, "freebusy-other", other) 139 if not filename: 140 return False 141 142 f = open(filename, "w") 143 try: 144 for item in freebusy: 145 f.write("\t".join(item) + "\n") 146 finally: 147 f.close() 148 fix_permissions(filename) 149 150 return True 151 152 def get_requests(self, user): 153 154 "Get requests for the given 'user'." 155 156 filename = self.get_object_in_store(user, "requests") 157 if not filename or not exists(filename): 158 return None 159 160 f = open(filename) 161 try: 162 return [line.strip() for line in f.readlines()] 163 finally: 164 f.close() 165 166 def set_requests(self, user, requests): 167 168 "For the given 'user', set the list of queued 'requests'." 169 170 filename = self.get_object_in_store(user, "requests") 171 if not filename: 172 return False 173 174 f = open(filename, "w") 175 try: 176 for request in requests: 177 print >>f, request 178 finally: 179 f.close() 180 fix_permissions(filename) 181 182 return True 183 184 def set_request(self, user, request): 185 186 "For the given 'user', set the queued 'request'." 187 188 filename = self.get_object_in_store(user, "requests") 189 if not filename: 190 return False 191 192 f = open(filename, "a") 193 try: 194 print >>f, request 195 finally: 196 f.close() 197 fix_permissions(filename) 198 199 return True 200 201 def queue_request(self, user, uid): 202 203 "Queue a request for 'user' having the given 'uid'." 204 205 requests = self.get_requests(user) or [] 206 207 if uid not in requests: 208 return self.set_request(user, uid) 209 210 return False 211 212 def dequeue_request(self, user, uid): 213 214 "Dequeue a request for 'user' having the given 'uid'." 215 216 requests = self.get_requests(user) or [] 217 218 try: 219 requests.remove(uid) 220 self.set_requests(user, requests) 221 except ValueError: 222 return False 223 else: 224 return True 225 226 class FilePublisher(FileBase): 227 228 "A publisher of objects." 229 230 def __init__(self, store_dir=PUBLISH_DIR): 231 FileBase.__init__(self, store_dir) 232 233 def set_freebusy(self, user, freebusy): 234 235 "For the given 'user', set 'freebusy' details." 236 237 filename = self.get_object_in_store(user, "freebusy") 238 if not filename: 239 return False 240 241 record = [] 242 rwrite = record.append 243 244 rwrite(("ORGANIZER", {}, user)) 245 rwrite(("UID", {}, user)) 246 rwrite(("DTSTAMP", {}, datetime.utcnow().strftime("%Y%m%dT%H%M%SZ"))) 247 248 for start, end, uid in freebusy: 249 rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([start, end]))) 250 251 f = open(filename, "w") 252 try: 253 to_stream(f, make_calendar([("VFREEBUSY", {}, record)], "PUBLISH")) 254 finally: 255 f.close() 256 fix_permissions(filename) 257 258 return True 259 260 # vim: tabstop=4 expandtab shiftwidth=4