diff options
Diffstat (limited to 'pyload/webui/app/pyload.py')
-rw-r--r-- | pyload/webui/app/pyload.py | 526 |
1 files changed, 526 insertions, 0 deletions
diff --git a/pyload/webui/app/pyload.py b/pyload/webui/app/pyload.py new file mode 100644 index 000000000..58acdf12c --- /dev/null +++ b/pyload/webui/app/pyload.py @@ -0,0 +1,526 @@ +# -*- coding: utf-8 -*- +# @author: RaNaN + +import datetime +import operator +import os +import sys +import time +import urllib + +import bottle + +from pyload.webui import PYLOAD, PYLOAD_DIR, THEME_DIR, THEME, SETUP, env + +from pyload.webui.app.utils import render_to_response, parse_permissions, parse_userdata, \ + login_required, get_permission, set_permission, permlist, toDict, set_session + +from pyload.utils.filters import relpath, unquotepath + +from pyload.utils import formatSize, fs_join, fs_encode, fs_decode + +# Helper + + +def pre_processor(): + s = request.environ.get('beaker.session') + user = parse_userdata(s) + perms = parse_permissions(s) + status = {} + captcha = False + update = False + plugins = False + if user['is_authenticated']: + status = PYLOAD.statusServer() + info = PYLOAD.getInfoByPlugin("UpdateManager") + captcha = PYLOAD.isCaptchaWaiting() + + # check if update check is available + if info: + if info['pyload'] == "True": + update = info['version'] + if info['plugins'] == "True": + plugins = True + + return {"user": user, + 'status': status, + 'captcha': captcha, + 'perms': perms, + 'url': request.url, + 'update': update, + 'plugins': plugins} + + +def base(messages): + return render_to_response('base.html', {'messages': messages}, [pre_processor]) + + +# Views +@bottle.error(403) +def error403(code): + return "The parameter you passed has the wrong format" + + +@bottle.error(404) +def error404(code): + return "Sorry, this page does not exist" + + +@bottle.error(500) +def error500(error): + traceback = error.traceback + if traceback: + print traceback + return base(["An Error occured, please enable debug mode to get more details.", error, + traceback.replace("\n", "<br>") if traceback else "No Traceback"]) + + +@bottle.route('/<theme>/<file:re:(.+/)?[^/]+\.min\.[^/]+>') +def server_min(theme, file): + filename = os.path.join(THEME_DIR, THEME, theme, file) + if not os.path.isfile(filename): + file = file.replace(".min.", ".") + if file.endswith(".js"): + return server_js(theme, file) + else: + return server_static(theme, file) + + +@bottle.route('/<theme>/<file:re:.+\.js>') +def server_js(theme, file): + response.headers['Content-Type'] = "text/javascript; charset=UTF-8" + + if "/render/" in file or ".render." in file or True: + response.headers['Expires'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", + time.gmtime(time.time() + 24 * 7 * 60 * 60)) + response.headers['Cache-control'] = "public" + + path = "/".join((theme, file)) + return env.get_template(path).render() + else: + return server_static(theme, file) + + +@bottle.route('/<theme>/<file:path>') +def server_static(theme, file): + response.headers['Expires'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", + time.gmtime(time.time() + 24 * 7 * 60 * 60)) + response.headers['Cache-control'] = "public" + + return bottle.static_file(file, root=join(THEME_DIR, THEME, theme)) + + +@bottle.route('/favicon.ico') +def favicon(): + return bottle.static_file("icon.ico", root=join(PYLOAD_DIR, "docs", "resources")) + + +@bottle.route('/login', method="GET") +def login(): + if not PYLOAD and SETUP: + bottle.redirect("/setup") + else: + return render_to_response("login.html", proc=[pre_processor]) + + +@bottle.route('/nopermission') +def nopermission(): + return base([_("You dont have permission to access this page.")]) + + +@bottle.route('/login', method='POST') +def login_post(): + user = request.forms.get("username") + password = request.forms.get("password") + + info = PYLOAD.checkAuth(user, password) + + if not info: + return render_to_response("login.html", {"errors": True}, [pre_processor]) + + set_session(request, info) + return bottle.redirect("/") + + +@bottle.route('/logout') +def logout(): + s = request.environ.get('beaker.session') + s.delete() + return render_to_response("logout.html", proc=[pre_processor]) + + +@bottle.route('/') +@bottle.route('/home') +@login_required("LIST") +def home(): + try: + res = [toDict(x) for x in PYLOAD.statusDownloads()] + except Exception: + s = request.environ.get('beaker.session') + s.delete() + return bottle.redirect("/login") + + for link in res: + if link['status'] == 12: + link['information'] = "%s kB @ %s kB/s" % (link['size'] - link['bleft'], link['speed']) + + return render_to_response("home.html", {"res": res}, [pre_processor]) + + +@bottle.route('/queue') +@login_required("LIST") +def queue(): + queue = PYLOAD.getQueue() + + queue.sort(key=operator.attrgetter("order")) + + return render_to_response('queue.html', {'content': queue, 'target': 1}, [pre_processor]) + + +@bottle.route('/collector') +@login_required('LIST') +def collector(): + queue = PYLOAD.getCollector() + + queue.sort(key=operator.attrgetter("order")) + + return render_to_response('queue.html', {'content': queue, 'target': 0}, [pre_processor]) + + +@bottle.route('/downloads') +@login_required('DOWNLOAD') +def downloads(): + root = PYLOAD.getConfigValue("general", "download_folder") + + if not os.path.isdir(root): + return base([_('Download directory not found.')]) + data = { + 'folder': [], + 'files': [] + } + + items = os.listdir(fs_encode(root)) + + for item in sorted([fs_decode(x) for x in items]): + if os.path.isdir(fs_join(root, item)): + folder = { + 'name': item, + 'path': item, + 'files': [] + } + files = os.listdir(fs_join(root, item)) + for file in sorted([fs_decode(x) for x in files]): + try: + if os.path.isfile(fs_join(root, item, file)): + folder['files'].append(file) + except Exception: + pass + + data['folder'].append(folder) + elif os.path.isfile(os.path.join(root, item)): + data['files'].append(item) + + return render_to_response('downloads.html', {'files': data}, [pre_processor]) + + +@bottle.route('/downloads/get/<path:path>') +@login_required("DOWNLOAD") +def get_download(path): + path = urllib.unquote(path).decode("utf8") + #@TODO some files can not be downloaded + + root = PYLOAD.getConfigValue("general", "download_folder") + + path = path.replace("..", "") + return bottle.static_file(fs_encode(path), fs_encode(root)) + + +@bottle.route('/settings') +@login_required('SETTINGS') +def config(): + conf = PYLOAD.getConfig() + plugin = PYLOAD.getPluginConfig() + conf_menu = [] + plugin_menu = [] + + for entry in sorted(conf.keys()): + conf_menu.append((entry, conf[entry].description)) + + last_name = None + for entry in sorted(plugin.keys()): + desc = plugin[entry].description + name, none, type = desc.partition("_") + + if type in PYLOAD.core.pluginManager.TYPES: + if name == last_name or len([a for a, b in plugin.iteritems() if b.description.startswith(name + "_")]) > 1: + desc = name + " (" + type.title() + ")" + else: + desc = name + last_name = name + plugin_menu.append((entry, desc)) + + accs = PYLOAD.getAccounts(False) + + for data in accs: + if data.trafficleft == -1: + data.trafficleft = _("unlimited") + elif not data.trafficleft: + data.trafficleft = _("not available") + else: + data.trafficleft = formatSize(data.trafficleft) + + if data.validuntil == -1: + data.validuntil = _("unlimited") + elif not data.validuntil: + data.validuntil = _("not available") + else: + t = time.localtime(data.validuntil) + data.validuntil = time.strftime("%d.%m.%Y - %H:%M:%S", t) + + try: + data.options['time'] = data.options['time'][0] + except Exception: + data.options['time'] = "0:00-0:00" + + if "limitDL" in data.options: + data.options['limitdl'] = data.options['limitDL'][0] + else: + data.options['limitdl'] = "0" + + return render_to_response('settings.html', + {'conf': {'plugin': plugin_menu, 'general': conf_menu, 'accs': accs}, + 'types': PYLOAD.getAccountTypes()}, + [pre_processor]) + + +@bottle.route('/filechooser') +@bottle.route('/pathchooser') +@bottle.route('/filechooser/<file:path>') +@bottle.route('/pathchooser/<path:path>') +@login_required('STATUS') +def os.path(file="", path=""): + type = "file" if file else "folder" + + path = os.path.normpath(unquotepath(path)) + + if os.path.isfile(path): + oldfile = path + path = os.path.dirname(path) + else: + oldfile = '' + + abs = False + + if os.path.isdir(path): + if os.path.isabs(path): + cwd = os.path.abspath(path) + abs = True + else: + cwd = os.relpath(path) + else: + cwd = os.getcwd() + + try: + cwd = cwd.encode("utf8") + except Exception: + pass + + cwd = os.path.normpath(os.path.abspath(cwd)) + parentdir = os.path.dirname(cwd) + if not abs: + if os.path.abspath(cwd) == "/": + cwd = os.relpath(cwd) + else: + cwd = os.relpath(cwd) + os.path.sep + parentdir = os.relpath(parentdir) + os.path.sep + + if os.path.abspath(cwd) == "/": + parentdir = "" + + try: + folders = os.listdir(cwd) + except Exception: + folders = [] + + files = [] + + for f in folders: + try: + f = f.decode(sys.getfilesystemencoding()) + data = {'name': f, 'fullpath': os.path.join(cwd, f)} + data['sort'] = data['fullpath'].lower() + data['modified'] = datetime.datetime.fromtimestamp(int(os.path.getmtime(os.path.join(cwd, f)))) + data['ext'] = os.path.splitext(f)[1] + except Exception: + continue + + data['type'] = 'dir' if os.path.isdir(os.path.join(cwd, f)) else 'file' + + if os.path.isfile(os.path.join(cwd, f)): + data['size'] = os.path.getsize(os.path.join(cwd, f)) + + power = 0 + while (data['size'] / 1024) > 0.3: + power += 1 + data['size'] /= 1024. + units = ('', 'K', 'M', 'G', 'T') + data['unit'] = units[power] + 'Byte' + else: + data['size'] = '' + + files.append(data) + + files = sorted(files, key=operator.itemgetter('type', 'sort')) + + return render_to_response('pathchooser.html', + {'cwd': cwd, 'files': files, 'parentdir': parentdir, 'type': type, 'oldfile': oldfile, + 'absolute': abs}, []) + + +@bottle.route('/logs') +@bottle.route('/logs', method='POST') +@bottle.route('/logs/<item>') +@bottle.route('/logs/<item>', method='POST') +@login_required('LOGS') +def logs(item=-1): + s = request.environ.get('beaker.session') + + perpage = s.get('perpage', 34) + reversed = s.get('reversed', False) + + warning = "" + conf = PYLOAD.getConfigValue("log", "file_log") + color_template = PYLOAD.getConfigValue("log", "color_template") if PYLOAD.getConfigValue("log", "color_console") else "" + if not conf: + warning = "Warning: File log is disabled, see settings page." + + perpage_p = ((20, 20), (34, 34), (40, 40), (100, 100), (0, 'all')) + fro = None + + if request.environ.get('REQUEST_METHOD', "GET") == "POST": + try: + fro = datetime.datetime.strptime(request.forms['from'], '%d.%m.%Y %H:%M:%S') + except Exception: + pass + try: + perpage = int(request.forms['perpage']) + s['perpage'] = perpage + + reversed = bool(request.forms.get('reversed', False)) + s['reversed'] = reversed + except Exception: + pass + + s.save() + + try: + item = int(item) + except Exception: + pass + + log = PYLOAD.getLog() + if not perpage: + item = 1 + + if item < 1 or type(item) is not int: + item = 1 if len(log) - perpage + 1 < 1 else len(log) - perpage + 1 + + if type(fro) is datetime.datetime: #: we will search for datetime.datetime + item = -1 + + data = [] + counter = 0 + perpagecheck = 0 + for l in log: + counter += 1 + + if counter >= item: + try: + date, time, level, message = l.decode("utf8", "ignore").split(" ", 3) + dtime = datetime.datetime.strptime(date + ' ' + time, '%Y-%m-%d %H:%M:%S') + except Exception: + dtime = None + date = '?' + time = ' ' + level = '?' + message = l + if item == -1 and dtime is not None and fro <= dtime: + item = counter #: found our datetime.datetime + if item >= 0: + data.append({'line': counter, 'date': date + " " + time, 'level': level, 'message': message}) + perpagecheck += 1 + if fro is None and dtime is not None: #: if fro not set set it to first showed line + fro = dtime + if perpagecheck >= perpage > 0: + break + + if fro is None: #: still not set, empty log? + fro = datetime.datetime.now() + if reversed: + data.reverse() + return render_to_response('logs.html', {'warning': warning, 'log': data, 'from': fro.strftime('%d.%m.%Y %H:%M:%S'), + 'reversed': reversed, 'perpage': perpage, 'perpage_p': sorted(perpage_p), + 'iprev': 1 if item - perpage < 1 else item - perpage, + 'inext': (item + perpage) if item + perpage < len(log) else item, + 'color_template': color_template.title()}, + [pre_processor]) + + +@bottle.route('/admin') +@bottle.route('/admin', method='POST') +@login_required("ADMIN") +def admin(): + # convert to dict + user = dict((name, toDict(y)) for name, y in PYLOAD.getAllUserData().iteritems()) + perms = permlist() + + for data in user.itervalues(): + data['perms'] = {} + get_permission(data['perms'], data['permission']) + data['perms']['admin'] = data['role'] is 0 + + s = request.environ.get('beaker.session') + if request.environ.get('REQUEST_METHOD', "GET") == "POST": + for name in user: + if request.POST.get("%s|admin" % name, False): + user[name]['role'] = 0 + user[name]['perms']['admin'] = True + elif name != s['name']: + user[name]['role'] = 1 + user[name]['perms']['admin'] = False + + # set all perms to false + for perm in perms: + user[name]['perms'][perm] = False + + for perm in request.POST.getall("%s|perms" % name): + user[name]['perms'][perm] = True + + user[name]['permission'] = set_permission(user[name]['perms']) + + PYLOAD.setUserPermission(name, user[name]['permission'], user[name]['role']) + + return render_to_response("admin.html", {"users": user, "permlist": perms}, [pre_processor]) + + +@bottle.route('/setup') +def setup(): + return base([_("Run pyload.py -s to access the setup.")]) + + +@bottle.route('/info') +def info(): + conf = PYLOAD.getConfigDict() + extra = os.uname() if hasattr(os, "uname") else tuple() + + data = {"python" : sys.version, + "os" : " ".join((os.name, sys.platform) + extra), + "version" : PYLOAD.getServerVersion(), + "folder" : os.path.abspath(PYLOAD_DIR), "config": os.path.abspath(""), + "download" : os.path.abspath(conf['general']['download_folder']['value']), + "freespace": formatSize(PYLOAD.freeSpace()), + "remote" : conf['remote']['port']['value'], + "webif" : conf['webui']['port']['value'], + "language" : conf['general']['language']['value']} + + return render_to_response("info.html", data, [pre_processor]) |