~justin-fathomdb/nova/justinsb-openstack-api-volumes

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/conch/scripts/tkconch.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.conch.test.test_scripts -*-
 
2
# Copyright (c) 2001-2007 Twisted Matrix Laboratories.
 
3
# See LICENSE for details.
 
4
 
 
5
#
 
6
# $Id: tkconch.py,v 1.6 2003/02/22 08:10:15 z3p Exp $
 
7
 
 
8
""" Implementation module for the `tkconch` command.
 
9
"""
 
10
 
 
11
from __future__ import nested_scopes
 
12
 
 
13
import Tkinter, tkFileDialog, tkFont, tkMessageBox, string
 
14
from twisted.conch.ui import tkvt100
 
15
from twisted.conch.ssh import transport, userauth, connection, common, keys
 
16
from twisted.conch.ssh import session, forwarding, channel
 
17
from twisted.conch.client.default import isInKnownHosts
 
18
from twisted.internet import reactor, defer, protocol, tksupport
 
19
from twisted.python import usage, log
 
20
 
 
21
import os, sys, getpass, struct, base64, signal
 
22
 
 
23
class TkConchMenu(Tkinter.Frame):
 
24
    def __init__(self, *args, **params):
 
25
        ## Standard heading: initialization
 
26
        apply(Tkinter.Frame.__init__, (self,) + args, params)
 
27
 
 
28
        self.master.title('TkConch')
 
29
        self.localRemoteVar = Tkinter.StringVar()
 
30
        self.localRemoteVar.set('local')
 
31
 
 
32
        Tkinter.Label(self, anchor='w', justify='left', text='Hostname').grid(column=1, row=1, sticky='w')
 
33
        self.host = Tkinter.Entry(self)
 
34
        self.host.grid(column=2, columnspan=2, row=1, sticky='nesw')
 
35
 
 
36
        Tkinter.Label(self, anchor='w', justify='left', text='Port').grid(column=1, row=2, sticky='w')
 
37
        self.port = Tkinter.Entry(self)
 
38
        self.port.grid(column=2, columnspan=2, row=2, sticky='nesw')
 
39
 
 
40
        Tkinter.Label(self, anchor='w', justify='left', text='Username').grid(column=1, row=3, sticky='w')
 
41
        self.user = Tkinter.Entry(self)
 
42
        self.user.grid(column=2, columnspan=2, row=3, sticky='nesw')
 
43
 
 
44
        Tkinter.Label(self, anchor='w', justify='left', text='Command').grid(column=1, row=4, sticky='w')
 
45
        self.command = Tkinter.Entry(self)
 
46
        self.command.grid(column=2, columnspan=2, row=4, sticky='nesw')
 
47
 
 
48
        Tkinter.Label(self, anchor='w', justify='left', text='Identity').grid(column=1, row=5, sticky='w')
 
49
        self.identity = Tkinter.Entry(self)
 
50
        self.identity.grid(column=2, row=5, sticky='nesw')
 
51
        Tkinter.Button(self, command=self.getIdentityFile, text='Browse').grid(column=3, row=5, sticky='nesw')
 
52
 
 
53
        Tkinter.Label(self, text='Port Forwarding').grid(column=1, row=6, sticky='w')
 
54
        self.forwards = Tkinter.Listbox(self, height=0, width=0)
 
55
        self.forwards.grid(column=2, columnspan=2, row=6, sticky='nesw')
 
56
        Tkinter.Button(self, text='Add', command=self.addForward).grid(column=1, row=7)
 
57
        Tkinter.Button(self, text='Remove', command=self.removeForward).grid(column=1, row=8)
 
58
        self.forwardPort = Tkinter.Entry(self)
 
59
        self.forwardPort.grid(column=2, row=7, sticky='nesw')
 
60
        Tkinter.Label(self, text='Port').grid(column=3, row=7, sticky='nesw')
 
61
        self.forwardHost = Tkinter.Entry(self)
 
62
        self.forwardHost.grid(column=2, row=8, sticky='nesw')
 
63
        Tkinter.Label(self, text='Host').grid(column=3, row=8, sticky='nesw')
 
64
        self.localForward = Tkinter.Radiobutton(self, text='Local', variable=self.localRemoteVar, value='local')
 
65
        self.localForward.grid(column=2, row=9)
 
66
        self.remoteForward = Tkinter.Radiobutton(self, text='Remote', variable=self.localRemoteVar, value='remote')
 
67
        self.remoteForward.grid(column=3, row=9)
 
68
 
 
69
        Tkinter.Label(self, text='Advanced Options').grid(column=1, columnspan=3, row=10, sticky='nesw')
 
70
 
 
71
        Tkinter.Label(self, anchor='w', justify='left', text='Cipher').grid(column=1, row=11, sticky='w')
 
72
        self.cipher = Tkinter.Entry(self, name='cipher')
 
73
        self.cipher.grid(column=2, columnspan=2, row=11, sticky='nesw')
 
74
 
 
75
        Tkinter.Label(self, anchor='w', justify='left', text='MAC').grid(column=1, row=12, sticky='w')
 
76
        self.mac = Tkinter.Entry(self, name='mac')
 
77
        self.mac.grid(column=2, columnspan=2, row=12, sticky='nesw')
 
78
 
 
79
        Tkinter.Label(self, anchor='w', justify='left', text='Escape Char').grid(column=1, row=13, sticky='w')
 
80
        self.escape = Tkinter.Entry(self, name='escape')
 
81
        self.escape.grid(column=2, columnspan=2, row=13, sticky='nesw')
 
82
        Tkinter.Button(self, text='Connect!', command=self.doConnect).grid(column=1, columnspan=3, row=14, sticky='nesw')
 
83
 
 
84
        # Resize behavior(s)
 
85
        self.grid_rowconfigure(6, weight=1, minsize=64)
 
86
        self.grid_columnconfigure(2, weight=1, minsize=2)
 
87
 
 
88
        self.master.protocol("WM_DELETE_WINDOW", sys.exit)
 
89
        
 
90
 
 
91
    def getIdentityFile(self):
 
92
        r = tkFileDialog.askopenfilename()
 
93
        if r:
 
94
            self.identity.delete(0, Tkinter.END)
 
95
            self.identity.insert(Tkinter.END, r)
 
96
 
 
97
    def addForward(self):
 
98
        port = self.forwardPort.get()
 
99
        self.forwardPort.delete(0, Tkinter.END)
 
100
        host = self.forwardHost.get()
 
101
        self.forwardHost.delete(0, Tkinter.END)
 
102
        if self.localRemoteVar.get() == 'local':
 
103
            self.forwards.insert(Tkinter.END, 'L:%s:%s' % (port, host))
 
104
        else:
 
105
            self.forwards.insert(Tkinter.END, 'R:%s:%s' % (port, host))
 
106
 
 
107
    def removeForward(self):
 
108
        cur = self.forwards.curselection()
 
109
        if cur:
 
110
            self.forwards.remove(cur[0])
 
111
 
 
112
    def doConnect(self):
 
113
        finished = 1
 
114
        options['host'] = self.host.get()
 
115
        options['port'] = self.port.get()
 
116
        options['user'] = self.user.get()
 
117
        options['command'] = self.command.get()
 
118
        cipher = self.cipher.get()
 
119
        mac = self.mac.get()
 
120
        escape = self.escape.get()
 
121
        if cipher:
 
122
            if cipher in SSHClientTransport.supportedCiphers:
 
123
                SSHClientTransport.supportedCiphers = [cipher]
 
124
            else:
 
125
                tkMessageBox.showerror('TkConch', 'Bad cipher.')
 
126
                finished = 0
 
127
 
 
128
        if mac:
 
129
            if mac in SSHClientTransport.supportedMACs:
 
130
                SSHClientTransport.supportedMACs = [mac]
 
131
            elif finished:
 
132
                tkMessageBox.showerror('TkConch', 'Bad MAC.')
 
133
                finished = 0
 
134
 
 
135
        if escape:
 
136
            if escape == 'none':
 
137
                options['escape'] = None
 
138
            elif escape[0] == '^' and len(escape) == 2:
 
139
                options['escape'] = chr(ord(escape[1])-64)
 
140
            elif len(escape) == 1:
 
141
                options['escape'] = escape
 
142
            elif finished:
 
143
                tkMessageBox.showerror('TkConch', "Bad escape character '%s'." % escape)
 
