summaryrefslogtreecommitdiffstats
path: root/deluge
diff options
context:
space:
mode:
authorCalum Lind <calumlind+deluge@gmail.com>2017-03-30 00:36:44 +0100
committerCalum Lind <calumlind+deluge@gmail.com>2017-06-07 12:36:09 +0100
commitac48ad982ec4923731b9d25dd8fdaea28c6a9042 (patch)
tree19f4d72f51c1d50381e10ce7212522e585137662 /deluge
parent54a081bdfd73b046a31b4d960b0f7ad75018d1be (diff)
downloaddeluge-ac48ad982ec4923731b9d25dd8fdaea28c6a9042.tar.gz
deluge-ac48ad982ec4923731b9d25dd8fdaea28c6a9042.tar.bz2
deluge-ac48ad982ec4923731b9d25dd8fdaea28c6a9042.zip
[UI] Refactor duplicated code out of connection managers
Diffstat (limited to 'deluge')
-rw-r--r--deluge/tests/common_web.py4
-rw-r--r--deluge/tests/test_authmanager.py4
-rw-r--r--deluge/tests/test_client.py17
-rw-r--r--deluge/tests/test_rpcserver.py2
-rw-r--r--deluge/tests/test_ui_entry.py3
-rw-r--r--deluge/tests/test_web_api.py10
-rw-r--r--deluge/ui/client.py2
-rw-r--r--deluge/ui/common.py41
-rw-r--r--deluge/ui/console/modes/connectionmanager.py108
-rw-r--r--deluge/ui/gtkui/connectionmanager.py305
-rw-r--r--deluge/ui/hostlist.py211
-rw-r--r--deluge/ui/web/auth.py12
-rw-r--r--deluge/ui/web/json_api.py144
13 files changed, 430 insertions, 433 deletions
diff --git a/deluge/tests/common_web.py b/deluge/tests/common_web.py
index 7ba5f9f42..1ca5fb68d 100644
--- a/deluge/tests/common_web.py
+++ b/deluge/tests/common_web.py
@@ -66,9 +66,9 @@ class WebServerTestBase(BaseTestCase, DaemonBase):
self.deluge_web = DelugeWeb(daemon=False)
- host = list(self.deluge_web.web_api.host_list['hosts'][0])
+ host = list(self.deluge_web.web_api.hostlist.get_hosts_info2()[0])
host[2] = self.listen_port
- self.deluge_web.web_api.host_list['hosts'][0] = tuple(host)
+ self.deluge_web.web_api.hostlist.config['hosts'][0] = tuple(host)
self.host_id = host[0]
self.deluge_web.start()
diff --git a/deluge/tests/test_authmanager.py b/deluge/tests/test_authmanager.py
index d8b502a89..2d25d0f33 100644
--- a/deluge/tests/test_authmanager.py
+++ b/deluge/tests/test_authmanager.py
@@ -9,6 +9,7 @@ from __future__ import unicode_literals
import deluge.component as component
from deluge.core.authmanager import AUTH_LEVEL_ADMIN, AuthManager
+from deluge.ui import hostlist
from .basetest import BaseTestCase
@@ -23,8 +24,7 @@ class AuthManagerTestCase(BaseTestCase):
return component.shutdown()
def test_authorize(self):
- from deluge.ui import common
self.assertEqual(
- self.auth.authorize(*common.get_localhost_auth()),
+ self.auth.authorize(*hostlist.get_localhost_auth()),
AUTH_LEVEL_ADMIN
)
diff --git a/deluge/tests/test_client.py b/deluge/tests/test_client.py
index 839f46fd1..84d7fd9f6 100644
--- a/deluge/tests/test_client.py
+++ b/deluge/tests/test_client.py
@@ -10,10 +10,11 @@ from __future__ import unicode_literals
from twisted.internet import defer
import deluge.component as component
-import deluge.ui.common
from deluge import error
+from deluge.common import AUTH_LEVEL_NORMAL
from deluge.core.authmanager import AUTH_LEVEL_ADMIN
from deluge.ui.client import Client, DaemonSSLProxy, client
+from deluge.ui.hostlist import get_localhost_auth
from .basetest import BaseTestCase
from .daemon_base import DaemonBase
@@ -98,7 +99,7 @@ class ClientTestCase(BaseTestCase, DaemonBase):
return d
def test_connect_localclient(self):
- username, password = deluge.ui.common.get_localhost_auth()
+ username, password = get_localhost_auth()
d = client.connect('localhost', self.listen_port, username=username, password=password)
def on_connect(result):
@@ -110,7 +111,7 @@ class ClientTestCase(BaseTestCase, DaemonBase):
return d
def test_connect_bad_password(self):
- username, password = deluge.ui.common.get_localhost_auth()
+ username, password = get_localhost_auth()
d = client.connect('localhost', self.listen_port, username=username, password=password + '1')
def on_failure(failure):
@@ -125,7 +126,7 @@ class ClientTestCase(BaseTestCase, DaemonBase):
return d
def test_connect_invalid_user(self):
- username, password = deluge.ui.common.get_localhost_auth()
+ username, password = get_localhost_auth()
d = client.connect('localhost', self.listen_port, username='invalid-user')
def on_failure(failure):
@@ -140,7 +141,7 @@ class ClientTestCase(BaseTestCase, DaemonBase):
return d
def test_connect_without_password(self):
- username, password = deluge.ui.common.get_localhost_auth()
+ username, password = get_localhost_auth()
d = client.connect('localhost', self.listen_port, username=username)
def on_failure(failure):
@@ -156,12 +157,12 @@ class ClientTestCase(BaseTestCase, DaemonBase):
@defer.inlineCallbacks
def test_connect_with_password(self):
- username, password = deluge.ui.common.get_localhost_auth()
+ username, password = get_localhost_auth()
yield client.connect('localhost', self.listen_port, username=username, password=password)
yield client.core.create_account('testuser', 'testpw', 'DEFAULT')
yield client.disconnect()
ret = yield client.connect('localhost', self.listen_port, username='testuser', password='testpw')
- self.assertEqual(ret, deluge.common.AUTH_LEVEL_NORMAL)
+ self.assertEqual(ret, AUTH_LEVEL_NORMAL)
yield
@defer.inlineCallbacks
@@ -176,7 +177,7 @@ class ClientTestCase(BaseTestCase, DaemonBase):
yield d
def test_connect_without_sending_client_version_fails(self):
- username, password = deluge.ui.common.get_localhost_auth()
+ username, password = get_localhost_auth()
no_version_sending_client = NoVersionSendingClient()
d = no_version_sending_client.connect(
'localhost', self.listen_port, username=username, password=password
diff --git a/deluge/tests/test_rpcserver.py b/deluge/tests/test_rpcserver.py
index ab8239bc1..dc74f850e 100644
--- a/deluge/tests/test_rpcserver.py
+++ b/deluge/tests/test_rpcserver.py
@@ -15,7 +15,7 @@ from deluge.core import rpcserver
from deluge.core.authmanager import AuthManager
from deluge.core.rpcserver import DelugeRPCProtocol, RPCServer
from deluge.log import setup_logger
-from deluge.ui.common import get_localhost_auth
+from deluge.ui.hostlist import get_localhost_auth
from .basetest import BaseTestCase
diff --git a/deluge/tests/test_ui_entry.py b/deluge/tests/test_ui_entry.py
index 977b84dcc..272a523e9 100644
--- a/deluge/tests/test_ui_entry.py
+++ b/deluge/tests/test_ui_entry.py
@@ -25,6 +25,7 @@ import deluge.ui.console.main
import deluge.ui.web.server
from deluge.common import utf8_encode_structure
from deluge.ui import ui_entry
+from deluge.ui.hostlist import get_localhost_auth
from deluge.ui.web.server import DelugeWeb
from . import common
@@ -338,7 +339,7 @@ class ConsoleUIWithDaemonBaseTestCase(UIWithDaemonBaseTestCase):
@defer.inlineCallbacks
def test_console_command_status(self):
- username, password = deluge.ui.common.get_localhost_auth()
+ username, password = get_localhost_auth()
self.patch(sys, 'argv', self.var['sys_arg_cmd'] + ['--port'] + ['58900'] + ['--username'] +
[username] + ['--password'] + [password] + ['status'])
fd = StringFileDescriptor(sys.stdout)
diff --git a/deluge/tests/test_web_api.py b/deluge/tests/test_web_api.py
index bc1330f90..6fb7d1008 100644
--- a/deluge/tests/test_web_api.py
+++ b/deluge/tests/test_web_api.py
@@ -91,11 +91,11 @@ class WebAPITestCase(WebServerTestBase):
def test_get_host(self):
self.assertFalse(self.deluge_web.web_api._get_host('invalid_id'))
- conn = self.deluge_web.web_api.host_list['hosts'][0]
+ conn = list(self.deluge_web.web_api.hostlist.get_hosts_info2()[0])
self.assertEqual(self.deluge_web.web_api._get_host(conn[0]), conn)
def test_add_host(self):
- conn = [None, '', 0, '', '']
+ conn = ['abcdef', '10.0.0.1', 0, 'user123', 'pass123']
self.assertFalse(self.deluge_web.web_api._get_host(conn[0]))
# Add valid host
ret = self.deluge_web.web_api.add_host(conn[1], conn[2], conn[3], conn[4])
@@ -105,16 +105,16 @@ class WebAPITestCase(WebServerTestBase):
# Add already existing host
ret = self.deluge_web.web_api.add_host(conn[1], conn[2], conn[3], conn[4])
- self.assertEqual(ret, (False, 'Host already in the list'))
+ self.assertEqual(ret, (False, 'Host details already in hostlist'))
# Add invalid port
conn[2] = 'bad port'
ret = self.deluge_web.web_api.add_host(conn[1], conn[2], conn[3], conn[4])
- self.assertEqual(ret, (False, 'Port is invalid'))
+ self.assertEqual(ret, (False, 'Invalid port. Must be an integer'))
def test_remove_host(self):
conn = ['connection_id', '', 0, '', '']
- self.deluge_web.web_api.host_list['hosts'].append(conn)
+ self.deluge_web.web_api.hostlist.config['hosts'].append(conn)
self.assertEqual(self.deluge_web.web_api._get_host(conn[0]), conn)
# Remove valid host
self.assertTrue(self.deluge_web.web_api.remove_host(conn[0]))
diff --git a/deluge/ui/client.py b/deluge/ui/client.py
index a494de576..564bf625d 100644
--- a/deluge/ui/client.py
+++ b/deluge/ui/client.py
@@ -21,7 +21,7 @@ import deluge.common
from deluge import error
from deluge.decorators import deprecated
from deluge.transfer import DelugeTransferProtocol
-from deluge.ui.common import get_localhost_auth
+from deluge.ui.hostlist import get_localhost_auth
RPC_RESPONSE = 1
RPC_ERROR = 2
diff --git a/deluge/ui/common.py b/deluge/ui/common.py
index ef7dda9af..c66e8eb27 100644
--- a/deluge/ui/common.py
+++ b/deluge/ui/common.py
@@ -15,10 +15,8 @@ from __future__ import unicode_literals
import logging
import os
-import time
from hashlib import sha1 as sha
-import deluge.configmanager
from deluge import bencode
from deluge.common import decode_bytes
@@ -163,12 +161,6 @@ FILE_PRIORITY = {
del _
-DEFAULT_HOST = '127.0.0.1'
-DEFAULT_PORT = 58846
-DEFAULT_HOSTS = {
- 'hosts': [(sha(str(time.time()).encode('utf8')).hexdigest(), DEFAULT_HOST, DEFAULT_PORT, '', '')]
-}
-
# The keys from session statistics for cache status.
DISK_CACHE_KEYS = [
'disk.num_blocks_read', 'disk.num_blocks_written', 'disk.num_read_ops', 'disk.num_write_ops',
@@ -533,36 +525,3 @@ class FileTree(object):
lines.append(' ' * depth + path)
self.walk(write)
return '\n'.join(lines)
-
-
-def get_localhost_auth():
- """
- Grabs the localclient auth line from the 'auth' file and creates a localhost uri
-
- :returns: with the username and password to login as
- :rtype: tuple
- """
- auth_file = deluge.configmanager.get_config_dir('auth')
- if not os.path.exists(auth_file):
- from deluge.common import create_localclient_account
- create_localclient_account()
-
- with open(auth_file) as auth:
- for line in auth:
- line = line.strip()
- if line.startswith('#') or not line:
- # This is a comment or blank line
- continue
-
- lsplit = line.split(':')
-
- if len(lsplit) == 2:
- username, password = lsplit
- elif len(lsplit) == 3:
- username, password, level = lsplit
- else:
- log.error('Your auth file is malformed: Incorrect number of fields!')
- continue
-
- if username == 'localclient':
- return (username, password)
diff --git a/deluge/ui/console/modes/connectionmanager.py b/deluge/ui/console/modes/connectionmanager.py
index 359e8407b..705279f8b 100644
--- a/deluge/ui/console/modes/connectionmanager.py
+++ b/deluge/ui/console/modes/connectionmanager.py
@@ -9,17 +9,14 @@
from __future__ import unicode_literals
-import hashlib
import logging
-import time
import deluge.component as component
-from deluge.configmanager import ConfigManager
from deluge.decorators import overrides
-from deluge.ui import common as uicommon
from deluge.ui.client import Client, client
from deluge.ui.console.modes.basemode import BaseMode
from deluge.ui.console.widgets.popup import InputPopup, PopupsHandler, SelectablePopup
+from deluge.ui.hostlist import HostList
try:
import curses
@@ -35,7 +32,7 @@ class ConnectionManager(BaseMode, PopupsHandler):
PopupsHandler.__init__(self)
self.statuses = {}
self.all_torrents = None
- self.config = ConfigManager('hostlist.conf.1.2', uicommon.DEFAULT_HOSTS)
+ self.hostlist = HostList()
self.update_hosts_status()
BaseMode.__init__(self, stdscr, encoding=encoding)
self.update_select_host_popup()
@@ -47,18 +44,19 @@ class ConnectionManager(BaseMode, PopupsHandler):
popup = SelectablePopup(self, _('Select Host'), self._host_selected, border_off_west=1, active_wrap=True)
popup.add_header("{!white,black,bold!}'Q'=%s, 'a'=%s, 'D'=%s" %
- (_('quit'), _('add new host'), _('delete host')),
+ (_('Quit'), _('Add New Host'), _('Delete Host')),
space_below=True)
self.push_popup(popup, clear=True)
- for host in self.config['hosts']:
- args = {'data': host[0], 'foreground': 'red'}
+ for host_entry in self.hostlist.get_host_info():
+ host_id, hostname, port, user = host_entry
+ args = {'data': host_id, 'foreground': 'red'}
state = 'Offline'
- if host[0] in self.statuses:
+ if host_id in self.statuses:
state = 'Online'
- args.update({'data': self.statuses[host[0]], 'foreground': 'green'})
- host_str = '%s:%d [%s]' % (host[1], host[2], state)
- self.popup.add_line(host[0], host_str, selectable=True, use_underline=True, **args)
+ args.update({'data': self.statuses[host_id], 'foreground': 'green'})
+ host_str = '%s:%d [%s]' % (hostname, port, state)
+ self.popup.add_line(host_id, host_str, selectable=True, use_underline=True, **args)
if selected_index is not None:
self.popup.set_selection(selected_index)
@@ -87,16 +85,13 @@ class ConnectionManager(BaseMode, PopupsHandler):
del self.statuses[host_id]
self.update_select_host_popup()
- for host in self.config['hosts']:
+ for host_entry in self.hostlist.get_hosts_info2():
c = Client()
- hadr = host[1]
- port = host[2]
- user = host[3]
- password = host[4]
- log.debug('connect: hadr=%s, port=%s, user=%s, password=%s', hadr, port, user, password)
- d = c.connect(hadr, port, user, password)
- d.addCallback(on_connect, c, host[0])
- d.addErrback(on_connect_failed, host[0])
+ host_id, host, port, user, password = host_entry
+ log.debug('Connect: host=%s, port=%s, user=%s, pass=%s', host, port, user, password)
+ d = c.connect(host, port, user, password)
+ d.addCallback(on_connect, c, host_id)
+ d.addErrback(on_connect_failed, host_id)
def _on_connected(self, result):
d = component.get('ConsoleUI').start_console()
@@ -112,57 +107,46 @@ class ConnectionManager(BaseMode, PopupsHandler):
log.exception(result)
def _host_selected(self, selected_host, *args, **kwargs):
- if selected_host not in self.statuses:
- return
- for host in self.config['hosts']:
- if host[0] == selected_host:
- d = client.connect(host[1], host[2], host[3], host[4])
- d.addCallback(self._on_connected)
- d.addErrback(self._on_connect_fail)
- return
- return False
+ if selected_host in self.statuses:
+ for host_entry in self.hostlist.get_hosts_info():
+ if host_entry[0] == selected_host:
+ __, host, port, user, password = host_entry
+ d = client.connect(host, port, user, password)
+ d.addCallback(self._on_connected)
+ d.addErrback(self._on_connect_fail)
def _do_add(self, result, **kwargs):
if not result or kwargs.get('close', False):
self.pop_popup()
- return
- hostname = result['hostname']['value']
- try:
- port = int(result['port']['value'])
- except ValueError:
- self.report_message('Cannot add host', 'Invalid port. Must be an integer')
- return
- username = result['username']['value']
- password = result['password']['value']
- for host in self.config['hosts']:
- if (host[1], host[2], host[3]) == (hostname, port, username):
- self.report_message('Cannot add host', 'Host already in list')
- return
- newid = hashlib.sha1(str(time.time())).hexdigest()
- self.config['hosts'].append((newid, hostname, port, username, password))
- self.config.save()
- self.update_select_host_popup()
+ else:
+ self.add_host(result['hostname']['value'], result['port']['value'],
+ result['username']['value'], result['password']['value'])
def add_popup(self):
self.inlist = False
- popup = InputPopup(self, 'Add Host (up & down arrows to navigate, esc to cancel)',
+ popup = InputPopup(self, _('Add Host (Up & Down arrows to navigate, Esc to cancel)'),
border_off_north=1, border_off_east=1,
close_cb=self._do_add)
- popup.add_text_input('hostname', '%s:' % _('Hostname'))
- popup.add_text_input('port', '%s:' % _('Port'))
- popup.add_text_input('username', '%s:' % _('Username'))
- popup.add_text_input('password', '%s:' % _('Password'))
+ popup.add_text_input('hostname', _('Hostname:'))
+ popup.add_text_input('port', _('Port:'))
+ popup.add_text_input('username', _('Username:'))
+ popup.add_text_input('password', _('Password:'))
self.push_popup(popup, clear=True)
self.refresh()
- def delete_current_host(self):
- idx, data = self.popup.current_selection()
- log.debug('deleting host: %s', data)
- for host in self.config['hosts']:
- if host[0] == data:
- self.config['hosts'].remove(host)
- break
- self.config.save()
+ def add_host(self, hostname, port, username, password):
+ try:
+ self.hostlist.add_host(hostname, port, username, password)
+ except ValueError as ex:
+ self.report_message(_('Error adding host'), '%s' % ex)
+ return
+ else:
+ self.update_select_host_popup()
+
+ def delete_host(self, host_id):
+ log.debug('deleting host: %s', host_id)
+ self.hostlist.remove_host(host_id)
+ self.update_select_host_popup()
@overrides(component.Component)
def start(self):
@@ -224,8 +208,8 @@ class ConnectionManager(BaseMode, PopupsHandler):
reactor.stop()
return
if chr(c) == 'D' and self.inlist:
- self.delete_current_host()
- self.update_select_host_popup()
+ host_id = self.popup.current_selection()[1]
+ self.delete_host(host_id)
return
if chr(c) == 'a' and self.inlist:
self.add_popup()
diff --git a/deluge/ui/gtkui/connectionmanager.py b/deluge/ui/gtkui/connectionmanager.py
index 095816f30..fd420b510 100644
--- a/deluge/ui/gtkui/connectionmanager.py
+++ b/deluge/ui/gtkui/connectionmanager.py
@@ -9,10 +9,8 @@
from __future__ import unicode_literals
-import hashlib
import logging
import os
-import time
from socket import gaierror, gethostbyname
import gtk
@@ -23,9 +21,9 @@ from deluge.common import resource_filename
from deluge.configmanager import ConfigManager, get_config_dir
from deluge.error import AuthenticationRequired, BadLoginError, IncompatibleClient
from deluge.ui.client import Client, client
-from deluge.ui.common import get_localhost_auth
from deluge.ui.gtkui.common import get_clipboard_text, get_deluge_icon, get_logo
from deluge.ui.gtkui.dialogs import AuthenticationDialog, ErrorDialog
+from deluge.ui.hostlist import DEFAULT_PORT, HostList
try:
from urllib.parse import urlparse
@@ -35,17 +33,15 @@ except ImportError:
log = logging.getLogger(__name__)
-DEFAULT_HOST = '127.0.0.1'
-DEFAULT_PORT = 58846
-
HOSTLIST_COL_ID = 0
HOSTLIST_COL_HOST = 1
HOSTLIST_COL_PORT = 2
-HOSTLIST_COL_STATUS = 3
-HOSTLIST_COL_USER = 4
-HOSTLIST_COL_PASS = 5
+HOSTLIST_COL_USER = 3
+HOSTLIST_COL_PASS = 4
+HOSTLIST_COL_STATUS = 5
HOSTLIST_COL_VERSION = 6
+LOCALHOST = ('127.0.0.1', 'localhost')
HOSTLIST_PIXBUFS = [
# This is populated in ConnectionManager.show
@@ -68,6 +64,7 @@ def cell_render_host(column, cell, model, row, data):
def cell_render_status(column, cell, model, row, data):
status = model[row][data]
+ status = status if status else 'Offline'
pixbuf = None
if status in HOSTLIST_STATUS:
pixbuf = HOSTLIST_PIXBUFS[HOSTLIST_STATUS.index(status)]
@@ -79,7 +76,7 @@ class ConnectionManager(component.Component):
def __init__(self):
component.Component.__init__(self, 'ConnectionManager')
self.gtkui_config = ConfigManager('gtkui.conf')
- self.config = self.__load_config()
+
self.running = False
# Component overrides
@@ -94,53 +91,27 @@ class ConnectionManager(component.Component):
def shutdown(self):
pass
- def __load_config(self):
- auth_file = get_config_dir('auth')
- if not os.path.exists(auth_file):
- from deluge.common import create_localclient_account
- create_localclient_account()
-
- localclient_username, localclient_password = get_localhost_auth()
- default_config = {
- 'hosts': [(
- hashlib.sha1(str(time.time())).hexdigest(),
- DEFAULT_HOST,
- DEFAULT_PORT,
- localclient_username,
- localclient_password
- )]
- }
- config = ConfigManager('hostlist.conf.1.2', defaults=default_config, file_version=2)
- config.run_converter((0, 1), 2, self.__migrate_config_1_to_2)
- return config
-
# Public methods
def show(self):
"""
Show the ConnectionManager dialog.
"""
- self.config = self.__load_config()
# Get the gtk builder file for the connection manager
self.builder = gtk.Builder()
# The main dialog
self.builder.add_from_file(resource_filename(
- 'deluge.ui.gtkui', os.path.join('glade', 'connection_manager.ui')
- ))
+ 'deluge.ui.gtkui', os.path.join('glade', 'connection_manager.ui')))
# The add host dialog
self.builder.add_from_file(resource_filename(
- 'deluge.ui.gtkui', os.path.join('glade', 'connection_manager.addhost.ui')
- ))
+ 'deluge.ui.gtkui', os.path.join('glade', 'connection_manager.addhost.ui')))
# The ask password dialog
self.builder.add_from_file(resource_filename(
- 'deluge.ui.gtkui', os.path.join('glade', 'connection_manager.askpassword.ui')
- ))
+ 'deluge.ui.gtkui', os.path.join('glade', 'connection_manager.askpassword.ui')))
# Setup the ConnectionManager dialog
self.connection_manager = self.builder.get_object('connection_manager')
self.connection_manager.set_transient_for(component.get('MainWindow').window)
-
self.connection_manager.set_icon(get_deluge_icon())
-
self.builder.get_object('image1').set_from_pixbuf(get_logo(32))
self.askpassword_dialog = self.builder.get_object('askpassword_dialog')
@@ -148,6 +119,7 @@ class ConnectionManager(component.Component):
self.askpassword_dialog.set_icon(get_deluge_icon())
self.askpassword_dialog_entry = self.builder.get_object('askpassword_dialog_entry')
+ self.hostlist_config = HostList()
self.hostlist = self.builder.get_object('hostlist')
# Create status pixbufs
@@ -160,18 +132,19 @@ class ConnectionManager(component.Component):
)
# Create the host list gtkliststore
- # id-hash, hostname, port, status, username, password, version
+ # id-hash, hostname, port, username, password, status, version
self.liststore = gtk.ListStore(str, str, int, str, str, str, str)
# Setup host list treeview
self.hostlist.set_model(self.liststore)
render = gtk.CellRendererPixbuf()
column = gtk.TreeViewColumn(_('Status'), render)
- column.set_cell_data_func(render, cell_render_status, 3)
+ column.set_cell_data_func(render, cell_render_status, HOSTLIST_COL_STATUS)
self.hostlist.append_column(column)
render = gtk.CellRendererText()
column = gtk.TreeViewColumn(_('Host'), render, text=HOSTLIST_COL_HOST)
- column.set_cell_data_func(render, cell_render_host, (1, 2, 4))
+ column.set_cell_data_func(
+ render, cell_render_host, (HOSTLIST_COL_HOST, HOSTLIST_COL_PORT, HOSTLIST_COL_USER))
column.set_expand(True)
self.hostlist.append_column(column)
render = gtk.CellRendererText()
@@ -202,7 +175,6 @@ class ConnectionManager(component.Component):
# Save the toggle options
self.__save_options()
- self.__save_hostlist()
self.connection_manager.destroy()
del self.builder
@@ -210,42 +182,6 @@ class ConnectionManager(component.Component):
del self.liststore
del self.hostlist
- def add_host(self, host, port, username='', password=''):
- """
- Adds a host to the list.
-
- :param host: str, the hostname
- :param port: int, the port
- :param username: str, the username to login as
- :param password: str, the password to login with
-
- """
- # Check to see if there is already an entry for this host and return
- # if thats the case
- for entry in self.liststore:
- if [entry[HOSTLIST_COL_HOST], entry[HOSTLIST_COL_PORT], entry[HOSTLIST_COL_USER]] == [host, port, username]:
- raise ValueError('Host already in list!')
-
- try:
- gethostbyname(host)
- except gaierror as ex:
- raise ValueError("Host '%s': %s" % (host, ex.args[1]))
-
- # Host isn't in the list, so lets add it
- row = self.liststore.append()
- self.liststore[row][HOSTLIST_COL_ID] = hashlib.sha1(str(time.time())).hexdigest()
- self.liststore[row][HOSTLIST_COL_HOST] = host
- self.liststore[row][HOSTLIST_COL_PORT] = port
- self.liststore[row][HOSTLIST_COL_USER] = username
- self.liststore[row][HOSTLIST_COL_PASS] = password
- self.liststore[row][HOSTLIST_COL_STATUS] = 'Offline'
-
- # Save the host list to file
- self.__save_hostlist()
-
- # Update the status of the hosts
- self.__update_list()
-
def on_entry_host_paste_clipboard(self, widget):
text = get_clipboard_text()
log.debug('on_entry_proxy_host_paste-clipboard: got paste: %s', text)
@@ -261,39 +197,21 @@ class ConnectionManager(component.Component):
if parsed.password:
self.builder.get_object('entry_password').set_text(parsed.password)
- # Private methods
- def __save_hostlist(self):
- """
- Save the current hostlist to the config file.
- """
- # Grab the hosts from the liststore
- self.config['hosts'] = []
- for row in self.liststore:
- self.config['hosts'].append((row[HOSTLIST_COL_ID],
- row[HOSTLIST_COL_HOST],
- row[HOSTLIST_COL_PORT],
- row[HOSTLIST_COL_USER],
- row[HOSTLIST_COL_PASS]))
-
- self.config.save()
-
def __load_hostlist(self):
- """
- Load saved host entries
- """
- for host in self.config['hosts']:
- new_row = self.liststore.append()
- self.liststore[new_row][HOSTLIST_COL_ID] = host[0]
- self.liststore[new_row][HOSTLIST_COL_HOST] = host[1]
- self.liststore[new_row][HOSTLIST_COL_PORT] = host[2]
- self.liststore[new_row][HOSTLIST_COL_USER] = host[3]
- self.liststore[new_row][HOSTLIST_COL_PASS] = host[4]
- self.liststore[new_row][HOSTLIST_COL_STATUS] = 'Offline'
- self.liststore[new_row][HOSTLIST_COL_VERSION] = ''
+ """Load saved host entries"""
+ status = version = ''
+ for host_entry in self.hostlist_config.get_hosts_info2():
+ host_id, host, port, username, password = host_entry
+ self.liststore.append([host_id, host, port, username, password, status, version])
def __get_host_row(self, host_id):
- """
- Returns the row in the liststore for `:param:host_id` or None
+ """Get the row in the liststore for the host_id.
+
+ Args:
+ host_id (str): The host id.
+
+ Returns:
+ list: The listsrore row with host details.
"""
for row in self.liststore:
@@ -352,14 +270,11 @@ class ConnectionManager(component.Component):
try:
ip = gethostbyname(host)
except gaierror as ex:
- log.error('Error resolving host %s to ip: %s', row[HOSTLIST_COL_HOST], ex.args[1])
+ log.error('Error resolving host %s to ip: %s', host, ex.args[1])
continue
- if client.connected() and (
- ip,
- port,
- 'localclient' if not user and host in ('127.0.0.1', 'localhost') else user
- ) == client.connection_info():
+ host_info = (ip, port, 'localclient' if not user and host in LOCALHOST else user)
+ if client.connected() and host_info == client.connection_info():
def on_info(info, row):
if not self.running:
return
@@ -383,14 +298,11 @@ class ConnectionManager(component.Component):
Set the widgets to show the correct options from the config.
"""
self.builder.get_object('chk_autoconnect').set_active(
- self.gtkui_config['autoconnect']
- )
+ self.gtkui_config['autoconnect'])
self.builder.get_object('chk_autostart').set_active(
- self.gtkui_config['autostart_localhost']
- )
+ self.gtkui_config['autostart_localhost'])
self.builder.get_object('chk_donotshow').set_active(
- not self.gtkui_config['show_connection_manager_on_start']
- )
+ not self.gtkui_config['show_connection_manager_on_start'])
def __save_options(self):
"""
@@ -402,17 +314,15 @@ class ConnectionManager(component.Component):
'chk_donotshow').get_active()
def __update_buttons(self):
- """
- Updates the buttons states.
- """
+ """Updates the buttons states."""
if len(self.liststore) == 0:
# There is nothing in the list
- self.builder.get_object('button_startdaemon').set_sensitive(True)
+ self.builder.get_object('button_startdaemon').set_sensitive(False)
self.builder.get_object('button_connect').set_sensitive(False)
self.builder.get_object('button_removehost').set_sensitive(False)
self.builder.get_object('image_startdaemon').set_from_stock(
gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU)
- self.builder.get_object('label_startdaemon').set_text('_Start Daemon')
+ self.builder.get_object('label_startdaemon').set_text_with_mnemonic('_Start Daemon')
model, row = self.hostlist.get_selection().get_selected()
if not row:
@@ -420,24 +330,22 @@ class ConnectionManager(component.Component):
return
self.builder.get_object('button_edithost').set_sensitive(True)
+ self.builder.get_object('button_startdaemon').set_sensitive(True)
+ self.builder.get_object('button_connect').set_sensitive(True)
+ self.builder.get_object('button_removehost').set_sensitive(True)
# Get some values about the selected host
- status = model[row][HOSTLIST_COL_STATUS]
- host = model[row][HOSTLIST_COL_HOST]
- port = model[row][HOSTLIST_COL_PORT]
- user = model[row][HOSTLIST_COL_USER]
- passwd = model[row][HOSTLIST_COL_PASS]
+ __, host, port, user, password, status, __ = model[row]
+
+ try:
+ ip = gethostbyname(host)
+ except gaierror as ex:
+ log.error('Error resolving host %s to ip: %s', row[HOSTLIST_COL_HOST], ex.args[1])
+ return
log.debug('Status: %s', status)
# Check to see if we have a localhost entry selected
- localhost = False
- if host in ('127.0.0.1', 'localhost'):
- localhost = True
-
- # Make sure buttons are sensitive at start
- self.builder.get_object('button_startdaemon').set_sensitive(True)
- self.builder.get_object('button_connect').set_sensitive(True)
- self.builder.get_object('button_removehost').set_sensitive(True)
+ localhost = host in LOCALHOST
# See if this is the currently connected host
if status == 'Connected':
@@ -453,30 +361,25 @@ class ConnectionManager(component.Component):
if status == 'Connected' or status == 'Online':
self.builder.get_object('image_startdaemon').set_from_stock(
gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
- self.builder.get_object('label_startdaemon').set_text(
- _('_Stop Daemon'))
+ self.builder.get_object('label_startdaemon').set_text_with_mnemonic(_('_Stop Daemon'))
# Update the start daemon button if the selected host is localhost
if localhost and status == 'Offline':
# The localhost is not online
self.builder.get_object('image_startdaemon').set_from_stock(
gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU)
- self.builder.get_object('label_startdaemon').set_text(
- _('_Start Daemon'))
+ self.builder.get_object('label_startdaemon').set_text_with_mnemonic(_('_Start Daemon'))
- if client.connected() and (host, port, user) == client.connection_info():
+ if client.connected() and (ip, port, user) == client.connection_info():
# If we're connected, we can stop the dameon
self.builder.get_object('button_startdaemon').set_sensitive(True)
- elif user and passwd:
+ elif user and password:
# In this case we also have all the info to shutdown the daemon
self.builder.get_object('button_startdaemon').set_sensitive(True)
else:
# Can't stop non localhost daemons, specially without the necessary info
self.builder.get_object('button_startdaemon').set_sensitive(False)
- # Make sure label is displayed correctly using mnemonics
- self.builder.get_object('label_startdaemon').set_use_underline(True)
-
def start_daemon(self, port, config):
"""
Attempts to start a daemon process and will show an ErrorDialog if unable
@@ -530,7 +433,7 @@ class ConnectionManager(component.Component):
self.connection_manager.response(gtk.RESPONSE_OK)
component.start()
- def __on_connected_failed(self, reason, host_id, host, port, user, passwd,
+ def __on_connected_failed(self, reason, host_id, host, port, user, password,
try_counter):
log.debug('Failed to connect: %s', reason.value)
@@ -552,9 +455,8 @@ class ConnectionManager(component.Component):
if try_counter:
log.info('Retrying connection.. Retries left: %s', try_counter)
return reactor.callLater(
- 0.5, self.__connect, host_id, host, port, user, passwd,
- try_counter=try_counter - 1
- )
+ 0.5, self.__connect, host_id, host, port, user, password,
+ try_counter=try_counter - 1)
msg = str(reason.value)
if not self.builder.get_object('chk_autostart').get_active():
@@ -566,28 +468,26 @@ class ConnectionManager(component.Component):
model, row = self.hostlist.get_selection().get_selected()
if not row:
return
+
status = model[row][HOSTLIST_COL_STATUS]
+
+ # If status is connected then connect button disconnects instead.
if status == 'Connected':
def on_disconnect(reason):
self.__update_list()
client.disconnect().addCallback(on_disconnect)
return
- host_id = model[row][HOSTLIST_COL_ID]
- host = model[row][HOSTLIST_COL_HOST]
- port = model[row][HOSTLIST_COL_PORT]
- user = model[row][HOSTLIST_COL_USER]
- password = model[row][HOSTLIST_COL_PASS]
-
- if (status == 'Offline' and self.builder.get_object('chk_autostart').get_active() and
- host in ('127.0.0.1', 'localhost')):
+ host_id, host, port, user, password, __, __ = model[row]
+ try_counter = 0
+ auto_start = self.builder.get_object('chk_autostart').get_active()
+ if status == 'Offline' and auto_start and host in LOCALHOST:
if not self.start_daemon(port, get_config_dir()):
log.debug('Failed to auto-start daemon')
return
- return self.__connect(
- host_id, host, port, user, password, try_counter=6
- )
- return self.__connect(host_id, host, port, user, password)
+ try_counter = 6
+
+ return self.__connect(host_id, host, port, user, password, try_counter=try_counter)
def on_button_close_clicked(self, widget):
self.connection_manager.response(gtk.RESPONSE_CLOSE)
@@ -610,25 +510,30 @@ class ConnectionManager(component.Component):
username = username_entry.get_text()
password = password_entry.get_text()
hostname = hostname_entry.get_text()
-
- if (not password and not username or username == 'localclient') and hostname in ['127.0.0.1', 'localhost']:
- username, password = get_localhost_auth()
+ port = port_spinbutton.get_value_as_int()
try:
- self.add_host(hostname, port_spinbutton.get_value_as_int(), username, password)
+ host_id = self.hostlist_config.add_host(hostname, port, username, password)
except ValueError as ex:
- ErrorDialog(_('Error Adding Host'), ex).run()
+ ErrorDialog(_('Error Adding Host'), ex, parent=dialog).run()
+ else:
+ self.liststore.append([host_id, hostname, port, username, password, 'Offline', ''])
+
+ # Update the status of the hosts
+ self.__update_list()
username_entry.set_text('')
password_entry.set_text('')
hostname_entry.set_text('')
- port_spinbutton.set_value(58846)
+ port_spinbutton.set_value(DEFAULT_PORT)
dialog.hide()
def on_button_edithost_clicked(self, widget=None):
log.debug('on_button_edithost_clicked')
model, row = self.hostlist.get_selection().get_selected()
status = model[row][HOSTLIST_COL_STATUS]
+ host_id = model[row][HOSTLIST_COL_ID]
+
if status == 'Connected':
def on_disconnect(reason):
self.__update_list()
@@ -658,18 +563,14 @@ class ConnectionManager(component.Component):
username = username_entry.get_text()
password = password_entry.get_text()
hostname = hostname_entry.get_text()
+ port = port_spinbutton.get_value_as_int()
- if (not password and not username or username == 'localclient') and hostname in ['127.0.0.1', 'localhost']:
- username, password = get_localhost_auth()
-
- self.liststore[row][HOSTLIST_COL_HOST] = hostname
- self.liststore[row][HOSTLIST_COL_PORT] = port_spinbutton.get_value_as_int()
- self.liststore[row][HOSTLIST_COL_USER] = username
- self.liststore[row][HOSTLIST_COL_PASS] = password
- self.liststore[row][HOSTLIST_COL_STATUS] = 'Offline'
-
- # Save the host list to file
- self.__save_hostlist()
+ try:
+ self.hostlist_config.update_host(host_id, hostname, port, username, password)
+ except ValueError as ex:
+ ErrorDialog(_('Error Updating Host'), ex, parent=dialog).run()
+ else:
+ self.liststore[row] = host_id, hostname, port, username, password, '', ''
# Update the status of the hosts
self.__update_list()
@@ -677,44 +578,38 @@ class ConnectionManager(component.Component):
username_entry.set_text('')
password_entry.set_text('')
hostname_entry.set_text('')
- port_spinbutton.set_value(58846)
+ port_spinbutton.set_value(DEFAULT_PORT)
dialog.hide()
def on_button_removehost_clicked(self, widget):
log.debug('on_button_removehost_clicked')
# Get the selected rows
- paths = self.hostlist.get_selection().get_selected_rows()[1]
- for path in paths:
- self.liststore.remove(self.liststore.get_iter(path))
-
+ model, row = self.hostlist.get_selection().get_selected()
+ self.hostlist_config.remove_host(model[row][HOSTLIST_COL_ID])
+ self.liststore.remove(row)
# Update the hostlist
self.__update_list()
- # Save the host list
- self.__save_hostlist()
-
def on_button_startdaemon_clicked(self, widget):
log.debug('on_button_startdaemon_clicked')
if self.liststore.iter_n_children(None) < 1:
# There is nothing in the list, so lets create a localhost entry
- self.add_host(DEFAULT_HOST, DEFAULT_PORT, *get_localhost_auth())
+ try:
+ self.hostlist_config.add_default_host()
+ except ValueError as ex:
+ log.error('Error adding default host: %s', ex)
+
# ..and start the daemon.
- self.start_daemon(
- DEFAULT_PORT, get_config_dir()
- )
+ self.start_daemon(DEFAULT_PORT, get_config_dir())
return
paths = self.hostlist.get_selection().get_selected_rows()[1]
if len(paths) < 1:
return
- status = self.liststore[paths[0]][HOSTLIST_COL_STATUS]
- host = self.liststore[paths[0]][HOSTLIST_COL_HOST]
- port = self.liststore[paths[0]][HOSTLIST_COL_PORT]
- user = self.liststore[paths[0]][HOSTLIST_COL_USER]
- password = self.liststore[paths[0]][HOSTLIST_COL_PASS]
+ __, host, port, user, password, status, __ = self.liststore[paths[0]]
- if host not in ('127.0.0.1', 'localhost'):
+ if host not in LOCALHOST:
return
if status in ('Online', 'Connected'):
@@ -754,15 +649,3 @@ class ConnectionManager(component.Component):
def on_askpassword_dialog_entry_activate(self, entry):
self.askpassword_dialog.response(gtk.RESPONSE_OK)
-
- def __migrate_config_1_to_2(self, config):
- localclient_username, localclient_password = get_localhost_auth()
- if not localclient_username:
- # Nothing to do here, there's no auth file
- return
- for idx, (_, host, _, username, _) in enumerate(config['hosts'][:]):
- if host in ('127.0.0.1', 'localhost'):
- if not username:
- config['hosts'][idx][3] = localclient_username
- config['hosts'][idx][4] = localclient_password
- return config
diff --git a/deluge/ui/hostlist.py b/deluge/ui/hostlist.py
new file mode 100644
index 000000000..5eb43889e
--- /dev/null
+++ b/deluge/ui/hostlist.py
@@ -0,0 +1,211 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) Calum Lind 2017 <calumlind+deluge@gmail.com>
+#
+# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
+# the additional special exception to link portions of this program with the OpenSSL library.
+# See LICENSE for more details.
+#
+
+"""
+The UI hostlist module contains methods useful for adding, removing and lookingup host in hostlist.conf.
+"""
+from __future__ import unicode_literals
+
+import logging
+import os
+import time
+from hashlib import sha1
+from socket import gaierror, gethostbyname
+
+from deluge.config import Config
+from deluge.configmanager import get_config_dir
+
+log = logging.getLogger(__name__)
+
+DEFAULT_HOST = '127.0.0.1'
+DEFAULT_PORT = 58846
+LOCALHOST = ('127.0.0.1', 'localhost')
+
+
+def default_hostlist():
+ """Create a new hosts for hostlist with a localhost entry"""
+ host_id = sha1(str(time.time()).encode('utf8')).hexdigest()
+ username, password = get_localhost_auth()
+ return {'hosts': [(host_id, DEFAULT_HOST, DEFAULT_PORT, username, password)]}
+
+
+def get_localhost_auth():
+ """Grabs the localclient auth line from the 'auth' file and creates a localhost uri.
+
+ Returns:
+ tuple: With the username and password to login as.
+
+ """
+ auth_file = get_config_dir('auth')
+ if not os.path.exists(auth_file):
+ from deluge.common import create_localclient_account
+ create_localclient_account()
+
+ with open(auth_file) as auth:
+ for line in auth:
+ line = line.strip()
+ if line.startswith('#') or not line:
+ # This is a comment or blank line
+ continue
+
+ lsplit = line.split(':')
+
+ if len(lsplit) == 2:
+ username, password = lsplit
+ elif len(lsplit) == 3:
+ username, password, level = lsplit
+ else:
+ log.error('Your auth file is malformed: Incorrect number of fields!')
+ continue
+
+ if username == 'localclient':
+ return (username, password)
+
+
+def validate_host_info(hostname, port):
+ """Checks that hostname and port are valid.
+
+ Args:
+ hostname (str): The IP or hostname of the deluge daemon.
+ port (int): The port of the deluge daemon.
+
+ Raises:
+ ValueError: Host details are not valid with reason.
+ """
+
+ try:
+ gethostbyname(hostname)
+ except gaierror as ex:
+ raise ValueError('Host %s: %s', hostname, ex.args[1])
+
+ try:
+ int(port)
+ except ValueError:
+ raise ValueError('Invalid port. Must be an integer')
+
+
+def _migrate_config_1_to_2(config):
+ localclient_username, localclient_password = get_localhost_auth()
+ if not localclient_username:
+ # Nothing to do here, there's no auth file
+ return
+ for idx, (__, host, __, username, __) in enumerate(config['hosts'][:]):
+ if host in LOCALHOST and not username:
+ config['hosts'][idx][3] = localclient_username
+ config['hosts'][idx][4] = localclient_password
+ return config
+
+
+class HostList(object):
+ def __init__(self):
+ self.config = Config('hostlist.conf', default_hostlist(), config_dir=get_config_dir(), file_version=2)
+ self.config.run_converter((0, 1), 2, _migrate_config_1_to_2)
+ self.config.save()
+
+ def check_info_exists(self, hostname, port, username, skip_host_id=None):
+ """Check for exising host entries with the same details.
+
+ Args:
+ hostname (str): The IP or hostname of the deluge daemon.
+ port (int): The port of the deluge daemon.
+ username (str): The username to login to the daemon with.
+ skip_host_id (str): A host_id to skip to check if other hosts match details.
+
+ Raises:
+ ValueError: Host details already exist.
+
+ """
+ for host_entry in self.config['hosts']:
+ if (hostname, port, username) == (host_entry[1], host_entry[2], host_entry[3]):
+ if skip_host_id is not None and skip_host_id == host_entry[0]:
+ continue
+ raise ValueError('Host details already in hostlist')
+
+ def add_host(self, hostname, port, username, password):
+ """Add a new host to hostlist.
+
+ Args:
+ hostname (str): The IP or hostname of the deluge daemon.
+ port (int): The port of the deluge daemon.
+ username (str): The username to login to the daemon with.
+ password (str): The password to login to the daemon with.
+
+ Returns:
+ str: The new host id.
+ """
+ if (not password and not username or username == 'localclient') and hostname in LOCALHOST:
+ username, password = get_localhost_auth()
+
+ validate_host_info(hostname, port)
+ self.check_info_exists(hostname, port, username)
+ host_id = sha1(str(time.time())).hexdigest()
+ self.config['hosts'].append((host_id, hostname, port, username, password))
+ self.config.save()
+ return host_id
+
+ def get_host_info(self, host_id):
+ """Get the host details for host_id.
+
+ Includes password details!
+
+ """
+ for host_entry in self.config['hosts']:
+ if host_entry[0] == host_id:
+ return host_entry
+ else:
+ return []
+
+ def get_hosts_info(self):
+ """Get all the hosts in the hostlist
+
+ Excluding password details.
+ """
+ return [host[0:4 + 1] for host in self.config['hosts']]
+
+ def get_hosts_info2(self):
+ """Get all the hosts in the hostlist
+
+ Excluding password details.
+ """
+ return [host for host in self.config['hosts']]
+
+ def update_host(self, host_id, hostname, port, username, password):
+ """Update the host with new details.
+
+ Args:
+ host_id (str): The host id to update.
+ hostname (str): The new IP or hostname of the deluge daemon.
+ port (int): The new port of the deluge daemon.
+ username (str): The new username to login to the daemon with.
+ password (str): The new password to login to the daemon with.
+
+ """
+ validate_host_info(hostname, port)
+ self.check_info_exists(hostname, port, username, skip_host_id=host_id)
+
+ if (not password and not username or username == 'localclient') and hostname in LOCALHOST:
+ username, password = get_localhost_auth()
+
+ for host_entry in self.config['hosts']:
+ if host_id == host_entry[0]:
+ host_entry = host_id, hostname, port, username, password
+ return True
+ return False
+
+ def remove_host(self, host_id):
+ for host_entry in self.config['hosts']:
+ if host_id == host_entry[0]:
+ self.config['hosts'].remove(host_entry)
+ self.config.save()
+ return True
+ else:
+ return False
+
+ def add_default_host(self):
+ self.add_host(DEFAULT_HOST, DEFAULT_PORT, *get_localhost_auth())
diff --git a/deluge/ui/web/auth.py b/deluge/ui/web/auth.py
index d80327f57..0a2ad0ac3 100644
--- a/deluge/ui/web/auth.py
+++ b/deluge/ui/web/auth.py
@@ -18,15 +18,9 @@ from email.utils import formatdate
from twisted.internet.task import LoopingCall
-log = logging.getLogger(__name__)
-
+from deluge.common import AUTH_LEVEL_ADMIN, AUTH_LEVEL_NONE
-AUTH_LEVEL_NONE = 0
-AUTH_LEVEL_READONLY = 1
-AUTH_LEVEL_NORMAL = 5
-AUTH_LEVEL_ADMIN = 10
-
-AUTH_LEVEL_DEFAULT = AUTH_LEVEL_NORMAL
+log = logging.getLogger(__name__)
class AuthError(Exception):
@@ -36,7 +30,7 @@ class AuthError(Exception):
"""
pass
-# Import after as json_api imports the above AuthError and AUTH_LEVEL_DEFAULT
+# Import after as json_api imports the above AuthError
from deluge.ui.web.json_api import export, JSONComponent # NOQA, isort:skip pylint: disable=wrong-import-position
diff --git a/deluge/ui/web/json_api.py b/deluge/ui/web/json_api.py
index 72052f7da..62ccd7709 100644
--- a/deluge/ui/web/json_api.py
+++ b/deluge/ui/web/json_api.py
@@ -10,13 +10,11 @@
from __future__ import division, unicode_literals
import base64
-import hashlib
import json
import logging
import os
import shutil
import tempfile
-import time
from types import FunctionType
from twisted.internet import defer, reactor
@@ -24,10 +22,11 @@ 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.configmanager import get_config_dir
from deluge.ui.client import Client, client
+from deluge.ui.common import FileTree2, TorrentInfo
from deluge.ui.coreconfig import CoreConfig
+from deluge.ui.hostlist import HostList
from deluge.ui.sessionproxy import SessionProxy
from deluge.ui.translations_util import get_languages
from deluge.ui.web.common import _, compress
@@ -58,7 +57,8 @@ def export(auth_level=AUTH_LEVEL_DEFAULT):
"""
global AUTH_LEVEL_DEFAULT, AuthError
if AUTH_LEVEL_DEFAULT is None:
- from deluge.ui.web.auth import AUTH_LEVEL_DEFAULT, AuthError # NOQA pylint: disable=redefined-outer-name
+ from deluge.common import AUTH_LEVEL_DEFAULT
+ from deluge.ui.web.auth import AuthError # NOQA pylint: disable=redefined-outer-name
def wrap(func, *args, **kwargs):
func._json_export = True
@@ -262,19 +262,6 @@ class JSON(resource.Resource, component.Component):
self._local_methods[name + '.' + d] = getattr(obj, d)
-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_USER = HOSTLIST_USER
-HOSTS_STATUS = 3
-HOSTS_INFO = 4
-
FILES_KEYS = ['files', 'file_progress', 'file_priorities']
@@ -370,9 +357,7 @@ class WebApi(JSONComponent):
def __init__(self):
super(WebApi, self).__init__('Web', depend=['SessionProxy'])
- self.host_list = ConfigManager('hostlist.conf.1.2', uicommon.DEFAULT_HOSTS)
- if not os.path.isfile(self.host_list.config_file):
- self.host_list.save()
+ self.hostlist = HostList()
self.core_config = CoreConfig()
self.event_queue = EventQueue()
try:
@@ -410,23 +395,6 @@ class WebApi(JSONComponent):
component.get('Web.PluginManager').stop()
return self.stop()
- def _get_host(self, host_id):
- """Information about a host from supplied host id.
-
- Args:
- host_id (str): The id of the host.
-
- Returns:
- list: The host information, empty list if not found.
-
- """
- host_info = []
- for host_entry in self.host_list['hosts']:
- if host_entry[0] == host_id:
- host_info = host_entry
- break
- return host_info
-
def start(self):
self.core_config.start()
return self.sessionproxy.start()
@@ -611,7 +579,7 @@ class WebApi(JSONComponent):
item.update(info[path])
return item
- file_tree = uicommon.FileTree2(paths)
+ file_tree = FileTree2(paths)
file_tree.walk(walk)
d.callback(file_tree.get_tree())
@@ -685,7 +653,7 @@ class WebApi(JSONComponent):
:rtype: dictionary
"""
try:
- torrent_info = uicommon.TorrentInfo(filename.strip(), 2)
+ torrent_info = TorrentInfo(filename.strip(), 2)
return torrent_info.as_dict('name', 'info_hash', 'files_tree')
except Exception as ex:
log.error(ex)
@@ -730,13 +698,25 @@ class WebApi(JSONComponent):
deferreds.append(d)
return DeferredList(deferreds, consumeErrors=False)
+ def _get_host(self, host_id):
+ """Information about a host from supplied host id.
+
+ Args:
+ host_id (str): The id of the host.
+
+ Returns:
+ list: The host information, empty list if not found.
+
+ """
+ return list(self.hostlist.get_host_info(host_id))
+
@export
def get_hosts(self):
"""
Return the hosts in the hostlist.
"""
log.debug('get_hosts called')
- return [(tuple(host[HOSTS_ID:HOSTS_USER + 1]) + ('Offline',)) for host in self.host_list['hosts']]
+ return self.hostlist.get_hosts() + ['']
@export
def get_host_status(self, host_id):
@@ -787,6 +767,39 @@ class WebApi(JSONComponent):
return d
@export
+ def add_host(self, host, port, username='', password=''):
+ """Adds a host to the list.
+
+ Args:
+ host (str): The IP or hostname of the deluge daemon.
+ port (int): The port of the deluge daemon.
+ username (str): The username to login to the daemon with.
+ password (str): The password to login to the daemon with.
+
+ Returns:
+ tuple: A tuple of (bool, str). If True will contain the host_id, otherwise
+ if False will contain the error message.
+ """
+ try:
+ host_id = self.hostlist.add_host(host, port, username, password)
+ except ValueError as ex:
+ return False, str(ex)
+ else:
+ return True, host_id
+
+ @export
+ def remove_host(self, host_id):
+ """Removes a host from the list.
+
+ Args:
+ host_id (str): The host identifying hash.
+
+ Returns:
+ bool: True if succesful, False otherwise.
+ """
+ return self.hostlist.remove_host(host_id)
+
+ @export
def start_daemon(self, port):
"""
Starts a local daemon.
@@ -828,55 +841,6 @@ class WebApi(JSONComponent):
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
- :param username: the username to login as
- :type username: string
- :param 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[1], entry[2], entry[3]) == (host, port, username):
- return (False, 'Host already in the list')
-
- try:
- port = int(port)
- except ValueError:
- 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, connection_id
-
- @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 not host:
- 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.