~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to twisted/conch/ssh/channel.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2007-01-17 14:52:35 UTC
  • mfrom: (1.1.5 upstream) (2.1.2 etch)
  • Revision ID: james.westby@ubuntu.com-20070117145235-btmig6qfmqfen0om
Tags: 2.5.0-0ubuntu1
New upstream version, compatible with python2.5.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
 
2
# See LICENSE for details.
 
3
 
 
4
#
 
5
"""The parent class for all the SSH Channels.  Currently implemented channels
 
6
are session. direct-tcp, and forwarded-tcp.
 
7
 
 
8
This module is unstable.
 
9
 
 
10
Maintainer: U{Paul Swartz<mailto:z3p@twistedmatrix.com>}
 
11
"""
 
12
 
 
13
from twisted.python import log, context
 
14
 
 
15
class SSHChannel(log.Logger):
 
16
    name = None # only needed for client channels
 
17
    def __init__(self, localWindow = 0, localMaxPacket = 0, 
 
18
                       remoteWindow = 0, remoteMaxPacket = 0, 
 
19
                       conn = None, data=None, avatar = None):
 
20
        self.localWindowSize = localWindow or 131072
 
21
        self.localWindowLeft = self.localWindowSize
 
22
        self.localMaxPacket = localMaxPacket or 32768
 
23
        self.remoteWindowLeft = remoteWindow
 
24
        self.remoteMaxPacket = remoteMaxPacket
 
25
        self.areWriting = 1
 
26
        self.conn = conn
 
27
        self.data = data
 
28
        self.avatar = avatar
 
29
        self.specificData = ''
 
30
        self.buf = ''
 
31
        self.extBuf = []
 
32
        self.closing = 0
 
33
        self.localClosed = 0
 
34
        self.remoteClosed = 0
 
35
        self.id = None # gets set later by SSHConnection
 
36
 
 
37
    def __str__(self):
 
38
        return '%s (lw %i rw %i)' % (self.name, self.localWindowLeft, self.remoteWindowLeft)
 
39
 
 
40
    def logPrefix(self):
 
41
        id = (self.id is not None and str(self.id)) or "unknown"
 
42
        return "SSHChannel %s (%s) on %s" % (self.name, id, self.conn.logPrefix())
 
43
 
 
44
    def channelOpen(self, specificData):
 
45
        """
 
46
        Called when the channel is opened.  specificData is any data that the
 
47
        other side sent us when opening the channel.
 
48
 
 
49
        @type specificData: C{str}
 
50
        """
 
51
        log.msg('channel open')
 
52
 
 
53
    def openFailed(self, reason):
 
54
        """
 
55
        Called when the the open failed for some reason.
 
56
        reason.desc is a string descrption, reason.code the the SSH error code.
 
57
 
 
58
        @type reason: L{error.ConchError}
 
59
        """
 
60
        log.msg('other side refused open\nreason: %s'% reason)
 
61
 
 
62
    def addWindowBytes(self, bytes):
 
63
        """
 
64
        Called when bytes are added to the remote window.  By default it clears
 
65
        the data buffers.
 
66
 
 
67
        @type bytes:    C{int}
 
68
        """
 
69
        self.remoteWindowLeft = self.remoteWindowLeft+bytes
 
70
        if not self.areWriting and not self.closing:
 
71
            self.areWriting = 0
 
72
            self.startWriting()
 
73
        if self.buf:
 
74
            b = self.buf
 
75
            self.buf = ''
 
76
            self.write(b)
 
77
        if self.extBuf:
 
78
            b = self.extBuf
 
79
            self.extBuf = []
 
80
            for i in b:
 
81
                self.writeExtended(*i)
 
82
 
 
83
    def requestReceived(self, requestType, data):
 
84
        """
 
85
        Called when a request is sent to this channel.  By default it delegates
 
86
        to self.request_<requestType>.
 
87
        If this function returns true, the request succeeded, otherwise it
 
88
        failed.
 
89
 
 
90
        @type requestType:  C{str}
 
91
        @type data:         C{str}
 
92
        @rtype:             C{bool}
 
93
        """
 
94
        foo = requestType.replace('-', '_')
 
95
        f = getattr(self, 'request_%s'%foo, None)
 
96
        if f:
 
97
            return f(data)
 
98
        log.msg('unhandled request for %s'%requestType)
 
99
        return 0
 
100
 
 
101
    def dataReceived(self, data):
 
102
        """
 
103
        Called when we receive data.
 
104
 
 
105
        @type data: C{str}
 
106
        """
 
107
        log.msg('got data %s'%repr(data))
 
108
 
 
109
    def extReceived(self, dataType, data):
 
