Home | History | Annotate | Download | only in pdist
      1 """File System Proxy.
      2 
      3 Provide an OS-neutral view on a file system, locally or remotely.
      4 The functionality is geared towards implementing some sort of
      5 rdist-like utility between a Mac and a UNIX system.
      6 
      7 The module defines three classes:
      8 
      9 FSProxyLocal  -- used for local access
     10 FSProxyServer -- used on the server side of remote access
     11 FSProxyClient -- used on the client side of remote access
     12 
     13 The remote classes are instantiated with an IP address and an optional
     14 verbosity flag.
     15 """
     16 
     17 import server
     18 import client
     19 import md5
     20 import os
     21 import fnmatch
     22 from stat import *
     23 import time
     24 import fnmatch
     25 
     26 maxnamelen = 255
     27 
     28 skipnames = (os.curdir, os.pardir)
     29 
     30 
     31 class 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 
    267 class 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 
    283 class FSProxyClient(client.Client):
    284 
    285     def __init__(self, address, verbose = client.VERBOSE):
    286         client.Client.__init__(self, address, verbose)
    287 
    288 
    289 def 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 
    300 if __name__ == '__main__':
    301     test()
    302