X-Git-Url: https://arthur.barton.de/gitweb/?a=blobdiff_plain;f=cmd%2Fweb-cmd.py;h=2ddc0b1a345737ee7998afecd1005a9a58a7948b;hb=96d4110838158be0f60484cfc84ac3a56772e09b;hp=c4e019c7a751e2b5c4d42ee747e1a393c62362ba;hpb=8be0d95980bda47142ec41f4252ec9563d49f2bc;p=bup.git diff --git a/cmd/web-cmd.py b/cmd/web-cmd.py index c4e019c..2ddc0b1 100755 --- a/cmd/web-cmd.py +++ b/cmd/web-cmd.py @@ -1,20 +1,92 @@ -#!/usr/bin/env python -import sys, stat, cgi, shutil, urllib, mimetypes, posixpath, time -import tornado.httpserver -import tornado.ioloop -import tornado.web +#!/bin/sh +"""": # -*-python-*- +bup_python="$(dirname "$0")/bup-python" || exit $? +exec "$bup_python" "$0" ${1+"$@"} +""" +# end of bup preamble + +import mimetypes, os, posixpath, stat, sys, time, urllib, webbrowser + from bup import options, git, vfs -from bup.helpers import * +from bup.helpers import debug1, handle_ctrl_c, log, resource_path + +try: + import tornado.httpserver + import tornado.ioloop + import tornado.web +except ImportError: + log('error: cannot find the python "tornado" module; please install it\n') + sys.exit(1) + handle_ctrl_c() + +def _compute_breadcrumbs(path, show_hidden=False): + """Returns a list of breadcrumb objects for a path.""" + breadcrumbs = [] + breadcrumbs.append(('[root]', '/')) + path_parts = path.split('/')[1:-1] + full_path = '/' + for part in path_parts: + full_path += part + "/" + url_append = "" + if show_hidden: + url_append = '?hidden=1' + breadcrumbs.append((part, full_path+url_append)) + return breadcrumbs + + +def _contains_hidden_files(n): + """Return True if n contains files starting with a '.', False otherwise.""" + for sub in n: + name = sub.name + if len(name)>1 and name.startswith('.'): + return True + + return False + + +def _compute_dir_contents(n, path, show_hidden=False): + """Given a vfs node, returns an iterator for display info of all subs.""" + url_append = "" + if show_hidden: + url_append = "?hidden=1" + + if path != "/": + yield('..', '../' + url_append, '') + for sub in n: + display = sub.name + link = urllib.quote(sub.name) + + # link should be based on fully resolved type to avoid extra + # HTTP redirect. + if stat.S_ISDIR(sub.try_resolve().mode): + link += "/" + + if not show_hidden and len(display)>1 and display.startswith('.'): + continue + + size = None + if stat.S_ISDIR(sub.mode): + display += '/' + elif stat.S_ISLNK(sub.mode): + display += '@' + else: + size = sub.size() + size = (opt.human_readable and format_filesize(size)) or size + + yield (display, link + url_append, size) + + class BupRequestHandler(tornado.web.RequestHandler): def get(self, path): return self._process_request(path) def head(self, path): return self._process_request(path) - + + @tornado.web.asynchronous def _process_request(self, path): path = urllib.unquote(path) print 'Handling request for %s' % path @@ -39,61 +111,18 @@ class BupRequestHandler(tornado.web.RequestHandler): print 'Redirecting from %s to %s' % (path, path + '/') return self.redirect(path + '/', permanent=True) - self.set_header("Content-Type", "text/html") - - displaypath = cgi.escape(path) - self.write(""" - - - - Directory listing for %(displaypath)s - - - - - - - - - -""") - for sub in n: - displayname = linkname = sub.name - # Append / for directories or @ for symbolic links - size = str(sub.size()) - if stat.S_ISDIR(sub.mode): - displayname = sub.name + "/" - linkname = sub.name + "/" - size = ' ' - if stat.S_ISLNK(sub.mode): - displayname = sub.name + "@" - # Note: a link to a directory displays with @ and links with / - size = ' ' - self.write(""" - - - """ % (urllib.quote(linkname), cgi.escape(displayname), size)) - self.write(""" -
NameSize
%s%s
- -""") + try: + show_hidden = int(self.request.arguments.get('hidden', [0])[-1]) + except ValueError as e: + show_hidden = False + + self.render( + 'list-directory.html', + path=path, + breadcrumbs=_compute_breadcrumbs(path, show_hidden), + files_hidden=_contains_hidden_files(n), + hidden_shown=show_hidden, + dir_contents=_compute_dir_contents(n, path, show_hidden)) def _get_file(self, path, n): """Process a request on a file. @@ -107,12 +136,25 @@ class BupRequestHandler(tornado.web.RequestHandler): self.set_header("Content-Type", ctype) size = n.size() self.set_header("Content-Length", str(size)) + assert(len(n.hash) == 20) + self.set_header("Etag", n.hash.encode('hex')) if self.request.method != 'HEAD': + self.flush() f = n.open() - for blob in chunkyreader(f): - self.write(blob) - f.close() + it = chunkyreader(f) + def write_more(me): + try: + blob = it.next() + except StopIteration: + f.close() + self.finish() + return + self.request.connection.stream.write(blob, + callback=lambda: me(me)) + write_more(write_more) + else: + self.finish() def _guess_type(self, path): """Guess the type of a file. @@ -153,14 +195,16 @@ class BupRequestHandler(tornado.web.RequestHandler): optspec = """ bup web [[hostname]:port] -- +human-readable display human readable file sizes (i.e. 3.9K, 4.7M) +browser open the site in the default browser """ -o = options.Options('bup web', optspec) +o = options.Options(optspec) (opt, flags, extra) = o.parse(sys.argv[1:]) if len(extra) > 1: o.fatal("at most one argument expected") -address = ('', 8080) +address = ('127.0.0.1', 8080) if len(extra) > 0: addressl = extra[0].split(':', 1) addressl[1] = int(addressl[1]) @@ -169,10 +213,10 @@ if len(extra) > 0: git.check_repo_or_die() top = vfs.RefList(None) -(pwd,junk) = os.path.split(sys.argv[0]) - settings = dict( debug = 1, + template_path = resource_path('web'), + static_path = resource_path('web/static') ) # Disable buffering on stdout, for debug messages @@ -182,11 +226,18 @@ application = tornado.web.Application([ (r"(/.*)", BupRequestHandler), ], **settings) -if __name__ == "__main__": - http_server = tornado.httpserver.HTTPServer(application) - http_server.listen(address[1], address=address[0]) +http_server = tornado.httpserver.HTTPServer(application) +http_server.listen(address[1], address=address[0]) + +try: + sock = http_server._socket # tornado < 2.0 +except AttributeError as e: + sock = http_server._sockets.values()[0] - print "Listening on port %s" % http_server._socket.getsockname()[1] - loop = tornado.ioloop.IOLoop.instance() - loop.start() +print "Serving HTTP on %s:%d..." % sock.getsockname() +loop = tornado.ioloop.IOLoop.instance() +if opt.browser: + browser_addr = 'http://' + address[0] + ':' + str(address[1]) + loop.add_callback(lambda : webbrowser.open(browser_addr)) +loop.start()