~launchpad-pqm/launchpad/devel

8687.15.18 by Karl Fogel
Add the copyright header block to files under lib/canonical/.
1
# Copyright 2009 Canonical Ltd.  This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
3
4
__metaclass__ = type
10918.2.1 by Steve Kowalik
Merge in changes for plumbling for poppy-sftp
5
__all__ = [
6
    'UploadFileSystem',
7
    ]
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
8
9
import datetime
10
import os
11
import stat
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
12
13
from zope.interface import implements
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
14
from zope.security.interfaces import Unauthorized
15
from zope.server.interfaces.ftp import IFileSystem
16
17
18
class UploadFileSystem:
19
20
    implements(IFileSystem)
21
22
    def __init__(self, rootpath):
23
        self.rootpath = rootpath
24
25
    def _full(self, path):
26
        """Returns the full path name (i.e. rootpath + path)"""
3691.444.3 by Celso Providelo
Add directory supporting for poppy: recursive mkdir & rmdir, ls/lsinfo/type and protection for underneath directories. Add also support to automatically creation of directories 'CWD'ed in. Tested with dupload and dput
27
        full_path = os.path.join(self.rootpath, path)
28
        if not os.path.realpath(full_path).startswith(self.rootpath):
29
            raise OSError("Path not allowed:", path)
30
        return full_path
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
31
32
    def _sanitize(self, path):
33
        if path.startswith('/'):
34
            path = path[1:]
35
        path = os.path.normpath(path)
3691.444.3 by Celso Providelo
Add directory supporting for poppy: recursive mkdir & rmdir, ls/lsinfo/type and protection for underneath directories. Add also support to automatically creation of directories 'CWD'ed in. Tested with dupload and dput
36
        return path
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
37
38
    def type(self, path):
39
        """Return the file type at path
40
41
        The 'type' command returns 'f' for a file, 'd' for a directory and
42
        None if there is no file.
43
        """
44
        path = self._sanitize(path)
45
        full_path = self._full(path)
46
        if os.path.exists(full_path):
47
            if os.path.isdir(full_path):
48
                return 'd'
49
            elif os.path.isfile(full_path):
50
                return 'f'
3691.444.3 by Celso Providelo
Add directory supporting for poppy: recursive mkdir & rmdir, ls/lsinfo/type and protection for underneath directories. Add also support to automatically creation of directories 'CWD'ed in. Tested with dupload and dput
51
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
52
    def names(self, path, filter=None):
53
        """Return a sequence of the names in a directory
54
55
        If the filter is not None, include only those names for which
56
        the filter returns a true value.
57
        """
58
        path = self._sanitize(path)
3691.444.3 by Celso Providelo
Add directory supporting for poppy: recursive mkdir & rmdir, ls/lsinfo/type and protection for underneath directories. Add also support to automatically creation of directories 'CWD'ed in. Tested with dupload and dput
59
        full_path = self._full(path)
60
        if not os.path.exists(full_path):
61
            raise OSError("Not exists:", path)
3691.443.23 by Celso Providelo
applying review comments for ppa-poppy.
62
        filenames = os.listdir(os.path.join(self.rootpath, path))
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
63
        files = []
3691.443.23 by Celso Providelo
applying review comments for ppa-poppy.
64
        for filename in filenames:
3691.444.3 by Celso Providelo
Add directory supporting for poppy: recursive mkdir & rmdir, ls/lsinfo/type and protection for underneath directories. Add also support to automatically creation of directories 'CWD'ed in. Tested with dupload and dput
65
            if not filter or filter(filename):
3691.443.26 by Celso Providelo
moving poppyinterface file to its right place and also fixed filesystem.txt.
66
                files.append(filename)
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
67
        return files
68
69
    def ls(self, path, filter=None):
3691.443.23 by Celso Providelo
applying review comments for ppa-poppy.
70
        """Return a sequence of information objects.
71
72
        It considers the names in the given path (returned self.name())
73
        and builds file information using self.lsinfo().
74
        """
3691.443.26 by Celso Providelo
moving poppyinterface file to its right place and also fixed filesystem.txt.
75
        return [self.lsinfo(name) for name in self.names(path, filter)]
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
76
77
    def readfile(self, path, outstream, start=0, end=None):
78
        """Outputs the file at path to a stream.
79
10918.2.1 by Steve Kowalik
Merge in changes for plumbling for poppy-sftp
80
        Not allowed - see filesystem.txt.
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
81
        """
82
        raise Unauthorized
83
84
    def lsinfo(self, path):
85
        """Return information for a unix-style ls listing for the path
86
87
        See zope3's interfaces/ftp.py:IFileSystem for details of the
88
        dictionary's content.
89
        """
