1# Copyright (c) Twisted Matrix Laboratories.
2# See LICENSE for details.
3
4# You can run this .tac file directly with:
5#    twistd -ny demo_draw.tac
6#
7# Re-using a private key is dangerous, generate one.
8#
9# For this example you can use:
10#
11# $ ckeygen -t rsa -f ssh-keys/ssh_host_rsa_key
12
13"""
14A trivial drawing application.
15
16Clients are allowed to connect and spew various characters out over
17the terminal.  Spacebar changes the drawing character, while the arrow
18keys move the cursor.
19"""
20
21from twisted.application import internet, service
22from twisted.conch.insults import insults
23from twisted.conch.manhole_ssh import ConchFactory, TerminalRealm
24from twisted.conch.ssh import keys
25from twisted.conch.telnet import TelnetBootstrapProtocol, TelnetTransport
26from twisted.cred import checkers, portal
27from twisted.internet import protocol
28
29
30class Draw(insults.TerminalProtocol):
31    """Protocol which accepts arrow key and spacebar input and places
32    the requested characters onto the terminal.
33    """
34
35    cursors = list("!@#$%^&*()_+-=")
36
37    def connectionMade(self):
38        self.terminal.eraseDisplay()
39        self.terminal.resetModes([insults.modes.IRM])
40        self.cursor = self.cursors[0]
41
42    def keystrokeReceived(self, keyID, modifier):
43        if keyID == self.terminal.UP_ARROW:
44            self.terminal.cursorUp()
45        elif keyID == self.terminal.DOWN_ARROW:
46            self.terminal.cursorDown()
47        elif keyID == self.terminal.LEFT_ARROW:
48            self.terminal.cursorBackward()
49        elif keyID == self.terminal.RIGHT_ARROW:
50            self.terminal.cursorForward()
51        elif keyID == " ":
52            self.cursor = self.cursors[
53                (self.cursors.index(self.cursor) + 1) % len(self.cursors)
54            ]
55        else:
56            return
57        self.terminal.write(self.cursor)
58        self.terminal.cursorBackward()
59
60
61def makeService(args):
62    checker = checkers.InMemoryUsernamePasswordDatabaseDontUse(username=b"password")
63
64    f = protocol.ServerFactory()
65    f.protocol = lambda: TelnetTransport(
66        TelnetBootstrapProtocol,
67        insults.ServerProtocol,
68        args["protocolFactory"],
69        *args.get("protocolArgs", ()),
70        **args.get("protocolKwArgs", {}),
71    )
72    tsvc = internet.TCPServer(args["telnet"], f)
73
74    def chainProtocolFactory():
75        return insults.ServerProtocol(
76            args["protocolFactory"],
77            *args.get("protocolArgs", ()),
78            **args.get("protocolKwArgs", {}),
79        )
80
81    rlm = TerminalRealm()
82    rlm.chainedProtocolFactory = chainProtocolFactory
83    ptl = portal.Portal(rlm, [checker])
84    f = ConchFactory(ptl)
85    f.publicKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key.pub")
86    f.privateKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key")
87    csvc = internet.TCPServer(args["ssh"], f)
88
89    m = service.MultiService()
90    tsvc.setServiceParent(m)
91    csvc.setServiceParent(m)
92    return m
93
94
95application = service.Application("Insults Demo App")
96makeService({"protocolFactory": Draw, "telnet": 6023, "ssh": 6022}).setServiceParent(
97    application
98)
99