Add help, normalize binaries, allow custom motd, Fix #46 Fix #52 Fix#67

This commit is contained in:
Florian Mounier
2015-04-30 12:04:14 +02:00
parent 2b5ea8dc9a
commit 3d14bce231
11 changed files with 112 additions and 57 deletions

View File

View File

13
bin/bopen Executable file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/env python
import os
import webbrowser
import argparse
parser = argparse.ArgumentParser(description='Butterfly tab opener.')
parser.add_argument(
'location',
default=os.getcwd(),
help='Directory to open the new tab in. (Defaults to current)')
args = parser.parse_args()
webbrowser.open('%swd%s' % (os.getenv('LOCATION'), args.location))

View File

@@ -1,9 +1,10 @@
#!/usr/bin/env python
from butterfly.escapes import image
from butterfly.utils import ansi_colors
import os
import base64
print("Welcome to the butterfly help.")
print(ansi_colors.white + "Welcome to the butterfly help." + ansi_colors.reset)
with image('image/png'):
with open(
os.path.join(
@@ -13,10 +14,20 @@ with image('image/png'):
print("""
Butterfly is a xterm compliant terminal built with python and javascript.
Terminal functionalities:
[Alt] + [a] : Set an alarm which sends a notification when a modification is detected.
[Ctrl] + [Shift] + [Up] : Trigger visual selection mode. Hitting [Enter] inserts the selection in the prompt.
[ScrollLock] : Lock the scrolling to the current position. Press again to release.
[Alt] + [z] : Escape: don't catch the next pressed key. Useful for using native search for example. ([Alt] + [z] then [Ctrl] + [f]).
[Ctrl] + [c] <<hold>> : Cut the output when [Ctrl] + [c] is not enough.
""")
{title}Terminal functionalities:{reset}
{strong}[Alt] + [a] : {reset}Set an alarm which sends a notification when a modification is detected.
{strong}[Ctrl] + [Shift] + [Up] : {reset}Trigger visual selection mode. Hitting [Enter] inserts the selection in the prompt.
{strong}[ScrollLock] : {reset}Lock the scrolling to the current position. Press again to release.
{strong}[Alt] + [z] : {reset}Escape: don't catch the next pressed key. Useful for using native search for example. ([Alt] + [z] then [Ctrl] + [f]).
{strong}[Ctrl] + [c] <<hold>> : {reset}Cut the output when [Ctrl] + [c] is not enough.
{title}Butterfly programs:{reset}
{strong}bcat : {reset}A wrapper around cat allowing to display images as <img> instead of binary.
{strong}bopen : {reset}Open a new terminal at specified location.
{strong}b16M : {reset}Test the 16M colors support in terminal.
{strong}bhr : {reset}Put a html hr. This is a test and needs --allow-html-escapes flag.
{strong}bcal : {reset}Display current month using html. This is a test and needs --allow-html-escapes flag.
""".format(
title=ansi_colors.light_blue,
strong=ansi_colors.white,
reset=ansi_colors.reset))

4
bin/nt
View File

@@ -1,4 +0,0 @@
#!/usr/bin/env python
import os
import webbrowser
webbrowser.open('%swd%s' % (os.getenv('LOCATION'), os.getcwd()))

View File

@@ -36,6 +36,7 @@ tornado.options.define("more", default=False,
tornado.options.define("host", default='localhost', help="Server host")
tornado.options.define("port", default=57575, type=int, help="Server port")
tornado.options.define("shell", help="Shell to execute at login")
tornado.options.define("motd", default='motd', help="Path to the motd file.")
tornado.options.define("cmd",
help="Command to run instead of shell, f.i.: 'ls -l'")
tornado.options.define("unsecure", default=False,

View File

@@ -49,44 +49,6 @@ def u(s):
return s
def motd(socket):
return (
'''
B ` '
;,,, ` ' ,,,;
`Y888888bo. : : .od888888Y'
8888888888b. : : .d8888888888
88888Y' `Y8b. ` ' .d8Y' `Y88888
j88888 R.db.B Yb. ' ' .dY R.db.B 88888k
`888 RY88YB `b ( ) d' RY88YB 888'
888b R'"B ,', R"'B d888
j888888bd8gf"' ':' `"?g8bd888888k
R'Y'B .8' d' 'b '8. R'Y'X
R!B .8' RdbB d'; ;`b RdbB '8. R!B
d88 R`'B 8 ; ; 8 R`'B 88b Rbutterfly Zv %sB
d888b .g8 ',' 8g. d888b
:888888888Y' 'Y888888888: AConnecting to:B
'! 8888888' `8888888 !' G%sB
'8Y R`Y Y'B Y8'
R Y Y AFrom:R
! ! G%sX
For more information type: $ butterfly_help
'''
.replace('G', '\x1b[3%d;1m' % (
1 if tornado.options.options.unsecure else 2))
.replace('B', '\x1b[34;1m')
.replace('R', '\x1b[37;1m')
.replace('Z', '\x1b[33;1m')
.replace('A', '\x1b[37;0m')
.replace('X', '\x1b[0m')
.replace('\n', '\r\n')
% (__version__,
'%s:%d' % (socket.local_addr, socket.local_port),
'%s:%d' % (socket.remote_addr, socket.remote_port)))
@url(r'/(?:user/(.+))?/?(?:wd/(.+))?')
class Index(Route):
def get(self, user, path):
@@ -260,6 +222,7 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
def open(self, user, path):
self.fd = None
self.closed = False
if self.request.headers['Origin'] not in (
'http://%s' % self.request.headers['Host'],
'https://%s' % self.request.headers['Host']):
@@ -304,7 +267,16 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
TermWebSocket.terminals.add(self)
self.write_message(motd(self.socket))
motd = (self.render_string(
tornado.options.options.motd,
butterfly=self,
version=__version__,
opts=tornado.options.options,
colors=utils.ansi_colors)
.decode('utf-8')
.replace('\r', '')
.replace('\n', '\r\n'))
self.write_message(motd)
self.pty()
def on_message(self, message):
@@ -318,7 +290,7 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
fcntl.ioctl(self.fd, termios.TIOCSWINSZ, s)
self.log.info('SIZE (%d, %d)' % (cols, rows))
elif message[0] == 'S':
self.log.info('WRIT<%r' % message)
self.log.debug('WRIT<%r' % message)
self.writer.write(message[1:])
self.writer.flush()
@@ -329,7 +301,7 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
except IOError:
read = ''
self.log.info('READ>%r' % read)
self.log.debug('READ>%r' % read)
if read and len(read) != 0 and self.ws_connection:
self.write_message(read.decode('utf-8', 'replace'))
else:
@@ -338,10 +310,13 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
if events & ioloop.ERROR:
self.log.info('Error on fd %d, closing' % fd)
# Terminated
self.on_close()
self.close()
def on_close(self):
utils.rm_user_info(self.fd, self.pid, self.callee.name)
if self.closed:
return
self.closed = True
if self.fd is not None:
self.log.info('Closing fd %d' % self.fd)
@@ -349,6 +324,8 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
self.log.info('pid is 0')
return
utils.rm_user_info(self.fd, self.pid, self.callee.name)
try:
ioloop.remove_handler(self.fd)
except Exception:

22
butterfly/templates/motd Normal file
View File

@@ -0,0 +1,22 @@
{{ colors.blue }} ` '
;,,, ` ' ,,,;
`Y888888bo. : : .od888888Y'
8888888888b. : : .d8888888888
88888Y' `Y8b. ` ' .d8Y' `Y88888
j88888 {{ colors.white }}.db.{{ colors.blue }} Yb. ' ' .dY {{ colors.white }}.db.{{ colors.blue }} 88888k
`888 {{ colors.white }}Y88Y{{ colors.blue }} `b ( ) d' {{ colors.white }}Y88Y{{ colors.blue }} 888'
888b {{ colors.white }}'"{{ colors.blue }} ,', {{ colors.white }}"'{{ colors.blue }} d888
j888888bd8gf"' ':' `"?g8bd888888k
{{ colors.white }}'Y'{{ colors.blue }} .8' d' 'b '8. {{ colors.white }}'Y'{{ colors.reset }}
{{ colors.white }}!{{ colors.blue }} .8' {{ colors.white }}db{{ colors.blue }} d'; ;`b {{ colors.white }}db{{ colors.blue }} '8. {{ colors.white }}!{{ colors.blue }}
d88 {{ colors.white }}`'{{ colors.blue }} 8 ; ; 8 {{ colors.white }}`'{{ colors.blue }} 88b {{ colors.white }}butterfly {{ colors.yellow }}v {{ version }}{{ colors.blue }}
d888b .g8 ',' 8g. d888b
:888888888Y' 'Y888888888: {{ colors.light_white }}Connecting to:{{ colors.blue }}
'! 8888888' `8888888 !' {{ colors.red if opts.unsecure else colors.green }}{{ butterfly.socket.local_addr }}:{{ butterfly.socket.local_port }}{{ colors.blue }}
'8Y {{ colors.white }}`Y Y'{{ colors.blue }} Y8'
{{ colors.white }} Y Y {{ colors.light_white }}From:{{ colors.white }}
! ! {{ colors.red if opts.unsecure else colors.green }}{{ butterfly.socket.remote_addr }}:{{ butterfly.socket.remote_port }}{{ colors.reset }}
For more information type: $ butterfly_help

View File

@@ -290,6 +290,10 @@ def utmp_line(type, pid, fd, user, host, ts):
def add_user_info(fd, pid, user, host):
# Freebsd format is not yet supported.
# Please submit PR
if sys.platform != 'linux':
return
utmp = utmp_line(7, pid, fd, user, host, time.time())
for kind, file in {
'utmp': get_utmp_file(),
@@ -310,10 +314,12 @@ def add_user_info(fd, pid, user, host):
else:
f.write(utmp_struct.pack(*utmp))
except Exception:
log.warning('Unable to write utmp info to ' + file, exc_info=True)
log.info('Unable to write utmp info to ' + file, exc_info=True)
def rm_user_info(fd, pid, user):
if sys.platform != 'linux':
return
utmp = utmp_line(8, pid, fd, user, '', time.time())
for kind, file in {
'utmp': get_utmp_file(),
@@ -335,4 +341,32 @@ def rm_user_info(fd, pid, user):
f.write(utmp_struct.pack(*utmp))
except Exception:
log.warning('Unable to update utmp info to ' + file, exc_info=True)
log.info('Unable to update utmp info to ' + file, exc_info=True)
class AnsiColors(object):
colors = {
'black': 30,
'red': 31,
'green': 32,
'yellow': 33,
'blue': 34,
'magenta': 35,
'cyan': 36,
'white': 37
}
def __getattr__(self, key):
bold = True
if key.startswith('light_'):
bold = False
key = key[len('light_'):]
if key in self.colors:
return '\x1b[%d%sm' % (
self.colors[key],
';1' if bold else '')
if key == 'reset':
return '\x1b[0m'
return ''
ansi_colors = AnsiColors()

View File

@@ -32,7 +32,8 @@ options = dict(
'static/images/favicon.png',
'static/main.css',
'static/*.min.js',
'templates/index.html'
'templates/index.html',
'templates/motd'
]
},
classifiers=[