[Zope3-checkins] CVS: Zope3/src/zope/server/vfs - __init__.py:1.2 osfilesystem.py:1.2 publisherfilesystem.py:1.2 testfilesystemaccess.py:1.2 usernamepassword.py:1.2

Jim Fulton jim@zope.com
Wed, 25 Dec 2002 09:15:58 -0500


Update of /cvs-repository/Zope3/src/zope/server/vfs
In directory cvs.zope.org:/tmp/cvs-serv20790/src/zope/server/vfs

Added Files:
	__init__.py osfilesystem.py publisherfilesystem.py 
	testfilesystemaccess.py usernamepassword.py 
Log Message:
Grand renaming:

- Renamed most files (especially python modules) to lower case.

- Moved views and interfaces into separate hierarchies within each
  project, where each top-level directory under the zope package
  is a separate project.

- Moved everything to src from lib/python.

  lib/python will eventually go away. I need access to the cvs
  repository to make this happen, however.

There are probably some bits that are broken. All tests pass
and zope runs, but I haven't tried everything. There are a number
of cleanups I'll work on tomorrow.



=== Zope3/src/zope/server/vfs/__init__.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:58 2002
+++ Zope3/src/zope/server/vfs/__init__.py	Wed Dec 25 09:15:28 2002
@@ -0,0 +1,2 @@
+#
+# This file is necessary to make this directory a package.


