Ticket #2157: windows.py

File windows.py, 7.5 KB (added by John Popplewell, 6 years ago)

twiste.conch.windows

Line 
1# Copyright (c) Twisted Matrix Laboratories.
2# See LICENSE for details.
3
4from twisted.cred import portal
5from twisted.python import components, log, win32
6from zope import interface
7from ssh import session, forwarding, filetransfer
8from ssh.filetransfer import FXF_READ, FXF_WRITE, FXF_APPEND, FXF_CREAT, FXF_TRUNC, FXF_EXCL
9from twisted.conch.ls import lsLine
10
11from avatar import ConchUser
12from interfaces import ISession, ISFTPServer, ISFTPFile
13
14import os, posixpath
15
16# TODO: forwarding support
17
18class WindowsSSHRealm:
19
20    interface.implements(portal.IRealm)
21
22    def requestAvatar(self, username, mind, *interfaces):
23        user = WindowsConchUser(username)
24        return interfaces[0], user, user.logout
25
26
27class WindowsConchUser(ConchUser):
28
29    def __init__(self, username):
30        ConchUser.__init__(self)
31        self.username = username
32        self.listeners = {}  # dict mapping (interface, port) -> listener
33        self.channelLookup.update({"session": session.SSHSession})
34        self.subsystemLookup.update({"sftp": filetransfer.FileTransferServer})
35
36    def getHomeDir(self):
37        basepath = os.path.expanduser("~")
38        path = os.path.splitdrive(basepath)[1]    # remove drive spec.
39        unixpath = path.replace('\\','/')+"/"
40        return posixpath.abspath(unixpath)
41
42    def logout(self):
43        # remove all listeners
44        for listener in self.listeners.itervalues():
45            self._runAsUser(listener.stopListening)
46        log.msg('avatar %s logging out (%i)' % (self.username, len(self.listeners)))
47
48    def _runAsUser(self, f, *args, **kw):
49        try:
50            f = iter(f)
51        except TypeError:
52            f = [(f, args, kw)]
53        for i in f:
54            func = i[0]
55            args = len(i)>1 and i[1] or ()
56            kw = len(i)>2 and i[2] or {}
57            r = func(*args, **kw)
58        return r
59
60
61class SSHSessionForWindowsConchUser:
62
63    interface.implements(ISession)
64
65    def __init__(self, avatar):
66        self.avatar = avatar
67
68    def openShell(self, proto):
69        proto.transport.write = self._writeHack
70        self.avatar.conn.transport.transport.setTcpNoDelay(1)
71
72    def execCommand(self, proto, cmd):
73        self.avatar.conn.transport.transport.setTcpNoDelay(1)
74
75    def eofReceived(self):
76        pass
77
78    def closed(self):
79        log.msg('shell closed')
80
81    def windowChanged(self, winSize):
82        self.winSize = winSize
83
84
85class SFTPServerForWindowsConchUser:
86
87    interface.implements(ISFTPServer)
88
89    def __init__(self, avatar):
90        self.avatar = avatar
91
92    def _setAttrs(self, path, attrs):
93        """
94        NOTE: this function assumes it runs as the logged-in user:
95        i.e. under _runAsUser()
96        """
97        if "permissions" in attrs:
98            os.chmod(path, attrs["permissions"])
99        if "atime" in attrs and "mtime" in attrs:
100            os.utime(path, (attrs["atime"], attrs["mtime"]))
101
102    def _getAttrs(self, s):
103        return {
104            "size"          : s.st_size,
105            "uid"           : s.st_uid,
106            "gid"           : s.st_gid,
107            "permissions"   : s.st_mode,
108            "atime"         : int(s.st_atime),
109            "mtime"         : int(s.st_mtime)
110        }
111
112    def _absPath(self, path):
113        home = self.avatar.getHomeDir()
114        return posixpath.abspath(posixpath.join(home, path))
115
116    def gotVersion(self, otherVersion, extData):
117        return {}
118
119    def openFile(self, filename, flags, attrs):
120        return WindowsSFTPFile(self, self._absPath(filename), flags, attrs)
121
122    def removeFile(self, filename):
123        filename = self._absPath(filename)
124        return self.avatar._runAsUser(os.remove, filename)
125
126    def renameFile(self, oldpath, newpath):
127        oldpath = self._absPath(oldpath)
128        newpath = self._absPath(newpath)
129        return self.avatar._runAsUser(os.rename, oldpath, newpath)
130
131    def makeDirectory(self, path, attrs):
132        path = self._absPath(path)
133        return self.avatar._runAsUser([(os.mkdir, (path,)),
134                                (self._setAttrs, (path, attrs))])
135
136    def removeDirectory(self, path):
137        path = self._absPath(path)
138        self.avatar._runAsUser(os.rmdir, path)
139
140    def openDirectory(self, path):
141        return WindowsSFTPDirectory(self, self._absPath(path))
142
143    def getAttrs(self, path, followLinks):
144        path = self._absPath(path)
145        if followLinks:
146            s = self.avatar._runAsUser(os.stat, path)
147        else:
148            s = self.avatar._runAsUser(os.lstat, path)
149        return self._getAttrs(s)
150
151    def setAttrs(self, path, attrs):
152        path = self._absPath(path)
153        self.avatar._runAsUser(self._setAttrs, path, attrs)
154
155    def readLink(self, path):
156        raise NotImplementedError
157
158    def makeLink(self, linkPath, targetPath):
159        raise NotImplementedError
160
161    def realPath(self, path):
162        return posixpath.realpath(self._absPath(path))
163
164    def extendedRequest(self, extName, extData):
165        raise NotImplementedError
166
167
168class WindowsSFTPFile:
169
170    interface.implements(ISFTPFile)
171
172    def __init__(self, server, filename, flags, attrs):
173        self.server = server
174        openFlags = 0
175        if flags & FXF_READ == FXF_READ and flags & FXF_WRITE == 0:
176            openFlags = os.O_RDONLY
177        if flags & FXF_WRITE == FXF_WRITE and flags & FXF_READ == 0:
178            openFlags = os.O_WRONLY
179        if flags & FXF_WRITE == FXF_WRITE and flags & FXF_READ == FXF_READ:
180            openFlags = os.O_RDWR
181        if flags & FXF_APPEND == FXF_APPEND:
182            openFlags |= os.O_APPEND
183        if flags & FXF_CREAT == FXF_CREAT:
184            openFlags |= os.O_CREAT
185        if flags & FXF_TRUNC == FXF_TRUNC:
186            openFlags |= os.O_TRUNC
187        if flags & FXF_EXCL == FXF_EXCL:
188            openFlags |= os.O_EXCL
189        if "permissions" in attrs:
190            mode = attrs["permissions"]
191            del attrs["permissions"]
192        else:
193            mode = 0777
194        openFlags |= win32.O_BINARY
195        fd = server.avatar._runAsUser(os.open, filename, openFlags, mode)
196        if attrs:
197            server.avatar._runAsUser(server._setAttrs, filename, attrs)
198        self.fd = fd
199
200    def close(self):
201        return self.server.avatar._runAsUser(os.close, self.fd)
202
203    def readChunk(self, offset, length):
204        return self.server.avatar._runAsUser([ (os.lseek, (self.fd, offset, 0)),
205                                            (os.read, (self.fd, length)) ])
206
207    def writeChunk(self, offset, data):
208        return self.server.avatar._runAsUser([ (os.lseek, (self.fd, offset, 0)),
209                                            (os.write, (self.fd, data)) ])
210
211    def getAttrs(self):
212        s = self.server.avatar._runAsUser(os.fstat, self.fd)
213        return self.server._getAttrs(s)
214
215    def setAttrs(self, attrs):
216        raise NotImplementedError
217
218
219class WindowsSFTPDirectory:
220
221    def __init__(self, server, directory):
222        self.server = server
223        self.files = server.avatar._runAsUser(os.listdir, directory)
224        self.dir_ = directory
225
226    def __iter__(self):
227        return self
228
229    def next(self):
230        try:
231            f = self.files.pop(0)
232        except IndexError:
233            raise StopIteration
234        else:
235            s = self.server.avatar._runAsUser(os.lstat, posixpath.join(self.dir_, f))
236            longname = lsLine(f, s)
237            attrs = self.server._getAttrs(s)
238            return (f, longname, attrs)
239
240    def close(self):
241        self.files = []
242
243
244components.registerAdapter(SFTPServerForWindowsConchUser, WindowsConchUser, filetransfer.ISFTPServer)
245#components.registerAdapter(SSHSessionForWindowsConchUser, WindowsConchUser, session.ISession)
246