90
        path = self._sanitize(path)
91
        full_path = self._full(path)
3691.444.3 by Celso Providelo
Add directory supporting for poppy: recursive mkdir & rmdir, ls/lsinfo/type and protection for underneath directories. Add also support to automatically creation of directories 'CWD'ed in. Tested with dupload and dput
92
        if not os.path.exists(full_path):
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
93
            raise OSError("Not exists:", path)
3691.444.3 by Celso Providelo
Add directory supporting for poppy: recursive mkdir & rmdir, ls/lsinfo/type and protection for underneath directories. Add also support to automatically creation of directories 'CWD'ed in. Tested with dupload and dput
94
95
        info = {"owner_name": "upload",
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
96
                "group_name": "upload",
3691.444.3 by Celso Providelo
Add directory supporting for poppy: recursive mkdir & rmdir, ls/lsinfo/type and protection for underneath directories. Add also support to automatically creation of directories 'CWD'ed in. Tested with dupload and dput
97
                "name": path.split("/")[-1]}
98
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
99
        s = os.stat(full_path)
3691.444.3 by Celso Providelo
Add directory supporting for poppy: recursive mkdir & rmdir, ls/lsinfo/type and protection for underneath directories. Add also support to automatically creation of directories 'CWD'ed in. Tested with dupload and dput
100
3691.443.23 by Celso Providelo
applying review comments for ppa-poppy.
101
        info["owner_readable"] = bool(s.st_mode & stat.S_IRUSR)
102
        info["owner_writable"] = bool(s.st_mode & stat.S_IWUSR)
103
        info["owner_executable"] = bool(s.st_mode & stat.S_IXUSR)
104
        info["group_readable"] = bool(s.st_mode & stat.S_IRGRP)
105
        info["group_writable"] = bool(s.st_mode & stat.S_IWGRP)
106
        info["group_executable"] = bool(s.st_mode & stat.S_IXGRP)
107
        info["other_readable"] = bool(s.st_mode & stat.S_IROTH)
108
        info["other_writable"] = bool(s.st_mode & stat.S_IWOTH)
109
        info["other_executable"] = bool(s.st_mode & stat.S_IXOTH)
3691.444.3 by Celso Providelo
Add directory supporting for poppy: recursive mkdir & rmdir, ls/lsinfo/type and protection for underneath directories. Add also support to automatically creation of directories 'CWD'ed in. Tested with dupload and dput
110
        info["mtime"] = datetime.datetime.fromtimestamp(self.mtime(path))
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
111
        info["size"] = self.size(path)
3691.444.3 by Celso Providelo
Add directory supporting for poppy: recursive mkdir & rmdir, ls/lsinfo/type and protection for underneath directories. Add also support to automatically creation of directories 'CWD'ed in. Tested with dupload and dput
112
        info["type"] = self.type(path)
3691.443.23 by Celso Providelo
applying review comments for ppa-poppy.
113
        info["nlinks"] = s.st_nlink
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
114
        return info
115
116
    def mtime(self, path):
117
        """Return the modification time for the file"""
118
        path = self._sanitize(path)
119
        full_path = self._full(path)
120
        if os.path.exists(full_path):
121
            return os.path.getmtime(full_path)
122
123
    def size(self, path):
124
        """Return the size of the file at path"""
125
        path = self._sanitize(path)
126
        full_path = self._full(path)
127
        if os.path.exists(full_path):
128
            return os.path.getsize(full_path)
129
130
    def mkdir(self, path):
3691.444.3 by Celso Providelo
Add directory supporting for poppy: recursive mkdir & rmdir, ls/lsinfo/type and protection for underneath directories. Add also support to automatically creation of directories 'CWD'ed in. Tested with dupload and dput
131
        """Create a directory."""
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
132
        path = self._sanitize(path)
3691.444.3 by Celso Providelo
Add directory supporting for poppy: recursive mkdir & rmdir, ls/lsinfo/type and protection for underneath directories. Add also support to automatically creation of directories 'CWD'ed in. Tested with dupload and dput
133
        full_path = self._full(path)
134
        if os.path.exists(full_path):
135
            if os.path.isfile(full_path):
136
                raise OSError("File already exists:", path)
137
            elif os.path.isdir(full_path):
138
                raise OSError("Directory already exists:", path)
139
            raise OSError("OOPS, can't create:", path)
140
        else:
3691.312.83 by Celso Providelo
Fix poppy to correctly set permissions of PPA uploads according to the multi-user setup used in production (g+rw)
141
            old_mask = os.umask(0)
142
            try:
143
                os.makedirs(full_path, 0775)
