[UI] Refactor duplicated code out of connection managers
This commit is contained in:
parent
54a081bdfd
commit
ac48ad982e
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,25 +330,23 @@ class ConnectionManager(component.Component):
|
|||
return
|
||||
|
||||
self.builder.get_object('button_edithost').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]
|
||||
|
||||
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)
|
||||
|
||||
# Get some values about the selected host
|
||||
__, 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 = host in LOCALHOST
|
||||
|
||||
# See if this is the currently connected host
|
||||
if status == 'Connected':
|
||||
# Display a disconnect button if we're connected to this host
|
||||
|
@ -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
|
||||
|
|
|
@ -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())
|
|
@ -18,17 +18,11 @@ from email.utils import formatdate
|
|||
|
||||
from twisted.internet.task import LoopingCall
|
||||
|
||||
from deluge.common import AUTH_LEVEL_ADMIN, AUTH_LEVEL_NONE
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
AUTH_LEVEL_NONE = 0
|
||||
AUTH_LEVEL_READONLY = 1
|
||||
AUTH_LEVEL_NORMAL = 5
|
||||
AUTH_LEVEL_ADMIN = 10
|
||||
|
||||
AUTH_LEVEL_DEFAULT = AUTH_LEVEL_NORMAL
|
||||
|
||||
|
||||
class AuthError(Exception):
|
||||
"""
|
||||
An exception that might be raised when checking a request for
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
@ -786,6 +766,39 @@ class WebApi(JSONComponent):
|
|||
d.addCallback(on_connect, c, host_id).addErrback(on_connect_failed, host_id)
|
||||
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):
|
||||
"""
|
||||
|
@ -827,55 +840,6 @@ class WebApi(JSONComponent):
|
|||
main_deferred.callback((False, 'An error occurred'))
|
||||
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):
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue