1"""File System Proxy. 2 3Provide an OS-neutral view on a file system, locally or remotely. 4The functionality is geared towards implementing some sort of 5rdist-like utility between a Mac and a UNIX system. 6 7The module defines three classes: 8 9FSProxyLocal -- used for local access 10FSProxyServer -- used on the server side of remote access 11FSProxyClient -- used on the client side of remote access 12 13The remote classes are instantiated with an IP address and an optional 14verbosity flag. 15""" 16 17import server 18import client 19import md5 20import os 21import fnmatch 22from stat import * 23import time 24import fnmatch 25 26maxnamelen = 255 27 28skipnames = (os.curdir, os.pardir) 29 30 31class FSProxyLocal: 32 33 def __init__(self): 34 self._dirstack = [] 35 self._ignore = ['*.pyc'] + self._readignore() 36 37 def _close(self): 38 while self._dirstack: 39 self.back() 40 41 def _readignore(self): 42 file = self._hide('ignore') 43 try: 44 f = open(file) 45 except IOError: 46 file = self._hide('synctree.ignorefiles') 47 try: 48 f = open(file) 49 except IOError: 50 return [] 51 ignore = [] 52 while 1: 53 line = f.readline() 54 if not line: break 55 if line[-1] == '\n': line = line[:-1] 56 ignore.append(line) 57 f.close() 58 return ignore 59 60 def _hidden(self, name): 61 return name[0] == '.' 62 63 def _hide(self, name): 64 return '.%s' % name 65 66 def visible(self, name): 67 if len(name) > maxnamelen: return 0 68 if name[-1] == '~': return 0 69 if name in skipnames: return 0 70 if self._hidden(name): return 0 71 head, tail = os.path.split(name) 72 if head or not tail: return 0 73 if os.path.islink(name): return 0 74 if '\0' in open(name, 'rb').read(512): return 0 75 for ign in self._ignore: 76 if fnmatch.fnmatch(name, ign): return 0 77 return 1 78 79 def check(self, name): 80 if not self.visible(name): 81 raise os.error, "protected name %s" % repr(name) 82 83 def checkfile(self, name): 84 self.check(name) 85 if not os.path.isfile(name): 86 raise os.error, "not a plain file %s" % repr(name) 87 88 def pwd(self): 89 return os.getcwd() 90 91 def cd(self, name): 92 self.check(name) 93 save = os.getcwd(), self._ignore 94 os.chdir(name) 95 self._dirstack.append(save) 96 self._ignore = self._ignore + self._readignore() 97 98 def back(self): 99 if not self._dirstack: 100 raise os.error, "empty directory stack" 101 dir, ignore = self._dirstack[-1] 102 os.chdir(dir) 103 del self._dirstack[-1] 104 self._ignore = ignore 105 106 def _filter(self, files, pat = None): 107 if pat: 108 def keep(name, pat = pat): 109 return fnmatch.fnmatch(name, pat) 110 files = filter(keep, files) 111 files = filter(self.visible, files) 112 files.sort() 113 return files 114 115 def list(self, pat = None): 116 files = os.listdir(os.curdir) 117 return self._filter(files, pat) 118 119 def listfiles(self, pat = None): 120 files = os.listdir(os.curdir) 121 files = filter(os.path.isfile, files) 122 return self._filter(files, pat) 123 124 def listsubdirs(self, pat = None): 125 files = os.listdir(os.curdir) 126 files = filter(os.path.isdir, files) 127 return self._filter(files, pat) 128 129 def exists(self, name): 130 return self.visible(name) and os.path.exists(name) 131 132 def isdir(self, name): 133 return self.visible(name) and os.path.isdir(name) 134 135 def islink(self, name): 136 return self.visible(name) and os.path.islink(name) 137 138 def isfile(self, name): 139 return self.visible(name) and os.path.isfile(name) 140 141 def sum(self, name): 142 self.checkfile(name) 143 BUFFERSIZE = 1024*8 144 f = open(name) 145 sum = md5.new() 146 while 1: 147 buffer = f.read(BUFFERSIZE) 148 if not buffer: 149 break 150 sum.update(buffer) 151 return sum.digest() 152 153 def size(self, name): 154 self.checkfile(name) 155 return os.stat(name)[ST_SIZE] 156 157 def mtime(self, name): 158 self.checkfile(name) 159 return time.localtime(os.stat(name)[ST_MTIME]) 160 161 def stat(self, name): 162 self.checkfile(name) 163 size = os.stat(name)[ST_SIZE] 164 mtime = time.localtime(os.stat(name)[ST_MTIME]) 165 return size, mtime 166 167 def info(self, name): 168 sum = self.sum(name) 169 size = os.stat(name)[ST_SIZE] 170 mtime = time.localtime(os.stat(name)[ST_MTIME]) 171 return sum, size, mtime 172 173 def _list(self, function, list): 174 if list is None: 175 list = self.listfiles() 176 res = [] 177 for name in list: 178 try: 179 res.append((name, function(name))) 180 except (os.error, IOError): 181 res.append((name, None)) 182 return res 183 184 def sumlist(self, list = None): 185 return self._list(self.sum, list) 186 187 def statlist(self, list = None): 188 return self._list(self.stat, list) 189 190 def mtimelist(self, list = None): 191 return self._list(self.mtime, list) 192 193 def sizelist(self, list = None): 194 return self._list(self.size, list) 195 196 def infolist(self, list = None): 197 return self._list(self.info, list) 198 199 def _dict(self, function, list): 200 if list is None: 201 list = self.listfiles() 202 dict = {} 203 for name in list: 204 try: 205 dict[name] = function(name) 206 except (os.error, IOError): 207 pass 208 return dict 209 210 def sumdict(self, list = None): 211 return self.dict(self.sum, list) 212 213 def sizedict(self, list = None): 214 return self.dict(self.size, list) 215 216 def mtimedict(self, list = None): 217 return self.dict(self.mtime, list) 218 219 def statdict(self, list = None): 220 return self.dict(self.stat, list) 221 222 def infodict(self, list = None): 223 return self._dict(self.info, list) 224 225 def read(self, name, offset = 0, length = -1): 226 self.checkfile(name) 227 f = open(name) 228 f.seek(offset) 229 if length == 0: 230 data = '' 231 elif length < 0: 232 data = f.read() 233 else: 234 data = f.read(length) 235 f.close() 236 return data 237 238 def create(self, name): 239 self.check(name) 240 if os.path.exists(name): 241 self.checkfile(name) 242 bname = name + '~' 243 try: 244 os.unlink(bname) 245 except os.error: 246 pass 247 os.rename(name, bname) 248 f = open(name, 'w') 249 f.close() 250 251 def write(self, name, data, offset = 0): 252 self.checkfile(name) 253 f = open(name, 'r+') 254 f.seek(offset) 255 f.write(data) 256 f.close() 257 258 def mkdir(self, name): 259 self.check(name) 260 os.mkdir(name, 0777) 261 262 def rmdir(self, name): 263 self.check(name) 264 os.rmdir(name) 265 266 267class FSProxyServer(FSProxyLocal, server.Server): 268 269 def __init__(self, address, verbose = server.VERBOSE): 270 FSProxyLocal.__init__(self) 271 server.Server.__init__(self, address, verbose) 272 273 def _close(self): 274 server.Server._close(self) 275 FSProxyLocal._close(self) 276 277 def _serve(self): 278 server.Server._serve(self) 279 # Retreat into start directory 280 while self._dirstack: self.back() 281 282 283class FSProxyClient(client.Client): 284 285 def __init__(self, address, verbose = client.VERBOSE): 286 client.Client.__init__(self, address, verbose) 287 288 289def test(): 290 import string 291 import sys 292 if sys.argv[1:]: 293 port = string.atoi(sys.argv[1]) 294 else: 295 port = 4127 296 proxy = FSProxyServer(('', port)) 297 proxy._serverloop() 298 299 300if __name__ == '__main__': 301 test() 302