~landscape/zope3/newer-from-ztk

« back to all changes in this revision

Viewing changes to src/twisted/protocols/telnet.py

  • Committer: Thomas Hervé
  • Date: 2009-07-08 13:52:04 UTC
  • Revision ID: thomas@canonical.com-20090708135204-df5eesrthifpylf8
Remove twisted copy

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
 
2
 
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3
 
# See LICENSE for details.
4
 
 
5
 
 
6
 
"""TELNET implementation, with line-oriented command handling.
7
 
"""
8
 
 
9
 
import warnings
10
 
warnings.warn(
11
 
    "As of Twisted 2.1, twisted.protocols.telnet is deprecated.  "
12
 
    "See twisted.conch.telnet for the current, supported API.",
13
 
    DeprecationWarning,
14
 
    stacklevel=2)
15
 
 
16
 
 
17
 
# System Imports
18
 
try:
19
 
    from cStringIO import StringIO
20
 
except ImportError:
21
 
    from StringIO import StringIO
22
 
 
23
 
# Twisted Imports
24
 
from twisted import copyright
25
 
from twisted.internet import protocol
26
 
 
27
 
# Some utility chars.
28
 
ESC =            chr(27) # ESC for doing fanciness
29
 
BOLD_MODE_ON =   ESC+"[1m" # turn bold on
30
 
BOLD_MODE_OFF=   ESC+"[m"  # no char attributes
31
 
 
32
 
 
33
 
# Characters gleaned from the various (and conflicting) RFCs.  Not all of these are correct.
34
 
 
35
 
NULL =            chr(0)  # No operation.
36
 
LF   =           chr(10)  # Moves the printer to the
37
 
                          # next print line, keeping the
38
 
                          # same horizontal position.
39
 
CR =             chr(13)  # Moves the printer to the left
40
 
                          # margin of the current line.
41
 
BEL =             chr(7)  # Produces an audible or
42
 
                          # visible signal (which does
43
 
                          # NOT move the print head).
44
 
BS  =             chr(8)  # Moves the print head one
45
 
                          # character position towards
46
 
                          # the left margin.
47
 
HT  =             chr(9)  # Moves the printer to the
48
 
                          # next horizontal tab stop.
49
 
                          # It remains unspecified how
50
 
                          # either party determines or
51
 
                          # establishes where such tab
52
 
                          # stops are located.
53
 
VT =             chr(11)  # Moves the printer to the
54
 
                          # next vertical tab stop.  It
55
 
                          # remains unspecified how
56
 
                          # either party determines or
57
 
                          # establishes where such tab
58
 
                          # stops are located.
59
 
FF =             chr(12)  # Moves the printer to the top
60
 
                          # of the next page, keeping
61
 
                          # the same horizontal position.
62
 
SE =            chr(240)  # End of subnegotiation parameters.
63
 
NOP=            chr(241)  # No operation.
64
 
DM =            chr(242)  # "Data Mark": The data stream portion
65
 
                          # of a Synch.  This should always be
66
 
                          # accompanied by a TCP Urgent
67
 
                          # notification.
68
 
BRK=            chr(243)  # NVT character Break.
69
 
IP =            chr(244)  # The function Interrupt Process.
70
 
AO =            chr(245)  # The function Abort Output
71
 
AYT=            chr(246)  # The function Are You There.
72
 
EC =            chr(247)  # The function Erase Character.
73
 
EL =            chr(248)  # The function Erase Line
74
 
GA =            chr(249)  # The Go Ahead signal.
75
 
SB =            chr(250)  # Indicates that what follows is
76
 
                          # subnegotiation of the indicated
77
 
                          # option.
78
 
WILL =          chr(251)  # Indicates the desire to begin
79
 
                          # performing, or confirmation that
80
 
                          # you are now performing, the
81
 
                          # indicated option.
82
 
WONT =          chr(252)  # Indicates the refusal to perform,
83
 
                          # or continue performing, the
84
 
                          # indicated option.
85
 
DO =            chr(253)  # Indicates the request that the
86
 
                          # other party perform, or
87
 
                          # confirmation that you are expecting
88
 
                          # the other party to perform, the
89
 
                          # indicated option.
90
 
DONT =          chr(254)  # Indicates the demand that the
91
 
                          # other party stop performing,
92
 
                          # or confirmation that you are no
93
 
                          # longer expecting the other party
94
 
                          # to perform, the indicated option.
95
 
IAC =           chr(255)  # Data Byte 255.
96
 
 
97
 
# features
98
 
 
99
 