110
        """
 
111
        Called when we receive extended data (usually standard error).
 
112
 
 
113
        @type dataType: C{int}
 
114
        @type data:     C{str}
 
115
        """
 
116
        log.msg('got extended data %s %s'%(dataType, repr(data)))
 
117
 
 
118
    def eofReceived(self):
 
119
        """
 
120
        Called when the other side will send no more data.
 
121
        """
 
122
        log.msg('remote eof')
 
123
 
 
124
    def closeReceived(self):
 
125
        """
 
126
        Called when the other side has closed the channel.
 
127
        """
 
128
        log.msg('remote close')
 
129
        self.loseConnection()
 
130
 
 
131
    def closed(self):
 
132
        """
 
133
        Called when the channel is closed.  This means that both our side and
 
134
        the remote side have closed the channel.
 
135
        """
 
136
        log.msg('closed')
 
137
 
 
138
    # transport stuff
 
139
    def write(self, data):
 
140
        """
 
141
        Write some data to the channel.  If there is not enough remote window
 
142
        available, buffer until it is.
 
143
 
 
144
        @type data: C{str}
 
145
        """
 
146
        #if not data: return
 
147
        if self.buf:
 
148
            self.buf += data
 
149
            return
 
150
        top = len(data)
 
151
        if top > self.remoteWindowLeft:
 
152
            data, self.buf = data[:self.remoteWindowLeft], data[self.remoteWindowLeft:]
 
153
            self.areWriting = 0
 
154
            self.stopWriting()
 
155
            top = self.remoteWindowLeft
 
156
        rmp = self.remoteMaxPacket
 
157
        write = self.conn.sendData
 
158
        r = range(0, top, rmp)
 
159
        for offset in r:
 
160
            write(self, data[offset: offset+rmp])
 
161
        self.remoteWindowLeft-=top
 
162
        if self.closing and not self.buf:
 
163
            self.loseConnection() # try again
 
164
 
 
165
    def writeExtended(self, dataType, data):
 
166
        """
 
167
        Send extended data to this channel.  If there is not enough remote
 
168
        window available, buffer until there is.
 
169
 
 
170
        @type dataType: C{int}
 
171
        @type data:     C{str}
 
172
        """
 
173
        if self.extBuf:
 
174
            if self.extBuf[-1][0] == dataType:
 
175
                self.extBuf[-1][1]+=data
 
176
            else:
 
177
                self.extBuf.append([dataType, data])
 
178
            return
 
179
        if len(data) > self.remoteWindowLeft:
 
180
            data, self.extBuf = data[:self.remoteWindowLeft], \
 
181
                                [[dataType, data[self.remoteWindowLeft:]]]
 
182
            self.areWriting = 0
 
183
            self.stopWriting()
 
184
        if not data: return
 
185
        while len(data) > self.remoteMaxPacket:
 
186
            self.conn.sendExtendedData(self, dataType, 
 
187
                                             data[:self.remoteMaxPacket])
 
188
            data = data[self.remoteMaxPacket:]
 
189
            self.remoteWindowLeft-=self.remoteMaxPacket
 
190
        if data:
 
191
            self.conn.sendExtendedData(self, dataType, data)
 
192
            self.remoteWindowLeft-=len(data)
 
193
        if self.closing:
 
194
            self.loseConnection() # try again
 
195
 
 
196
    def writeSequence(self, data):
 
197
        """
 
198
        Part of the Transport interface.  Write a list of strings to the
 
199
        channel.
 
200
 
 
201
        @type data: C{list} of C{str}
 
202
        """
 
203
        self.write(''.join(data))
 
204
 
 
205
    def loseConnection(self):
 
206
        """
 
207
        Close the channel.
 
208
        """
 
209
        self.closing = 1
 
210
        if not self.buf and not self.extBuf:
 
211
            self.conn.sendClose(self)
 
212
 
 
213
    def getPeer(self):
 
214
        """
 
215
        Return a tuple describing the other side of the connection.
 
216
 
 
217
        @rtype: C{tuple}
 
218
        """
 
219
        return('SSH', )+self.conn.transport.getPeer()
 
220
 
 
221
    def getHost(self):
 
222
        """
 
223
        Return a tuple describing our side of the connection.
 
224
 
 
225
        @rtype: C{tuple}
 
226
        """
 
227
        return('SSH', )+self.conn.transport.getHost()
 
228
 
 
229
    def stopWriting(self):
 
230
        """
 
231
        Called when the remote buffer is full, as a hint to stop writing.
 
232
        This can be ignored, but it can be helpful.
 
233
        """
 
234
 
 
235
    def startWriting(self):
 
236
        """
 
237
        Called when the remote buffer has more room, as a hint to continue
 
238
        writing.
 
239
        """