diff options
Diffstat (limited to 'pyload')
-rw-r--r-- | pyload/Core.py | 75 | ||||
-rw-r--r-- | pyload/api/CoreApi.py | 21 | ||||
-rw-r--r-- | pyload/config/default.py | 12 | ||||
-rw-r--r-- | pyload/plugins/addons/ClickAndLoad.py | 13 | ||||
-rw-r--r-- | pyload/remote/RemoteManager.py | 4 | ||||
-rw-r--r-- | pyload/remote/WebSocketBackend.py | 16 | ||||
-rw-r--r-- | pyload/remote/wsbackend/AbstractHandler.py | 13 | ||||
-rw-r--r-- | pyload/remote/wsbackend/Server.py | 5 | ||||
-rw-r--r-- | pyload/web/Gruntfile.js | 8 | ||||
-rw-r--r-- | pyload/web/ServerThread.py | 10 | ||||
-rw-r--r-- | pyload/web/app/index.html | 8 | ||||
-rw-r--r-- | pyload/web/app/scripts/app.js | 7 | ||||
-rw-r--r-- | pyload/web/app/scripts/views/headerView.js | 36 | ||||
-rw-r--r-- | pyload/web/package.json | 4 | ||||
-rw-r--r-- | pyload/web/pyload_app.py | 9 | ||||
-rw-r--r-- | pyload/web/servers.py | 2 | ||||
-rw-r--r-- | pyload/web/webinterface.py | 6 |
17 files changed, 148 insertions, 101 deletions
diff --git a/pyload/Core.py b/pyload/Core.py index 4893283a8..a13346567 100644 --- a/pyload/Core.py +++ b/pyload/Core.py @@ -35,9 +35,11 @@ from time import time, sleep from traceback import print_exc import locale + locale.locale_alias = locale.windows_locale = {} #save ~100kb ram, no known sideeffects for now import subprocess + subprocess.__doc__ = None # the module with the largest doc we are using import InitHomeDir @@ -60,8 +62,10 @@ from utils.fs import free_space, exists, makedirs, join, chmod from codecs import getwriter # test runner overwrites sys.stdout -if hasattr(sys.stdout, "encoding"): enc = get_console_encoding(sys.stdout.encoding) -else: enc = "utf8" +if hasattr(sys.stdout, "encoding"): + enc = get_console_encoding(sys.stdout.encoding) +else: + enc = "utf8" sys._stdout = sys.stdout sys.stdout = getwriter(enc)(sys.stdout, errors="replace") @@ -100,9 +104,9 @@ class Core(object): if len(argv) > 1: try: options, args = getopt(argv[1:], 'vchdusqp:', - ["version", "clear", "clean", "help", "debug", "user", - "setup", "configdir=", "changedir", "daemon", - "quit", "status", "no-remote","pidfile="]) + ["version", "clear", "clean", "help", "debug", "user", + "setup", "configdir=", "changedir", "daemon", + "quit", "status", "no-remote", "pidfile="]) for option, argument in options: if option in ("-v", "--version"): @@ -299,14 +303,16 @@ class Core(object): exit() - try: signal.signal(signal.SIGQUIT, self.quit) - except: pass + try: + signal.signal(signal.SIGQUIT, self.quit) + except: + pass self.config = ConfigParser() gettext.setpaths([join(os.sep, "usr", "share", "pyload", "locale"), None]) translation = gettext.translation("pyLoad", self.path("locale"), - languages=[self.config['general']['language'],"en"],fallback=True) + languages=[self.config['general']['language'], "en"], fallback=True) translation.install(True) # load again so translations are propagated @@ -448,19 +454,19 @@ class Core(object): self.eventManager.dispatchEvent("core:ready") #test api -# from pyload.common.APIExerciser import startApiExerciser -# startApiExerciser(self, 3) + # from pyload.common.APIExerciser import startApiExerciser + # startApiExerciser(self, 3) #some memory stats -# from guppy import hpy -# hp=hpy() -# print hp.heap() -# import objgraph -# objgraph.show_most_common_types(limit=30) -# import memdebug -# memdebug.start(8002) -# from meliae import scanner -# scanner.dump_all_objects(self.path('objs.json')) + # from guppy import hpy + # hp=hpy() + # print hp.heap() + # import objgraph + # objgraph.show_most_common_types(limit=30) + # import memdebug + # memdebug.start(8002) + # from meliae import scanner + # scanner.dump_all_objects(self.path('objs.json')) locals().clear() @@ -491,9 +497,8 @@ class Core(object): self.db.manager = self.files #ugly? def init_webserver(self): - if self.config['webinterface']['activated']: - self.webserver = WebServer(self) - self.webserver.start() + self.webserver = WebServer(self) + self.webserver.start() def init_logger(self, level): console = logging.StreamHandler(sys.stdout) @@ -526,18 +531,18 @@ class Core(object): if self.config['log']['color_theme'] == "full": cfmt = "%(asctime)s %(log_color)s%(bold)s%(white)s %(levelname)-8s %(reset)s %(message)s" clr = { - 'DEBUG': 'bg_cyan', - 'INFO': 'bg_green', - 'WARNING': 'bg_yellow', - 'ERROR': 'bg_red', + 'DEBUG': 'bg_cyan', + 'INFO': 'bg_green', + 'WARNING': 'bg_yellow', + 'ERROR': 'bg_red', 'CRITICAL': 'bg_purple', } else: #light theme cfmt = "%(log_color)s%(asctime)s %(levelname)-8s %(message)s" clr = { - 'DEBUG': 'cyan', - 'WARNING': 'yellow', - 'ERROR': 'red', + 'DEBUG': 'cyan', + 'WARNING': 'yellow', + 'ERROR': 'red', 'CRITICAL': 'purple', } @@ -578,10 +583,10 @@ class Core(object): self.shutdown() chdir(owd) # close some open fds - for i in range(3,50): + for i in range(3, 50): try: close(i) - except : + except: pass execl(executable, executable, *sys.argv) @@ -591,9 +596,9 @@ class Core(object): self.log.info(_("shutting down...")) self.eventManager.dispatchEvent("coreShutdown") try: - if self.config['webinterface']['activated'] and hasattr(self, "webserver"): + if hasattr(self, "webserver"): pass # TODO: quit webserver? -# self.webserver.quit() + # self.webserver.quit() for thread in self.threadManager.threads: thread.put("quit") @@ -616,12 +621,14 @@ class Core(object): """ stop and open an ipython shell inplace""" if self.debug: from IPython import embed + sys.stdout = sys._stdout embed() def breakpoint(self): if self.debug: from IPython.core.debugger import Pdb + sys.stdout = sys._stdout if not self.pdb: self.pdb = Pdb() self.pdb.set_trace() @@ -678,7 +685,7 @@ def main(): #from module.lib.rename_process import renameProcess #renameProcess('pyLoadCore') if "--daemon" in sys.argv: - deamon() + deamon() else: pyload_core = Core() try: diff --git a/pyload/api/CoreApi.py b/pyload/api/CoreApi.py index ebb194134..187286b48 100644 --- a/pyload/api/CoreApi.py +++ b/pyload/api/CoreApi.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- from pyload.Api import Api, RequirePerm, Permission, ServerStatus, Interaction -from pyload.utils.fs import join, free_space +from pyload.utils.fs import join, free_space, exists from pyload.utils import compare_time from ApiComponent import ApiComponent @@ -15,11 +15,26 @@ class CoreApi(ApiComponent): """pyLoad Core version """ return self.core.version + def isWSSecure(self): + # needs to use TLS when either requested or webUI is also using encryption + if not self.core.config['ssl']['activated'] or self.core.config['webUI']['https']: + return False + + if not exists(self.core.config['ssl']['cert']) or not exists(self.core.config['ssl']['key']): + self.core.log.warning(_('SSL key or certificate not found')) + return False + + return True + @RequirePerm(Permission.All) def getWSAddress(self): """Gets and address for the websocket based on configuration""" - # TODO SSL (wss) - return "ws://%%s:%d" % self.core.config['remote']['port'] + if self.isWSSecure(): + ws = "wss" + else: + ws = "ws" + + return "%s://%%s:%d" % (ws, self.core.config['webUI']['wsPort']) @RequirePerm(Permission.All) def getServerStatus(self): diff --git a/pyload/config/default.py b/pyload/config/default.py index 5a02ef758..26152a09a 100644 --- a/pyload/config/default.py +++ b/pyload/config/default.py @@ -11,12 +11,6 @@ def make_config(config): # Check if gettext is installed _ = lambda x: x - config.addConfigSection("remote", _("Remote"), _("Description"), _("Long description"), - [ - ("port", "int", _("Port"), 7227), - ("listenaddr", "ip", _("Address"), "0.0.0.0"), - ]) - config.addConfigSection("log", _("Log"), _("Description"), _("Long description"), [ ("log_size", "int", _("Size in kb"), 100), @@ -58,16 +52,18 @@ def make_config(config): ("key", "file", _("SSL Key"), "ssl.key"), ]) - config.addConfigSection("webinterface", _("Webinterface"), _("Description"), _("Long description"), + config.addConfigSection("webUI", _("webUI"), _("Description"), _("Long description"), [ ("template", "str", _("Template"), "default"), - ("activated", "bool", _("Activated"), True), ("prefix", "str", _("Path Prefix"), ""), + ("external", "bool", _("Served external"), False), ("server", "auto;threaded;fallback;fastcgi", _("Server"), "auto"), ("force_server", "str", _("Favor specific server"), ""), ("host", "ip", _("IP"), "0.0.0.0"), ("https", "bool", _("Use HTTPS"), False), ("port", "int", _("Port"), 8001), + ("wsHost", "ip", _("IP"), "0.0.0.0"), + ("wsPort", "int", _("Port"), 7227), ("develop", "bool", _("Development mode"), False), ]) diff --git a/pyload/plugins/addons/ClickAndLoad.py b/pyload/plugins/addons/ClickAndLoad.py index 0d9538543..be360c30c 100644 --- a/pyload/plugins/addons/ClickAndLoad.py +++ b/pyload/plugins/addons/ClickAndLoad.py @@ -33,14 +33,13 @@ class ClickAndLoad(Addon): __author_mail__ = ("RaNaN@pyload.de", "mkaay@mkaay.de") def coreReady(self): - self.port = int(self.config['webinterface']['port']) - if self.config['webinterface']['activated']: - if self.getConfig("extern"): - ip = "0.0.0.0" - else: - ip = "127.0.0.1" + self.port = int(self.config['webUI']['port']) + if self.getConfig("extern"): + ip = "0.0.0.0" + else: + ip = "127.0.0.1" - thread.start_new_thread(proxy, (self, ip, self.port, 9666)) + thread.start_new_thread(proxy, (self, ip, self.port, 9666)) def proxy(self, *settings): diff --git a/pyload/remote/RemoteManager.py b/pyload/remote/RemoteManager.py index 7aeeb8a7a..b66b8b10a 100644 --- a/pyload/remote/RemoteManager.py +++ b/pyload/remote/RemoteManager.py @@ -67,8 +67,8 @@ class RemoteManager(): def startBackends(self): - host = self.core.config["remote"]["listenaddr"] - port = self.core.config["remote"]["port"] + host = self.core.config["webUI"]["wsHost"] + port = self.core.config["webUI"]["wsPort"] for b in self.available: klass = getattr(__import__("pyload.remote.%s" % b, globals(), locals(), [b], -1), b) diff --git a/pyload/remote/WebSocketBackend.py b/pyload/remote/WebSocketBackend.py index d29470067..7238af679 100644 --- a/pyload/remote/WebSocketBackend.py +++ b/pyload/remote/WebSocketBackend.py @@ -21,12 +21,15 @@ import logging from RemoteManager import BackendBase from mod_pywebsocket import util + + def get_class_logger(o=None): return logging.getLogger('log') # Monkey patch for our logger util.get_class_logger = get_class_logger + class WebSocketBackend(BackendBase): def setup(self, host, port): @@ -42,8 +45,19 @@ class WebSocketBackend(BackendBase): options.dispatcher.addHandler(ApiHandler.PATH, ApiHandler(self.core.api)) options.dispatcher.addHandler(AsyncHandler.PATH, AsyncHandler(self.core.api)) - self.server = WebSocketServer(options) + # tls is needed when requested or webUI is also on tls + if self.core.api.isWSSecure(): + from wsbackend.Server import import_ssl + if import_ssl(): + options.use_tls = True + options.certificate = self.core.config['ssl']['cert'] + options.ca_certificate = options.certificate + options.private_key = self.core.config['ssl']['key'] + self.core.log.info(_('Using secure WebSocket')) + else: + self.core.log.warning(_('SSL could not be imported')) + self.server = WebSocketServer(options) def serve(self): self.server.serve_forever() diff --git a/pyload/remote/wsbackend/AbstractHandler.py b/pyload/remote/wsbackend/AbstractHandler.py index 842d87473..e69ff2573 100644 --- a/pyload/remote/wsbackend/AbstractHandler.py +++ b/pyload/remote/wsbackend/AbstractHandler.py @@ -46,13 +46,12 @@ class AbstractHandler: req.api = None #when api is set client is logged in # allow login via session when webinterface is active - if self.core.config['webinterface']['activated']: - cookie = req.headers_in.getheader('Cookie') - s = self.load_session(cookie) - if s: - uid = s.get('uid', None) - req.api = self.api.withUserContext(uid) - self.log.debug("WS authenticated user with cookie: %d" % uid) + cookie = req.headers_in.getheader('Cookie') + s = self.load_session(cookie) + if s: + uid = s.get('uid', None) + req.api = self.api.withUserContext(uid) + self.log.debug("WS authenticated user with cookie: %d" % uid) self.on_open(req) diff --git a/pyload/remote/wsbackend/Server.py b/pyload/remote/wsbackend/Server.py index 02da44f04..9a6649ca9 100644 --- a/pyload/remote/wsbackend/Server.py +++ b/pyload/remote/wsbackend/Server.py @@ -67,6 +67,7 @@ _MAX_MEMORIZED_LINES = 1024 def import_ssl(): global _HAS_SSL, _HAS_OPEN_SSL + global ssl, OpenSSL try: import ssl _HAS_SSL = True @@ -77,6 +78,8 @@ def import_ssl(): except ImportError: pass + return _HAS_OPEN_SSL or _HAS_SSL + class _StandaloneConnection(object): """Mimic mod_python mp_conn.""" @@ -648,6 +651,8 @@ class DefaultOptions: private_key = '' certificate = '' ca_certificate = '' + tls_client_ca = '' + tls_client_auth = False dispatcher = None request_queue_size = _DEFAULT_REQUEST_QUEUE_SIZE use_basic_auth = False diff --git a/pyload/web/Gruntfile.js b/pyload/web/Gruntfile.js index 0a97e7360..4711ca66d 100644 --- a/pyload/web/Gruntfile.js +++ b/pyload/web/Gruntfile.js @@ -21,7 +21,8 @@ module.exports = function(grunt) { var yeomanConfig = { app: 'app', dist: 'dist', - banner: '/* Copyright(c) 2008-2013 pyLoad Team */\n' + banner: '/* Copyright(c) 2008-2013 pyLoad Team */\n', + protocol: 'http' }; grunt.initConfig({ @@ -50,7 +51,8 @@ module.exports = function(grunt) { options: { port: 9000, // change this to '0.0.0.0' to access the server from outside - hostname: 'localhost' + hostname: 'localhost', + protocol: '<%= yeoman.protocol %>' }, livereload: { options: { @@ -85,7 +87,7 @@ module.exports = function(grunt) { }, open: { // Opens the webbrowser server: { - path: 'http://localhost:<%= connect.options.port %>' + path: '<%= yeoman.protocol %>://localhost:<%= connect.options.port %>' } }, clean: { diff --git a/pyload/web/ServerThread.py b/pyload/web/ServerThread.py index 809c6c800..a2e375f1f 100644 --- a/pyload/web/ServerThread.py +++ b/pyload/web/ServerThread.py @@ -26,14 +26,14 @@ class WebServer(threading.Thread): else: raise Exception("No config context provided") - self.server = config['webinterface']['server'] - self.https = config['webinterface']['https'] + self.server = config['webUI']['server'] + self.https = config['webUI']['https'] self.cert = config["ssl"]["cert"] self.key = config["ssl"]["key"] - self.host = config['webinterface']['host'] - self.port = config['webinterface']['port'] + self.host = config['webUI']['host'] + self.port = config['webUI']['port'] self.debug = config['general']['debug_mode'] - self.force_server = config['webinterface']['force_server'] + self.force_server = config['webUI']['force_server'] self.error = None self.setDaemon(True) diff --git a/pyload/web/app/index.html b/pyload/web/app/index.html index 98e1bf233..08366f665 100644 --- a/pyload/web/app/index.html +++ b/pyload/web/app/index.html @@ -22,7 +22,7 @@ // Use value set by templateEngine or default val function configValue(string, defaultValue) { - if (string.indexOf('{{') > -1) + if (string.indexOf('{{') > -1 && string !== 'None' && string !== '') return defaultValue; return string; } @@ -38,10 +38,10 @@ window.hostProtocol = window.location.protocol + '//'; window.hostAddress = window.location.hostname; window.hostPort = configValue('{{web}}', '8001'); - // TODO - window.pathPrefix = '/'; + window.external = configValue('{{external}}', 'true').toLowerCase(); + window.pathPrefix = configValue('{{prefix}}', ''); window.wsAddress = configValue('{{ws}}', 'ws://%s:7227'); - window.setup = configValue('{{setup}}', 'false'); + window.setup = configValue('{{setup}}', 'false').toLowerCase(); require(['config'], function(Config) { require(['default'], function(App) { diff --git a/pyload/web/app/scripts/app.js b/pyload/web/app/scripts/app.js index af5c50b14..68a20666d 100644 --- a/pyload/web/app/scripts/app.js +++ b/pyload/web/app/scripts/app.js @@ -41,8 +41,11 @@ define([ }; App.apiUrl = function(path) { - var url = window.hostProtocol + window.hostAddress + ':' + window.hostPort + window.pathPrefix + path; - return url; + var prefix = window.pathPrefix; + if (window.external !== 'false') + prefix = window.hostProtocol + window.hostAddress + ':' + window.hostPort + prefix; + + return prefix + '/' + path; }; // Add Global Helper functions diff --git a/pyload/web/app/scripts/views/headerView.js b/pyload/web/app/scripts/views/headerView.js index 49298d450..7d892bf01 100644 --- a/pyload/web/app/scripts/views/headerView.js +++ b/pyload/web/app/scripts/views/headerView.js @@ -62,21 +62,27 @@ define(['jquery', 'underscore', 'backbone', 'app', 'models/ServerStatus', 'colle }); // TODO: button to start stop refresh - var ws = App.openWebSocket('/async'); - ws.onopen = function() { - ws.send(JSON.stringify('start')); - }; - // TODO compare with polling - ws.onmessage = _.bind(this.onData, this); - ws.onerror = function(error) { - console.log(error); - alert('WebSocket error' + error); - }; - ws.onclose = function() { - alert('WebSocket was closed'); - }; - - this.ws = ws; + // TODO: catch ws errors / switch into ws less mode + try { + var ws = App.openWebSocket('/async'); + ws.onopen = function() { + ws.send(JSON.stringify('start')); + }; + // TODO compare with polling + ws.onmessage = _.bind(this.onData, this); + ws.onerror = function(error) { + console.log(error); + alert('WebSocket error ' + error); + }; + ws.onclose = function() { + alert('WebSocket was closed'); + }; + + this.ws = ws; + + } catch (e) { + alert('Could not open WebSocket: ' + e); + } }, gotoDashboard: function() { diff --git a/pyload/web/package.json b/pyload/web/package.json index 5de79a814..4ea7ce484 100644 --- a/pyload/web/package.json +++ b/pyload/web/package.json @@ -14,7 +14,7 @@ "grunt-contrib-jshint": "~0.4.1", "grunt-contrib-less": "~0.5.2", "grunt-contrib-cssmin": "~0.6.0", - "grunt-contrib-connect": "~0.2.0", + "grunt-contrib-connect": "~0.5.0", "grunt-contrib-clean": "~0.4.0", "grunt-contrib-htmlmin": "~0.1.3", "grunt-contrib-requirejs": "~0.4.1", @@ -29,7 +29,7 @@ "grunt-concurrent": "~0.1.0", "matchdep": "~0.1.1", "rjs-build-analysis": "0.0.3", - "connect-livereload": "~0.2.0" + "connect-livereload": "~0.3.0" }, "engines": { "node": ">=0.8.0" diff --git a/pyload/web/pyload_app.py b/pyload/web/pyload_app.py index 50d9b9731..1a54c4a93 100644 --- a/pyload/web/pyload_app.py +++ b/pyload/web/pyload_app.py @@ -21,7 +21,7 @@ from os.path import join, exists from bottle import route, static_file, response, request, redirect, template -from webinterface import PYLOAD, PROJECT_DIR, SETUP, APP_PATH, UNAVAILALBE +from webinterface import PYLOAD, PROJECT_DIR, SETUP, APP_PATH, UNAVAILALBE, PREFIX from utils import login_required, add_json_header, select_language @@ -71,16 +71,17 @@ def index(): # set variable depending on setup mode setup = 'false' if SETUP is None else 'true' ws = PYLOAD.getWSAddress() if PYLOAD else False + external = PYLOAD.getConfigValue('webUI', 'external') if PYLOAD else None web = None if PYLOAD: - web = PYLOAD.getConfigValue('webinterface', 'port') + web = PYLOAD.getConfigValue('webUI', 'port') elif SETUP: - web = SETUP.config['webinterface']['port'] + web = SETUP.config['webUI']['port'] # Render variables into the html page if resp.status_code == 200: content = resp.body.read() - resp.body = template(content, ws=ws, web=web, setup=setup) + resp.body = template(content, ws=ws, web=web, setup=setup, external=external, prefix=PREFIX) resp.content_length = len(resp.body) return resp diff --git a/pyload/web/servers.py b/pyload/web/servers.py index a3c51e36b..2755cbaff 100644 --- a/pyload/web/servers.py +++ b/pyload/web/servers.py @@ -157,6 +157,6 @@ class FlupFCGIServer(ServerAdapter): flup.server.fcgi.WSGIServer(handler, **self.options).run() # Order is important and gives every server precedence over others! -all_server = [BjoernServer, TornadoServer, EventletServer, CherryPyWSGI] +all_server = [TornadoServer, EventletServer, CherryPyWSGI] # Some are deactivated because they have some flaws ##all_server = [FapwsServer, MeinheldServer, BjoernServer, TornadoServer, EventletServer, CherryPyWSGI]
\ No newline at end of file diff --git a/pyload/web/webinterface.py b/pyload/web/webinterface.py index 21c5f4a03..f732a933d 100644 --- a/pyload/web/webinterface.py +++ b/pyload/web/webinterface.py @@ -46,9 +46,9 @@ else: from pyload.utils.JsEngine import JsEngine JS = JsEngine() -TEMPLATE = config.get('webinterface', 'template') +TEMPLATE = config.get('webUI', 'template') DL_ROOT = config.get('general', 'download_folder') -PREFIX = config.get('webinterface', 'prefix') +PREFIX = config.get('webUI', 'prefix') if PREFIX: PREFIX = PREFIX.rstrip("/") @@ -59,7 +59,7 @@ APP_PATH = "app" UNAVAILALBE = True # webUI build is available -if exists(join(PROJECT_DIR, "app", "components")) and exists(join(PROJECT_DIR, ".tmp")) and config.get('webinterface', 'develop'): +if exists(join(PROJECT_DIR, "app", "components")) and exists(join(PROJECT_DIR, ".tmp")) and config.get('webUI', 'develop'): UNAVAILALBE = False elif exists(join(PROJECT_DIR, "dist", "index.html")): APP_PATH = "dist" |