| """File System Proxy. | |
| Provide an OS-neutral view on a file system, locally or remotely. | |
| The functionality is geared towards implementing some sort of | |
| rdist-like utility between a Mac and a UNIX system. | |
| The module defines three classes: | |
| FSProxyLocal -- used for local access | |
| FSProxyServer -- used on the server side of remote access | |
| FSProxyClient -- used on the client side of remote access | |
| The remote classes are instantiated with an IP address and an optional | |
| verbosity flag. | |
| """ | |
| import server | |
| import client | |
| import md5 | |
| import os | |
| import fnmatch | |
| from stat import * | |
| import time | |
| import fnmatch | |
| maxnamelen = 255 | |
| skipnames = (os.curdir, os.pardir) | |
| class FSProxyLocal: | |
| def __init__(self): | |
| self._dirstack = [] | |
| self._ignore = ['*.pyc'] + self._readignore() | |
| def _close(self): | |
| while self._dirstack: | |
| self.back() | |
| def _readignore(self): | |
| file = self._hide('ignore') | |
| try: | |
| f = open(file) | |
| except IOError: | |
| file = self._hide('synctree.ignorefiles') | |
| try: | |
| f = open(file) | |
| except IOError: | |
| return [] | |
| ignore = [] | |
| while 1: | |
| line = f.readline() | |
| if not line: break | |
| if line[-1] == '\n': line = line[:-1] | |
| ignore.append(line) | |
| f.close() | |
| return ignore | |
| def _hidden(self, name): | |
| return name[0] == '.' | |
| def _hide(self, name): | |
| return '.%s' % name | |
| def visible(self, name): | |
| if len(name) > maxnamelen: return 0 | |
| if name[-1] == '~': return 0 | |
| if name in skipnames: return 0 | |
| if self._hidden(name): return 0 | |
| head, tail = os.path.split(name) | |
| if head or not tail: return 0 | |
| if os.path.islink(name): return 0 | |
| if '\0' in open(name, 'rb').read(512): return 0 | |
| for ign in self._ignore: | |
| if fnmatch.fnmatch(name, ign): return 0 | |
| return 1 | |
| def check(self, name): | |
| if not self.visible(name): | |
| raise os.error, "protected name %s" % repr(name) | |
| def checkfile(self, name): | |
| self.check(name) | |
| if not os.path.isfile(name): | |
| raise os.error, "not a plain file %s" % repr(name) | |
| def pwd(self): | |
| return os.getcwd() | |
| def cd(self, name): | |
| self.check(name) | |
| save = os.getcwd(), self._ignore | |
| os.chdir(name) | |
| self._dirstack.append(save) | |
| self._ignore = self._ignore + self._readignore() | |
| def back(self): | |
| if not self._dirstack: | |
| raise os.error, "empty directory stack" | |
| dir, ignore = self._dirstack[-1] | |
| os.chdir(dir) | |
| del self._dirstack[-1] | |
| self._ignore = ignore | |
| def _filter(self, files, pat = None): | |
| if pat: | |
| def keep(name, pat = pat): | |
| return fnmatch.fnmatch(name, pat) | |
| files = filter(keep, files) | |
| files = filter(self.visible, files) | |
| files.sort() | |
| return files | |
| def list(self, pat = None): | |
| files = os.listdir(os.curdir) | |
| return self._filter(files, pat) | |
| def listfiles(self, pat = None): | |
| files = os.listdir(os.curdir) | |
| files = filter(os.path.isfile, files) | |
| return self._filter(files, pat) | |
| def listsubdirs(self, pat = None): | |
| files = os.listdir(os.curdir) | |
| files = filter(os.path.isdir, files) | |
| return self._filter(files, pat) | |
| def exists(self, name): | |
| return self.visible(name) and os.path.exists(name) | |
| def isdir(self, name): | |
| return self.visible(name) and os.path.isdir(name) | |
| def islink(self, name): | |
| return self.visible(name) and os.path.islink(name) | |
| def isfile(self, name): | |
| return self.visible(name) and os.path.isfile(name) | |
| def sum(self, name): | |
| self.checkfile(name) | |
| BUFFERSIZE = 1024*8 | |
| f = open(name) | |
| sum = md5.new() | |
| while 1: | |
| buffer = f.read(BUFFERSIZE) | |
| if not buffer: | |
| break | |
| sum.update(buffer) | |
| return sum.digest() | |
| def size(self, name): | |
| self.checkfile(name) | |
| return os.stat(name)[ST_SIZE] | |
| def mtime(self, name): | |
| self.checkfile(name) | |
| return time.localtime(os.stat(name)[ST_MTIME]) | |
| def stat(self, name): | |
| self.checkfile(name) | |
| size = os.stat(name)[ST_SIZE] | |
| mtime = time.localtime(os.stat(name)[ST_MTIME]) | |
| return size, mtime | |
| def info(self, name): | |
| sum = self.sum(name) | |
| size = os.stat(name)[ST_SIZE] | |
| mtime = time.localtime(os.stat(name)[ST_MTIME]) | |
| return sum, size, mtime | |
| def _list(self, function, list): | |
| if list is None: | |
| list = self.listfiles() | |
| res = [] | |
| for name in list: | |
| try: | |
| res.append((name, function(name))) | |
| except (os.error, IOError): | |
| res.append((name, None)) | |
| return res | |
| def sumlist(self, list = None): | |
| return self._list(self.sum, list) | |
| def statlist(self, list = None): | |
| return self._list(self.stat, list) | |
| def mtimelist(self, list = None): | |
| return self._list(self.mtime, list) | |
| def sizelist(self, list = None): | |
| return self._list(self.size, list) | |
| def infolist(self, list = None): | |
| return self._list(self.info, list) | |
| def _dict(self, function, list): | |
| if list is None: | |
| list = self.listfiles() | |
| dict = {} | |
| for name in list: | |
| try: | |
| dict[name] = function(name) | |
| except (os.error, IOError): | |
| pass | |
| return dict | |
| def sumdict(self, list = None): | |
| return self.dict(self.sum, list) | |
| def sizedict(self, list = None): | |
| return self.dict(self.size, list) | |
| def mtimedict(self, list = None): | |
| return self.dict(self.mtime, list) | |
| def statdict(self, list = None): | |
| return self.dict(self.stat, list) | |
| def infodict(self, list = None): | |
| return self._dict(self.info, list) | |
| def read(self, name, offset = 0, length = -1): | |
| self.checkfile(name) | |
| f = open(name) | |
| f.seek(offset) | |
| if length == 0: | |
| data = '' | |
| elif length < 0: | |
| data = f.read() | |
| else: | |
| data = f.read(length) | |
| f.close() | |
| return data | |
| def create(self, name): | |
| self.check(name) | |
| if os.path.exists(name): | |
| self.checkfile(name) | |
| bname = name + '~' | |
| try: | |
| os.unlink(bname) | |
| except os.error: | |
| pass | |
| os.rename(name, bname) | |
| f = open(name, 'w') | |
| f.close() | |
| def write(self, name, data, offset = 0): | |
| self.checkfile(name) | |
| f = open(name, 'r+') | |
| f.seek(offset) | |
| f.write(data) | |
| f.close() | |
| def mkdir(self, name): | |
| self.check(name) | |
| os.mkdir(name, 0777) | |
| def rmdir(self, name): | |
| self.check(name) | |
| os.rmdir(name) | |
| class FSProxyServer(FSProxyLocal, server.Server): | |
| def __init__(self, address, verbose = server.VERBOSE): | |
| FSProxyLocal.__init__(self) | |
| server.Server.__init__(self, address, verbose) | |
| def _close(self): | |
| server.Server._close(self) | |
| FSProxyLocal._close(self) | |
| def _serve(self): | |
| server.Server._serve(self) | |
| # Retreat into start directory | |
| while self._dirstack: self.back() | |
| class FSProxyClient(client.Client): | |
| def __init__(self, address, verbose = client.VERBOSE): | |
| client.Client.__init__(self, address, verbose) | |
| def test(): | |
| import string | |
| import sys | |
| if sys.argv[1:]: | |
| port = string.atoi(sys.argv[1]) | |
| else: | |
| port = 4127 | |
| proxy = FSProxyServer(('', port)) | |
| proxy._serverloop() | |
| if __name__ == '__main__': | |
| test() |