From 7a503302fbe6fcc23af94de2fe313298c3a6d95c Mon Sep 17 00:00:00 2001 From: RaNaN Date: Wed, 2 Feb 2011 21:46:15 +0100 Subject: removed django => now using bottle, new builtin threaded ssl server Dont forget to install jinja and beaker ! --- module/web/ServerThread.py | 282 +++++------- module/web/cnl_app.py | 151 +++++++ module/web/createsuperuser.py | 43 -- module/web/filters.py | 63 +++ module/web/json_app.py | 322 ++++++++++++++ module/web/locale/cs/LC_MESSAGES/django.mo | Bin 0 -> 7652 bytes module/web/manage.py | 13 - module/web/middlewares.py | 124 ++++++ module/web/pyload_app.py | 483 +++++++++++++++++++++ module/web/run_fcgi.py | 170 -------- module/web/run_server.py | 90 ---- module/web/settings.py | 165 ------- module/web/syncdb.py | 152 ------- module/web/syncdb_django11.py | 154 ------- module/web/templates/jinja/default/base.html | 317 ++++++++++++++ module/web/templates/jinja/default/captcha.html | 35 ++ module/web/templates/jinja/default/collector.html | 84 ++++ module/web/templates/jinja/default/downloads.html | 50 +++ .../web/templates/jinja/default/edit_package.html | 40 ++ module/web/templates/jinja/default/home.html | 241 ++++++++++ module/web/templates/jinja/default/login.html | 35 ++ module/web/templates/jinja/default/logout.html | 9 + module/web/templates/jinja/default/logs.html | 61 +++ module/web/templates/jinja/default/package_ui.js | 408 +++++++++++++++++ .../web/templates/jinja/default/pathchooser.html | 76 ++++ module/web/templates/jinja/default/queue.html | 85 ++++ module/web/templates/jinja/default/settings.html | 232 ++++++++++ module/web/templates/jinja/default/test.html | 12 + module/web/templates/jinja/default/window.html | 45 ++ module/web/urls.py | 26 -- module/web/utils.py | 86 ++++ module/web/webinterface.py | 151 +++++++ 32 files changed, 3208 insertions(+), 997 deletions(-) create mode 100644 module/web/cnl_app.py delete mode 100644 module/web/createsuperuser.py create mode 100644 module/web/filters.py create mode 100644 module/web/json_app.py create mode 100644 module/web/locale/cs/LC_MESSAGES/django.mo delete mode 100755 module/web/manage.py create mode 100644 module/web/middlewares.py create mode 100644 module/web/pyload_app.py delete mode 100644 module/web/run_fcgi.py delete mode 100755 module/web/run_server.py delete mode 100644 module/web/settings.py delete mode 100644 module/web/syncdb.py delete mode 100644 module/web/syncdb_django11.py create mode 100644 module/web/templates/jinja/default/base.html create mode 100644 module/web/templates/jinja/default/captcha.html create mode 100644 module/web/templates/jinja/default/collector.html create mode 100644 module/web/templates/jinja/default/downloads.html create mode 100644 module/web/templates/jinja/default/edit_package.html create mode 100644 module/web/templates/jinja/default/home.html create mode 100644 module/web/templates/jinja/default/login.html create mode 100644 module/web/templates/jinja/default/logout.html create mode 100644 module/web/templates/jinja/default/logs.html create mode 100644 module/web/templates/jinja/default/package_ui.js create mode 100644 module/web/templates/jinja/default/pathchooser.html create mode 100644 module/web/templates/jinja/default/queue.html create mode 100644 module/web/templates/jinja/default/settings.html create mode 100644 module/web/templates/jinja/default/test.html create mode 100644 module/web/templates/jinja/default/window.html delete mode 100644 module/web/urls.py create mode 100644 module/web/utils.py create mode 100644 module/web/webinterface.py (limited to 'module/web') diff --git a/module/web/ServerThread.py b/module/web/ServerThread.py index db5e3be05..7f47f80c6 100644 --- a/module/web/ServerThread.py +++ b/module/web/ServerThread.py @@ -1,17 +1,11 @@ #!/usr/bin/env python from __future__ import with_statement from os.path import exists -from os.path import join -from os.path import abspath -from os import makedirs -from subprocess import PIPE -from subprocess import Popen -from subprocess import call -from sys import version_info -from cStringIO import StringIO import threading -import sys import logging +import sqlite3 + +import webinterface core = None log = logging.getLogger("log") @@ -25,188 +19,108 @@ class WebServer(threading.Thread): self.running = True self.server = pycore.config['webinterface']['server'] self.https = pycore.config['webinterface']['https'] + self.cert = pycore.config["ssl"]["cert"] + self.key = pycore.config["ssl"]["key"] + self.host = pycore.config['webinterface']['host'] + self.port = pycore.config['webinterface']['port'] + self.setDaemon(True) - + def run(self): - sys.path.append(join(pypath, "module", "web")) - avail = ["builtin"] - host = self.core.config['webinterface']['host'] - port = self.core.config['webinterface']['port'] - serverpath = join(pypath, "module", "web") - path = join(abspath(""), "servers") - out = StringIO() - - if not exists("pyload.db"): - #print "########## IMPORTANT ###########" - #print "### Database for Webinterface does not exitst, it will not be available." - #print "### Please run: python %s syncdb" % join(self.pycore.path, "module", "web", "manage.py") - #print "### You have to add at least one User, to gain access to webinterface: python %s createsuperuser" % join(self.pycore.path, "module", "web", "manage.py") - #print "### Dont forget to restart pyLoad if you are done." + self.checkDB() + + if self.https: + if not exists(self.cert) or not exists(self.key): + log.warning(_("SSL certificates not found.")) + self.https = False + + if self.server in ("lighttpd", "nginx"): + log.warning(_("Sorry, we dropped support for starting %s directly within pyLoad") % self.server) + log.warning(_("You can use the threaded server which offers good performance and ssl,")) + log.warning(_("of course you can still use your existing %s with pyLoads fastcgi server") % self.server) + log.warning(_("sample configs are located in the module/web/servers directory")) + self.server = "builtin" + + if self.server == "fastcgi": + try: + import flup + except: + log.warning(_("Can't use %(server)s, python-flup is not installed!") % { + "server": self.server}) + self.server = "builtin" + + if self.server == "fastcgi": + self.start_fcgi() + elif self.server == "threaded": + self.start_threaded() + else: + self.start_builtin() + + + def checkDB(self): + conn = sqlite3.connect('web.db') + c = conn.cursor() + c.execute("SELECT * from users LIMIT 1") + empty = True + if c.fetchone(): + empty = False + + c.close() + conn.close() + + if not empty: + return True + + if exists("pyload.db"): + log.info(_("Converting old database to new web.db")) + conn = sqlite3.connect('pyload.db') + c = conn.cursor() + c.execute("SELECT username, password, email from auth_user WHERE is_superuser") + users = [] + for r in c: + pw = r[1].split("$") + users.append((r[0], pw[1] + pw[2], r[2])) + + c.close() + conn.close() + + conn = sqlite3.connect('web.db') + c = conn.cursor() + c.executemany("INSERT INTO users(name, password, email) VALUES (?,?,?)", users) + conn.commit() + c.close() + conn.close() + return True + + else: log.warning(_("Database for Webinterface does not exitst, it will not be available.")) log.warning(_("Please run: python pyLoadCore.py -s")) log.warning(_("Go through the setup and create a database and add an user to gain access.")) - return None - - try: - import flup - avail.append("fastcgi") - except: - pass - - try: - call(["lighttpd", "-v"], stdout=PIPE, stderr=PIPE) - import flup - avail.append("lighttpd") - - except: - pass - - try: - call(["nginx", "-v"], stdout=PIPE, stderr=PIPE) - import flup - avail.append("nginx") - except: - pass - - - try: - if self.https: - if exists(self.core.config["ssl"]["cert"]) and exists(self.core.config["ssl"]["key"]): - if not exists("ssl.pem"): - key = file(self.core.config["ssl"]["key"], "rb") - cert = file(self.core.config["ssl"]["cert"], "rb") - - pem = file("ssl.pem", "wb") - pem.writelines(key.readlines()) - pem.writelines(cert.readlines()) - - key.close() - cert.close() - pem.close() - - else: - log.warning(_("SSL certificates not found.")) - self.https = False - else: - pass - except: - self.https = False - - - if not self.server in avail: - log.warning(_("Can't use %(server)s, either python-flup or %(server)s is not installed!") % {"server": self.server}) - self.server = "builtin" + return False + + + def start_builtin(self): + + if self.https: + log.warning(_("The simple builtin server offers no SSL, please consider using threaded instead")) + self.core.log.info(_("Starting builtin webserver: %(host)s:%(port)d") % {"host": self.host, "port": self.port}) + webinterface.run_simple(host=self.host, port=self.port) - if self.server == "nginx": - - if not exists(join(path, "nginx")): - makedirs(join(path, "nginx")) - - config = file(join(serverpath, "servers", "nginx_default.conf"), "rb") - content = config.read() - config.close() - - content = content.replace("%(path)", join(path, "nginx")) - content = content.replace("%(host)", host) - content = content.replace("%(port)", str(port)) - content = content.replace("%(media)", join(serverpath, "media")) - content = content.replace("%(version)", ".".join(map(str, version_info[0:2]))) - - if self.https: - content = content.replace("%(ssl)", """ - ssl on; - ssl_certificate %s; - ssl_certificate_key %s; - """ % (abspath(self.core.config["ssl"]["cert"]), abspath(self.core.config["ssl"]["key"]) )) - else: - content = content.replace("%(ssl)", "") - - new_config = file(join(path, "nginx.conf"), "wb") - new_config.write(content) - new_config.close() - - command = ['nginx', '-c', join(path, "nginx.conf")] - self.p = Popen(command, stderr=PIPE, stdin=PIPE, stdout=Output(out)) - - log.info(_("Starting nginx Webserver: %(host)s:%(port)d") % {"host": host, "port": port}) - import run_fcgi - run_fcgi.handle("daemonize=false", "method=threaded", "host=127.0.0.1", "port=9295") - - - elif self.server == "lighttpd": - - if not exists(join(path, "lighttpd")): - makedirs(join(path, "lighttpd")) - - - config = file(join(serverpath, "servers", "lighttpd_default.conf"), "rb") - content = config.readlines() - config.close() - content = "".join(content) - - content = content.replace("%(path)", join("servers", "lighttpd")) - content = content.replace("%(host)", host) - content = content.replace("%(port)", str(port)) - content = content.replace("%(media)", join(serverpath, "media")) - content = content.replace("%(version)", ".".join(map(str, version_info[0:2]))) - - if self.https: - content = content.replace("%(ssl)", """ - ssl.engine = "enable" - ssl.pemfile = "%s" - ssl.ca-file = "%s" - """ % (abspath("ssl.pem") , abspath(self.core.config["ssl"]["cert"])) ) - else: - content = content.replace("%(ssl)", "") - new_config = file(join("servers", "lighttpd.conf"), "wb") - new_config.write(content) - new_config.close() - - command = ['lighttpd', '-D', '-f', join(path, "lighttpd.conf")] - self.p = Popen(command, stderr=PIPE, stdin=PIPE, stdout=Output(out)) - - log.info(_("Starting lighttpd Webserver: %(host)s:%(port)d") % {"host": host, "port": port}) - import run_fcgi - run_fcgi.handle("daemonize=false", "method=threaded", "host=127.0.0.1", "port=9295") - - - elif self.server == "fastcgi": - #run fastcgi on port - import run_fcgi - run_fcgi.handle("daemonize=false", "method=threaded", "host=127.0.0.1", "port=%s" % str(port)) + def start_threaded(self): + if self.https: + self.core.log.info(_("Starting threaded SSL webserver: %(host)s:%(port)d") % {"host": self.host, "port": self.port}) else: - self.core.log.info(_("Starting django builtin Webserver: %(host)s:%(port)d") % {"host": host, "port": port}) - import run_server - run_server.handle(host, port) + self.cert = "" + self.key = "" + self.core.log.info(_("Starting threaded webserver: %(host)s:%(port)d") % {"host": self.host, "port": self.port}) - def quit(self): + webinterface.run_threaded(host=self.host, port=self.port, cert=self.cert, key=self.key) + + def start_fcgi(self): - try: - if self.server == "lighttpd" or self.server == "nginx": - self.p.kill() - #self.p2.kill() - return True - - else: - #self.p.kill() - return True - except: - pass - - - self.running = False - -class Output: - def __init__(self, stream): - self.stream = stream - - def fileno(self): - return 1 - - def write(self, data): # Do nothing - return None - #self.stream.write(data) - #self.stream.flush() - def __getattr__(self, attr): - return getattr(self.stream, attr) + self.core.log.info(_("Starting fastcgi server: %(host)s:%(port)d") % {"host": self.host, "port": self.port}) + webinterface.run_fcgi(host=self.host, port=self.port) + + def quit(self): + self.running = False \ No newline at end of file diff --git a/module/web/cnl_app.py b/module/web/cnl_app.py new file mode 100644 index 000000000..058a298d3 --- /dev/null +++ b/module/web/cnl_app.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from os.path import join +import re +from urllib import unquote +from base64 import standard_b64decode +from binascii import unhexlify + +from bottle import route, request, HTTPError +from webinterface import PYLOAD, DL_ROOT, JS + +try: + from Crypto.Cipher import AES +except: + pass + + +def local_check(function): + def _view(*args, **kwargs): + if request.environ.get('REMOTE_ADDR', "0") in ('127.0.0.1', 'localhost') \ + or request.environ.get('HTTP_HOST','0') == '127.0.0.1:9666': + return function(*args, **kwargs) + else: + return HTTPError(403, "Forbidden") + + return _view + + +@route("/flash") +@route("/flash", method="POST") +@local_check +def flash(): + return "JDownloader" + +@route("/flash/add", method="POST") +@local_check +def add(request): + package = request.POST.get('referer', 'ClickAndLoad Package') + urls = filter(lambda x: x != "", request.POST['urls'].split("\n")) + + PYLOAD.add_package(package, urls, False) + + return "" + +@route("/flash/addcrypted", method="POST") +@local_check +def addcrypted(): + + package = request.forms.get('referer', 'ClickAndLoad Package') + dlc = request.forms['crypted'].replace(" ", "+") + + dlc_path = join(DL_ROOT, package.replace("/", "").replace("\\", "").replace(":", "") + ".dlc") + dlc_file = file(dlc_path, "wb") + dlc_file.write(dlc) + dlc_file.close() + + try: + PYLOAD.add_package(package, [dlc_path], False) + except: + return HTTPError() + else: + return "success" + +@route("/flash/addcrypted2", method="POST") +@local_check +def addcrypted2(): + + package = request.forms.get("source", "ClickAndLoad Package") + crypted = request.forms["crypted"] + jk = request.forms["jk"] + + crypted = standard_b64decode(unquote(crypted.replace(" ", "+"))) + if JS: + jk = "%s f()" % jk + jk = JS.eval(jk) + + else: + try: + jk = re.findall(r"return ('|\")(.+)('|\")", jk)[0][1] + except: + ## Test for some known js functions to decode + if jk.find("dec") > -1 and jk.find("org") > -1: + org = re.findall(r"var org = ('|\")([^\"']+)", jk)[0][1] + jk = list(org) + jk.reverse() + jk = "".join(jk) + else: + print "Could not decrypt key, please install py-spidermonkey or ossp-js" + + try: + Key = unhexlify(jk) + except: + print "Could not decrypt key, please install py-spidermonkey or ossp-js" + return "failed" + + IV = Key + + obj = AES.new(Key, AES.MODE_CBC, IV) + result = obj.decrypt(crypted).replace("\x00", "").replace("\r","").split("\n") + + result = filter(lambda x: x != "", result) + + try: + PYLOAD.add_package(package, result, False) + except: + return "failed can't add" + else: + return "success" + +@route("/flashgot", method="POST") +@local_check +def flashgot(request): + if request.environ['HTTP_REFERER'] != "http://localhost:9666/flashgot" and request.environ['HTTP_REFERER'] != "http://127.0.0.1:9666/flashgot": + return HTTPError() + + autostart = int(request.forms.get('autostart', 0)) + package = request.forms.get('package', "FlashGot") + urls = filter(lambda x: x != "", request.forms['urls'].split("\n")) + folder = request.forms.get('dir', None) + + PYLOAD.add_package(package, urls, autostart) + + return "" + +@route("/crossdomain.xml") +@local_check +def crossdomain(): + rep = "\n" + rep += "\n" + rep += "\n" + rep += "\n" + rep += "" + return rep + + +@route("/flash/checkSupportForUrl") +@local_check +def checksupport(): + + url = request.GET.get("url") + res = PYLOAD.checkURLs([url]) + supported = (not res[0][1] is None) + + return str(supported).lower() + +@route("/jdcheck.js") +@local_check +def jdcheck(): + rep = "jdownloader=true;\n" + rep += "var version='9.581;'" + return rep diff --git a/module/web/createsuperuser.py b/module/web/createsuperuser.py deleted file mode 100644 index 0ff1d15b8..000000000 --- a/module/web/createsuperuser.py +++ /dev/null @@ -1,43 +0,0 @@ -""" -Management utility to create superusers. -""" - -import os -import sys - -os.environ["DJANGO_SETTINGS_MODULE"] = 'settings' -sys.path.append(os.path.join(pypath, "module", "web")) - -import getpass -import re -from optparse import make_option -from django.contrib.auth.models import User -from django.core import exceptions -from django.core.management.base import BaseCommand, CommandError -from django.utils.translation import ugettext as _ - -RE_VALID_USERNAME = re.compile('[\w.@+-]+$') - - -def handle(username, email): - #username = options.get('username', None) - #email = options.get('email', None) - interactive = False - - # Do quick and dirty validation if --noinput - if not interactive: - if not username or not email: - raise CommandError("You must use --username and --email with --noinput.") - if not RE_VALID_USERNAME.match(username): - raise CommandError("Invalid username. Use only letters, digits, and underscores") - - password = '' - default_username = '' - - User.objects.create_superuser(username, email, password) - print "Superuser created successfully." - -if __name__ == "__main__": - username = sys.argv[1] - email = sys.argv[2] - handle(username, email) \ No newline at end of file diff --git a/module/web/filters.py b/module/web/filters.py new file mode 100644 index 000000000..1b10f7cb4 --- /dev/null +++ b/module/web/filters.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import os +from os.path import abspath, commonprefix, join +from time import strftime, mktime, gmtime + +quotechar = "::/" + +try: + from os.path import relpath +except: + from posixpath import curdir, sep, pardir + def relpath(path, start=curdir): + """Return a relative version of a path""" + if not path: + raise ValueError("no path specified") + start_list = abspath(start).split(sep) + path_list = abspath(path).split(sep) + # Work out how much of the filepath is shared by start and path. + i = len(commonprefix([start_list, path_list])) + rel_list = [pardir] * (len(start_list)-i) + path_list[i:] + if not rel_list: + return curdir + return join(*rel_list) + + +def quotepath(path): + try: + return path.replace("../", quotechar) + except AttributeError: + return path + except: + return "" + +def unquotepath(path): + try: + return path.replace(quotechar, "../") + except AttributeError: + return path + except: + return "" + +def path_make_absolute(path): + p = os.path.abspath(path) + if p[-1] == os.path.sep: + return p + else: + return p + os.path.sep + +def path_make_relative(path): + p = relpath(path) + if p[-1] == os.path.sep: + return p + else: + return p + os.path.sep + +def truncate(value, n): + if (n - len(value)) < 3: + return value[:n]+"..." + return value + +def date(date, format): + return date \ No newline at end of file diff --git a/module/web/json_app.py b/module/web/json_app.py new file mode 100644 index 000000000..2c95eaf5b --- /dev/null +++ b/module/web/json_app.py @@ -0,0 +1,322 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import base64 +from os.path import join +from traceback import print_exc + +from bottle import route, request, HTTPError, validate + +from webinterface import PYLOAD + +from utils import login_required + + +def format_time(seconds): + seconds = int(seconds) + + hours, seconds = divmod(seconds, 3600) + minutes, seconds = divmod(seconds, 60) + return "%.2i:%.2i:%.2i" % (hours, minutes, seconds) + +def get_sort_key(item): + return item["order"] + + +@route("/json/status") +@route("/json/status", method="POST") +@login_required('can_see_dl') +def status(): + try: + status = PYLOAD.status_server() + status['captcha'] = PYLOAD.is_captcha_waiting() + return status + except: + return HTTPError() + + +@route("/json/links") +@route("/json/links", method="POST") +@login_required('can_see_dl') +def links(): + try: + links = PYLOAD.status_downloads() + ids = [] + for link in links: + ids.append(link['id']) + + if link['status'] == 12: + link['info'] = "%s @ %s kb/s" % (link['format_eta'], round(link['speed'], 2)) + elif link['status'] == 5: + link['percent'] = 0 + link['size'] = 0 + link['kbleft'] = 0 + link['info'] = _("waiting %s") % link['format_wait'] + else: + link['info'] = "" + + data = {'links': links, 'ids': ids} + return data + except Exception, e: + return HTTPError() + +@route("/json/queue") +@login_required('can_see_dl') +def queue(): + try: + return PYLOAD.get_queue() + + except: + return HTTPError() + + +@route("/json/pause") +@login_required('can_change_satus') +def pause(): + try: + return PYLOAD.pause_server() + + except: + return HTTPError() + + +@route("/json/unpause") +@login_required('can_change_status') +def unpause(): + try: + return PYLOAD.unpause_server() + + except: + return HTTPError() + + +@route("/json/cancel") +@login_required('can_change_status') +def cancel(): + try: + return PYLOAD.stop_downloads() + except: + return HTTPError() + +@route("/json/packages") +@login_required('can_see_dl') +def packages(): + try: + data = PYLOAD.get_queue() + + for package in data: + package['links'] = [] + for file in PYLOAD.get_package_files(package['id']): + package['links'].append(PYLOAD.get_file_info(file)) + + return data + + except: + return HTTPError() + +@route("/json/package/:id") +@validate(id=int) +@login_required('pyload.can_see_dl') +def package(id): + try: + data = PYLOAD.get_package_data(id) + + for pyfile in data["links"].itervalues(): + if pyfile["status"] == 0: + pyfile["icon"] = "status_finished.png" + elif pyfile["status"] in (2, 3): + pyfile["icon"] = "status_queue.png" + elif pyfile["status"] in (9, 1): + pyfile["icon"] = "status_offline.png" + elif pyfile["status"] == 5: + pyfile["icon"] = "status_waiting.png" + elif pyfile["status"] == 8: + pyfile["icon"] = "status_failed.png" + elif pyfile["status"] in (11, 13): + pyfile["icon"] = "status_proc.png" + else: + pyfile["icon"] = "status_downloading.png" + + tmp = data["links"].values() + tmp.sort(key=get_sort_key) + data["links"] = tmp + return data + + except: + return HTTPError() + +@route("/json/package_order/:ids") +@login_required('can_add') +def package_order(ids): + try: + pid, pos = ids.split("|") + PYLOAD.order_package(int(pid), int(pos)) + return "success" + except: + return HTTPError() + +@route("/json/link/:id") +@validate(id=int) +@login_required('can_see_dl') +def link(id): + try: + data = PYLOAD.get_file_info(id) + return data + except: + return HTTPError() + +@route("/json/remove_link/:id") +@validate(id=int) +@login_required('can_delete') +def remove_link(id): + try: + PYLOAD.del_links([id]) + return "success" + except Exception, e: + return HTTPError() + +@route("/json/restart_link/:id") +@validate(id=int) +@login_required('can_add') +def restart_link(id): + try: + PYLOAD.restart_file(id) + return "success" + except Exception: + return HTTPError() + +@route("/json/abort_link/:id") +@validate(id=int) +@login_required('can_delete') +def abort_link(id): + try: + PYLOAD.stop_download("link", id) + return "success" + except: + return HTTPError() + +@route("/json/link_order/:ids") +@login_required('can_add') +def link_order(ids): + try: + pid, pos = ids.split("|") + PYLOAD.order_file(int(pid), int(pos)) + return "success" + except: + return HTTPError() + +@route("/json/add_package") +@route("/json/add_package", method="POST") +@login_required('can_add') +def add_package(): + name = request.forms['add_name'] + queue = int(request.forms['add_dest']) + links = request.forms['add_links'].split("\n") + pw = request.forms.get("add_password", "").strip("\n\r") + + try: + f = request.files['add_file'] + + if name is None or name == "": + name = f.name + + fpath = join(PYLOAD.get_conf_val("general", "download_folder"), "tmp_" + f.name) + destination = open(fpath, 'wb') + for chunk in f.chunks(): + destination.write(chunk) + destination.close() + links.insert(0, fpath) + except: + pass + + if name is None or name == "": + return HTTPError() + + links = map(lambda x: x.strip(), links) + links = filter(lambda x: x != "", links) + + pack = PYLOAD.add_package(name, links, queue) + if pw: + data = {"password": pw} + PYLOAD.set_package_data(pack, data) + + return "success" + + +@route("/json/remove_package/:id") +@validate(id=int) +@login_required('can_delete') +def remove_package(id): + try: + PYLOAD.del_packages([id]) + return "success" + except Exception, e: + return HTTPError() + +@route("/json/restart_package/:id") +@validate(id=int) +@login_required('can_add') +def restart_package(id): + try: + PYLOAD.restart_package(id) + return "success" + except Exception: + print_exc() + return HTTPError() + +@route("/json/move_package/:dest/:id") +@validate(dest=int, id=int) +@login_required('can_add') +def move_package(dest, id): + try: + PYLOAD.move_package(dest, id) + return "success" + except: + return HTTPError() + +@route("/json/edit_package", method="POST") +@login_required('can_add') +def edit_package(): + try: + id = int(request.forms.get("pack_id")) + data = {"name": request.forms.get("pack_name"), + "folder": request.forms.get("pack_folder"), + "priority": request.forms.get("pack_prio"), + "password": request.forms.get("pack_pws")} + + PYLOAD.set_package_data(id, data) + return "success" + + except: + return HTTPError() + +@route("/json/set_captcha") +@route("/json/set_captcha", method="POST") +@login_required('can_add') +def set_captcha(): + if request.environ.get('REQUEST_METHOD', "GET") == "POST": + try: + PYLOAD.set_captcha_result(request.forms["cap_id"], request.forms["cap_text"]) + except: + pass + + id, binary, typ = PYLOAD.get_captcha_task() + + if id: + binary = base64.standard_b64encode(str(binary)) + src = "data:image/%s;base64,%s" % (typ, binary) + + return {'captcha': True, 'src': src, 'id': id} + else: + return {'captcha': False} + + +@route("/json/delete_finished") +@login_required('pyload.can_delete') +def delete_finished(): + return {"del": PYLOAD.delete_finished()} + +@route("/json/restart_failed") +@login_required('pyload.can_delete') +def restart_failed(): + return PYLOAD.restart_failed() \ No newline at end of file diff --git a/module/web/locale/cs/LC_MESSAGES/django.mo b/module/web/locale/cs/LC_MESSAGES/django.mo new file mode 100644 index 000000000..1eebf6ce1 Binary files /dev/null and b/module/web/locale/cs/LC_MESSAGES/django.mo differ diff --git a/module/web/manage.py b/module/web/manage.py deleted file mode 100755 index 34b964ffc..000000000 --- a/module/web/manage.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -from django.core.management import execute_manager - -try: - import settings # Assumed to be in the same directory. -except ImportError: - import sys - sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__) - sys.exit(1) - -if __name__ == "__main__": - execute_manager(settings) \ No newline at end of file diff --git a/module/web/middlewares.py b/module/web/middlewares.py new file mode 100644 index 000000000..745d7e6b5 --- /dev/null +++ b/module/web/middlewares.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import gzip + +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + +class StripPathMiddleware(object): + def __init__(self, app): + self.app = app + + def __call__(self, e, h): + e['PATH_INFO'] = e['PATH_INFO'].rstrip('/') + return self.app(e, h) + + +class PrefixMiddleware(object): + def __init__(self, app, prefix="/pyload"): + self.app = app + self.prefix = prefix + + def __call__(self, e, h): + path = e["PATH_INFO"] + if path.startswith(self.prefix): + e['PATH_INFO'] = path.relace(self.prefix, "", 1) + return self.app(e, h) + +# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) +# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php + +# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) +# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php + +# WSGI middleware +# Gzip-encodes the response. + +class GZipMiddleWare(object): + + def __init__(self, application, compress_level=6): + self.application = application + self.compress_level = int(compress_level) + + def __call__(self, environ, start_response): + if 'gzip' not in environ.get('HTTP_ACCEPT_ENCODING', ''): + # nothing for us to do, so this middleware will + # be a no-op: + return self.application(environ, start_response) + response = GzipResponse(start_response, self.compress_level) + app_iter = self.application(environ, + response.gzip_start_response) + if app_iter is not None: + response.finish_response(app_iter) + + return response.write() + +def header_value(headers, key): + for header, value in headers: + if key.lower() == header.lower(): + return value + +def update_header(headers, key, value): + remove_header(headers, key) + headers.append((key, value)) + +def remove_header(headers, key): + for header, value in headers: + if key.lower() == header.lower(): + headers.remove((header, value)) + break + +class GzipResponse(object): + + def __init__(self, start_response, compress_level): + self.start_response = start_response + self.compress_level = compress_level + self.buffer = StringIO() + self.compressible = False + self.content_length = None + self.headers = () + + def gzip_start_response(self, status, headers, exc_info=None): + self.headers = headers + ct = header_value(headers,'content-type') + ce = header_value(headers,'content-encoding') + self.compressible = False + if ct and (ct.startswith('text/') or ct.startswith('application/')) \ + and 'zip' not in ct: + self.compressible = True + if ce: + self.compressible = False + if self.compressible: + headers.append(('content-encoding', 'gzip')) + remove_header(headers, 'content-length') + self.headers = headers + self.status = status + return self.buffer.write + + def write(self): + out = self.buffer + out.seek(0) + s = out.getvalue() + out.close() + return [s] + + def finish_response(self, app_iter): + if self.compressible: + output = gzip.GzipFile(mode='wb', compresslevel=self.compress_level, + fileobj=self.buffer) + else: + output = self.buffer + try: + for s in app_iter: + output.write(s) + if self.compressible: + output.close() + finally: + if hasattr(app_iter, 'close'): + app_iter.close() + content_length = self.buffer.tell() + update_header(self.headers, "Content-Length" , str(content_length)) + self.start_response(self.status, self.headers) \ No newline at end of file diff --git a/module/web/pyload_app.py b/module/web/pyload_app.py new file mode 100644 index 000000000..ab0cbfb00 --- /dev/null +++ b/module/web/pyload_app.py @@ -0,0 +1,483 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + + @author: RaNaN +""" +from copy import deepcopy +import datetime +from datetime import datetime +from itertools import chain +from operator import itemgetter +import os + +import sqlite3 +import time +from os import listdir +from os import stat +from os.path import isdir +from os.path import isfile +from os.path import join +from sys import getfilesystemencoding +from hashlib import sha1 +from urllib import unquote + +from bottle import route, static_file, request, response, redirect, HTTPError + +from webinterface import PYLOAD, PROJECT_DIR + +from utils import render_to_response, parse_permissions, parse_userdata, formatSize, login_required +from filters import relpath, quotepath, unquotepath + +# Helper + +def pre_processor(): + s = request.environ.get('beaker.session') + user = parse_userdata(s) + perms = parse_permissions(s) + return {"user": user, + 'status': PYLOAD.status_server(), + 'captcha': PYLOAD.is_captcha_waiting(), + 'perms': perms} + + +def get_sort_key(item): + return item[1]["order"] + + +def base(messages): + return render_to_response('base.html', {'messages': messages}, [pre_processor]) + + +## Views + +@route('/media/:path#.+#') +def server_static(path): + response.header['Expires'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(time.time() + 60 * 60 * 24 * 7)) + response.header['Cache-control'] = "public" + return static_file(path, root=join(PROJECT_DIR, "media")) + +@route('/favicon.ico') +def favicon(): + return static_file("favicon.ico", root=join(PROJECT_DIR, "media", "img")) + +@route('/login', method="GET") +def login(): + return render_to_response("login.html", proc=[pre_processor]) + +@route("/login", method="POST") +def login_post(): + user = request.forms.get("username") + password = request.forms.get("password") + + conn = sqlite3.connect('web.db') + c = conn.cursor() + c.execute('SELECT name, password, role, permission,template FROM "users" WHERE name=?', (user,)) + r = c.fetchone() + c.close() + conn.commit() + conn.close() + + if not r: + return render_to_response("login.html", {"errors": True}, [pre_processor]) + + salt = r[1][:5] + pw = r[1][5:] + + hash = sha1(salt + password) + if hash.hexdigest() == pw: + s = request.environ.get('beaker.session') + s["authenticated"] = True + s["name"] = r[0] + s["role"] = r[2] + s["perms"] = r[3] + s["template"] = r[4] + s.save() + + return redirect("/") + + + else: + return render_to_response("login.html", {"errors": True}, [pre_processor]) + +@route("/logout") +def logout(): + s = request.environ.get('beaker.session') + s.delete() + return render_to_response("logout.html", proc=[pre_processor]) + + +@route("/") +@route("/home") +@login_required("can_see_dl") +def home(): + res = PYLOAD.status_downloads() + + for link in res: + if link["status"] == 12: + link["information"] = "%s kB @ %s kB/s" % (link["size"] - link["kbleft"], link["speed"]) + + return render_to_response("home.html", {"res": res}, [pre_processor]) + + +@route("/queue") +@login_required("can_see_dl") +def queue(): + queue = PYLOAD.get_queue_info() + + data = zip(queue.keys(), queue.values()) + data.sort(key=get_sort_key) + + return render_to_response('queue.html', {'content': data}, [pre_processor]) + +@route("/collector") +@login_required('can_see_dl') +def collector(): + queue = PYLOAD.get_collector_info() + + data = zip(queue.keys(), queue.values()) + data.sort(key=get_sort_key) + + return render_to_response('collector.html', {'content': data}, [pre_processor]) + +@route("/downloads") +@login_required('can_download') +def downloads(): + root = PYLOAD.get_conf_val("general", "download_folder") + + if not isdir(root): + return base([_('Download directory not found.')]) + data = { + 'folder': [], + 'files': [] + } + + for item in sorted(listdir(root)): + if isdir(join(root, item)): + folder = { + 'name': item, + 'path': item, + 'files': [] + } + for file in sorted(listdir(join(root, item))): + try: + if isfile(join(root, item, file)): + folder['files'].append(file) + except: + pass + + data['folder'].append(folder) + elif isfile(join(root, item)): + data['files'].append(item) + + return render_to_response('downloads.html', {'files': data}, [pre_processor]) + +@route("/downloads/get/:path#.+#") +@login_required("can_download") +def get_download(path): + path = unquote(path) + #@TODO some files can not be downloaded + + root = PYLOAD.get_conf_val("general", "download_folder") + + path = path.replace("..", "") + try: + return static_file(path, root) + + except Exception, e: + print e + return HTTPError(404, "File not Found.") + +@route("/settings") +@route("/settings", method="POST") +@login_required('can_change_status') +def config(): + conf = PYLOAD.get_config() + plugin = PYLOAD.get_plugin_config() + accs = PYLOAD.get_accounts() + messages = [] + + for section in chain(conf.itervalues(), plugin.itervalues()): + for key, option in section.iteritems(): + if key == "desc": continue + + if ";" in option["type"]: + option["list"] = option["type"].split(";") + + if request.environ.get('REQUEST_METHOD', "GET") == "POST": + errors = [] + + for key, value in request.POST.iteritems(): + if not "|" in key: continue + sec, skey, okey = key.split("|")[:] + + if sec == "General": + if conf.has_key(skey): + if conf[skey].has_key(okey): + try: + if str(conf[skey][okey]['value']) != value: + PYLOAD.set_conf_val(skey, okey, value) + except Exception, e: + errors.append("%s | %s : %s" % (skey, okey, e)) + else: + continue + else: + continue + + elif sec == "Plugin": + if plugin.has_key(skey): + if plugin[skey].has_key(okey): + try: + if str(plugin[skey][okey]['value']) != value: + PYLOAD.set_conf_val(skey, okey, value, "plugin") + except Exception, e: + errors.append("%s | %s : %s" % (skey, okey, e)) + else: + continue + else: + continue + elif sec == "Accounts": + if ";" in okey: + action, name = okey.split(";") + if action == "delete": + PYLOAD.remove_account(skey, name) + + if okey == "newacc" and value: + # add account + + pw = request.POST.get("Accounts|%s|newpw" % skey) + PYLOAD.update_account(skey, value, pw) + + for pluginname, accdata in accs.iteritems(): + for data in accdata: + newpw = request.POST.get("Accounts|%s|password;%s" % (pluginname, data["login"]), "").strip() + time = request.POST.get("Accounts|%s|time;%s" % (pluginname, data["login"]), "").strip() + + if newpw or (time and (not data["options"].has_key("time") or [time] != data["options"]["time"])): + PYLOAD.update_account(pluginname, data["login"], newpw, {"time": [time]}) + + if errors: + messages.append(_("Error occured when setting the following options:")) + messages.append("") + messages += errors + else: + messages.append(_("All options were set correctly.")) + + accs = deepcopy(PYLOAD.get_accounts(False, False)) + for accounts in accs.itervalues(): + for data in accounts: + 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", t) + + if data["options"].has_key("time"): + try: + data["time"] = data["options"]["time"][0] + except: + data["time"] = "invalid" + + + return render_to_response('settings.html', + {'conf': {'Plugin': plugin, 'General': conf, 'Accounts': accs}, 'errors': messages}, + [pre_processor]) + +@route("/package_ui.js") +@login_required('can_see_dl') +def package_ui(): + response.header['Expires'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(time.time() + 60 * 60 * 24 * 7)) + response.header['Cache-control'] = "public" + return render_to_response('package_ui.js') + + +@route("/filechooser") +@route("/pathchooser") +@route("/filechooser/:file#.+#") +@route("/pathchooser/:path#.+#") +@login_required('can_change_status') +def path(file="", path=""): + if file: + type = "file" + else: + type = "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 = relpath(path) + else: + cwd = os.getcwd() + + try: + cwd = cwd.encode("utf8") + except: + pass + + cwd = os.path.normpath(os.path.abspath(cwd)) + parentdir = os.path.dirname(cwd) + if not abs: + if os.path.abspath(cwd) == "/": + cwd = relpath(cwd) + else: + cwd = relpath(cwd) + os.path.sep + parentdir = relpath(parentdir) + os.path.sep + + if os.path.abspath(cwd) == "/": + parentdir = "" + + try: + folders = os.listdir(cwd) + except: + folders = [] + + files = [] + + for f in folders: + try: + f = f.decode(getfilesystemencoding()) + data = {} + data['name'] = f + data['fullpath'] = join(cwd, f) + data['sort'] = data['fullpath'].lower() + data['modified'] = datetime.fromtimestamp(int(os.path.getmtime(join(cwd, f)))) + data['ext'] = os.path.splitext(f)[1] + except: + continue + + if os.path.isdir(join(cwd, f)): + data['type'] = 'dir' + else: + data['type'] = 'file' + + if os.path.isfile(join(cwd, f)): + data['size'] = os.path.getsize(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=itemgetter('type', 'sort')) + + return render_to_response('pathchooser.html', {'cwd': cwd, 'files': files, 'parentdir': parentdir, 'type': type, 'oldfile': oldfile, 'absolute': abs}, []) + +@route("/logs") +@route("/logs/:item") +@route("/logs/:item", method="POST") +@login_required('can_see_logs') +def logs(item=-1): + s = request.environ.get('beaker.session') + + perpage = s.get('perpage', 34) + reversed = s.get('reversed', False) + + warning = "" + conf = PYLOAD.get_config() + if not conf['log']['file_log']['value']: + 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.strptime(request.forms['from'], '%d.%m.%Y %H:%M:%S') + except: + pass + try: + perpage = int(request.forms['perpage']) + s['perpage'] = perpage + + reversed = bool(request.forms.get('reversed', False)) + s['reversed'] = reversed + except: + pass + + s.save() + + try: + item = int(item) + except: + pass + + log = PYLOAD.get_log() + if not perpage: + item = 0 + + 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: # we will search for datetime + item = -1 + + data = [] + counter = 0 + perpagecheck = 0 + for l in log: + counter += 1 + + if counter >= item: + try: + date,time,level,message = l.split(" ", 3) + dtime = datetime.strptime(date+' '+time, '%d.%m.%Y %H:%M:%S') + except: + dtime = None + date = '?' + time = ' ' + level = '?' + message = l + if item == -1 and dtime is not None and fro <= dtime: + item = counter #found our 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.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}, [pre_processor]) \ No newline at end of file diff --git a/module/web/run_fcgi.py b/module/web/run_fcgi.py deleted file mode 100644 index 8091de5ea..000000000 --- a/module/web/run_fcgi.py +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import os -import sys - -from flup.server.fcgi_base import BaseFCGIServer -from flup.server.fcgi_base import FCGI_RESPONDER -from flup.server.threadedserver import ThreadedServer - - -os.environ["DJANGO_SETTINGS_MODULE"] = 'settings' - -def handle(*args, **options): - from django.conf import settings - from django.utils import translation - # Activate the current language, because it won't get activated later. - try: - translation.activate(settings.LANGUAGE_CODE) - except AttributeError: - pass - #from django.core.servers.fastcgi import runfastcgi - runfastcgi(args) - - -FASTCGI_OPTIONS = { - 'protocol': 'fcgi', - 'host': None, - 'port': None, - 'socket': None, - 'method': 'fork', - 'daemonize': None, - 'workdir': '/', - 'pidfile': None, - 'maxspare': 5, - 'minspare': 2, - 'maxchildren': 50, - 'maxrequests': 0, - 'debug': None, - 'outlog': None, - 'errlog': None, - 'umask': None, -} - - -def runfastcgi(argset=[], **kwargs): - options = FASTCGI_OPTIONS.copy() - options.update(kwargs) - for x in argset: - if "=" in x: - k, v = x.split('=', 1) - else: - k, v = x, True - options[k.lower()] = v - - try: - import flup - except ImportError, e: - print >> sys.stderr, "ERROR: %s" % e - print >> sys.stderr, " Unable to load the flup package. In order to run django" - print >> sys.stderr, " as a FastCGI application, you will need to get flup from" - print >> sys.stderr, " http://www.saddi.com/software/flup/ If you've already" - print >> sys.stderr, " installed flup, then make sure you have it in your PYTHONPATH." - return False - - flup_module = 'server.' + options['protocol'] - - if options['method'] in ('prefork', 'fork'): - wsgi_opts = { - 'maxSpare': int(options["maxspare"]), - 'minSpare': int(options["minspare"]), - 'maxChildren': int(options["maxchildren"]), - 'maxRequests': int(options["maxrequests"]), - } - flup_module += '_fork' - elif options['method'] in ('thread', 'threaded'): - wsgi_opts = { - 'maxSpare': int(options["maxspare"]), - 'minSpare': int(options["minspare"]), - 'maxThreads': int(options["maxchildren"]), - } - else: - print "ERROR: Implementation must be one of prefork or thread." - - wsgi_opts['debug'] = options['debug'] is not None - - #try: - # module = importlib.import_module('.%s' % flup_module, 'flup') - # WSGIServer = module.WSGIServer - #except: - # print "Can't import flup." + flup_module - # return False - - # Prep up and go - from django.core.handlers.wsgi import WSGIHandler - - if options["host"] and options["port"] and not options["socket"]: - wsgi_opts['bindAddress'] = (options["host"], int(options["port"])) - elif options["socket"] and not options["host"] and not options["port"]: - wsgi_opts['bindAddress'] = options["socket"] - elif not options["socket"] and not options["host"] and not options["port"]: - wsgi_opts['bindAddress'] = None - else: - return fastcgi_help("Invalid combination of host, port, socket.") - - daemon_kwargs = {} - if options['outlog']: - daemon_kwargs['out_log'] = options['outlog'] - if options['errlog']: - daemon_kwargs['err_log'] = options['errlog'] - if options['umask']: - daemon_kwargs['umask'] = int(options['umask']) - - ownWSGIServer(WSGIHandler(), **wsgi_opts).run() - -class ownThreadedServer(ThreadedServer): - def _installSignalHandlers(self): - return - - def _restoreSignalHandlers(self): - return - - -class ownWSGIServer(BaseFCGIServer, ownThreadedServer): - - def __init__(self, application, environ=None, - multithreaded=True, multiprocess=False, - bindAddress=None, umask=None, multiplexed=False, - debug=True, roles=(FCGI_RESPONDER,), forceCGI=False, **kw): - BaseFCGIServer.__init__(self, application, - environ=environ, - multithreaded=multithreaded, - multiprocess=multiprocess, - bindAddress=bindAddress, - umask=umask, - multiplexed=multiplexed, - debug=debug, - roles=roles, - forceCGI=forceCGI) - for key in ('jobClass', 'jobArgs'): - if kw.has_key(key): - del kw[key] - ownThreadedServer.__init__(self, jobClass=self._connectionClass, - jobArgs=(self,), **kw) - - def _isClientAllowed(self, addr): - return self._web_server_addrs is None or \ - (len(addr) == 2 and addr[0] in self._web_server_addrs) - - def run(self): - """ - The main loop. Exits on SIGHUP, SIGINT, SIGTERM. Returns True if - SIGHUP was received, False otherwise. - """ - self._web_server_addrs = os.environ.get('FCGI_WEB_SERVER_ADDRS') - if self._web_server_addrs is not None: - self._web_server_addrs = map(lambda x: x.strip(), - self._web_server_addrs.split(',')) - - sock = self._setupSocket() - - ret = ownThreadedServer.run(self, sock) - - self._cleanupSocket(sock) - - return ret - -if __name__ == "__main__": - handle(*sys.argv[1:]) - diff --git a/module/web/run_server.py b/module/web/run_server.py deleted file mode 100755 index 2dc97353a..000000000 --- a/module/web/run_server.py +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -import os -import sys -import django -from django.core.servers.basehttp import AdminMediaHandler, WSGIServerException, WSGIServer, WSGIRequestHandler -from django.core.handlers.wsgi import WSGIHandler - -os.environ["DJANGO_SETTINGS_MODULE"] = 'settings' - -class Output: - def __init__(self, stream): - self.stream = stream - def write(self, data): # Do nothing - return None - #self.stream.write(data) - #self.stream.flush() - def __getattr__(self, attr): - return getattr(self.stream, attr) - -#sys.stderr = Output(sys.stderr) -#sys.stdout = Output(sys.stdout) - -def handle(* args): - try: - if len(args) == 1: - try: - addr, port = args[0].split(":") - except: - addr = "127.0.0.1" - port = args[0] - else: - addr = args[0] - port = args[1] - except: - addr = '127.0.0.1' - port = '8000' - - #print addr, port - - admin_media_path = '' - shutdown_message = '' - quit_command = (sys.platform == 'win32') and 'CTRL-BREAK' or 'CONTROL-C' - - from django.conf import settings - from django.utils import translation - - #print "Django version %s, using settings %r" % (django.get_version(), settings.SETTINGS_MODULE) - #print "Development server is running at http://%s:%s/" % (addr, port) - #print "Quit the server with %s." % quit_command - - translation.activate(settings.LANGUAGE_CODE) - - try: - handler = AdminMediaHandler(WSGIHandler(), admin_media_path) - run(addr, int(port), handler) - #@TODO catch unimportant Broken Pipe Errors - - except WSGIServerException, e: - # Use helpful error messages instead of ugly tracebacks. - ERRORS = { - 13: "You don't have permission to access that port.", - 98: "That port is already in use.", - 99: "That IP address can't be assigned-to.", - } - try: - error_text = ERRORS[e.args[0].args[0]] - except (AttributeError, KeyError): - error_text = str(e) - sys.stderr.write(("Error: %s" % error_text) + '\n') - # Need to use an OS exit because sys.exit doesn't work in a thread - #os._exit(1) - except KeyboardInterrupt: - if shutdown_message: - print shutdown_message - sys.exit(0) - -class ownRequestHandler(WSGIRequestHandler): - def log_message(self, format, *args): - return - - -def run(addr, port, wsgi_handler): - server_address = (addr, port) - httpd = WSGIServer(server_address, ownRequestHandler) - httpd.set_app(wsgi_handler) - httpd.serve_forever() - -if __name__ == "__main__": - handle(*sys.argv[1:]) diff --git a/module/web/settings.py b/module/web/settings.py deleted file mode 100644 index 5a836e11c..000000000 --- a/module/web/settings.py +++ /dev/null @@ -1,165 +0,0 @@ -# -*- coding: utf-8 -*- -# Django settings for pyload project. - -DEBUG = True -TEMPLATE_DEBUG = DEBUG - -import os -import sys -import django - -SERVER_VERSION = "0.4.4" - -PROJECT_DIR = os.path.dirname(__file__) - -#chdir(dirname(abspath(__file__)) + sep) - -PYLOAD_DIR = os.path.join(PROJECT_DIR,"..","..") - -sys.path.append(PYLOAD_DIR) - - -sys.path.append(os.path.join(PYLOAD_DIR, "module")) - -import InitHomeDir -sys.path.append(pypath) - -config = None -#os.chdir(PROJECT_DIR) # UNCOMMENT FOR LOCALE GENERATION - - -try: - import module.web.ServerThread - if not module.web.ServerThread.core: - raise Exception - PYLOAD = module.web.ServerThread.core.server_methods - config = module.web.ServerThread.core.config -except: - import xmlrpclib - ssl = "" - - from module.ConfigParser import ConfigParser - config = ConfigParser() - - if config.get("ssl", "activated"): - ssl = "s" - - server_url = "http%s://%s:%s@%s:%s/" % ( - ssl, - config.username, - config.password, - config.get("remote", "listenaddr"), - config.get("remote", "port") - ) - - PYLOAD = xmlrpclib.ServerProxy(server_url, allow_none=True) - -DEBUG = TEMPLATE_DEBUG = config.get("general","debug_mode") - -from module.JsEngine import JsEngine -JS = JsEngine() - -TEMPLATE = config.get('webinterface','template') -DL_ROOT = os.path.join(PYLOAD_DIR, config.get('general','download_folder')) -LOG_ROOT = os.path.join(PYLOAD_DIR, config.get('log','log_folder')) - -ADMINS = ( - # ('Your Name', 'your_email@domain.com'), - ) - -MANAGERS = ADMINS - -DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. -#DATABASE_NAME = os.path.join(PROJECT_DIR, 'pyload.db') # Or path to database file if using sqlite3. -DATABASE_NAME = 'pyload.db' # Or path to database file if using sqlite3. -DATABASE_USER = '' # Not used with sqlite3. -DATABASE_PASSWORD = '' # Not used with sqlite3. -DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3. -DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3. - -# Local time zone for this installation. Choices can be found here: -# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name -# although not all choices may be available on all operating systems. -# If running in a Windows environment this must be set to the same as your -# system time zone. -if (django.VERSION[0] > 1 or django.VERSION[1] > 1) and os.name != "nt": - zone = None -else: - zone = 'Europe' -TIME_ZONE = zone - -# Language code for this installation. All choices can be found here: -# http://www.i18nguy.com/unicode/language-identifiers.html -LANGUAGE_CODE = config.get("general","language") - -SITE_ID = 1 - -# If you set this to False, Django will make some optimizations so as not -# to load the internationalization machinery. -USE_I18N = True - -# Absolute path to the directory that holds media. -# Example: "/home/media/media.lawrence.com/" -MEDIA_ROOT = os.path.join(PROJECT_DIR, "media/") - - -# URL that handles the media served from MEDIA_ROOT. Make sure to use a -# trailing slash if there is a path component (optional in other cases). -# Examples: "http://media.lawrence.com", "http://example.com/media/" - -#MEDIA_URL = 'http://localhost:8000/media' -MEDIA_URL = '/media/' + config.get('webinterface','template') + '/' -#MEDIA_URL = os.path.join(PROJECT_DIR, "media/") - -LOGIN_REDIRECT_URL = "/" - -# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a -# trailing slash. -# Examples: "http://foo.com/media/", "/media/". -ADMIN_MEDIA_PREFIX = '/admin/media/' - -# Make this unique, and don't share it with anybody. -SECRET_KEY = '+u%%1t&c7!e$0$*gu%w2$@to)h0!&x-r*9e+-=wa4*zxat%x^t' - -# List of callables that know how to import templates from various sources. -TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.load_template_source', - 'django.template.loaders.app_directories.load_template_source', - # 'django.template.loaders.eggs.load_template_source', - ) - - -MIDDLEWARE_CLASSES = ( - 'django.middleware.gzip.GZipMiddleware', - 'django.middleware.http.ConditionalGetMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.locale.LocaleMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - #'django.contrib.csrf.middleware.CsrfViewMiddleware', - 'django.contrib.csrf.middleware.CsrfResponseMiddleware' - ) - -ROOT_URLCONF = 'urls' - -TEMPLATE_DIRS = ( - # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". - # Always use forward slashes, even on Windows. - # Don't forget to use absolute paths, not relative paths. - os.path.join(PROJECT_DIR, "templates"), - ) - -INSTALLED_APPS = ( - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - #'django.contrib.sites', - 'django.contrib.admin', - 'pyload', - 'ajax', - 'cnl', - ) - - -AUTH_PROFILE_MODULE = 'pyload.UserProfile' -LOGIN_URL = '/login/' diff --git a/module/web/syncdb.py b/module/web/syncdb.py deleted file mode 100644 index 669f22681..000000000 --- a/module/web/syncdb.py +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import os -import sys - -os.environ["DJANGO_SETTINGS_MODULE"] = 'settings' -sys.path.append(os.path.join(pypath, "module", "web")) - -from django.conf import settings -from django.core.management.base import NoArgsCommand -from django.core.management.color import no_style -from django.core.management.sql import custom_sql_for_model, emit_post_sync_signal -from django.db import connections, router, transaction, models, DEFAULT_DB_ALIAS -from django.utils.datastructures import SortedDict -from django.utils.importlib import import_module - - - -def handle_noargs(**options): - - verbosity = int(options.get('verbosity', 1)) - interactive = False - show_traceback = options.get('traceback', False) - - style = no_style() - - # Import the 'management' module within each installed app, to register - # dispatcher events. - for app_name in settings.INSTALLED_APPS: - try: - import_module('.management', app_name) - except ImportError, exc: - # This is slightly hackish. We want to ignore ImportErrors - # if the "management" module itself is missing -- but we don't - # want to ignore the exception if the management module exists - # but raises an ImportError for some reason. The only way we - # can do this is to check the text of the exception. Note that - # we're a bit broad in how we check the text, because different - # Python implementations may not use the same text. - # CPython uses the text "No module named management" - # PyPy uses "No module named myproject.myapp.management" - msg = exc.args[0] - if not msg.startswith('No module named') or 'management' not in msg: - raise - - db = options.get('database', DEFAULT_DB_ALIAS) - connection = connections[db] - cursor = connection.cursor() - - # Get a list of already installed *models* so that references work right. - tables = connection.introspection.table_names() - seen_models = connection.introspection.installed_models(tables) - created_models = set() - pending_references = {} - - # Build the manifest of apps and models that are to be synchronized - all_models = [ - (app.__name__.split('.')[-2], - [m for m in models.get_models(app, include_auto_created=True) - if router.allow_syncdb(db, m)]) - for app in models.get_apps() - ] - def model_installed(model): - opts = model._meta - converter = connection.introspection.table_name_converter - return not ((converter(opts.db_table) in tables) or - (opts.auto_created and converter(opts.auto_created._meta.db_table) in tables)) - - manifest = SortedDict( - (app_name, filter(model_installed, model_list)) - for app_name, model_list in all_models - ) - - # Create the tables for each model - for app_name, model_list in manifest.items(): - for model in model_list: - # Create the model's database table, if it doesn't already exist. - if verbosity >= 2: - print "Processing %s.%s model" % (app_name, model._meta.object_name) - sql, references = connection.creation.sql_create_model(model, style, seen_models) - seen_models.add(model) - created_models.add(model) - for refto, refs in references.items(): - pending_references.setdefault(refto, []).extend(refs) - if refto in seen_models: - sql.extend(connection.creation.sql_for_pending_references(refto, style, pending_references)) - sql.extend(connection.creation.sql_for_pending_references(model, style, pending_references)) - if verbosity >= 1 and sql: - print "Creating table %s" % model._meta.db_table - for statement in sql: - cursor.execute(statement) - tables.append(connection.introspection.table_name_converter(model._meta.db_table)) - - - transaction.commit_unless_managed(using=db) - - # Send the post_syncdb signal, so individual apps can do whatever they need - # to do at this point. - emit_post_sync_signal(created_models, verbosity, interactive, db) - - # The connection may have been closed by a syncdb handler. - cursor = connection.cursor() - - # Install custom SQL for the app (but only if this - # is a model we've just created) - for app_name, model_list in manifest.items(): - for model in model_list: - if model in created_models: - custom_sql = custom_sql_for_model(model, style, connection) - if custom_sql: - if verbosity >= 1: - print "Installing custom SQL for %s.%s model" % (app_name, model._meta.object_name) - try: - for sql in custom_sql: - cursor.execute(sql) - except Exception, e: - sys.stderr.write("Failed to install custom SQL for %s.%s model: %s\n" % \ - (app_name, model._meta.object_name, e)) - if show_traceback: - import traceback - traceback.print_exc() - transaction.rollback_unless_managed(using=db) - else: - transaction.commit_unless_managed(using=db) - else: - if verbosity >= 2: - print "No custom SQL for %s.%s model" % (app_name, model._meta.object_name) - - # Install SQL indicies for all newly created models - for app_name, model_list in manifest.items(): - for model in model_list: - if model in created_models: - index_sql = connection.creation.sql_indexes_for_model(model, style) - if index_sql: - if verbosity >= 1: - print "Installing index for %s.%s model" % (app_name, model._meta.object_name) - try: - for sql in index_sql: - cursor.execute(sql) - except Exception, e: - sys.stderr.write("Failed to install index for %s.%s model: %s\n" % \ - (app_name, model._meta.object_name, e)) - transaction.rollback_unless_managed(using=db) - else: - transaction.commit_unless_managed(using=db) - - #from django.core.management import call_command - #call_command('loaddata', 'initial_data', verbosity=verbosity, database=db) - -if __name__ == "__main__": - handle_noargs() \ No newline at end of file diff --git a/module/web/syncdb_django11.py b/module/web/syncdb_django11.py deleted file mode 100644 index c579718e0..000000000 --- a/module/web/syncdb_django11.py +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import os -import sys - -os.environ["DJANGO_SETTINGS_MODULE"] = 'settings' -sys.path.append(os.path.join(pypath, "module", "web")) - -from django.core.management.base import NoArgsCommand -from django.core.management.color import no_style -from django.utils.importlib import import_module -from optparse import make_option - -try: - set -except NameError: - from sets import Set as set # Python 2.3 fallback - -def handle_noargs(**options): - from django.db import connection, transaction, models - from django.conf import settings - from django.core.management.sql import custom_sql_for_model, emit_post_sync_signal - - verbosity = int(options.get('verbosity', 1)) - interactive = False - show_traceback = options.get('traceback', False) - - style = no_style() - - # Import the 'management' module within each installed app, to register - # dispatcher events. - for app_name in settings.INSTALLED_APPS: - try: - import_module('.management', app_name) - except ImportError, exc: - # This is slightly hackish. We want to ignore ImportErrors - # if the "management" module itself is missing -- but we don't - # want to ignore the exception if the management module exists - # but raises an ImportError for some reason. The only way we - # can do this is to check the text of the exception. Note that - # we're a bit broad in how we check the text, because different - # Python implementations may not use the same text. - # CPython uses the text "No module named management" - # PyPy uses "No module named myproject.myapp.management" - msg = exc.args[0] - if not msg.startswith('No module named') or 'management' not in msg: - raise - - cursor = connection.cursor() - - # Get a list of already installed *models* so that references work right. - tables = connection.introspection.table_names() - seen_models = connection.introspection.installed_models(tables) - created_models = set() - pending_references = {} - - # Create the tables for each model - for app in models.get_apps(): - app_name = app.__name__.split('.')[-2] - model_list = models.get_models(app) - for model in model_list: - # Create the model's database table, if it doesn't already exist. - if verbosity >= 2: - print "Processing %s.%s model" % (app_name, model._meta.object_name) - if connection.introspection.table_name_converter(model._meta.db_table) in tables: - continue - sql, references = connection.creation.sql_create_model(model, style, seen_models) - seen_models.add(model) - created_models.add(model) - for refto, refs in references.items(): - pending_references.setdefault(refto, []).extend(refs) - if refto in seen_models: - sql.extend(connection.creation.sql_for_pending_references(refto, style, pending_references)) - sql.extend(connection.creation.sql_for_pending_references(model, style, pending_references)) - if verbosity >= 1 and sql: - print "Creating table %s" % model._meta.db_table - for statement in sql: - cursor.execute(statement) - tables.append(connection.introspection.table_name_converter(model._meta.db_table)) - - # Create the m2m tables. This must be done after all tables have been created - # to ensure that all referred tables will exist. - for app in models.get_apps(): - app_name = app.__name__.split('.')[-2] - model_list = models.get_models(app) - for model in model_list: - if model in created_models: - sql = connection.creation.sql_for_many_to_many(model, style) - if sql: - if verbosity >= 2: - print "Creating many-to-many tables for %s.%s model" % (app_name, model._meta.object_name) - for statement in sql: - cursor.execute(statement) - - transaction.commit_unless_managed() - - # Send the post_syncdb signal, so individual apps can do whatever they need - # to do at this point. - emit_post_sync_signal(created_models, verbosity, interactive) - - # The connection may have been closed by a syncdb handler. - cursor = connection.cursor() - - # Install custom SQL for the app (but only if this - # is a model we've just created) - for app in models.get_apps(): - app_name = app.__name__.split('.')[-2] - for model in models.get_models(app): - if model in created_models: - custom_sql = custom_sql_for_model(model, style) - if custom_sql: - if verbosity >= 1: - print "Installing custom SQL for %s.%s model" % (app_name, model._meta.object_name) - try: - for sql in custom_sql: - cursor.execute(sql) - except Exception, e: - sys.stderr.write("Failed to install custom SQL for %s.%s model: %s\n" % \ - (app_name, model._meta.object_name, e)) - if show_traceback: - import traceback - traceback.print_exc() - transaction.rollback_unless_managed() - else: - transaction.commit_unless_managed() - else: - if verbosity >= 2: - print "No custom SQL for %s.%s model" % (app_name, model._meta.object_name) - # Install SQL indicies for all newly created models - for app in models.get_apps(): - app_name = app.__name__.split('.')[-2] - for model in models.get_models(app): - if model in created_models: - index_sql = connection.creation.sql_indexes_for_model(model, style) - if index_sql: - if verbosity >= 1: - print "Installing index for %s.%s model" % (app_name, model._meta.object_name) - try: - for sql in index_sql: - cursor.execute(sql) - except Exception, e: - sys.stderr.write("Failed to install index for %s.%s model: %s\n" % \ - (app_name, model._meta.object_name, e)) - transaction.rollback_unless_managed() - else: - transaction.commit_unless_managed() - - # Install the 'initial_data' fixture, using format discovery - #from django.core.management import call_command - #call_command('loaddata', 'initial_data', verbosity=verbosity) - -if __name__ == "__main__": - handle_noargs() \ No newline at end of file diff --git a/module/web/templates/jinja/default/base.html b/module/web/templates/jinja/default/base.html new file mode 100644 index 000000000..04c6dfbad --- /dev/null +++ b/module/web/templates/jinja/default/base.html @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + +{% block title %}pyLoad {{_("Webinterface")}}{% endblock %} + + + +{% block head %} +{% endblock %} + + + + +
+ +
+ + {% if user.is_authenticated %} + + +Captcha: +{{_("Captcha waiting")}} + + +User:{{user.name}} + +{% else %} + {{_("Please Login!")}} +{% endif %} + +
+ + + +
+ +
+ +
+
+ +{% if perms.can_change_status %} + +{% endif %} + +{% if perms.can_see_dl %} + +{% endif %} + +{% block pageactions %} +{% endblock %} +
+ +
+ +
+ +

