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 STORE_DIR = "/var/lib/imip-agent/store" 9 PUBLISH_DIR = "/var/www/imip-agent" 10 11 def check_dir(base, dir): 12 return commonprefix([base, abspath(dir)]) == base 13 14 def make_calendar(fragment, method=None): 15 return ("VCALENDAR", {}, 16 (method and [("METHOD", {}, method)] or []) + 17 [("VERSION", {}, "2.0")] + 18 fragment 19 ) 20 21 def to_stream(out, fragment, encoding="utf-8"): 22 iterwrite(out, encoding=encoding).append(fragment) 23 24 class FileBase: 25 26 "Basic filesystem operations." 27 28 def __init__(self, store_dir=STORE_DIR): 29 self.store_dir = store_dir 30 if not exists(self.store_dir): 31 makedirs(self.store_dir) 32 33 def get_file_object(self, base, *parts): 34 pathname = join(base, *parts) 35 return check_dir(base, pathname) and pathname or None 36 37 def get_object_in_store(self, *parts): 38 39 """ 40 Return the name of any valid object stored within a hierarchy specified 41 by the given 'parts'. 42 """ 43 44 parent = expected = self.store_dir 45 46 for part in parts: 47 filename = self.get_file_object(expected, part) 48 if not filename: 49 return False 50 parent = expected 51 expected = filename 52 53 if not exists(parent): 54 makedirs(parent) 55 56 return filename 57 58 class FileStore(FileBase): 59 60 "A file store of tabular free/busy data and objects." 61 62 def get_event(self, user, uid): 63 64 "Get the event for the given 'user' with the given 'uid'." 65 66 filename = self.get_object_in_store(user, uid) 67 if not filename or not exists(filename): 68 return None 69 70 return exists(filename) and open(filename) or None 71 72 def set_event(self, user, uid, node): 73 74 "Set an event for 'user' having the given 'uid' and 'node'." 75 76 filename = self.get_object_in_store(user, uid) 77 if not filename: 78 return False 79 80 f = open(filename, "w") 81 try: 82 to_stream(f, node) 83 finally: 84 f.close() 85 86 return True 87 88 def get_freebusy(self, user): 89 90 "Get free/busy details for the given 'user'." 91 92 filename = self.get_object_in_store(user, "freebusy") 93 if not filename or not exists(filename): 94 return None 95 96 f = open(filename) 97 try: 98 l = [] 99 for line in f.readlines(): 100 l.append(tuple(line.strip().split("\t"))) 101 return l 102 finally: 103 f.close() 104 105 def set_freebusy(self, user, freebusy): 106 107 "For the given 'user', set 'freebusy' details." 108 109 filename = self.get_object_in_store(user, "freebusy") 110 if not filename: 111 return False 112 113 f = open(filename, "w") 114 try: 115 for item in freebusy: 116 f.write("\t".join(item) + "\n") 117 finally: 118 f.close() 119 120 return True 121 122 class FilePublisher(FileBase): 123 124 "A publisher of objects." 125 126 def __init__(self, store_dir=PUBLISH_DIR): 127 FileBase.__init__(self, store_dir) 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 record = [] 138 rwrite = record.append 139 140 rwrite(("ORGANIZER", {}, user)) 141 rwrite(("UID", {}, user)) 142 rwrite(("DTSTAMP", {}, datetime.utcnow().strftime("%Y%m%dT%H%M%SZ"))) 143 144 for start, end, uid in freebusy: 145 rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([start, end]))) 146 147 f = open(filename, "w") 148 try: 149 to_stream(f, make_calendar([("VFREEBUSY", {}, record)], "PUBLISH")) 150 finally: 151 f.close() 152 153 return True 154 155 # vim: tabstop=4 expandtab shiftwidth=4