ECHO  =           chr(1)  # User-to-Server:  Asks the server to send
100
 
                          # Echos of the transmitted data.
101
 
 
102
 
                          # Server-to User:  States that the server is
103
 
                          # sending echos of the transmitted data.
104
 
                          # Sent only as a reply to ECHO or NO ECHO.
105
 
 
106
 
SUPGA =           chr(3)  # Supress Go Ahead...? "Modern" telnet servers
107
 
                          # are supposed to do this.
108
 
 
109
 
LINEMODE =       chr(34)  # I don't care that Jon Postel is dead.
110
 
 
111
 
HIDE  =         chr(133)  # The intention is that a server will send
112
 
                          # this signal to a user system which is
113
 
                          # echoing locally (to the user) when the user
114
 
                          # is about to type something secret (e.g. a
115
 
                          # password).  In this case, the user system
116
 
                          # is to suppress local echoing or overprint
117
 
                          # the input (or something) until the server
118
 
                          # sends a NOECHO signal.  In situations where
119
 
                          # the user system is not echoing locally,
120
 
                          # this signal must not be sent by the server.
121
 
 
122
 
 
123
 
NOECHO=         chr(131)  # User-to-Server:  Asks the server not to
124
 
                          # return Echos of the transmitted data.
125
 
                          # 
126
 
                          # Server-to-User:  States that the server is
127
 
                          # not sending echos of the transmitted data.
128
 
                          # Sent only as a reply to ECHO or NO ECHO,
129
 
                          # or to end the hide your input.
130
 
 
131
 
 
132
 
 
133
 
iacBytes = {
134
 
    DO:   'DO',
135
 
    DONT: 'DONT',
136
 
    WILL: 'WILL',
137
 
    WONT: 'WONT',
138
 
    IP:   'IP'
139
 
    }
140
 
 
141
 
def multireplace(st, dct):
142
 
    for k, v in dct.items():
143
 
        st = st.replace(k, v)
144
 
    return st
145
 
 
146
 
class Telnet(protocol.Protocol):
147
 
    """I am a Protocol for handling Telnet connections. I have two
148
 
    sets of special methods, telnet_* and iac_*.
149
 
 
150
 
    telnet_* methods get called on every line sent to me. The method
151
 
    to call is decided by the current mode. The initial mode is 'User';
152
 
    this means that telnet_User is the first telnet_* method to be called.
153
 
    All telnet_* methods should return a string which specifies the mode
154
 
    to go into next; thus dictating which telnet_* method to call next.
155
 
    For example, the default telnet_User method returns 'Password' to go
156
 
    into Password mode, and the default telnet_Password method returns
157
 
    'Command' to go into Command mode.
158
 
 
159
 
    The iac_* methods are less-used; they are called when an IAC telnet
160
 
    byte is received. You can define iac_DO, iac_DONT, iac_WILL, iac_WONT,
161
 
    and iac_IP methods to do what you want when one of these bytes is
162
 
    received."""
163
 
 
164
 
 
165
 
    gotIAC = 0
166
 
    iacByte = None
167
 
    lastLine = None
168
 
    buffer = ''
169
 
    echo = 0
170
 
    delimiters = ['\r\n', '\r\000']
171
 
    mode = "User"
172
 
 
173
 
    def write(self, data):
174
 
        """Send the given data over my transport."""
175
 
        self.transport.write(data)
176
 
 
177
 
 
178
 
    def connectionMade(self):
179
 
        """I will write a welcomeMessage and loginPrompt to the client."""
180
 
        self.write(self.welcomeMessage() + self.loginPrompt())
181
 
 
182
 
    def welcomeMessage(self):
183
 
        """Override me to return a string which will be sent to the client
184
 
        before login."""
185
 
        x = self.factory.__class__
186
 
        return ("\r\n" + x.__module__ + '.' + x.__name__ +
187
 
                '\r\nTwisted %s\r\n' % copyright.version
188
 
                )
189
 
 
190
 
    def loginPrompt(self):
191
 
        """Override me to return a 'login:'-type prompt."""
192
 
        return "username: "
193
 
 
194
 
    def iacSBchunk(self, chunk):
195
 
        pass
196
 
 
197
 
    def iac_DO(self, feature):
198
 
        pass
199
 
 
200
 
    def iac_DONT(self, feature):
201
 
        pass
202
 
 
203
 
    def iac_WILL(self, feature):
204
 
        pass
205
 
 
206
 
    def iac_WONT(self, feature):
207
 
        pass