144
            finally:
145
                os.umask(old_mask)
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
146
147
    def remove(self, path):
148
        """Remove a file."""
149
        path = self._sanitize(path)
150
        full_path = self._full(path)
151
        if os.path.exists(full_path):
152
            if os.path.isfile(full_path):
153
                os.unlink(full_path)
154
            elif os.path.isdir(full_path):
155
                raise OSError("Is a directory:", path)
156
        else:
157
            raise OSError("Not exists:", path)
158
159
    def rmdir(self, path):
160
        """Remove a directory.
161
3691.444.4 by Celso Providelo
Add a functional test for poppy features.
162
        Remove a target path recursively.
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
163
        """
164
        path = self._sanitize(path)
3691.444.3 by Celso Providelo
Add directory supporting for poppy: recursive mkdir & rmdir, ls/lsinfo/type and protection for underneath directories. Add also support to automatically creation of directories 'CWD'ed in. Tested with dupload and dput
165
        full_path = self._full(path)
166
        if os.path.exists(full_path):
3691.443.23 by Celso Providelo
applying review comments for ppa-poppy.
167
            os.rmdir(full_path)
3691.444.3 by Celso Providelo
Add directory supporting for poppy: recursive mkdir & rmdir, ls/lsinfo/type and protection for underneath directories. Add also support to automatically creation of directories 'CWD'ed in. Tested with dupload and dput
168
        else:
169
            raise OSError("Not exists:", path)
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
170
171
    def rename(self, old, new):
172
        """Rename a file."""
173
        old = self._sanitize(old)
174
        new = self._sanitize(new)
175
        full_old = self._full(old)
176
        full_new = self._full(new)
177
178
        if os.path.isdir(full_new):
179
            raise OSError("Is a directory:", new)
180
181
        if os.path.exists(full_old):
182
            if os.path.isfile(full_old):
183
                os.rename(full_old, full_new)
184
            elif os.path.isdir(full_old):
185
                raise OSError("Is a directory:", old)
186
        else:
187
            raise OSError("Not exists:", old)
188
189
    def writefile(self, path, instream, start=None, end=None, append=False):
190
        """Write data to a file.
191
192
        See zope3's interfaces/ftp.py:IFileSystem for details of the
193
        handling of the various arguments.
194
        """
195
        path = self._sanitize(path)
196
        full_path = self._full(path)
197
        if os.path.exists(full_path):
198
            if os.path.isdir(full_path):
199
                raise OSError("Is a directory:", path)
3691.444.3 by Celso Providelo
Add directory supporting for poppy: recursive mkdir & rmdir, ls/lsinfo/type and protection for underneath directories. Add also support to automatically creation of directories 'CWD'ed in. Tested with dupload and dput
200
        else:
201
            dirname = os.path.dirname(full_path)
202
            if dirname:
203
                if not os.path.exists(dirname):
3691.312.83 by Celso Providelo
Fix poppy to correctly set permissions of PPA uploads according to the multi-user setup used in production (g+rw)
204
                    old_mask = os.umask(0)
205
                    try:
206
                        os.makedirs(dirname, 0775)
207
                    finally:
208
                        os.umask(old_mask)
3691.444.3 by Celso Providelo
Add directory supporting for poppy: recursive mkdir & rmdir, ls/lsinfo/type and protection for underneath directories. Add also support to automatically creation of directories 'CWD'ed in. Tested with dupload and dput
209
1102 by Canonical.com Patch Queue Manager
Lucille had some XXXs which should have been NOTEs
210
        if start and start < 0:
211
            raise ValueError("Negative start argument:", start)
212
        if end and end < 0:
213
            raise ValueError("Negative end argument:", end)
214
        if start and end and end <= start:
215
            return
216
        if append:
217
            open_flag = 'a'
218
        elif start or end:
219
            open_flag = "r+"
220
            if not os.path.exists(full_path):
221
                open(full_path, 'w')
222
223
        else:
224
            open_flag = 'w'
225
        outstream = open(full_path, open_flag)
226
        if start:
227
            outstream.seek(start)
228
        chunk = instream.read()
229
        while chunk:
230
            outstream.write(chunk)
231
            chunk = instream.read()
232
        if not end:
233
            outstream.truncate()
234
        instream.close()
235
        outstream.close()
236
237
    def writable(self, path):
238
        """Return boolean indicating whether a file at path is writable."""
239
        path = self._sanitize(path)
240
        full_path = self._full(path)
241
        if os.path.exists(full_path):
242
            if os.path.isfile(full_path):
243
                return True
244
            elif os.path.isdir(full_path):
245
                return False
246
        else:
247
            return True
248