144
                finished = 0
 
145
 
 
146
        if self.identity.get():
 
147
            options.identitys.append(self.identity.get())
 
148
 
 
149
        for line in self.forwards.get(0,Tkinter.END):
 
150
            if line[0]=='L':
 
151
                options.opt_localforward(line[2:])
 
152
            else:
 
153
                options.opt_remoteforward(line[2:])
 
154
 
 
155
        if '@' in options['host']:
 
156
            options['user'], options['host'] = options['host'].split('@',1)
 
157
 
 
158
        if (not options['host'] or not options['user']) and finished:
 
159
            tkMessageBox.showerror('TkConch', 'Missing host or username.')
 
160
            finished = 0
 
161
        if finished:
 
162
            self.master.quit()
 
163
            self.master.destroy()        
 
164
            if options['log']:
 
165
                realout = sys.stdout
 
166
                log.startLogging(sys.stderr)
 
167
                sys.stdout = realout
 
168
            else:
 
169
                log.discardLogs()
 
170
            log.deferr = handleError # HACK
 
171
            if not options.identitys:
 
172
                options.identitys = ['~/.ssh/id_rsa', '~/.ssh/id_dsa']
 
173
            host = options['host']
 
174
            port = int(options['port'] or 22)
 
175
            log.msg((host,port))
 
176
            reactor.connectTCP(host, port, SSHClientFactory())
 
177
            frame.master.deiconify()
 
178
            frame.master.title('%s@%s - TkConch' % (options['user'], options['host']))
 
179
        else:
 
180
            self.focus()
 
181
 
 
182
class GeneralOptions(usage.Options):
 
183
    synopsis = """Usage:    tkconch [options] host [command]
 
184
 """
 
185
 
 
186
    optParameters = [['user', 'l', None, 'Log in using this user name.'],
 
187
                    ['identity', 'i', '~/.ssh/identity', 'Identity for public key authentication'],
 
188
                    ['escape', 'e', '~', "Set escape character; ``none'' = disable"],
 
189
                    ['cipher', 'c', None, 'Select encryption algorithm.'],
 
190
                    ['macs', 'm', None, 'Specify MAC algorithms for protocol version 2.'],
 
191
                    ['port', 'p', None, 'Connect to this port.  Server must be on the same port.'],
 
192
                    ['localforward', 'L', None, 'listen-port:host:port   Forward local port to remote address'],
 
193
                    ['remoteforward', 'R', None, 'listen-port:host:port   Forward remote port to local address'],
 
194
                    ]
 
195
    
 
196
    optFlags = [['tty', 't', 'Tty; allocate a tty even if command is given.'],
 
197
                ['notty', 'T', 'Do not allocate a tty.'],
 
198
                ['version', 'V', 'Display version number only.'],
 
199
                ['compress', 'C', 'Enable compression.'],
 
200
                ['noshell', 'N', 'Do not execute a shell or command.'],
 
201
                ['subsystem', 's', 'Invoke command (mandatory) as SSH2 subsystem.'],
 
202
                ['log', 'v', 'Log to stderr'],
 
203
                ['ansilog', 'a', 'Print the receieved data to stdout']]
 
204
 
 
205
    #zsh_altArgDescr = {"foo":"use this description for foo instead"}
 
206
    #zsh_multiUse = ["foo", "bar"]
 
207
    zsh_mutuallyExclusive = [("tty", "notty")]
 
208
    zsh_actions = {"cipher":"(%s)" % " ".join(transport.SSHClientTransport.supportedCiphers),
 
209
                   "macs":"(%s)" % " ".join(transport.SSHClientTransport.supportedMACs)}
 
210
    zsh_actionDescr = {"localforward":"listen-port:host:port",
 
211
                       "remoteforward":"listen-port:host:port"}
 
212
    # user, host, or user@host completion similar to zsh's ssh completion
 
213
    zsh_extras = ['1:host | user@host:{_ssh;if compset -P "*@"; then _wanted hosts expl "remote host name" _ssh_hosts && ret=0 elif compset -S "@*"; then _wanted users expl "login name" _ssh_users -S "" && ret=0 else if (( $+opt_args[-l] )); then tmp=() else tmp=( "users:login name:_ssh_users -qS@" ) fi; _alternative "hosts:remote host name:_ssh_hosts" "$tmp[@]" && ret=0 fi}',
 
214
                  '*:command: ']
 