{% block subtitle %}pyLoad - {{_("Webinterface")}}{% endblock %}

+ +{% block statusbar %} +{% endblock %} + + +
+ +
+
+ +{% for message in messages %} +

{{message}}

+{% endfor %} + +{% block content %} +{% endblock content %} + +
+ + +
+
+ +{% include "default/window.html" %} +{% include "default/captcha.html" %} + + diff --git a/module/web/templates/jinja/default/captcha.html b/module/web/templates/jinja/default/captcha.html new file mode 100644 index 000000000..b3be3deca --- /dev/null +++ b/module/web/templates/jinja/default/captcha.html @@ -0,0 +1,35 @@ + + + + +
+
+

{{_("Captcha reading")}}

+

{{_("Please read the text on the captcha.")}}

+ + + + + + + + + + + + + + + + + +
+ + +
+ +
\ No newline at end of file diff --git a/module/web/templates/jinja/default/collector.html b/module/web/templates/jinja/default/collector.html new file mode 100644 index 000000000..3e6b47234 --- /dev/null +++ b/module/web/templates/jinja/default/collector.html @@ -0,0 +1,84 @@ +{% extends 'default/base.html' %} +{% block head %} + + + + +{% endblock %} + +{% block title %}{{_("Collector")}} - {{super()}} {% endblock %} +{% block subtitle %}{{_("Collector")}}{% endblock %} + +{% block menu %} +
  • + {{_("Home")}} +
  • +
  • + {{_("Queue")}} +
  • +
  • + {{_("Collector")}} +
  • +
  • + {{_("Downloads")}} +
  • +
  • + {{_("Logs")}} +
  • +
  • + {{_("Config")}} +
  • {% endblock %} + +{% block pageactions %} + +{% endblock %} + +{% block content %} +
    {{_("success")}}
    +
    {{_("failure")}}
    +
    + + {{_("loading")}} +
    + + + +{% include "default/edit_package.html" %} + +{% endblock %} \ No newline at end of file diff --git a/module/web/templates/jinja/default/downloads.html b/module/web/templates/jinja/default/downloads.html new file mode 100644 index 000000000..813dc8d06 --- /dev/null +++ b/module/web/templates/jinja/default/downloads.html @@ -0,0 +1,50 @@ +{% extends 'default/base.html' %} + +{% block title %}Downloads - {{super()}} {% endblock %} + +{% block menu %} +
  • + {{_("Home")}} +
  • +
  • + {{_("Queue")}} +
  • +
  • + {{_("Collector")}} +
  • +
  • + {{_("Downloads")}} +
  • +
  • + {{_("Logs")}} +
  • +
  • + {{_("Config")}} +
  • +{% endblock %} + +{% block subtitle %} +{{_("Downloads")}} +{% endblock %} + +{% block content %} + + + +{% endblock %} \ No newline at end of file diff --git a/module/web/templates/jinja/default/edit_package.html b/module/web/templates/jinja/default/edit_package.html new file mode 100644 index 000000000..0c9dcff42 --- /dev/null +++ b/module/web/templates/jinja/default/edit_package.html @@ -0,0 +1,40 @@ +
    +
    +

    {{_("Edit Package")}}

    +

    {{_("Edit the package detais below.")}}

    + + + + + + + + + + + + + + + +
    + +
    + +
    \ No newline at end of file diff --git a/module/web/templates/jinja/default/home.html b/module/web/templates/jinja/default/home.html new file mode 100644 index 000000000..b2cef2cb7 --- /dev/null +++ b/module/web/templates/jinja/default/home.html @@ -0,0 +1,241 @@ +{% extends 'default/base.html' %} +{% block head %} + + + +{% endblock %} + +{% block subtitle %} +{{_("Active Downloads")}} +{% endblock %} + +{% block content %} + + + + + + + + + + + + + {% for link in content %} + + + + + + + + + + + {% endfor %} + + +
    {{_("Name")}}{{_("Status")}}{{_("Information")}}{{_("Size")}}{{_("Progress")}}
    +{% endblock %} \ No newline at end of file diff --git a/module/web/templates/jinja/default/login.html b/module/web/templates/jinja/default/login.html new file mode 100644 index 000000000..0e9e4d568 --- /dev/null +++ b/module/web/templates/jinja/default/login.html @@ -0,0 +1,35 @@ +{% extends 'default/base.html' %} + +{% block title %}{{_("Login")}} - {{super()}} {% endblock %} + +{% block content %} + +
    +
    +
    + +
    + Login + +
    + +
    + +
    +
    +
    + +{% if errors %} +

    {{_("Your username and password didn't match. Please try again.")}}

    +{% endif %} + +
    +
    + +{% endblock %} diff --git a/module/web/templates/jinja/default/logout.html b/module/web/templates/jinja/default/logout.html new file mode 100644 index 000000000..d3f07472b --- /dev/null +++ b/module/web/templates/jinja/default/logout.html @@ -0,0 +1,9 @@ +{% extends 'default/base.html' %} + +{% block head %} + +{% endblock %} + +{% block content %} +

    {{_("You were successfully logged out.")}}

    +{% endblock %} \ No newline at end of file diff --git a/module/web/templates/jinja/default/logs.html b/module/web/templates/jinja/default/logs.html new file mode 100644 index 000000000..7a95b4364 --- /dev/null +++ b/module/web/templates/jinja/default/logs.html @@ -0,0 +1,61 @@ +{% extends 'default/base.html' %} + +{% block title %}{{_("Logs")}} - {{super()}} {% endblock %} +{% block subtitle %}{{_("Logs")}}{% endblock %} +{% block head %} + +{% endblock %} +{% block menu %} +
  • + {{_("Home")}} +
  • +
  • + {{_("Queue")}} +
  • +
  • + {{_("Collector")}} +
  • +
  • + {{_("Downloads")}} +
  • +
  • + {{_("Logs")}} +
  • +
  • + {{_("Config")}} +
  • +{% endblock %} + +{% block content %} +
    + + +
    +
    + +   + + +
    +
    +
    {{warning}}
    +
    +
    + + {% for line in log %} + + {% endfor %} +
    {{line.line}}{{line.date}}{{line.level}}{{line.message}}
    +
    +
    +
    + + +
    +
    +
     
    +{% endblock %} \ No newline at end of file diff --git a/module/web/templates/jinja/default/package_ui.js b/module/web/templates/jinja/default/package_ui.js new file mode 100644 index 000000000..45e284903 --- /dev/null +++ b/module/web/templates/jinja/default/package_ui.js @@ -0,0 +1,408 @@ +var load, success, fail, pack_box; + +document.addEvent("domready", function() { + load = new Fx.Tween($("load-indicator"), {link: "cancel"}); + success = new Fx.Tween($("load-success"), {link: "chain"}); + fail = new Fx.Tween($("load-failure"), {link: "chain"}); + + [load,success,fail].each(function(fx) { + fx.set("opacity", 0) + }); + + pack_box = new Fx.Tween($('pack_box')); + $('pack_reset').addEvent('click', function() { + hide_pack() + }); +}); + +function indicateLoad() { + //$("load-indicator").reveal(); + load.start("opacity", 1) +} + +function indicateFinish() { + load.start("opacity", 0) +} + +function indicateSuccess() { + indicateFinish(); + success.start("opacity", 1).chain(function() { + (function() { + success.start("opacity", 0); + }).delay(250); + }); + +} + +function indicateFail() { + indicateFinish(); + fail.start("opacity", 1).chain(function() { + (function() { + fail.start("opacity", 0); + }).delay(250); + }); +} + +function show_pack() { + bg_show(); + $("pack_box").setStyle('display', 'block'); + pack_box.start('opacity', 1) +} + +function hide_pack() { + bg_hide(); + pack_box.start('opacity', 0).chain(function() { + $('pack_box').setStyle('display', 'none'); + }); +} + +var PackageUI = new Class({ + initialize: function(url, type) { + this.url = url; + this.type = type; + this.packages = []; + this.parsePackages(); + + this.sorts = new Sortables($("package-list"), { + constrain: false, + clone: true, + revert: true, + opacity: 0.4, + handle: ".package_drag", + //onStart: this.startSort, + onComplete: this.saveSort.bind(this) + }); + + $("del_finished").addEvent("click", this.deleteFinished.bind(this)); + $("restart_failed").addEvent("click", this.restartFailed.bind(this)); + + }, + + parsePackages: function() { + $("package-list").getChildren("li").each(function(ele) { + var id = ele.getFirst().get("id").match(/[0-9]+/); + this.packages.push(new Package(this, id, ele)) + }.bind(this)) + }, + + loadPackages: function() { + }, + + deleteFinished: function() { + indicateLoad(); + new Request.JSON({ + method: 'get', + url: '/json/delete_finished', + onSuccess: function(data) { + if (data.del.length > 0) { + window.location.reload() + } else { + this.packages.each(function(pack) { + pack.close(); + }); + indicateSuccess(); + } + }.bind(this), + onFailure: indicateFail + }).send(); + }, + + restartFailed: function() { + indicateLoad(); + new Request.JSON({ + method: 'get', + url: '/json/restart_failed', + onSuccess: function(data) { + this.packages.each(function(pack) { + pack.close(); + }); + indicateSuccess(); + }.bind(this), + onFailure: indicateFail + }).send(); + }, + + startSort: function(ele, copy) { + }, + + saveSort: function(ele, copy) { + var order = []; + this.sorts.serialize(function(li, pos) { + if (li == ele && ele.retrieve("order") != pos) { + order.push(ele.retrieve("pid") + "|" + pos) + } + li.store("order", pos) + }); + if (order.length > 0) { + indicateLoad(); + new Request.JSON({ + method: 'get', + url: '/json/package_order/' + order[0], + onSuccess: indicateFinish, + onFailure: indicateFail + }).send(); + } + } + +}); + +var Package = new Class({ + initialize: function(ui, id, ele, data) { + this.ui = ui; + this.id = id; + this.linksLoaded = false; + + if (!ele) { + this.createElement(data); + } else { + this.ele = ele; + this.order = ele.getElements("div.order")[0].get("html"); + this.ele.store("order", this.order); + this.ele.store("pid", this.id); + this.parseElement(); + } + + var pname = this.ele.getElements(".packagename")[0]; + this.buttons = new Fx.Tween(this.ele.getElements(".buttons")[0], {link: "cancel"}); + this.buttons.set("opacity", 0); + + pname.addEvent("mouseenter", function(e) { + this.buttons.start("opacity", 1) + }.bind(this)); + + pname.addEvent("mouseleave", function(e) { + this.buttons.start("opacity", 0) + }.bind(this)); + + + }, + + createElement: function() { + alert("create") + }, + + parseElement: function() { + var imgs = this.ele.getElements('img'); + + this.name = this.ele.getElements('.name')[0]; + this.folder = this.ele.getElements('.folder')[0]; + this.password = this.ele.getElements('.password')[0]; + this.prio = this.ele.getElements('.prio')[0]; + + imgs[1].addEvent('click', this.deletePackage.bind(this)); + + imgs[2].addEvent('click', this.restartPackage.bind(this)); + + imgs[3].addEvent('click', this.editPackage.bind(this)); + + imgs[4].addEvent('click', this.movePackage.bind(this)); + + this.ele.getElement('.packagename').addEvent('click', this.toggle.bind(this)); + + }, + + loadLinks: function() { + indicateLoad(); + new Request.JSON({ + method: 'get', + url: '/json/package/' + this.id, + onSuccess: this.createLinks.bind(this), + onFailure: indicateFail + }).send(); + }, + + createLinks: function(data) { + var ul = $("sort_children_{id}".substitute({"id": this.id})); + ul.erase("html"); + data.links.each(function(link) { + var li = new Element("li", { + "style": { + "margin-left": 0 + } + }); + + var html = "\n".substitute({"icon": link.icon}); + html += "{name}
    ".substitute({"name": link.name}); + html += "{statusmsg}{error} ".substitute({"statusmsg": link.statusmsg, "error":link.error}); + html += "{format_size}".substitute({"format_size": link.format_size}); + html += "{plugin}  ".substitute({"plugin": link.plugin}); + html += "  "; + html += "
    "; + + var div = new Element("div", { + "id": "file_" + link.id, + "class": "child", + "html": html + }); + + li.store("order", link.order); + li.store("lid", link.id); + + li.adopt(div); + ul.adopt(li); + }); + this.sorts = new Sortables(ul, { + constrain: false, + clone: true, + revert: true, + opacity: 0.4, + handle: ".sorthandle", + onComplete: this.saveSort.bind(this) + }); + this.registerLinkEvents(); + this.linksLoaded = true; + indicateFinish(); + this.toggle(); + }, + + registerLinkEvents: function() { + this.ele.getElements('.child').each(function(child) { + var lid = child.get('id').match(/[0-9]+/); + var imgs = child.getElements('.child_secrow img'); + imgs[0].addEvent('click', function(e) { + new Request({ + method: 'get', + url: '/json/remove_link/' + this, + onSuccess: function() { + $('file_' + this).nix() + }.bind(this), + onFailure: indicateFail + }).send(); + }.bind(lid)); + + imgs[1].addEvent('click', function(e) { + new Request({ + method: 'get', + url: '/json/restart_link/' + this, + onSuccess: function() { + var ele = $('file_' + this); + var imgs = ele.getElements("img"); + imgs[0].set("src", "/media/default/img/status_queue.png"); + var spans = ele.getElements(".child_status"); + spans[1].set("html", "queued"); + indicateSuccess(); + }.bind(this), + onFailure: indicateFail + }).send(); + }.bind(lid)); + }); + }, + + toggle: function() { + var child = this.ele.getElement('.children'); + if (child.getStyle('display') == "block") { + child.dissolve(); + } else { + if (!this.linksLoaded) { + this.loadLinks(); + } else { + child.reveal(); + } + } + }, + + deletePackage: function(event) { + indicateLoad(); + new Request({ + method: 'get', + url: '/json/remove_package/' + this.id, + onSuccess: function() { + this.ele.nix(); + indicateFinish(); + }.bind(this), + onFailure: indicateFail + }).send(); + event.stop(); + }, + + restartPackage: function(event) { + indicateLoad(); + new Request({ + method: 'get', + url: '/json/restart_package/' + this.id, + onSuccess: function() { + this.close(); + indicateSuccess(); + }.bind(this), + onFailure: indicateFail + }).send(); + event.stop(); + }, + + close: function() { + var child = this.ele.getElement('.children'); + if (child.getStyle('display') == "block") { + child.dissolve(); + } + var ul = $("sort_children_{id}".substitute({"id": this.id})); + ul.erase("html"); + this.linksLoaded = false; + }, + + movePackage: function(event) { + indicateLoad(); + new Request({ + method: 'get', + url: '/json/move_package/' + ((this.ui.type + 1) % 2) + "/" + this.id, + onSuccess: function() { + this.ele.nix(); + indicateFinish(); + }.bind(this), + onFailure: indicateFail + }).send(); + event.stop(); + }, + + editPackage: function(event) { + $("pack_form").removeEvents("submit"); + $("pack_form").addEvent("submit", this.savePackage.bind(this)); + + $("pack_id").set("value", this.id); + $("pack_name").set("value", this.name.get("text")); + $("pack_folder").set("value", this.folder.get("text")); + $("pack_pws").set("value", this.password.get("text")); + + var prio = 3; + $("pack_prio").getChildren("option").each(function(item, index) { + item.erase("selected"); + if (prio.toString() == this.prio.get("text")) { + item.set("selected", "selected"); + } + prio--; + }.bind(this)); + + + show_pack(); + event.stop(); + }, + + savePackage: function(event) { + $("pack_form").send(); + this.name.set("text", $("pack_name").get("value")); + this.folder.set("text", $("pack_folder").get("value")); + this.password.set("text", $("pack_pws").get("value")); + this.prio.set("text", $("pack_prio").get("value")); + hide_pack(); + event.stop(); + }, + + saveSort: function(ele, copy) { + var order = []; + this.sorts.serialize(function(li, pos) { + if (li == ele && ele.retrieve("order") != pos) { + order.push(ele.retrieve("lid") + "|" + pos) + } + li.store("order", pos) + }); + if (order.length > 0) { + indicateLoad(); + new Request.JSON({ + method: 'get', + url: '/json/link_order/' + order[0], + onSuccess: indicateFinish, + onFailure: indicateFail + }).send(); + } + } + +}); \ No newline at end of file diff --git a/module/web/templates/jinja/default/pathchooser.html b/module/web/templates/jinja/default/pathchooser.html new file mode 100644 index 000000000..d00637055 --- /dev/null +++ b/module/web/templates/jinja/default/pathchooser.html @@ -0,0 +1,76 @@ + + + + + + +
    +
    +
    + + +
    + + {% if type == 'folder' %} + {{_("Path")}}: {{_("absolute")}} | {{_("relative")}} + {% else %} + {{_("Path")}}: {{_("absolute")}} | {{_("relative")}} + {% endif %} +
    + + + + + + + + {% if parentdir %} + + + + {% endif %} +{% for file in files %} + + {% if type == 'folder' %} + + {% else %} + + {% endif %} + + + + + +{% endfor %} +
    {{_("name")}}{{_("size")}}{{_("type")}}{{_("last modified")}}
    + {{_("parent directory")}} +
    {% if file.type == 'dir' %}{{ file.name|truncate(25) }}{% else %}{{ file.name|truncate(25) }}{% endif %}{% if file.type == 'dir' %}{{ file.name|truncate(25) }}{% else %}{{ file.name|truncate(25) }}{% endif %}{{ file.size|float|filesizeformat }}{% if file.type == 'dir' %}directory{% else %}{{ file.ext|default("file") }}{% endif %}{{ file.modified|date("d.m.Y - H:i:s") }}
    +
    + + \ No newline at end of file diff --git a/module/web/templates/jinja/default/queue.html b/module/web/templates/jinja/default/queue.html new file mode 100644 index 000000000..e72871873 --- /dev/null +++ b/module/web/templates/jinja/default/queue.html @@ -0,0 +1,85 @@ +{% extends 'default/base.html' %} +{% block head %} + + + + +{% endblock %} + +{% block title %}{{_("Queue")}} - {{super()}} {% endblock %} +{% block subtitle %}{{_("Queue")}}{% endblock %} + +{% block menu %} +
  • + {{_("Home")}} +
  • +
  • + {{_("Queue")}} +
  • +
  • + {{_("Collector")}} +
  • +
  • + {{_("Downloads")}} +
  • +
  • + {{_("Logs")}} +
  • +
  • + {{_("Config")}} +
  • +{% endblock %} + +{% block pageactions %} + +{% endblock %} + +{% block content %} +
    {{_("success")}}
    +
    {{_("failure")}}
    +
    + + {{_("loading")}} +
    + +
      +{% for id, package in content %} +
    • +
      + + +
      + + {{package.name}} +    + + +    + +    + +    + + +
      + +
      +
    • +{% endfor %} +
    + +{% include "default/edit_package.html" %} + +{% endblock %} \ No newline at end of file diff --git a/module/web/templates/jinja/default/settings.html b/module/web/templates/jinja/default/settings.html new file mode 100644 index 000000000..18bc78e30 --- /dev/null +++ b/module/web/templates/jinja/default/settings.html @@ -0,0 +1,232 @@ +{% extends 'default/base.html' %} + +{% block title %}{{ _("Config") }} - {{ super() }} {% endblock %} +{% block subtitle %}{{ _("Config") }}{% endblock %} + +{% block head %} + + +{% endblock %} + +{% block menu %} +
  • + {{ _("Home") }} +
  • +
  • + {{ _("Queue") }} +
  • +
  • + {{ _("Collector") }} +
  • +
  • + {{ _("Downloads") }} +
  • +
  • + {{ _("Logs") }} +
  • +
  • + {{ _("Config") }} +
  • +{% endblock %} + +{% block content %} + +
      + {% for configname, config in conf.iteritems() %} +
    • {{ configname }}
    • + {% endfor %} +
    + +
    +
      + {% for configname, config in conf.iteritems() %} + + {% if configname != "Accounts" %} + {% for skey, section in config.iteritems() %} +
    • {{ section.desc }}
    • + {% endfor %} + {% else %} + {% for skey, section in config.iteritems() %} +
    • {{ skey }}
    • + {% endfor %} + {% endif %} +
      + {% endfor %} +
    +
    +
    + {% for configname, config in conf.iteritems() %} + {% if configname != "Accounts" %} + {% for skey, section in config.iteritems() %} +
    + + {% for okey, option in section.iteritems() %} + {% if okey != "desc" %} + + + + + {% endif %} + {% endfor %} +
    + {% if option.type == "bool" %} + + {% elif ";" in option.type %} + + {% elif option.type == "folder" %} + + + {% elif option.type == "file" %} + + + {% else %} + + {% endif %} +
    +
    + {% endfor %} + {% else %} + + {% for plugin, accounts in config.iteritems() %} +
    + + {% for account in accounts %} + + + + + + + + + + + {% endfor %} + + + + + + + + + + + + + + + +
    + + + {{ _("Status:") }} + {% if account.valid %} + + {{ _("valid") }} + {% else %} + + {{ _("not valid") }} + {% endif %} + + + {{ _("Valid until:") }} + + {{ account.validuntil }} + + + {{ _("Traffic left:") }} + + {{ account.trafficleft }} + + + {{ _("Time:") }} + + + {{ _("Delete? ") }} + +
     
    + +
    + +
    +
    + {% endfor %} + {% endif %} + {% endfor %} + {% if conf %} + +
    + +
    + {% for message in errors %} + {{ message }}
    + {% endfor %} + + {% endif %} + +{% endblock %} diff --git a/module/web/templates/jinja/default/test.html b/module/web/templates/jinja/default/test.html new file mode 100644 index 000000000..b4f17f134 --- /dev/null +++ b/module/web/templates/jinja/default/test.html @@ -0,0 +1,12 @@ + + + + Test + + +

    Template Test

    +{{ user }} +{{ status }} + + \ No newline at end of file diff --git a/module/web/templates/jinja/default/window.html b/module/web/templates/jinja/default/window.html new file mode 100644 index 000000000..734745887 --- /dev/null +++ b/module/web/templates/jinja/default/window.html @@ -0,0 +1,45 @@ + + + + + +
    +
    +

    {{_("Add Package")}}

    +

    {{_("Paste your links or upload a container.")}}

    + + + + + + + + + + + + + + + {{_("Queue")}} + + {{_("Collector")}} + + + + + +
    + +
    + +
    \ No newline at end of file diff --git a/module/web/urls.py b/module/web/urls.py deleted file mode 100644 index 9fe11f925..000000000 --- a/module/web/urls.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -from django.conf.urls.defaults import * -from django.contrib import admin -from django.conf import settings - - -admin.autodiscover() - -urlpatterns = patterns('', - # Example: - - # Uncomment the admin/doc line below and add 'django.contrib.admindocs' - # to INSTALLED_APPS to enable admin documentation: - # (r'^admin/doc/', include('django.contrib.admindocs.urls')), - - (r'^admin/', include(admin.site.urls)), # django 1.0 not working - (r'^json/', include('ajax.urls')), - (r'^flashgot$', 'cnl.views.flashgot'), - (r'^flash(got)?/?', include('cnl.urls')), - (r'^crossdomain.xml$', 'cnl.views.crossdomain'), - (r'^jdcheck.js', 'cnl.views.jdcheck'), - (r'^favicon\.ico$', 'django.views.generic.simple.redirect_to', {'url': '/media/img/favicon.ico'}), - (r'^media/(?P.*)$', 'django.views.static.serve', - {'document_root': settings.MEDIA_ROOT}), - (r'^', include('pyload.urls')), - ) diff --git a/module/web/utils.py b/module/web/utils.py new file mode 100644 index 000000000..cf3f2d5f3 --- /dev/null +++ b/module/web/utils.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this plrogram; if not, see . + + @author: RaNaN +""" +from os.path import join, abspath, commonprefix + +from bottle import request, HTTPError, redirect, ServerAdapter + +from webinterface import env, TEMPLATE + +def render_to_response(name, args={}, proc=[]): + for p in proc: + args.update(p()) + + t = env.get_template(join(TEMPLATE, name)) + return t.render(**args) + +def parse_permissions(session): + perms = {"can_change_status": False, + "can_see_dl": False} + + if not session.get("authenticated", False): + return perms + + perms["can_change_status"] = True + perms["can_see_dl"] = True + + return perms + +def parse_userdata(session): + return {"name": session.get("name", "Anonymous"), + "is_staff": True, + "is_authenticated": session.get("authenticated", False)} + +def formatSize(size): + """formats size of bytes""" + size = int(size) + steps = 0 + sizes = ["KB", "MB", "GB", "TB"] + + while size > 1000: + size /= 1024.0 + steps += 1 + + return "%.2f %s" % (size, sizes[steps]) + +def login_required(perm=None): + def _dec(func): + def _view(*args, **kwargs): + s = request.environ.get('beaker.session') + if s.get("name", None) and s.get("authenticated", False): + if perm: + pass + #print perm + return func(*args, **kwargs) + else: + if request.header.get('X-Requested-With') == 'XMLHttpRequest': + return HTTPError(403, "Forbidden") + else: + return redirect("/login") + + return _view + + return _dec + +class CherryPyWSGI(ServerAdapter): + + def run(self, handler): + from wsgiserver import CherryPyWSGIServer + + server = CherryPyWSGIServer((self.host, self.port), handler) + server.start() diff --git a/module/web/webinterface.py b/module/web/webinterface.py new file mode 100644 index 000000000..fe59c57b1 --- /dev/null +++ b/module/web/webinterface.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + + @author: RaNaN +""" + +import sys +import gettext +import sqlite3 + +from os.path import join, abspath,dirname, exists +from os import makedirs + +PROJECT_DIR = abspath(dirname(__file__)) +PYLOAD_DIR = abspath(join(PROJECT_DIR, "..", "..")) + +sys.path.append(PYLOAD_DIR) +sys.path.append(join(PYLOAD_DIR, "module", "lib")) + +from module import InitHomeDir + +import bottle +from bottle import run, app + +from jinja2 import Environment, FileSystemLoader, FileSystemBytecodeCache +from middlewares import StripPathMiddleware, GZipMiddleWare, PrefixMiddleware + +try: + import module.web.ServerThread + + if not module.web.ServerThread.core: + raise Exception + PYLOAD = module.web.ServerThread.core.server_methods + config = module.web.ServerThread.core.config +except: + import xmlrpclib + + ssl = "" + + from module.ConfigParser import ConfigParser + + config = ConfigParser() + + if config.get("ssl", "activated"): + ssl = "s" + + server_url = "http%s://%s:%s@%s:%s/" % ( + ssl, + config.username, + config.password, + config.get("remote", "listenaddr"), + config.get("remote", "port") + ) + + PYLOAD = xmlrpclib.ServerProxy(server_url, allow_none=True) + +from module.JsEngine import JsEngine + +JS = JsEngine() + +TEMPLATE = config.get('webinterface', 'template') +DL_ROOT = join(PYLOAD_DIR, config.get('general', 'download_folder')) +LOG_ROOT = join(PYLOAD_DIR, config.get('log', 'log_folder')) +DEBUG = config.get("general","debug_mode") +bottle.debug(DEBUG) + +def setup_database(): + conn = sqlite3.connect('web.db') + c = conn.cursor() + c.execute( + 'CREATE TABLE IF NOT EXISTS "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT, "name" TEXT NOT NULL, "email" TEXT DEFAULT "" NOT NULL, "password" TEXT NOT NULL, "role" INTEGER DEFAULT 0 NOT NULL, "permission" INTEGER DEFAULT 0 NOT NULL, "template" TEXT DEFAULT "default" NOT NULL)') + c.close() + conn.commit() + conn.close() + +setup_database() + + +if not exists(join("tmp", "jinja_cache")): + makedirs(join("tmp", "jinja_cache")) + +bcc = FileSystemBytecodeCache(join("tmp","jinja_cache")) +env = Environment(loader=FileSystemLoader(join(PROJECT_DIR, "templates", "jinja")), extensions=['jinja2.ext.i18n'], trim_blocks=True, auto_reload=False, bytecode_cache=bcc) + +from filters import quotepath, path_make_relative, path_make_absolute, truncate,date + +env.filters["quotepath"] = quotepath +env.filters["truncate"] = truncate +env.filters["date"] = date +env.filters["path_make_relative"] = path_make_relative +env.filters["path_make_absolute"] = path_make_absolute + + +translation = gettext.translation("django", join(PROJECT_DIR, "locale"), + languages=["en", config.get("general","language")]) +translation.install(True) +env.install_gettext_translations(translation) + +from beaker.middleware import SessionMiddleware + +session_opts = { + 'session.type': 'file', + 'session.cookie_expires': -1, + 'session.data_dir': './tmp', + 'session.auto': False +} + +web = StripPathMiddleware(SessionMiddleware(app(), session_opts)) +web = PrefixMiddleware(web) +web = GZipMiddleWare(web) + +import pyload_app +import json_app +import cnl_app + + +def run_simple(host="0.0.0.0", port="8000"): + run(app=web, host=host, port=port, quiet=True) + +def run_threaded(host="0.0.0.0", port="8000", theads=3, cert="", key=""): + from wsgiserver import CherryPyWSGIServer + if cert and key: + CherryPyWSGIServer.ssl_certificate = cert + CherryPyWSGIServer.ssl_private_key = key + + CherryPyWSGIServer.numthreads = theads + + from utils import CherryPyWSGI + run(app=web, host=host, port=port, server=CherryPyWSGI, quiet=True) + +def run_fcgi(host="0.0.0.0", port="8000"): + from bottle import FlupFCGIServer + run(app=web, host=host, port=port, server=FlupFCGIServer, quiet=True) + + +if __name__ == "__main__": + + run(app=web, port=8001) \ No newline at end of file -- cgit v1.2.3