10918.2.3
by Steve Kowalik
Merge in the code for poppy-sftp |
1 |
# Copyright 2010 Canonical Ltd. This software is licensed under the
|
2 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
|
3 |
||
4 |
"""Twisted SFTP implementation of the Poppy upload server."""
|
|
5 |
||
6 |
__metaclass__ = type |
|
7 |
__all__ = [ |
|
8 |
'SFTPFile', |
|
9 |
'SFTPServer', |
|
10 |
]
|
|
11 |
||
10935.2.2
by Steve Kowalik
* Fix the FileIsADirectory code to actually work. |
12 |
import errno |
10918.2.3
by Steve Kowalik
Merge in the code for poppy-sftp |
13 |
import logging |
14 |
import os |
|
15 |
import tempfile |
|
16 |
||
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
17 |
from twisted.conch.interfaces import ( |
18 |
ISFTPFile, |
|
19 |
ISFTPServer, |
|
20 |
)
|
|
21 |
from zope.component import ( |
|
22 |
adapter, |
|
23 |
provideHandler, |
|
24 |
)
|
|
10918.2.3
by Steve Kowalik
Merge in the code for poppy-sftp |
25 |
from zope.interface import implements |
26 |
||
27 |
from lp.poppy.filesystem import UploadFileSystem |
|
28 |
from lp.poppy.hooks import Hooks |
|
29 |
from lp.services.sshserver.events import SFTPClosed |
|
11086.2.1
by James Westby
Move FileIsADirectory from codehosting.sftp to services.sshserver. |
30 |
from lp.services.sshserver.sftp import FileIsADirectory |
10918.2.3
by Steve Kowalik
Merge in the code for poppy-sftp |
31 |
|
32 |
||
33 |
class SFTPServer: |
|
34 |
"""An implementation of `ISFTPServer` that backs onto a Poppy filesystem.
|
|
35 |
"""
|
|
36 |
||
37 |
implements(ISFTPServer) |
|
38 |
||
39 |
def __init__(self, avatar, fsroot): |
|
40 |
provideHandler(self.connectionClosed) |
|
41 |
self._avatar = avatar |
|
42 |
self._fs_root = fsroot |
|
43 |
self.uploadfilesystem = UploadFileSystem(tempfile.mkdtemp()) |
|
44 |
self._current_upload = self.uploadfilesystem.rootpath |
|
10935.2.1
by Steve Kowalik
* Export FileIsADirectory from codehosting.sftp, so I can use it for poppy-sftp. |
45 |
os.chmod(self._current_upload, 0770) |
10918.2.3
by Steve Kowalik
Merge in the code for poppy-sftp |
46 |
self._log = logging.getLogger("poppy-sftp") |
10935.2.1
by Steve Kowalik
* Export FileIsADirectory from codehosting.sftp, so I can use it for poppy-sftp. |
47 |
self.hook = Hooks( |
48 |
self._fs_root, self._log, "ubuntu", perms='g+rws', prefix='-sftp') |
|
10918.2.3
by Steve Kowalik
Merge in the code for poppy-sftp |
49 |
self.hook.new_client_hook(self._current_upload, 0, 0) |
50 |
self.hook.auth_verify_hook(self._current_upload, None, None) |
|
51 |
||
52 |
def gotVersion(self, other_version, ext_data): |
|
53 |
return {} |
|
54 |
||
55 |
def openFile(self, filename, flags, attrs): |
|
56 |
self._create_missing_directories(filename) |
|
57 |
absfile = self._translate_path(filename) |
|
58 |
return SFTPFile(absfile) |
|
59 |
||
60 |
def removeFile(self, filename): |
|
61 |
pass
|
|
62 |
||
63 |
def renameFile(self, old_path, new_path): |
|
64 |
abs_old = self._translate_path(old_path) |
|
65 |
abs_new = self._translate_path(new_path) |
|
66 |
os.rename(abs_old, abs_new) |
|
67 |
||
68 |
def makeDirectory(self, path, attrs): |
|
69 |
# XXX: We ignore attrs here
|
|
70 |
self.uploadfilesystem.mkdir(path) |
|
71 |
||
72 |
def removeDirectory(self, path): |
|
73 |
self.uploadfilesystem.rmdir(path) |
|
74 |
||
75 |
def openDirectory(self, path): |
|
76 |
pass
|
|
77 |
||
78 |
def getAttrs(self, path, follow_links): |
|
79 |
pass
|
|
80 |
||
81 |
def setAttrs(self, path, attrs): |
|
82 |
pass
|
|
83 |
||
84 |
def readLink(self, path): |
|
85 |
pass
|
|
86 |
||
87 |
def makeLink(self, link_path, target_path): |
|
88 |
pass
|
|
89 |
||
90 |
def realPath(self, path): |
|
91 |
return path |
|
92 |
||
93 |
def extendedRequest(self, extended_name, extended_data): |
|
94 |
pass
|
|
95 |
||
96 |
def _create_missing_directories(self, filename): |
|
97 |
new_dir, new_file = os.path.split( |
|
98 |
self.uploadfilesystem._sanitize(filename)) |
|
99 |
if new_dir != '': |
|
100 |
if not os.path.exists( |
|
101 |
os.path.join(self._current_upload, new_dir)): |
|
102 |
self.uploadfilesystem.mkdir(new_dir) |
|
103 |
||
104 |
def _translate_path(self, filename): |
|
105 |
return self.uploadfilesystem._full( |
|
106 |
self.uploadfilesystem._sanitize(filename)) |
|
107 |
||
108 |
@adapter(SFTPClosed) |
|
109 |
def connectionClosed(self, event): |
|
110 |
if event.avatar is not self._avatar: |
|
111 |
return
|
|
112 |
self.hook.client_done_hook(self._current_upload, 0, 0) |
|
113 |
||
10935.2.1
by Steve Kowalik
* Export FileIsADirectory from codehosting.sftp, so I can use it for poppy-sftp. |
114 |
|
10918.2.3
by Steve Kowalik
Merge in the code for poppy-sftp |
115 |
class SFTPFile: |
116 |
||
117 |
implements(ISFTPFile) |
|
118 |
||
119 |
def __init__(self, filename): |
|
120 |
self.filename = filename |
|
121 |
||
122 |
def close(self): |
|
123 |
pass
|
|
124 |
||
125 |
def readChunk(self, offset, length): |
|
126 |
pass
|
|
127 |
||
128 |
def writeChunk(self, offset, data): |
|
129 |
try: |
|
130 |
chunk_file = os.open( |
|
12392.7.23
by Julian Edwards
Fix permissions for sftp-written files |
131 |
self.filename, os.O_CREAT | os.O_WRONLY, 0664) |
10918.2.3
by Steve Kowalik
Merge in the code for poppy-sftp |
132 |
except OSError, e: |
133 |
if e.errno != errno.EISDIR: |
|
134 |
raise
|
|
10935.2.2
by Steve Kowalik
* Fix the FileIsADirectory code to actually work. |
135 |
raise FileIsADirectory(self.filename) |
10918.2.3
by Steve Kowalik
Merge in the code for poppy-sftp |
136 |
os.lseek(chunk_file, offset, 0) |
137 |
os.write(chunk_file, data) |
|
138 |
os.close(chunk_file) |
|
139 |
||
140 |
def getAttrs(self): |
|
141 |
pass
|
|
142 |
||
143 |
def setAttrs(self, attr): |
|
144 |
pass
|