215
 
 
216
    identitys = []
 
217
    localForwards = []
 
218
    remoteForwards = []
 
219
 
 
220
    def opt_identity(self, i):
 
221
        self.identitys.append(i)
 
222
 
 
223
    def opt_localforward(self, f):
 
224
        localPort, remoteHost, remotePort = f.split(':') # doesn't do v6 yet
 
225
        localPort = int(localPort)
 
226
        remotePort = int(remotePort)
 
227
        self.localForwards.append((localPort, (remoteHost, remotePort)))
 
228
 
 
229
    def opt_remoteforward(self, f):
 
230
        remotePort, connHost, connPort = f.split(':') # doesn't do v6 yet
 
231
        remotePort = int(remotePort)
 
232
        connPort = int(connPort)
 
233
        self.remoteForwards.append((remotePort, (connHost, connPort)))
 
234
 
 
235
    def opt_compress(self):
 
236
        SSHClientTransport.supportedCompressions[0:1] = ['zlib']
 
237
 
 
238
    def parseArgs(self, *args):
 
239
        if args:
 
240
            self['host'] = args[0]
 
241
            self['command'] = ' '.join(args[1:])
 
242
        else:
 
243
            self['host'] = ''
 
244
            self['command'] = ''
 
245
 
 
246
# Rest of code in "run"
 
247
options = None
 
248
menu = None
 
249
exitStatus = 0
 
250
frame = None
 
251
 
 
252
def deferredAskFrame(question, echo):
 
253
    if frame.callback:
 
254
        raise ValueError("can't ask 2 questions at once!")
 
255
    d = defer.Deferred()
 
256
    resp = []
 
257
    def gotChar(ch, resp=resp):
 
258
        if not ch: return
 
259
        if ch=='\x03': # C-c
 
260
            reactor.stop()
 
261
        if ch=='\r':
 
262
            frame.write('\r\n')
 
263
            stresp = ''.join(resp)
 
264
            del resp
 
265
            frame.callback = None
 
266
            d.callback(stresp)
 
267
            return
 
268
        elif 32 <= ord(ch) < 127:
 
269
            resp.append(ch)
 
270
            if echo:
 
271
                frame.write(ch)
 
272
        elif ord(ch) == 8 and resp: # BS
 
273
            if echo: frame.write('\x08 \x08')
 
274
            resp.pop()
 
275
    frame.callback = gotChar
 
276
    frame.write(question)
 
277
    frame.canvas.focus_force()
 
278
    return d
 
279
 
 
280
def run():
 
281
    global menu, options, frame
 
282
    args = sys.argv[1:]
 
283
    if '-l' in args: # cvs is an idiot
 
284
        i = args.index('-l')
 
285
        args = args[i:i+2]+args
 
286
        del args[i+2:i+4]
 
287
    for arg in args[:]:
 
288
        try:
 
289
            i = args.index(arg)
 
290
            if arg[:2] == '-o' and args[i+1][0]!='-':
 
291
                args[i:i+2] = [] # suck on it scp
 
292
        except ValueError:
 
293
            pass
 
294
    root = Tkinter.Tk()
 
295
    root.withdraw()
 
296
    top = Tkinter.Toplevel()
 
297
    menu = TkConchMenu(top)
 
298
    menu.pack(side=Tkinter.TOP, fill=Tkinter.BOTH, expand=1)
 
299
    options = GeneralOptions()
 
300
    try:
 
301
        options.parseOptions(args)
 
302
    except usage.UsageError, u:
 
303
        print 'ERROR: %s' % u
 
304
        options.opt_help()
 
305
        sys.exit(1)
 
306
    for k,v in options.items():
 
307
        if v and hasattr(menu, k):
 
308
            getattr(menu,k).insert(Tkinter.END, v)
 
309
    for (p, (rh, rp)) in options.localForwards:
 
310
        menu.forwards.insert(Tkinter.END, 'L:%s:%s:%s' % (p, rh, rp))
 
311
    options.localForwards = []
 
