summaryrefslogtreecommitdiffstats
path: root/deluge/ui
diff options
context:
space:
mode:
authorbendikro <bro.devel+deluge@gmail.com>2015-12-15 18:26:55 +0100
committerbendikro <bro.devel+deluge@gmail.com>2016-04-10 00:10:53 +0200
commitd58960d723f8fac5e090ae08ff7f6d680b736395 (patch)
tree874cd36c333caaa900db2884291f0a08211ea9b1 /deluge/ui
parentbcc1db12e5763e9cc6b2ad5379b16381221f52cb (diff)
downloaddeluge-d58960d723f8fac5e090ae08ff7f6d680b736395.tar.gz
deluge-d58960d723f8fac5e090ae08ff7f6d680b736395.tar.bz2
deluge-d58960d723f8fac5e090ae08ff7f6d680b736395.zip
[Tests] [Web] Make JSON independent of Web component
* Implement JSONTestCase in test_json_api.py * Implement WebAPITestCase test case in test_web_api.py
Diffstat (limited to 'deluge/ui')
-rw-r--r--deluge/ui/web/auth.py41
-rw-r--r--deluge/ui/web/json_api.py174
-rw-r--r--deluge/ui/web/server.py21
3 files changed, 91 insertions, 145 deletions
diff --git a/deluge/ui/web/auth.py b/deluge/ui/web/auth.py
index d90273aea..ae6b11da5 100644
--- a/deluge/ui/web/auth.py
+++ b/deluge/ui/web/auth.py
@@ -16,7 +16,6 @@ from email.utils import formatdate
from twisted.internet.task import LoopingCall
-from deluge import component
from deluge.common import utf8_encoded
log = logging.getLogger(__name__)
@@ -79,9 +78,10 @@ class Auth(JSONComponent):
The component that implements authentification into the JSON interface.
"""
- def __init__(self):
+ def __init__(self, config):
super(Auth, self).__init__("Auth")
self.worker = LoopingCall(self._clean_sessions)
+ self.config = config
def start(self):
self.worker.start(5)
@@ -90,19 +90,18 @@ class Auth(JSONComponent):
self.worker.stop()
def _clean_sessions(self):
- config = component.get("DelugeWeb").config
- session_ids = config["sessions"].keys()
+ session_ids = self.config["sessions"].keys()
now = time.gmtime()
for session_id in session_ids:
- session = config["sessions"][session_id]
+ session = self.config["sessions"][session_id]
if "expires" not in session:
- del config["sessions"][session_id]
+ del self.config["sessions"][session_id]
continue
if time.gmtime(session["expires"]) < now:
- del config["sessions"][session_id]
+ del self.config["sessions"][session_id]
continue
def _create_session(self, request, login='admin'):
@@ -117,21 +116,18 @@ class Auth(JSONComponent):
m.update(os.urandom(32))
session_id = m.hexdigest()
- config = component.get("DelugeWeb").config
-
- expires, expires_str = make_expires(config["session_timeout"])
+ expires, expires_str = make_expires(self.config["session_timeout"])
checksum = str(make_checksum(session_id))
request.addCookie('_session_id', session_id + checksum,
path=request.base + "json", expires=expires_str)
log.debug("Creating session for %s", login)
- config = component.get("DelugeWeb").config
- if isinstance(config["sessions"], list):
- config.config["sessions"] = {}
+ if isinstance(self.config["sessions"], list):
+ self.config["sessions"] = {}
- config["sessions"][session_id] = {
+ self.config["sessions"][session_id] = {
"login": login,
"level": AUTH_LEVEL_ADMIN,
"expires": expires
@@ -139,7 +135,7 @@ class Auth(JSONComponent):
return True
def check_password(self, password):
- config = component.get("DelugeWeb").config
+ config = self.config
if "pwd_md5" in config.config:
# We are using the 1.2-dev auth method
log.debug("Received a password via the 1.2-dev auth method")
@@ -206,16 +202,15 @@ class Auth(JSONComponent):
:raises: Exception
"""
- config = component.get("DelugeWeb").config
session_id = get_session_id(request.getCookie("_session_id"))
- if session_id not in config["sessions"]:
+ if session_id not in self.config["sessions"]:
auth_level = AUTH_LEVEL_NONE
session_id = None
else:
- session = config["sessions"][session_id]
+ session = self.config["sessions"][session_id]
auth_level = session["level"]
- expires, expires_str = make_expires(config["session_timeout"])
+ expires, expires_str = make_expires(self.config["session_timeout"])
session["expires"] = expires
_session_id = request.getCookie("_session_id")
@@ -253,9 +248,8 @@ class Auth(JSONComponent):
salt = hashlib.sha1(os.urandom(32)).hexdigest()
s = hashlib.sha1(salt)
s.update(utf8_encoded(new_password))
- config = component.get("DelugeWeb").config
- config["pwd_salt"] = salt
- config["pwd_sha1"] = s.hexdigest()
+ self.config["pwd_salt"] = salt
+ self.config["pwd_sha1"] = s.hexdigest()
return True
@export
@@ -290,8 +284,7 @@ class Auth(JSONComponent):
:param session_id: the id for the session to remove
:type session_id: string
"""
- config = component.get("DelugeWeb").config
- del config["sessions"][__request__.session_id]
+ del self.config["sessions"][__request__.session_id]
return True
@export(AUTH_LEVEL_NONE)
diff --git a/deluge/ui/web/json_api.py b/deluge/ui/web/json_api.py
index 7e63d7e81..af65af545 100644
--- a/deluge/ui/web/json_api.py
+++ b/deluge/ui/web/json_api.py
@@ -16,14 +16,12 @@ import shutil
import tempfile
import time
from types import FunctionType
-from urllib import unquote_plus
from twisted.internet import defer, reactor
from twisted.internet.defer import Deferred, DeferredList
from twisted.web import http, resource, server
-from deluge import component, httpdownloader
-from deluge.common import is_magnet
+from deluge import common, component, httpdownloader
from deluge.configmanager import ConfigManager, get_config_dir
from deluge.ui import common as uicommon
from deluge.ui.client import Client, client
@@ -104,47 +102,6 @@ class JSON(resource.Resource, component.Component):
return methods
return client.daemon.get_method_list().addCallback(on_get_methods)
- def connect(self, host="localhost", port=58846, username="", password=""):
- """
- Connects the client to a daemon
- """
- d = client.connect(host, port, username, password)
-
- def on_client_connected(connection_id):
- """
- Handles the client successfully connecting to the daemon and
- invokes retrieving the method names.
- """
- d = self.get_remote_methods()
- component.get("Web.PluginManager").start()
- component.get("Web").start()
- return d
-
- return d.addCallback(on_client_connected)
-
- def disable(self):
- if not client.is_classicmode():
- client.disconnect()
- client.set_disconnect_callback(None)
-
- def enable(self):
- if not client.is_classicmode():
- client.set_disconnect_callback(self._on_client_disconnect)
- client.register_event_handler("PluginEnabledEvent", self.get_remote_methods)
- client.register_event_handler("PluginDisabledEvent", self.get_remote_methods)
- if component.get("DelugeWeb").config["default_daemon"]:
- # Sort out getting the default daemon here
- default = component.get("DelugeWeb").config["default_daemon"]
- host = component.get("Web").get_host(default)
- if host:
- self.connect(*host[1:])
- else:
- self.connect()
-
- def _on_client_disconnect(self, *args):
- component.get("Web.PluginManager").stop()
- component.get("Web").stop()
-
def _exec_local(self, method, params, request):
"""
Handles executing all local methods.
@@ -180,9 +137,8 @@ class JSON(resource.Resource, component.Component):
"""
try:
request.json = json.loads(request.json)
- except ValueError:
+ except (ValueError, TypeError):
raise JSONException("JSON not decodable")
-
if "method" not in request.json or "id" not in request.json or \
"params" not in request.json:
raise JSONException("Invalid JSON request")
@@ -257,12 +213,12 @@ class JSON(resource.Resource, component.Component):
request.setHeader("content-type", "application/x-json")
request.write(compress(response, request))
request.finish()
+ return server.NOT_DONE_YET
def render(self, request):
"""
Handles all the POST requests made to the /json controller.
"""
-
if request.method != "POST":
request.setResponseCode(http.NOT_ALLOWED)
request.finish()
@@ -421,7 +377,30 @@ class WebApi(JSONComponent):
except KeyError:
self.sessionproxy = SessionProxy()
- def get_host(self, host_id):
+ def disable(self):
+ if not client.is_classicmode():
+ client.disconnect()
+ client.set_disconnect_callback(None)
+
+ def enable(self):
+ if not client.is_classicmode():
+ client.set_disconnect_callback(self._on_client_disconnect)
+ client.register_event_handler("PluginEnabledEvent", self._json.get_remote_methods)
+ client.register_event_handler("PluginDisabledEvent", self._json.get_remote_methods)
+ if component.get("DelugeWeb").config["default_daemon"]:
+ # Sort out getting the default daemon here
+ default = component.get("DelugeWeb").config["default_daemon"]
+ host = component.get("Web")._get_host(default)
+ if host:
+ self._connect_daemon(*host[1:])
+ else:
+ self._connect_daemon()
+
+ def _on_client_disconnect(self, *args):
+ component.get("Web.PluginManager").stop()
+ self.stop()
+
+ def _get_host(self, host_id):
"""
Return the information about a host
@@ -442,6 +421,24 @@ class WebApi(JSONComponent):
self.core_config.stop()
self.sessionproxy.stop()
+ def _connect_daemon(self, host="localhost", port=58846, username="", password=""):
+ """
+ Connects the client to a daemon
+ """
+ d = client.connect(host, port, username, password)
+
+ def on_client_connected(connection_id):
+ """
+ Handles the client successfully connecting to the daemon and
+ invokes retrieving the method names.
+ """
+ d = self._json.get_remote_methods()
+ component.get("Web.PluginManager").start()
+ self.start()
+ return d
+
+ return d.addCallback(on_client_connected)
+
@export
def connect(self, host_id):
"""
@@ -452,16 +449,10 @@ class WebApi(JSONComponent):
:returns: the methods the daemon supports
:rtype: list
"""
- d = Deferred()
-
- def on_connected(methods):
- d.callback(methods)
- host = self.get_host(host_id)
+ host = self._get_host(host_id)
if host:
- self._json.connect(*host[1:]).addCallback(on_connected)
- else:
- return defer.fail(Exception("Bad host id"))
- return d
+ return self._connect_daemon(*host[1:])
+ return defer.fail(Exception("Bad host id"))
@export
def connected(self):
@@ -478,8 +469,12 @@ class WebApi(JSONComponent):
"""
Disconnect the web interface from the connected daemon.
"""
- client.disconnect()
- return True
+ d = client.disconnect()
+
+ def on_disconnect(reason):
+ return str(reason)
+ d.addCallback(on_disconnect)
+ return d
@export
def update_ui(self, keys, filter_dict):
@@ -677,47 +672,7 @@ class WebApi(JSONComponent):
@export
def get_magnet_info(self, uri):
- """
- Return information about a magnet link.
-
- :param uri: the magnet link
- :type uri: string
-
- :returns: information about the magnet link:
-
- ::
-
- {
- "name": the torrent name,
- "info_hash": the torrents info_hash,
- "files_tree": empty value for magnet links
- }
-
- :rtype: dictionary
- """
- magnet_scheme = 'magnet:?'
- xt_param = 'xt=urn:btih:'
- dn_param = 'dn='
- if uri.startswith(magnet_scheme):
- name = None
- info_hash = None
- for param in uri[len(magnet_scheme):].split('&'):
- if param.startswith(xt_param):
- xt_hash = param[len(xt_param):]
- if len(xt_hash) == 32:
- info_hash = base64.b32decode(xt_hash).encode("hex")
- elif len(xt_hash) == 40:
- info_hash = xt_hash
- else:
- break
- elif param.startswith(dn_param):
- name = unquote_plus(param[len(dn_param):])
-
- if info_hash:
- if not name:
- name = info_hash
- return {"name": name, "info_hash": info_hash, "files_tree": ''}
- return False
+ return common.get_magnet_info(uri)
@export
def add_torrents(self, torrents):
@@ -737,7 +692,7 @@ class WebApi(JSONComponent):
"""
for torrent in torrents:
- if is_magnet(torrent["path"]):
+ if common.is_magnet(torrent["path"]):
log.info("Adding torrent from magnet uri `%s` with options `%r`",
torrent["path"], torrent["options"])
client.core.add_torrent_magnet(torrent["path"], torrent["options"])
@@ -769,7 +724,7 @@ class WebApi(JSONComponent):
return host_id, host, port, status, info
try:
- host_id, host, port, user, password = self.get_host(host_id)
+ host_id, host, port, user, password = self._get_host(host_id)
except TypeError:
host = None
port = None
@@ -808,8 +763,8 @@ class WebApi(JSONComponent):
@export
def start_daemon(self, port):
"""
- Starts a local daemon.
- """
+ Starts a local daemon.
+ """
client.start_daemon(port, get_config_dir())
@export
@@ -821,7 +776,7 @@ class WebApi(JSONComponent):
:type host_id: string
"""
main_deferred = Deferred()
- host = self.get_host(host_id)
+ host = self._get_host(host_id)
if not host:
main_deferred.callback((False, _("Daemon doesn't exist")))
return main_deferred
@@ -864,7 +819,7 @@ class WebApi(JSONComponent):
# Check to see if there is already an entry for this host and return
# if thats the case
for entry in self.host_list["hosts"]:
- if (entry[0], entry[1], entry[2]) == (host, port, username):
+ if (entry[1], entry[2], entry[3]) == (host, port, username):
return (False, "Host already in the list")
try:
@@ -877,7 +832,7 @@ class WebApi(JSONComponent):
self.host_list["hosts"].append([connection_id, host, port, username,
password])
self.host_list.save()
- return (True,)
+ return True, connection_id
@export
def remove_host(self, connection_id):
@@ -887,7 +842,7 @@ class WebApi(JSONComponent):
:param host_id: the hash id of the host
:type host_id: string
"""
- host = self.get_host(connection_id)
+ host = self._get_host(connection_id)
if host is None:
return False
@@ -919,6 +874,9 @@ class WebApi(JSONComponent):
"""
web_config = component.get("DelugeWeb").config
for key in config.keys():
+ if key in ["sessions", "pwd_salt", "pwd_sha1"]:
+ log.warn("Ignored attempt to overwrite web config key '%s'" % key)
+ continue
if isinstance(config[key], basestring):
config[key] = config[key].encode("utf8")
web_config[key] = config[key]
diff --git a/deluge/ui/web/server.py b/deluge/ui/web/server.py
index d4d49f37e..31546401d 100644
--- a/deluge/ui/web/server.py
+++ b/deluge/ui/web/server.py
@@ -16,7 +16,7 @@ import tempfile
from OpenSSL.crypto import FILETYPE_PEM
from twisted.application import internet, service
-from twisted.internet import defer, error, reactor
+from twisted.internet import defer, reactor
from twisted.internet.ssl import SSL, Certificate, CertificateOptions, KeyPair
from twisted.web import http, resource, server, static
@@ -533,7 +533,6 @@ class DelugeWeb(component.Component):
def __init__(self):
super(DelugeWeb, self).__init__("DelugeWeb")
self.config = configmanager.ConfigManager("web.conf", CONFIG_DEFAULTS)
-
self.socket = None
self.top_level = TopLevel()
self.site = server.Site(self.top_level)
@@ -544,7 +543,7 @@ class DelugeWeb(component.Component):
self.cert = self.config["cert"]
self.base = self.config["base"]
self.web_api = WebApi()
- self.auth = Auth()
+ self.auth = Auth(self.config)
# Initalize the plugins
self.plugins = PluginManager()
@@ -568,17 +567,15 @@ class DelugeWeb(component.Component):
return 1
SetConsoleCtrlHandler(win_handler)
- def start(self, start_reactor=True):
+ def start(self):
log.info("%s %s.", _("Starting server in PID"), os.getpid())
if self.https:
self.start_ssl()
else:
self.start_normal()
- component.get("JSON").enable()
-
- if start_reactor:
- reactor.run()
+ component.get("Web").enable()
+ reactor.run()
def start_normal(self):
self.socket = reactor.listenTCP(self.port, self.site, interface=self.interface)
@@ -600,7 +597,7 @@ class DelugeWeb(component.Component):
def stop(self):
log.info("Shutting down webserver")
- component.get("JSON").disable()
+ component.get("Web").disable()
self.plugins.disable_plugins()
log.debug("Saving configuration file")
@@ -616,10 +613,8 @@ class DelugeWeb(component.Component):
def shutdown(self, *args):
self.stop()
- try:
- reactor.stop()
- except error.ReactorNotRunning:
- log.debug("Reactor not running")
+ reactor.stop()
+
if __name__ == "__builtin__":
deluge_web = DelugeWeb()