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 get_requests(self, user): 135 136 "Get requests for the given 'user'." 137 138 filename = self.get_object_in_store(user, "requests") 139 if not filename or not exists(filename): 140 return None 141 142 f = open(filename) 143 try: 144 return [line.strip() for line in f.readlines()] 145 finally: 146 f.close() 147 148 def set_requests(self, user, requests): 149 150 "For the given 'user', set the list of queued 'requests'." 151 152 filename = self.get_object_in_store(user, "requests") 153 if not filename: 154 return False 155 156 f = open(filename, "w") 157 try: 158 for request in requests: 159 print >>f, request 160 finally: 161 f.close() 162 fix_permissions(filename) 163 164 return True 165 166 def set_request(self, user, request): 167 168 "For the given 'user', set the queued 'request'." 169 170 filename = self.get_object_in_store(user, "requests") 171 if not filename: 172 return False 173 174 f = open(filename, "a") 175 try: 176 print >>f, request 177 finally: 178 f.close() 179 fix_permissions(filename) 180 181 return True 182 183 def queue_request(self, user, uid): 184 185 "Queue a request for 'user' having the given 'uid'." 186 187 requests = self.get_requests(user) or [] 188 189 if uid not in requests: 190 return self.set_request(user, uid) 191 192 return False 193 194 def dequeue_request(self, user, uid): 195 196 "Dequeue a request for 'user' having the given 'uid'." 197 198 requests = self.get_requests(user) or [] 199 200 try: 201 requests.remove(uid) 202 self.set_requests(user, requests) 203 except ValueError: 204 return False 205 else: 206 return True 207 208 class FilePublisher(FileBase): 209 210 "A publisher of objects." 211 212 def __init__(self, store_dir=PUBLISH_DIR): 213 FileBase.__init__(self, store_dir) 214 215 def set_freebusy(self, user, freebusy): 216 217 "For the given 'user', set 'freebusy' details." 218 219 filename = self.get_object_in_store(user, "freebusy") 220 if not filename: 221 return False 222 223 record = [] 224 rwrite = record.append 225 226 rwrite(("ORGANIZER", {}, user)) 227 rwrite(("UID", {}, user)) 228 rwrite(("DTSTAMP", {}, datetime.utcnow().strftime("%Y%m%dT%H%M%SZ"))) 229 230 for start, end, uid in freebusy: 231 rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([start, end]))) 232 233 f = open(filename, "w") 234 try: 235 to_stream(f, make_calendar([("VFREEBUSY", {}, record)], "PUBLISH")) 236 finally: 237 f.close() 238 fix_permissions(filename) 239 240 return True 241 242 # vim: tabstop=4 expandtab shiftwidth=4