312
    for (p, (rh, rp)) in options.remoteForwards:
 
313
        menu.forwards.insert(Tkinter.END, 'R:%s:%s:%s' % (p, rh, rp))
 
314
    options.remoteForwards = []
 
315
    frame = tkvt100.VT100Frame(root, callback=None)
 
316
    root.geometry('%dx%d'%(tkvt100.fontWidth*frame.width+3, tkvt100.fontHeight*frame.height+3))
 
317
    frame.pack(side = Tkinter.TOP)
 
318
    tksupport.install(root)
 
319
    root.withdraw()
 
320
    if (options['host'] and options['user']) or '@' in options['host']:
 
321
        menu.doConnect()
 
322
    else:
 
323
        top.mainloop()
 
324
    reactor.run()
 
325
    sys.exit(exitStatus)
 
326
 
 
327
def handleError():
 
328
    from twisted.python import failure
 
329
    global exitStatus
 
330
    exitStatus = 2
 
331
    log.err(failure.Failure())
 
332
    reactor.stop()
 
333
    raise
 
334
 
 
335
class SSHClientFactory(protocol.ClientFactory):
 
336
    noisy = 1 
 
337
 
 
338
    def stopFactory(self):
 
339
        reactor.stop()
 
340
 
 
341
    def buildProtocol(self, addr):
 
342
        return SSHClientTransport()
 
343
 
 
344
    def clientConnectionFailed(self, connector, reason):
 
345
        tkMessageBox.showwarning('TkConch','Connection Failed, Reason:\n %s: %s' % (reason.type, reason.value))
 
346
 
 
347
class SSHClientTransport(transport.SSHClientTransport):
 
348
 
 
349
    def receiveError(self, code, desc):
 
350
        global exitStatus
 
351
        exitStatus = 'conch:\tRemote side disconnected with error code %i\nconch:\treason: %s' % (code, desc)
 
352
 
 
353
    def sendDisconnect(self, code, reason):
 
354
        global exitStatus
 
355
        exitStatus = 'conch:\tSending disconnect with error code %i\nconch:\treason: %s' % (code, reason)
 
356
        transport.SSHClientTransport.sendDisconnect(self, code, reason)
 
357
 
 
358
    def receiveDebug(self, alwaysDisplay, message, lang):
 
359
        global options
 
360
        if alwaysDisplay or options['log']:
 
361
            log.msg('Received Debug Message: %s' % message)
 
362
 
 
363
    def verifyHostKey(self, pubKey, fingerprint):
 
364
        #d = defer.Deferred()
 
365
        #d.addCallback(lambda x:defer.succeed(1))
 
366
        #d.callback(2)
 
367
        #return d
 
368
        goodKey = isInKnownHosts(options['host'], pubKey, {'known-hosts': None})
 
369
        if goodKey == 1: # good key
 
370
            return defer.succeed(1)
 
371
        elif goodKey == 2: # AAHHHHH changed
 
372
            return defer.fail(error.ConchError('bad host key'))
 
373
        else:
 
374
            if options['host'] == self.transport.getPeer()[1]:
 
375
                host = options['host']
 
376
                khHost = options['host']
 
377
            else:
 
378
                host = '%s (%s)' % (options['host'], 
 
379
                                    self.transport.getPeer()[1])
 
380
                khHost = '%s,%s' % (options['host'], 
 
381
                                    self.transport.getPeer()[1])
 
382
            keyType = common.getNS(pubKey)[0]
 
383
            ques = """The authenticity of host '%s' can't be established.\r
 
384
%s key fingerprint is %s.""" % (host, 
 
385
                                {'ssh-dss':'DSA', 'ssh-rsa':'RSA'}[keyType], 
 
386
                                fingerprint) 
 
387
            ques+='\r\nAre you sure you want to continue connecting (yes/no)? '
 
388
            return deferredAskFrame(ques, 1).addCallback(self._cbVerifyHostKey, pubKey, khHost, keyType)
 
389
 
 
390
    def _cbVerifyHostKey(self, ans, pubKey, khHost, keyType):
 
391
        if ans.lower() not in ('yes', 'no'):
 
392
            return deferredAskFrame("Please type  'yes' or 'no': ",1).addCallback(self._cbVerifyHostKey, pubKey, khHost, keyType)
 
