also update the update_ui method in json_api so it gets the status more like how the gtkui does
850 lines
27 KiB
Python
850 lines
27 KiB
Python
#
|
|
# deluge/ui/web/json_api.py
|
|
#
|
|
# Copyright (C) 2009 Damien Churchill <damoxc@gmail.com>
|
|
#
|
|
# Deluge is free software.
|
|
#
|
|
# You may 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.
|
|
#
|
|
# deluge 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 deluge. If not, write to:
|
|
# The Free Software Foundation, Inc.,
|
|
# 51 Franklin Street, Fifth Floor
|
|
# Boston, MA 02110-1301, USA.
|
|
#
|
|
# In addition, as a special exception, the copyright holders give
|
|
# permission to link the code of portions of this program with the OpenSSL
|
|
# library.
|
|
# You must obey the GNU General Public License in all respects for all of
|
|
# the code used other than OpenSSL. If you modify file(s) with this
|
|
# exception, you may extend this exception to your version of the file(s),
|
|
# but you are not obligated to do so. If you do not wish to do so, delete
|
|
# this exception statement from your version. If you delete this exception
|
|
# statement from all source files in the program, then also delete it here.
|
|
#
|
|
#
|
|
|
|
import os
|
|
import time
|
|
import base64
|
|
import urllib
|
|
import logging
|
|
import hashlib
|
|
import tempfile
|
|
|
|
from types import FunctionType
|
|
from twisted.internet.defer import Deferred, DeferredList
|
|
from twisted.web import http, resource, server
|
|
|
|
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
|
|
from deluge.ui.coreconfig import CoreConfig
|
|
|
|
from deluge.ui.web.common import _
|
|
json = common.json
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
AUTH_LEVEL_DEFAULT = None
|
|
AuthError = None
|
|
|
|
class JSONComponent(component.Component):
|
|
def __init__(self, name, interval=1, depend=None):
|
|
super(JSONComponent, self).__init__(name, interval, depend)
|
|
self._json = component.get("JSON")
|
|
self._json.register_object(self, name)
|
|
|
|
def export(auth_level=AUTH_LEVEL_DEFAULT):
|
|
"""
|
|
Decorator function to register an object's method as a RPC. The object
|
|
will need to be registered with a `:class:JSON` to be effective.
|
|
|
|
:param func: the function to export
|
|
:type func: function
|
|
:keyword auth_level: the auth level required to call this method
|
|
:type auth_level: int
|
|
|
|
"""
|
|
global AUTH_LEVEL_DEFAULT, AuthError
|
|
if AUTH_LEVEL_DEFAULT is None:
|
|
from deluge.ui.web.auth import AUTH_LEVEL_DEFAULT, AuthError
|
|
|
|
def wrap(func, *args, **kwargs):
|
|
func._json_export = True
|
|
func._json_auth_level = auth_level
|
|
return func
|
|
|
|
if type(auth_level) is FunctionType:
|
|
func = auth_level
|
|
auth_level = AUTH_LEVEL_DEFAULT
|
|
return wrap(func)
|
|
else:
|
|
return wrap
|
|
|
|
class JSONException(Exception):
|
|
def __init__(self, inner_exception):
|
|
self.inner_exception = inner_exception
|
|
Exception.__init__(self, str(inner_exception))
|
|
|
|
class JSON(resource.Resource, component.Component):
|
|
"""
|
|
A Twisted Web resource that exposes a JSON-RPC interface for web clients \
|
|
to use.
|
|
"""
|
|
|
|
def __init__(self):
|
|
resource.Resource.__init__(self)
|
|
component.Component.__init__(self, "JSON")
|
|
self._remote_methods = []
|
|
self._local_methods = {}
|
|
client.disconnect_callback = self._on_client_disconnect
|
|
|
|
def connect(self, host="localhost", port=58846, username="", password=""):
|
|
"""
|
|
Connects the client to a daemon
|
|
"""
|
|
d = Deferred()
|
|
_d = client.connect(host, port, username, password)
|
|
|
|
def on_get_methods(methods):
|
|
"""
|
|
Handles receiving the method names
|
|
"""
|
|
self._remote_methods = methods
|
|
methods = list(self._remote_methods)
|
|
methods.extend(self._local_methods)
|
|
d.callback(methods)
|
|
|
|
def on_client_connected(connection_id):
|
|
"""
|
|
Handles the client successfully connecting to the daemon and
|
|
invokes retrieving the method names.
|
|
"""
|
|
d = client.daemon.get_method_list()
|
|
d.addCallback(on_get_methods)
|
|
component.get("Web.PluginManager").start()
|
|
component.get("Web").core_config.start()
|
|
_d.addCallback(on_client_connected)
|
|
return d
|
|
|
|
def disable(self):
|
|
client.disconnect()
|
|
|
|
def enable(self):
|
|
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)
|
|
self.connect()
|
|
|
|
def _on_client_disconnect(self, *args):
|
|
component.get("Web.PluginManager").stop()
|
|
|
|
def _exec_local(self, method, params, request):
|
|
"""
|
|
Handles executing all local methods.
|
|
"""
|
|
if method == "system.listMethods":
|
|
d = Deferred()
|
|
methods = list(self._remote_methods)
|
|
methods.extend(self._local_methods)
|
|
d.callback(methods)
|
|
return d
|
|
elif method in self._local_methods:
|
|
# This will eventually process methods that the server adds
|
|
# and any plugins.
|
|
meth = self._local_methods[method]
|
|
meth.func_globals['__request__'] = request
|
|
component.get("Auth").check_request(request, meth)
|
|
return meth(*params)
|
|
raise JSONException("Unknown system method")
|
|
|
|
def _exec_remote(self, method, params, request):
|
|
"""
|
|
Executes methods using the Deluge client.
|
|
"""
|
|
component.get("Auth").check_request(request, level=AUTH_LEVEL_DEFAULT)
|
|
core_component, method = method.split(".")
|
|
return getattr(getattr(client, core_component), method)(*params)
|
|
|
|
def _handle_request(self, request):
|
|
"""
|
|
Takes some json data as a string and attempts to decode it, and process
|
|
the rpc object that should be contained, returning a deferred for all
|
|
procedure calls and the request id.
|
|
"""
|
|
try:
|
|
request.json = json.loads(request.json)
|
|
except ValueError:
|
|
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")
|
|
|
|
method, params = request.json["method"], request.json["params"]
|
|
request_id = request.json["id"]
|
|
result = None
|
|
error = None
|
|
|
|
try:
|
|
if method.startswith("system.") or method in self._local_methods:
|
|
result = self._exec_local(method, params, request)
|
|
elif method in self._remote_methods:
|
|
result = self._exec_remote(method, params, request)
|
|
else:
|
|
error = {"message": "Unknown method", "code": 2}
|
|
except AuthError, e:
|
|
error = {"message": "Not authenticated", "code": 1}
|
|
except Exception, e:
|
|
log.error("Error calling method `%s`", method)
|
|
log.exception(e)
|
|
|
|
error = {"message": e.message, "code": 3}
|
|
|
|
return request_id, result, error
|
|
|
|
def _on_rpc_request_finished(self, result, response, request):
|
|
"""
|
|
Sends the response of any rpc calls back to the json-rpc client.
|
|
"""
|
|
response["result"] = result
|
|
return self._send_response(request, response)
|
|
|
|
def _on_rpc_request_failed(self, reason, response, request):
|
|
"""
|
|
Handles any failures that occured while making an rpc call.
|
|
"""
|
|
request.setResponseCode(http.INTERNAL_SERVER_ERROR)
|
|
return ""
|
|
|
|
def _on_json_request(self, request):
|
|
"""
|
|
Handler to take the json data as a string and pass it on to the
|
|
_handle_request method for further processing.
|
|
"""
|
|
log.debug("json-request: %s", request.json)
|
|
response = {"result": None, "error": None, "id": None}
|
|
response["id"], d, response["error"] = self._handle_request(request)
|
|
|
|
if isinstance(d, Deferred):
|
|
d.addCallback(self._on_rpc_request_finished, response, request)
|
|
d.addErrback(self._on_rpc_request_failed, response, request)
|
|
return d
|
|
else:
|
|
response["result"] = d
|
|
return self._send_response(request, response)
|
|
|
|
def _on_json_request_failed(self, reason, request):
|
|
"""
|
|
Errback handler to return a HTTP code of 500.
|
|
"""
|
|
log.exception(reason)
|
|
request.setResponseCode(http.INTERNAL_SERVER_ERROR)
|
|
return ""
|
|
|
|
def _send_response(self, request, response):
|
|
response = json.dumps(response)
|
|
request.setHeader("content-type", "application/x-json")
|
|
request.write(response)
|
|
request.finish()
|
|
|
|
def render(self, request):
|
|
"""
|
|
Handles all the POST requests made to the /json controller.
|
|
"""
|
|
|
|
if request.method != "POST":
|
|
request.setResponseCode(http.NOT_ALLOWED)
|
|
return ""
|
|
|
|
try:
|
|
request.content.seek(0)
|
|
request.json = request.content.read()
|
|
d = self._on_json_request(request)
|
|
return server.NOT_DONE_YET
|
|
except Exception, e:
|
|
return self._on_json_request_failed(e, request)
|
|
|
|
def register_object(self, obj, name=None):
|
|
"""
|
|
Registers an object to export it's rpc methods. These methods should
|
|
be exported with the export decorator prior to registering the object.
|
|
|
|
:param obj: the object that we want to export
|
|
:type obj: object
|
|
:param name: the name to use, if None, it will be the class name of the object
|
|
:type name: string
|
|
"""
|
|
name = name or obj.__class__.__name__
|
|
name = name.lower()
|
|
|
|
for d in dir(obj):
|
|
if d[0] == "_":
|
|
continue
|
|
if getattr(getattr(obj, d), '_json_export', False):
|
|
log.debug("Registering method: %s", name + "." + d)
|
|
self._local_methods[name + "." + d] = getattr(obj, d)
|
|
|
|
DEFAULT_HOST = "127.0.0.1"
|
|
DEFAULT_PORT = 58846
|
|
|
|
DEFAULT_HOSTS = {
|
|
"hosts": [(hashlib.sha1(str(time.time())).hexdigest(),
|
|
DEFAULT_HOST, DEFAULT_PORT, "", "")]
|
|
}
|
|
HOSTLIST_ID = 0
|
|
HOSTLIST_NAME = 1
|
|
HOSTLIST_PORT = 2
|
|
HOSTLIST_USER = 3
|
|
HOSTLIST_PASS = 4
|
|
|
|
HOSTS_ID = HOSTLIST_ID
|
|
HOSTS_NAME = HOSTLIST_NAME
|
|
HOSTS_PORT = HOSTLIST_PORT
|
|
HOSTS_STATUS = 3
|
|
HOSTS_INFO = 4
|
|
|
|
FILES_KEYS = ["files", "file_progress", "file_priorities"]
|
|
|
|
class EventQueue(object):
|
|
"""
|
|
This class subscribes to events from the core and stores them until all
|
|
the subscribed listeners have received the events.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.__events = {}
|
|
self.__handlers = {}
|
|
self.__queue = {}
|
|
|
|
def add_listener(self, listener_id, event):
|
|
"""
|
|
Add a listener to the event queue.
|
|
|
|
:param listener_id: A unique id for the listener
|
|
:type listener_id: string
|
|
:param event: The event name
|
|
:type event: string
|
|
"""
|
|
if event not in self.__events:
|
|
|
|
def on_event(*args):
|
|
for listener in self.__events[event]:
|
|
if listener not in self.__queue:
|
|
self.__queue[listener] = []
|
|
self.__queue[listener].append((event, args))
|
|
|
|
client.register_event_handler(event, on_event)
|
|
self.__handlers[event] = on_event
|
|
self.__events[event] = [listener_id]
|
|
elif listener_id not in self.__events[event]:
|
|
self.__events[event].append(listener_id)
|
|
|
|
def get_events(self, listener_id):
|
|
"""
|
|
Retrieve the pending events for the listener.
|
|
|
|
:param listener_id: A unique id for the listener
|
|
:type listener_id: string
|
|
"""
|
|
if listener_id in self.__queue:
|
|
queue = self.__queue[listener_id]
|
|
del self.__queue[listener_id]
|
|
return queue
|
|
return None
|
|
|
|
def remove_listener(self, listener_id, event):
|
|
"""
|
|
Remove a listener from the event queue.
|
|
|
|
:param listener_id: The unique id for the listener
|
|
:type listener_id: string
|
|
:param event: The event name
|
|
:type event: string
|
|
"""
|
|
self.__events[event].remove(listener_id)
|
|
if not self.__events[event]:
|
|
client.deregister_event_handler(event, self.__handlers[event])
|
|
del self.__events[event]
|
|
del self.__handlers[event]
|
|
|
|
class WebApi(JSONComponent):
|
|
"""
|
|
The component that implements all the methods required for managing
|
|
the web interface. The complete web json interface also exposes all the
|
|
methods available from the core RPC.
|
|
"""
|
|
|
|
def __init__(self):
|
|
super(WebApi, self).__init__("Web")
|
|
self.host_list = ConfigManager("hostlist.conf.1.2", DEFAULT_HOSTS)
|
|
self.core_config = CoreConfig()
|
|
self.event_queue = EventQueue()
|
|
|
|
def get_host(self, host_id):
|
|
"""
|
|
Return the information about a host
|
|
|
|
:param host_id: the id of the host
|
|
:type host_id: string
|
|
:returns: the host information
|
|
:rtype: list
|
|
"""
|
|
for host in self.host_list["hosts"]:
|
|
if host[0] == host_id:
|
|
return host
|
|
|
|
@export
|
|
def connect(self, host_id):
|
|
"""
|
|
Connect the client to a daemon
|
|
|
|
:param host_id: the id of the daemon in the host list
|
|
:type host_id: string
|
|
:returns: the methods the daemon supports
|
|
:rtype: list
|
|
"""
|
|
d = Deferred()
|
|
def on_connected(methods):
|
|
d.callback(methods)
|
|
for host in self.host_list["hosts"]:
|
|
if host_id != host[0]:
|
|
continue
|
|
self._json.connect(*host[1:]).addCallback(on_connected)
|
|
return d
|
|
|
|
@export
|
|
def connected(self):
|
|
"""
|
|
The current connection state.
|
|
|
|
:returns: True if the client is connected
|
|
:rtype: booleon
|
|
"""
|
|
return client.connected()
|
|
|
|
@export
|
|
def disconnect(self):
|
|
"""
|
|
Disconnect the web interface from the connected daemon.
|
|
"""
|
|
client.disconnect()
|
|
return True
|
|
|
|
@export
|
|
def update_ui(self, keys, filter_dict):
|
|
"""
|
|
Gather the information required for updating the web interface.
|
|
|
|
:param keys: the information about the torrents to gather
|
|
:type keys: list
|
|
:param filter_dict: the filters to apply when selecting torrents.
|
|
:type filter_dict: dictionary
|
|
:returns: The torrent and ui information.
|
|
:rtype: dictionary
|
|
"""
|
|
d = Deferred()
|
|
ui_info = {
|
|
"torrents": None,
|
|
"filters": None,
|
|
"stats": {
|
|
"max_download": self.core_config.get("max_download_speed"),
|
|
"max_upload": self.core_config.get("max_upload_speed"),
|
|
"max_num_connections": self.core_config.get("max_connections_global")
|
|
}
|
|
}
|
|
|
|
if not client.connected():
|
|
d.callback(ui_info)
|
|
return d
|
|
|
|
def got_connections(connections):
|
|
ui_info["stats"]["num_connections"] = connections
|
|
|
|
def got_dht_nodes(nodes):
|
|
ui_info["stats"]["dht_nodes"] = nodes
|
|
|
|
def got_stats(stats):
|
|
ui_info["stats"]["upload_rate"] = stats["payload_upload_rate"]
|
|
ui_info["stats"]["download_rate"] = stats["payload_download_rate"]
|
|
ui_info["stats"]["download_protocol_rate"] = stats["download_rate"] - stats["payload_download_rate"]
|
|
ui_info["stats"]["upload_protocol_rate"] = stats["upload_rate"] - stats["payload_upload_rate"]
|
|
|
|
def got_filters(filters):
|
|
ui_info["filters"] = filters
|
|
|
|
def got_health(health):
|
|
ui_info["stats"]["has_incoming_connections"] = health
|
|
|
|
def got_torrents(torrents):
|
|
ui_info["torrents"] = torrents
|
|
|
|
def on_complete(result):
|
|
d.callback(ui_info)
|
|
|
|
d1 = client.core.get_torrents_status(filter_dict, keys)
|
|
d1.addCallback(got_torrents)
|
|
|
|
d2 = client.core.get_filter_tree()
|
|
d2.addCallback(got_filters)
|
|
|
|
d3 = client.core.get_session_status([
|
|
"payload_download_rate",
|
|
"payload_upload_rate",
|
|
"download_rate",
|
|
"upload_rate"
|
|
])
|
|
d3.addCallback(got_stats)
|
|
|
|
d4 = client.core.get_num_connections()
|
|
d4.addCallback(got_connections)
|
|
|
|
d5 = client.core.get_dht_nodes()
|
|
d5.addCallback(got_dht_nodes)
|
|
|
|
d6 = client.core.get_health()
|
|
d6.addCallback(got_health)
|
|
|
|
dl = DeferredList([d1, d2, d3, d4, d5, d6], consumeErrors=True)
|
|
dl.addCallback(on_complete)
|
|
return d
|
|
|
|
def _on_got_files(self, torrent, d):
|
|
files = torrent.get("files")
|
|
file_progress = torrent.get("file_progress")
|
|
file_priorities = torrent.get("file_priorities")
|
|
|
|
paths = []
|
|
info = {}
|
|
for index, torrent_file in enumerate(files):
|
|
path = torrent_file["path"]
|
|
paths.append(path)
|
|
torrent_file["progress"] = file_progress[index]
|
|
torrent_file["priority"] = file_priorities[index]
|
|
torrent_file["index"] = index
|
|
info[path] = torrent_file
|
|
|
|
def walk(path, item):
|
|
if type(item) is dict:
|
|
return item
|
|
return [info[path]["index"], info[path]["size"],
|
|
info[path]["progress"], info[path]["priority"]]
|
|
|
|
file_tree = uicommon.FileTree(paths)
|
|
file_tree.walk(walk)
|
|
d.callback(file_tree.get_tree())
|
|
|
|
@export
|
|
def get_torrent_files(self, torrent_id):
|
|
"""
|
|
Gets the files for a torrent in tree format
|
|
|
|
:param torrent_id: the id of the torrent to retrieve.
|
|
:type torrent_id: string
|
|
:returns: The torrents files in a tree
|
|
:rtype: dictionary
|
|
"""
|
|
main_deferred = Deferred()
|
|
d = client.core.get_torrent_status(torrent_id, FILES_KEYS)
|
|
d.addCallback(self._on_got_files, main_deferred)
|
|
return main_deferred
|
|
|
|
@export
|
|
def download_torrent_from_url(self, url, cookie=None):
|
|
"""
|
|
Download a torrent file from a url to a temporary directory.
|
|
|
|
:param url: the url of the torrent
|
|
:type url: string
|
|
:returns: the temporary file name of the torrent file
|
|
:rtype: string
|
|
"""
|
|
|
|
tmp_file = os.path.join(tempfile.gettempdir(), url.split("/")[-1])
|
|
log.debug("filename: %s", tmp_file)
|
|
headers = {}
|
|
if cookie:
|
|
headers["Cookie"] = cookie
|
|
log.debug("cookie: %s", cookie)
|
|
return httpdownloader.download_file(url, tmp_file, headers=headers)
|
|
|
|
@export
|
|
def get_torrent_info(self, filename):
|
|
"""
|
|
Return information about a torrent on the filesystem.
|
|
|
|
:param filename: the path to the torrent
|
|
:type filename: string
|
|
|
|
:returns: information about the torrent:
|
|
|
|
::
|
|
|
|
{
|
|
"filename": the torrent file,
|
|
"name": the torrent name,
|
|
"size": the total size of the torrent,
|
|
"files": the files the torrent contains,
|
|
"info_hash" the torrents info_hash
|
|
}
|
|
|
|
:rtype: dictionary
|
|
"""
|
|
try:
|
|
torrent_info = uicommon.TorrentInfo(filename.strip())
|
|
return torrent_info.as_dict("name", "info_hash", "files_tree")
|
|
except:
|
|
return False
|
|
|
|
@export
|
|
def add_torrents(self, torrents):
|
|
"""
|
|
Add torrents by file
|
|
|
|
:param torrents: A list of dictionaries containing the torrent \
|
|
path and torrent options to add with.
|
|
:type torrents: list
|
|
|
|
**Usage**
|
|
|
|
>>> json_api.web.add_torrents([{
|
|
"path": "/tmp/deluge-web/some-torrent-file.torrent",
|
|
"options": {"download_path": "/home/deluge/"}
|
|
}])
|
|
|
|
"""
|
|
for torrent in torrents:
|
|
filename = os.path.basename(torrent["path"])
|
|
fdump = base64.encodestring(open(torrent["path"], "rb").read())
|
|
log.info("Adding torrent from file `%s` with options `%r`",
|
|
filename, torrent["options"])
|
|
client.core.add_torrent_file(filename, fdump, torrent["options"])
|
|
return True
|
|
|
|
@export
|
|
def get_hosts(self):
|
|
"""
|
|
Return the hosts in the hostlist.
|
|
"""
|
|
log.debug("get_hosts called")
|
|
return [(tuple(host[HOSTS_ID:HOSTS_PORT+1]) + (_("Offline"),)) for host in self.host_list["hosts"]]
|
|
|
|
@export
|
|
def get_host_status(self, host_id):
|
|
"""
|
|
Returns the current status for the specified host.
|
|
|
|
:param host_id: the hash id of the host
|
|
:type host_id: string
|
|
"""
|
|
main_deferred = Deferred()
|
|
|
|
(host_id, host, port, user, password) = self.get_host(host_id)
|
|
|
|
def callback(status, info=None):
|
|
main_deferred.callback((host_id, host, port, status, info))
|
|
|
|
def on_connect(connected, c, host_id):
|
|
def on_info(info, c):
|
|
c.disconnect()
|
|
callback(_("Online"), info)
|
|
|
|
def on_info_fail(reason):
|
|
callback(_("Offline"))
|
|
|
|
if not connected:
|
|
callback(_("Offline"))
|
|
return
|
|
|
|
d = c.daemon.info()
|
|
d.addCallback(on_info, c)
|
|
d.addErrback(on_info_fail)
|
|
|
|
def on_connect_failed(reason, host_id):
|
|
callback(_("Offline"))
|
|
|
|
if client.connected() and (host, port, "localclient" if not \
|
|
user and host in ("127.0.0.1", "localhost") else \
|
|
user) == client.connection_info():
|
|
def on_info(info):
|
|
callback(_("Connected"), info)
|
|
|
|
client.daemon.info().addCallback(on_info)
|
|
else:
|
|
c = Client()
|
|
d = c.connect(host, port, user, password)
|
|
d.addCallback(on_connect, c, host_id)
|
|
d.addErrback(on_connect_failed, host_id)
|
|
return main_deferred
|
|
|
|
@export
|
|
def start_daemon(self, port):
|
|
"""
|
|
Starts a local daemon.
|
|
"""
|
|
client.start_daemon(port, get_config_dir())
|
|
|
|
@export
|
|
def stop_daemon(self, host_id):
|
|
"""
|
|
Stops a running daemon.
|
|
|
|
:param host_id: the hash id of the host
|
|
:type host_id: string
|
|
"""
|
|
main_deferred = Deferred()
|
|
host = self.get_host(host_id)
|
|
if not host:
|
|
main_deferred.callback((False, _("Daemon doesn't exist")))
|
|
return main_deferred
|
|
|
|
try:
|
|
def on_connect(connected, c):
|
|
if not connected:
|
|
main_deferred.callback((False, _("Daemon not running")))
|
|
return
|
|
c.daemon.shutdown()
|
|
main_deferred.callback((True, ))
|
|
|
|
def on_connect_failed(reason):
|
|
main_deferred.callback((False, reason))
|
|
|
|
host, port, user, password = host[1:5]
|
|
c = Client()
|
|
d = c.connect(host, port, user, password)
|
|
d.addCallback(on_connect, c)
|
|
d.addErrback(on_connect_failed)
|
|
except:
|
|
main_deferred.callback((False, "An error occured"))
|
|
return main_deferred
|
|
|
|
@export
|
|
def add_host(self, host, port, username="", password=""):
|
|
"""
|
|
Adds a host to the list.
|
|
|
|
:param host: the hostname
|
|
:type host: string
|
|
:param port: the port
|
|
:type port: int
|
|
:keyword username: the username to login as
|
|
:type username: string
|
|
:keyword password: the password to login with
|
|
:type password: string
|
|
|
|
"""
|
|
# 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):
|
|
return (False, "Host already in the list")
|
|
|
|
try:
|
|
port = int(port)
|
|
except:
|
|
return (False, "Port is invalid")
|
|
|
|
# Host isn't in the list, so lets add it
|
|
connection_id = hashlib.sha1(str(time.time())).hexdigest()
|
|
self.host_list["hosts"].append([connection_id, host, port, username,
|
|
password])
|
|
self.host_list.save()
|
|
return (True,)
|
|
|
|
@export
|
|
def remove_host(self, connection_id):
|
|
"""
|
|
Removes a host for the list
|
|
|
|
:param host_id: the hash id of the host
|
|
:type host_id: string
|
|
"""
|
|
host = self.get_host(connection_id)
|
|
if host is None:
|
|
return False
|
|
|
|
self.host_list["hosts"].remove(host)
|
|
self.host_list.save()
|
|
return True
|
|
|
|
@export
|
|
def get_config(self):
|
|
"""
|
|
Get the configuration dictionary for the web interface.
|
|
|
|
:rtype: dictionary
|
|
:returns: the configuration
|
|
"""
|
|
config = component.get("DelugeWeb").config.config.copy()
|
|
del config["sessions"]
|
|
del config["pwd_salt"]
|
|
del config["pwd_sha1"]
|
|
return config
|
|
|
|
@export
|
|
def set_config(self, config):
|
|
"""
|
|
Sets the configuration dictionary for the web interface.
|
|
|
|
:param config: The configuration options to update
|
|
:type config: dictionary
|
|
"""
|
|
web_config = component.get("DelugeWeb").config
|
|
for key in config.keys():
|
|
if isinstance(config[key], unicode) or isinstance(config[key], str):
|
|
config[key] = config[key].encode("utf8")
|
|
web_config[key] = config[key]
|
|
|
|
@export
|
|
def get_plugins(self):
|
|
return {
|
|
"enabled_plugins": component.get("Web.PluginManager").plugins.keys(),
|
|
"available_plugins": component.get("Web.PluginManager").available_plugins
|
|
}
|
|
|
|
@export
|
|
def get_plugin_info(self, name):
|
|
return component.get("Web.PluginManager").get_plugin_info(name)
|
|
|
|
@export
|
|
def get_plugin_resources(self, name):
|
|
return component.get("Web.PluginManager").get_plugin_resources(name)
|
|
|
|
@export
|
|
def register_event_listener(self, event):
|
|
"""
|
|
Add a listener to the event queue.
|
|
|
|
:param event: The event name
|
|
:type event: string
|
|
"""
|
|
self.event_queue.add_listener(__request__.session_id, event)
|
|
|
|
@export
|
|
def deregister_event_listener(self, event):
|
|
"""
|
|
Remove an event listener from the event queue.
|
|
|
|
:param event: The event name
|
|
:type event: string
|
|
"""
|
|
self.event_queue.remove_listener(__request__.session_id, event)
|
|
|
|
@export
|
|
def get_events(self):
|
|
"""
|
|
Retrieve the pending events for the session.
|
|
"""
|
|
return self.event_queue.get_events(__request__.session_id) |