208
 
 
209
 
    def iac_IP(self, feature):
210
 
        pass
211
 
 
212
 
    def processLine(self, line):
213
 
        """I call a method that looks like 'telnet_*' where '*' is filled
214
 
        in by the current mode. telnet_* methods should return a string which
215
 
        will become the new mode.  If None is returned, the mode will not change.
216
 
        """
217
 
        mode = getattr(self, "telnet_"+self.mode)(line)
218
 
        if mode is not None:
219
 
            self.mode = mode
220
 
 
221
 
    def telnet_User(self, user):
222
 
        """I take a username, set it to the 'self.username' attribute,
223
 
        print out a password prompt, and switch to 'Password' mode. If
224
 
        you want to do something else when the username is received (ie,
225
 
        create a new user if the user doesn't exist), override me."""
226
 
        self.username = user
227
 
        self.write(IAC+WILL+ECHO+"password: ")
228
 
        return "Password"
229
 
 
230
 
    def telnet_Password(self, paswd):
231
 
        """I accept a password as an argument, and check it with the
232
 
        checkUserAndPass method. If the login is successful, I call
233
 
        loggedIn()."""
234
 
        self.write(IAC+WONT+ECHO+"*****\r\n")
235
 
        try:
236
 
            checked = self.checkUserAndPass(self.username, paswd)
237
 
        except:
238
 
            return "Done"
239
 
        if not checked:
240
 
            return "Done"
241
 
        self.loggedIn()
242
 
        return "Command"
243
 
 
244
 
    def telnet_Command(self, cmd):
245
 
        """The default 'command processing' mode. You probably want to
246
 
        override me."""
247
 
        return "Command"
248
 
 
249
 
    def processChunk(self, chunk):
250
 
        """I take a chunk of data and delegate out to telnet_* methods
251
 
        by way of processLine. If the current mode is 'Done', I'll close
252
 
        the connection. """
253
 
        self.buffer = self.buffer + chunk
254
 
 
255
 
        #yech.
256
 
        for delim in self.delimiters:
257
 
            idx = self.buffer.find(delim)
258
 
            if idx != -1:
259
 
                break
260
 
            
261
 
        while idx != -1:
262
 
            buf, self.buffer = self.buffer[:idx], self.buffer[idx+2:]
263
 
            self.processLine(buf)
264
 
            if self.mode == 'Done':
265
 
                self.transport.loseConnection()
266
 
 
267
 
            for delim in self.delimiters:
268
 
                idx = self.buffer.find(delim)
269
 
                if idx != -1:
270
 
                    break
271
 
 
272
 
    def dataReceived(self, data):
273
 
        chunk = StringIO()
274
 
        # silly little IAC state-machine
275
 
        for char in data:
276
 
            if self.gotIAC:
277
 
                # working on an IAC request state
278
 
                if self.iacByte:
279
 
                    # we're in SB mode, getting a chunk
280
 
                    if self.iacByte == SB:
281
 
                        if char == SE:
282
 
                            self.iacSBchunk(chunk.getvalue())
283
 
                            chunk = StringIO()
284
 
                            del self.iacByte
285
 
                            del self.gotIAC
286
 
                        else:
287
 
                            chunk.write(char)
288
 
                    else:
289
 
                        # got all I need to know state
290
 
                        try:
291
 
                            getattr(self, 'iac_%s' % iacBytes[self.iacByte])(char)
292
 
                        except KeyError:
293
 
                            pass
294
 
                        del self.iacByte
295
 
                        del self.gotIAC
296
 
                else:
297
 
                    # got IAC, this is my W/W/D/D (or perhaps sb)
298
 
                    self.iacByte = char
299
 
            elif char == IAC:
300
 
                # Process what I've got so far before going into
301
 
                # the IAC state; don't want to process characters
302
 
                # in an inconsistent state with what they were
303
 
                # received in.
304
 
                c = chunk.getvalue()
305
 
                if c:
306
 
                    why = self.processChunk(c)
307
 
                    if why:
308
 
                        return why
309
 
                    chunk = StringIO()
310
 
                self.gotIAC = 1
311
 
            else:
312
 
                chunk.write(char)
313
 
        # chunks are of a relatively indeterminate size.
314
 
        c = chunk.getvalue()
315
 
        if c:
316
 
            why = self.processChunk(c)
317
 
            if why:
318
 
                return why
319
 
 
320
 
    def loggedIn(self):
321
 
        """Called after the user succesfully logged in.
322
 
        
323
 
        Override in subclasses.
324
 
        """
325
 
        pass