=== Zope3/src/zope/server/vfs/osfilesystem.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:58 2002
+++ Zope3/src/zope/server/vfs/osfilesystem.py	Wed Dec 25 09:15:28 2002
@@ -0,0 +1,227 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Filesystem implementation for a real (unix-like) OS filesystem.
+
+$Id$
+"""
+import os
+import re
+import stat
+import datetime
+import fnmatch
+fromts = datetime.datetime.fromtimestamp
+
+from zope.server.interfaces.vfs import IPosixFileSystem
+
+
+class OSFileSystem(object):
+    """Generic OS FileSystem implementation.
+
+       The root of this file system is a string describing the path
+       to the directory used as root.
+    """
+
+    __implements__ = IPosixFileSystem
+
+    copy_bytes = 65536
+
+
+    def __init__ (self, root):
+        self.root = root
+
+    def chmod(self, path, mode):
+        'See IWriteFileSystem'
+        p = self.translate (path)
+        return os.chmod(p, mode)
+
+
+    def chown(self, path, uid, gid):
+        'See IWriteFileSystem'
+        p = self.translate (path)
+        return os.chown(p, uid, gid)
+
+
+    def link(self, src, dst):
+        'See IWriteFileSystem'
+        src = self.translate(src)
+        dst = self.translate(dst)
+        return os.link(src, dst)
+
+
+    def mkfifo(self, path, mode=6*2**6):
+        'See IWriteFileSystem'
+        return os.mkfifo(path, mode)
+
+
+    def symlink(self, src, dst):
+        'See IWriteFileSystem'
+        src = self.translate(src)
+        dst = self.translate(dst)
+        return os.symlink(src, dst)
+
+    def exists(self, path):
+        'See IReadFileSystem'
+        p = self.translate(path)
+        return os.path.exists(p)
+
+
+    def isdir(self, path):
+        'See IReadFileSystem'
+        p = self.translate(path)
+        return os.path.isdir(p)
+
+
+    def isfile(self, path):
+        'See IReadFileSystem'
+        p = self.translate(path)
+        return os.path.isfile(p)
+
+
+    def listdir(self, path, with_stats=0, pattern='*'):
+        'See IReadFileSystem'
+        p = self.translate(path)
+        # list the directory's files
+        ld = os.listdir(p)
+        # filter them using the pattern
+        ld = filter(lambda f, p=pattern, fnm=fnmatch.fnmatch: fnm(f, p), ld)
+        # sort them alphabetically
+        ld.sort()
+        if not with_stats:
+            result = ld
+        else:
+            result = []
+            for file in ld:
+                path = os.path.join(p, file)
+                stat = safe_stat(path)
+                if stat is not None:
+                    result.append((file, stat))
+        return result
+
+
+    def readfile(self, path, mode, outstream, start=0, end=-1):
+        'See IReadFileSystem'
+        p = self.translate(path)
+        instream = open(p, mode)
+        if start:
+            instream.seek(start)
+        pos = start
+        while end < 0 or pos < end:
+            toread = self.copy_bytes
+            if end >= 0:
+                toread = min(toread, end - pos)
+            data = instream.read(toread)
+            if not data:
+                break
+            pos += len(data)
+            outstream.write(data)
+
+
+    def stat(self, path):
+        'See IReadFileSystem'
+        p = self.translate(path)
+        stat = os.stat(p)
+        return stat[0:6], fromts(stat[7]), fromts(stat[8]), fromts(stat[9])
+
+    def mkdir(self, path, mode=6*2**6):
+        'See IWriteFileSystem'
+        p = self.translate(path)
+        return os.mkdir(p, mode)
+
+
+    def remove(self, path):
+        'See IWriteFileSystem'
+        p = self.translate (path)
+        return os.remove(p)
+
+
+    def rmdir(self, path):
+        'See IWriteFileSystem'
+        p = self.translate (path)
+        return os.rmdir(p)
+
+
+    def rename(self, old, new):
+        'See IWriteFileSystem'
+        old = self.translate(old)
+        new = self.translate(new)
+        return os.rename(old, new)
+
+
+    def writefile(self, path, mode, instream, start=0):
+        'See IWriteFileSystem'
+        p = self.translate(path)
+        outstream = open(p, mode)
+        if start:
+            outstream.seek(start)
+        while 1:
+            data = instream.read(self.copy_bytes)
+            if not data:
+                break
+            outstream.write(data)
+
+    def check_writable(self, path):
+        'See IWriteFileSystem'
+        p = self.translate(path)
+        if os.path.exists(p):
+            remove = 0
+        else:
+            remove = 1
+        f = open(p, 'a')  # append mode
+        f.close()
+        if remove:
+            os.remove(p)
+
+    # utility methods
+
+    def normalize(self, path):
+        # watch for the ever-sneaky '/+' path element
+        # XXX It is unclear why "/+" is dangerous.  It is definitely
+        # unexpected.
+        path = re.sub('/+', '/', path)
+        path = os.path.normpath(path)
+        if path.startswith('..'):
+            # Someone is trying to get lower than the permitted root.
+            # We just ignore it.
+            path = os.sep
+        return path
+
+
+    def translate(self, path):
+        """We need to join together three separate path components,
+           and do it safely.  <real_root>/<path>
+           use the operating system's path separator.
+
+           We need to be extremly careful to include the cases where a hacker
+           could attempt to a directory below root!
+        """
+        # Normalize the directory
+        path = self.normalize(path)
+        # Prepare for joining with root
+        while path.startswith(os.sep):
+            path = path[1:]
+        # Join path with root
+        return os.path.join(self.root, path)
+
+
+    def __repr__(self):
+        return '<OSFileSystem, root=%s>' % self.root
+
+
+
+def safe_stat(path):
+    try:
+        stat = os.stat(path)
+        return stat[:7] + (fromts(stat[7]), fromts(stat[8]), fromts(stat[9]))
+    except OSError:
+        return None


=== Zope3/src/zope/server/vfs/publisherfilesystem.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:58 2002
+++ Zope3/src/zope/server/vfs/publisherfilesystem.py	Wed Dec 25 09:15:28 2002
@@ -0,0 +1,180 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+
+import re
+import stat
+import time
+import posixpath
+
+from cStringIO import StringIO
+
+from zope.server.interfaces.vfs import IReadFileSystem
+from zope.server.interfaces.vfs import IWriteFileSystem
+
+from zope.publisher.publish import publish
+
+
+
+class NoOutput:
+    """An output stream lookalike that warns you if you try to
+    dump anything into it."""
+
+    def write(self, data):
+        raise RuntimeError, "Not a writable stream"
+
+    def flush(self):
+        pass
+
+    close = flush
+
+
+
+class PublisherFileSystem:
+    """Generic Publisher FileSystem implementation.
+    """
+
+    __implements__ = IReadFileSystem, IWriteFileSystem
+
+    def __init__ (self, credentials, request_factory):
+        self.credentials = credentials
+        self.request_factory = request_factory
+
+
+    def _execute(self, path, command, env=None):
+        if env is None:
+            env = {}
+
+        env['command'] = command
+        env['path'] = path
+        env['credentials'] = self.credentials
+        # NoOutput avoids creating a black hole.
+        request = self.request_factory(StringIO(''), NoOutput(), env)
+        # Note that publish() calls close() on request, which deletes the
+        # response from the request, so that we need to keep track of it.
+        response = request.response
+        publish(request)
+        return response.getResult()
+
+    def exists(self, path):
+        'See IReadFileSystem'
+        path = self.translate(path)
+        if path == '/':
+            return 1
+        path, file = posixpath.split(path)
+        env = {'name': file}
+        return self._execute(path, 'exists', env)
+
+
+    def isdir(self, path):
+        'See IReadFileSystem'
+        path = self.translate(path)
+        return self._execute(path, 'isdir')
+
+
+    def isfile(self, path):
+        'See IReadFileSystem'
+        path = self.translate(path)
+        return self._execute(path, 'isfile')
+
+
+    def listdir(self, path, with_stats=0, pattern='*'):
+        'See IReadFileSystem'
+        path = self.translate(path)
+        env = {'with_stats' : with_stats,
+               'pattern' : pattern}
+        return self._execute(path, 'listdir', env)
+
+
+    def readfile(self, path, mode, outstream, start=0, end=-1):
+        'See IReadFileSystem'
+        path = self.translate(path)
+        env = {'mode'      : mode,
+               'outstream' : outstream,
+               'start'     : start,
+               'end'       : end}
+        return self._execute(path, 'read', env)
+
+
+    def stat(self, path):
+        'See IReadFileSystem'
+        path = self.translate(path)
+        return self._execute(path, 'stat')
+
+    def mkdir(self, path, mode=777):
+        'See IWriteFileSystem'
+        path = self.translate(path)
+        path, dir = posixpath.split(path)
+        env = {'name': dir}
+        return self._execute(path, 'mkdir', env)
+
+
+    def remove(self, path):
+        'See IWriteFileSystem'
+        path = self.translate(path)
+        path, name = posixpath.split(path)
+        env = {'name': name}
+        return self._execute(path, 'remove', env)
+
+
+    def rmdir(self, path):
+        'See IWriteFileSystem'
+        path = self.translate(path)
+        path, dir = posixpath.split(path)
+        env = {'name': dir}
+        return self._execute(path, 'rmdir', env)
+
+
+    def rename(self, old, new):
+        'See IWriteFileSystem'
+        old = self.translate(old)
+        new = self.translate(new)
+        path0, old = posixpath.split(old)
+        path1, new = posixpath.split(new)
+        assert path0 == path1
+        env = {'old' : old,
+               'new' : new}
+        return self._execute(path0, 'rename', env)
+
+    def writefile(self, path, mode, instream, start=0):
+        'See IWriteFileSystem'
+        path = self.translate(path)
+        path, name = posixpath.split(path)
+        env = {'name'      : name,
+               'mode'      : mode,
+               'instream'  : instream,
+               'start'     : start}
+        return self._execute(path, 'writefile', env)
+
+
+    def check_writable(self, path):
+        'See IWriteFileSystem'
+        path = self.translate(path)
+        path, name = posixpath.split(path)
+        env = {'name'      : name}
+        return self._execute(path, 'check_writable', env)
+
+    # utility methods
+
+    def translate (self, path):
+        # Normalize
+        path = posixpath.normpath(path)
+        if path.startswith('..'):
+            # Someone is trying to get lower than the permitted root.
+            # We just ignore it.
+            path = '/'
+        return path


=== Zope3/src/zope/server/vfs/testfilesystemaccess.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:58 2002
+++ Zope3/src/zope/server/vfs/testfilesystemaccess.py	Wed Dec 25 09:15:28 2002
@@ -0,0 +1,44 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Implementation of IFilesystemAccess intended only for testing.
+
+$Id$
+"""
+
+from zope.server.interfaces.vfs import IFilesystemAccess
+from zope.server.interfaces.vfs import IUsernamePassword
+from zope.exceptions import Unauthorized
+
+
+class TestFilesystemAccess:
+
+    __implements__ = IFilesystemAccess
+
+    passwords = {'foo': 'bar'}
+
+    def __init__(self, fs):
+        self.fs = fs
+
+    def authenticate(self, credentials):
+        if not IUsernamePassword.isImplementedBy(credentials):
+            raise Unauthorized
+        name = credentials.getUserName()
+        if not (name in self.passwords):
+            raise Unauthorized
+        if credentials.getPassword() != self.passwords[name]:
+            raise Unauthorized
+
+    def open(self, credentials):
+        self.authenticate(credentials)
+        return self.fs


=== Zope3/src/zope/server/vfs/usernamepassword.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:58 2002
+++ Zope3/src/zope/server/vfs/usernamepassword.py	Wed Dec 25 09:15:28 2002
@@ -0,0 +1,34 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+
+from zope.server.interfaces.vfs import IUsernamePassword
+
+
+class UsernamePassword:
+
+    __implements__ = IUsernamePassword
+
+    def __init__(self, username, password):
+        self.username = username
+        self.password = password
+
+    def getUserName(self):
+        return self.username
+
+    def getPassword(self):
+        return self.password