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 return ("VCALENDAR", {}, 20 (method and [("METHOD", {}, method)] or []) + 21 [("VERSION", {}, "2.0")] + 22 fragment 23 ) 24 25 def to_stream(out, fragment, encoding="utf-8"): 26 iterwrite(out, encoding=encoding).append(fragment) 27 28 class FileBase: 29 30 "Basic filesystem operations." 31 32 def __init__(self, store_dir=STORE_DIR): 33 self.store_dir = store_dir 34 if not exists(self.store_dir): 35 makedirs(self.store_dir) 36 37 def get_file_object(self, base, *parts): 38 pathname = join(base, *parts) 39 return check_dir(base, pathname) and pathname or None 40 41 def get_object_in_store(self, *parts): 42 43 """ 44 Return the name of any valid object stored within a hierarchy specified 45 by the given 'parts'. 46 """ 47 48 parent = expected = self.store_dir 49 50 for part in parts: 51 filename = self.get_file_object(expected, part) 52 if not filename: 53 return False 54 parent = expected 55 expected = filename 56 57 if not exists(parent): 58 makedirs(parent) 59 60 return filename 61 62 class FileStore(FileBase): 63 64 "A file store of tabular free/busy data and objects." 65 66 def get_event(self, user, uid): 67 68 "Get the event for the given 'user' with the given 'uid'." 69 70 filename = self.get_object_in_store(user, uid) 71 if not filename or not exists(filename): 72 return None 73 74 return exists(filename) and open(filename) or None 75 76 def set_event(self, user, uid, node): 77 78 "Set an event for 'user' having the given 'uid' and 'node'." 79 80 filename = self.get_object_in_store(user, uid) 81 if not filename: 82 return False 83 84 f = open(filename, "w") 85 try: 86 to_stream(f, node) 87 finally: 88 f.close() 89 fix_permissions(filename) 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 fix_permissions(filename) 125 126 return True 127 128 def get_requests(self, user): 129 130 "Get requests for the given 'user'." 131 132 filename = self.get_object_in_store(user, "requests") 133 if not filename or not exists(filename): 134 return None 135 136 f = open(filename) 137 try: 138 return [line.strip() for line in f.readlines()] 139 finally: 140 f.close() 141 142 def set_requests(self, user, requests): 143 144 "For the given 'user', set the list of queued 'requests'." 145 146 filename = self.get_object_in_store(user, "requests") 147 if not filename: 148 return False 149 150 f = open(filename, "w") 151 try: 152 for request in requests: 153 print >>f, request 154 finally: 155 f.close() 156 fix_permissions(filename) 157 158 return True 159 160 def set_request(self, user, request): 161 162 "For the given 'user', set the queued 'request'." 163 164 filename = self.get_object_in_store(user, "requests") 165 if not filename: 166 return False 167 168 f = open(filename, "a") 169 try: 170 print >>f, request 171 finally: 172 f.close() 173 fix_permissions(filename) 174 175 return True 176 177 def queue_request(self, user, uid): 178 179 "Queue a request for 'user' having the given 'uid'." 180 181 requests = self.get_requests(user) or [] 182 183 if uid not in requests: 184 return self.set_request(user, uid) 185 186 return False 187 188 class FilePublisher(FileBase): 189 190 "A publisher of objects." 191 192 def __init__(self, store_dir=PUBLISH_DIR): 193 FileBase.__init__(self, store_dir) 194 195 def set_freebusy(self, user, freebusy): 196 197 "For the given 'user', set 'freebusy' details." 198 199 filename = self.get_object_in_store(user, "freebusy") 200 if not filename: 201 return False 202 203 record = [] 204 rwrite = record.append 205 206 rwrite(("ORGANIZER", {}, user)) 207 rwrite(("UID", {}, user)) 208 rwrite(("DTSTAMP", {}, datetime.utcnow().strftime("%Y%m%dT%H%M%SZ"))) 209 210 for start, end, uid in freebusy: 211 rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([start, end]))) 212 213 f = open(filename, "w") 214 try: 215 to_stream(f, make_calendar([("VFREEBUSY", {}, record)], "PUBLISH")) 216 finally: 217 f.close() 218 fix_permissions(filename) 219 220 return True 221 222 # vim: tabstop=4 expandtab shiftwidth=4