393
        if ans.lower() == 'no':
 
394
            frame.write('Host key verification failed.\r\n')
 
395
            raise error.ConchError('bad host key')
 
396
        try:
 
397
            frame.write("Warning: Permanently added '%s' (%s) to the list of known hosts.\r\n" % (khHost, {'ssh-dss':'DSA', 'ssh-rsa':'RSA'}[keyType]))
 
398
            known_hosts = open(os.path.expanduser('~/.ssh/known_hosts'), 'a')
 
399
            encodedKey = base64.encodestring(pubKey).replace('\n', '')
 
400
            known_hosts.write('\n%s %s %s' % (khHost, keyType, encodedKey))
 
401
            known_hosts.close()
 
402
        except:
 
403
            log.deferr()
 
404
            raise error.ConchError 
 
405
 
 
406
    def connectionSecure(self):
 
407
        if options['user']:
 
408
            user = options['user']
 
409
        else:
 
410
            user = getpass.getuser()
 
411
        self.requestService(SSHUserAuthClient(user, SSHConnection()))
 
412
 
 
413
class SSHUserAuthClient(userauth.SSHUserAuthClient):
 
414
    usedFiles = []
 
415
 
 
416
    def getPassword(self, prompt = None):
 
417
        if not prompt:
 
418
            prompt = "%s@%s's password: " % (self.user, options['host'])
 
419
        return deferredAskFrame(prompt,0) 
 
420
 
 
421
    def getPublicKey(self):
 
422
        files = [x for x in options.identitys if x not in self.usedFiles]
 
423
        if not files:
 
424
            return None
 
425
        file = files[0]
 
426
        log.msg(file)
 
427
        self.usedFiles.append(file)
 
428
        file = os.path.expanduser(file) 
 
429
        file += '.pub'
 
430
        if not os.path.exists(file):
 
431
            return
 
432
        try:
 
433
            return keys.getPublicKeyString(file) 
 
434
        except:
 
435
            return self.getPublicKey() # try again
 
436
    
 
437
    def getPrivateKey(self):
 
438
        file = os.path.expanduser(self.usedFiles[-1])
 
439
        if not os.path.exists(file):
 
440
            return None
 
441
        try:
 
442
            return defer.succeed(keys.getPrivateKeyObject(file))
 
443
        except keys.BadKeyError, e:
 
444
            if e.args[0] == 'encrypted key with no password':
 
445
                prompt = "Enter passphrase for key '%s': " % \
 
446
                       self.usedFiles[-1]
 
447
                return deferredAskFrame(prompt, 0).addCallback(self._cbGetPrivateKey, 0)
 
448
    def _cbGetPrivateKey(self, ans, count):
 
449
        file = os.path.expanduser(self.usedFiles[-1])
 
450
        try:
 
451
            return keys.getPrivateKeyObject(file, password = ans)
 
452
        except keys.BadKeyError:
 
453
            if count == 2:
 
454
                raise
 
455
            prompt = "Enter passphrase for key '%s': " % \
 
456
                   self.usedFiles[-1]
 
457
            return deferredAskFrame(prompt, 0).addCallback(self._cbGetPrivateKey, count+1)
 
458
 
 
459
class SSHConnection(connection.SSHConnection):
 
460
    def serviceStarted(self):
 
461
        if not options['noshell']:
 
462
            self.openChannel(SSHSession())
 
463
        if options.localForwards:
 
464
            for localPort, hostport in options.localForwards:
 
465
                reactor.listenTCP(localPort,
 
466
                            forwarding.SSHListenForwardingFactory(self, 
 
467
                                hostport,
 
468
                                forwarding.SSHListenClientForwardingChannel))
 
469
        if options.remoteForwards:
 
470
            for remotePort, hostport in options.remoteForwards:
 
471
                log.msg('asking for remote forwarding for %s:%s' %
 
472
                        (remotePort, hostport))
 
473
                data = forwarding.packGlobal_tcpip_forward(
 
474
                    ('0.0.0.0', remotePort))
 
475
                d = self.sendGlobalRequest('tcpip-forward', data)
 
476
                self.remoteForwards[remotePort] = hostport
 
477
 
 
478
class SSHSession(channel.SSHChannel):
 
