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