479
 
 
480
    name = 'session'
 
481
    
 
482
    def channelOpen(self, foo):
 
483
        #global globalSession
 
484
        #globalSession = self
 
485
        # turn off local echo
 
486
        self.escapeMode = 1
 
487
        c = session.SSHSessionClient()
 
488
        if options['escape']:
 
489
            c.dataReceived = self.handleInput
 
490
        else:
 
491
            c.dataReceived = self.write
 
492
        c.connectionLost = self.sendEOF
 
493
        frame.callback = c.dataReceived
 
494
        frame.canvas.focus_force()
 
495
        if options['subsystem']:
 
496
            self.conn.sendRequest(self, 'subsystem', \
 
497
                common.NS(options['command']))
 
498
        elif options['command']:
 
499
            if options['tty']:
 
500
                term = os.environ.get('TERM', 'xterm')
 
501
                #winsz = fcntl.ioctl(fd, tty.TIOCGWINSZ, '12345678')
 
502
                winSize = (25,80,0,0) #struct.unpack('4H', winsz)
 
503
                ptyReqData = session.packRequest_pty_req(term, winSize, '')
 
504
                self.conn.sendRequest(self, 'pty-req', ptyReqData)                
 
505
            self.conn.sendRequest(self, 'exec', \
 
506
                common.NS(options['command']))
 
507
        else:
 
508
            if not options['notty']:
 
509
                term = os.environ.get('TERM', 'xterm')
 
510
                #winsz = fcntl.ioctl(fd, tty.TIOCGWINSZ, '12345678')
 
511
                winSize = (25,80,0,0) #struct.unpack('4H', winsz)
 
512
                ptyReqData = session.packRequest_pty_req(term, winSize, '')
 
513
                self.conn.sendRequest(self, 'pty-req', ptyReqData)
 
514
            self.conn.sendRequest(self, 'shell', '')
 
515
        self.conn.transport.transport.setTcpNoDelay(1)
 
516
 
 
517
    def handleInput(self, char):
 
518
        #log.msg('handling %s' % repr(char))
 
519
        if char in ('\n', '\r'):
 
520
            self.escapeMode = 1
 
521
            self.write(char)
 
522
        elif self.escapeMode == 1 and char == options['escape']:
 
523
            self.escapeMode = 2
 
524
        elif self.escapeMode == 2:
 
525
            self.escapeMode = 1 # so we can chain escapes together
 
526
            if char == '.': # disconnect
 
527
                log.msg('disconnecting from escape')
 
528
                reactor.stop()
 
529
                return
 
530
            elif char == '\x1a': # ^Z, suspend
 
531
                # following line courtesy of Erwin@freenode
 
532
                os.kill(os.getpid(), signal.SIGSTOP)
 
533
                return
 
534
            elif char == 'R': # rekey connection
 
535
                log.msg('rekeying connection')
 
536
                self.conn.transport.sendKexInit()
 
537
                return
 
538
            self.write('~' + char)
 
539
        else:
 
540
            self.escapeMode = 0
 
541
            self.write(char)
 
542
 
 
543
    def dataReceived(self, data):
 
544
        if options['ansilog']:
 
545
            print repr(data)
 
546
        frame.write(data)
 
547
 
 
548
    def extReceived(self, t, data):
 
549
        if t==connection.EXTENDED_DATA_STDERR:
 
550
            log.msg('got %s stderr data' % len(data))
 
551
            sys.stderr.write(data)
 
552
            sys.stderr.flush()
 
553
 
 
554
    def eofReceived(self):
 
555
        log.msg('got eof')
 
556
        sys.stdin.close()
 
557
 
 
558
    def closed(self):
 
559
        log.msg('closed %s' % self)
 
560
        if len(self.conn.channels) == 1: # just us left
 
561
            reactor.stop()
 
562
 
 
563
    def request_exit_status(self, data):
 
564
        global exitStatus
 
565
        exitStatus = int(struct.unpack('>L', data)[0])
 
566
        log.msg('exit status: %s' % exitStatus)
 
567
 
 
568
    def sendEOF(self):
 
569
        self.conn.sendEOF(self)
 
570
 
 
571
if __name__=="__main__":
 
572
    run()