mirror of
https://git.deluge-torrent.org/deluge
synced 2025-04-20 11:35:49 +00:00
[UI] Further refactoring of the connection managers
* Add host Edit button to WebUI. * Updated and fixed associated tests. * Refactored related gtkui code to better understand code flow. * Removed dead code in gtkui.
This commit is contained in:
parent
2f11bb8303
commit
31555ee5ed
19 changed files with 822 additions and 965 deletions
|
@ -997,6 +997,40 @@ def create_localclient_account(append=False):
|
|||
os.fsync(_file.fileno())
|
||||
|
||||
|
||||
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.
|
||||
|
||||
"""
|
||||
from deluge.configmanager import get_config_dir
|
||||
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 set_env_variable(name, value):
|
||||
"""
|
||||
:param name: environment variable name
|
||||
|
|
|
@ -66,7 +66,7 @@ class WebServerTestBase(BaseTestCase, DaemonBase):
|
|||
|
||||
self.deluge_web = DelugeWeb(daemon=False)
|
||||
|
||||
host = list(self.deluge_web.web_api.hostlist.get_hosts_info2()[0])
|
||||
host = list(self.deluge_web.web_api.hostlist.config['hosts'][0])
|
||||
host[2] = self.listen_port
|
||||
self.deluge_web.web_api.hostlist.config['hosts'][0] = tuple(host)
|
||||
self.host_id = host[0]
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import deluge.component as component
|
||||
from deluge.common import get_localhost_auth
|
||||
from deluge.core.authmanager import AUTH_LEVEL_ADMIN, AuthManager
|
||||
from deluge.ui import hostlist
|
||||
|
||||
from .basetest import BaseTestCase
|
||||
|
||||
|
@ -25,6 +25,6 @@ class AuthManagerTestCase(BaseTestCase):
|
|||
|
||||
def test_authorize(self):
|
||||
self.assertEqual(
|
||||
self.auth.authorize(*hostlist.get_localhost_auth()),
|
||||
self.auth.authorize(*get_localhost_auth()),
|
||||
AUTH_LEVEL_ADMIN
|
||||
)
|
||||
|
|
|
@ -11,10 +11,9 @@ from twisted.internet import defer
|
|||
|
||||
import deluge.component as component
|
||||
from deluge import error
|
||||
from deluge.common import AUTH_LEVEL_NORMAL
|
||||
from deluge.common import AUTH_LEVEL_NORMAL, get_localhost_auth
|
||||
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
|
||||
|
|
|
@ -11,11 +11,11 @@ from __future__ import unicode_literals
|
|||
|
||||
import deluge.component as component
|
||||
import deluge.error
|
||||
from deluge.common import get_localhost_auth
|
||||
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.hostlist import get_localhost_auth
|
||||
|
||||
from .basetest import BaseTestCase
|
||||
|
||||
|
|
|
@ -23,9 +23,8 @@ import deluge.ui.console
|
|||
import deluge.ui.console.cmdline.commands.quit
|
||||
import deluge.ui.console.main
|
||||
import deluge.ui.web.server
|
||||
from deluge.common import utf8_encode_structure
|
||||
from deluge.common import get_localhost_auth, 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
|
||||
|
@ -334,7 +333,7 @@ class ConsoleUIWithDaemonBaseTestCase(UIWithDaemonBaseTestCase):
|
|||
|
||||
def set_up(self):
|
||||
# Avoid calling reactor.shutdown after commands are executed by main.exec_args()
|
||||
self.patch(deluge.ui.console.cmdline.commands.quit, 'reactor', common.ReactorOverride())
|
||||
deluge.ui.console.main.reactor = common.ReactorOverride()
|
||||
return UIWithDaemonBaseTestCase.set_up(self)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
|
@ -344,7 +343,6 @@ class ConsoleUIWithDaemonBaseTestCase(UIWithDaemonBaseTestCase):
|
|||
[username] + ['--password'] + [password] + ['status'])
|
||||
fd = StringFileDescriptor(sys.stdout)
|
||||
self.patch(sys, 'stdout', fd)
|
||||
self.patch(deluge.ui.console.main, 'reactor', common.ReactorOverride())
|
||||
|
||||
yield self.exec_command()
|
||||
|
||||
|
|
|
@ -91,17 +91,17 @@ class WebAPITestCase(WebServerTestBase):
|
|||
|
||||
def test_get_host(self):
|
||||
self.assertFalse(self.deluge_web.web_api._get_host('invalid_id'))
|
||||
conn = list(self.deluge_web.web_api.hostlist.get_hosts_info2()[0])
|
||||
self.assertEqual(self.deluge_web.web_api._get_host(conn[0]), conn)
|
||||
conn = list(self.deluge_web.web_api.hostlist.get_hosts_info()[0])
|
||||
self.assertEqual(self.deluge_web.web_api._get_host(conn[0]), conn[0:4])
|
||||
|
||||
def test_add_host(self):
|
||||
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])
|
||||
self.assertEqual(ret[0], True)
|
||||
conn[0] = ret[1]
|
||||
self.assertEqual(self.deluge_web.web_api._get_host(conn[0]), conn)
|
||||
result, host_id = self.deluge_web.web_api.add_host(conn[1], conn[2], conn[3], conn[4])
|
||||
self.assertEqual(result, True)
|
||||
conn[0] = host_id
|
||||
self.assertEqual(self.deluge_web.web_api._get_host(conn[0]), conn[0:4])
|
||||
|
||||
# Add already existing host
|
||||
ret = self.deluge_web.web_api.add_host(conn[1], conn[2], conn[3], conn[4])
|
||||
|
@ -115,7 +115,7 @@ class WebAPITestCase(WebServerTestBase):
|
|||
def test_remove_host(self):
|
||||
conn = ['connection_id', '', 0, '', '']
|
||||
self.deluge_web.web_api.hostlist.config['hosts'].append(conn)
|
||||
self.assertEqual(self.deluge_web.web_api._get_host(conn[0]), conn)
|
||||
self.assertEqual(self.deluge_web.web_api._get_host(conn[0]), conn[0:4])
|
||||
# Remove valid host
|
||||
self.assertTrue(self.deluge_web.web_api.remove_host(conn[0]))
|
||||
self.assertFalse(self.deluge_web.web_api._get_host(conn[0]))
|
||||
|
|
|
@ -17,11 +17,10 @@ import sys
|
|||
from twisted.internet import defer, reactor, ssl
|
||||
from twisted.internet.protocol import ClientFactory
|
||||
|
||||
import deluge.common
|
||||
from deluge import error
|
||||
from deluge.common import get_localhost_auth, get_version
|
||||
from deluge.decorators import deprecated
|
||||
from deluge.transfer import DelugeTransferProtocol
|
||||
from deluge.ui.hostlist import get_localhost_auth
|
||||
|
||||
RPC_RESPONSE = 1
|
||||
RPC_ERROR = 2
|
||||
|
@ -384,8 +383,7 @@ class DaemonSSLProxy(DaemonProxy):
|
|||
def authenticate(self, username, password):
|
||||
log.debug('%s.authenticate: %s', self.__class__.__name__, username)
|
||||
login_deferred = defer.Deferred()
|
||||
d = self.call('daemon.login', username, password,
|
||||
client_version=deluge.common.get_version())
|
||||
d = self.call('daemon.login', username, password, client_version=get_version())
|
||||
d.addCallbacks(self.__on_login, self.__on_login_fail, callbackArgs=[username, login_deferred],
|
||||
errbackArgs=[login_deferred])
|
||||
return login_deferred
|
||||
|
@ -619,17 +617,14 @@ class Client(object):
|
|||
self.stop_standalone()
|
||||
|
||||
def start_daemon(self, port, config):
|
||||
"""
|
||||
Starts a daemon process.
|
||||
"""Starts a daemon process.
|
||||
|
||||
:param port: the port for the daemon to listen on
|
||||
:type port: int
|
||||
:param config: the path to the current config folder
|
||||
:type config: str
|
||||
:returns: True if started, False if not
|
||||
:rtype: bool
|
||||
Args:
|
||||
port (int): Port for the daemon to listen on.
|
||||
config (str): Config path to pass to daemon.
|
||||
|
||||
:raises OSError: received from subprocess.call()
|
||||
Returns:
|
||||
bool: True is successfully started the daemon, False otherwise.
|
||||
|
||||
"""
|
||||
# subprocess.popen does not work with unicode args (with non-ascii characters) on windows
|
||||
|
@ -644,13 +639,12 @@ class Client(object):
|
|||
'the deluged package is installed, or added to your PATH.'))
|
||||
else:
|
||||
log.exception(ex)
|
||||
raise ex
|
||||
except Exception as ex:
|
||||
log.error('Unable to start daemon!')
|
||||
log.exception(ex)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_localhost(self):
|
||||
"""
|
||||
|
|
|
@ -13,7 +13,7 @@ import logging
|
|||
|
||||
import deluge.component as component
|
||||
from deluge.decorators import overrides
|
||||
from deluge.ui.client import Client, client
|
||||
from deluge.ui.client import 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
|
||||
|
@ -48,7 +48,7 @@ class ConnectionManager(BaseMode, PopupsHandler):
|
|||
space_below=True)
|
||||
self.push_popup(popup, clear=True)
|
||||
|
||||
for host_entry in self.hostlist.get_host_info():
|
||||
for host_entry in self.hostlist.get_hosts_info():
|
||||
host_id, hostname, port, user = host_entry
|
||||
args = {'data': host_id, 'foreground': 'red'}
|
||||
state = 'Offline'
|
||||
|
@ -64,34 +64,13 @@ class ConnectionManager(BaseMode, PopupsHandler):
|
|||
self.refresh()
|
||||
|
||||
def update_hosts_status(self):
|
||||
"""Updates the host status"""
|
||||
def on_connect(result, c, host_id):
|
||||
def on_info(info, c):
|
||||
self.statuses[host_id] = info
|
||||
self.update_select_host_popup()
|
||||
c.disconnect()
|
||||
|
||||
def on_info_fail(reason, c):
|
||||
if host_id in self.statuses:
|
||||
del self.statuses[host_id]
|
||||
c.disconnect()
|
||||
|
||||
d = c.daemon.info()
|
||||
d.addCallback(on_info, c)
|
||||
d.addErrback(on_info_fail, c)
|
||||
|
||||
def on_connect_failed(reason, host_id):
|
||||
if host_id in self.statuses:
|
||||
del self.statuses[host_id]
|
||||
for host_entry in self.hostlist.get_hosts_info():
|
||||
def on_host_status(status_info):
|
||||
self.statuses[status_info[0]] = status_info
|
||||
self.update_select_host_popup()
|
||||
|
||||
for host_entry in self.hostlist.get_hosts_info2():
|
||||
c = Client()
|
||||
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)
|
||||
self.hostlist.get_host_status(host_entry[0]).addCallback(on_host_status)
|
||||
|
||||
def _on_connected(self, result):
|
||||
d = component.get('ConsoleUI').start_console()
|
||||
|
@ -108,12 +87,9 @@ class ConnectionManager(BaseMode, PopupsHandler):
|
|||
|
||||
def _host_selected(self, selected_host, *args, **kwargs):
|
||||
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)
|
||||
d = self.hostlist.connect_host(selected_host)
|
||||
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):
|
||||
|
|
|
@ -14,14 +14,14 @@ import os
|
|||
from socket import gaierror, gethostbyname
|
||||
|
||||
import gtk
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet import defer, reactor
|
||||
|
||||
import deluge.component as component
|
||||
from deluge.common import resource_filename
|
||||
from deluge.common import resource_filename, windows_check
|
||||
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.gtkui.common import get_clipboard_text, get_deluge_icon
|
||||
from deluge.ui.gtkui.common import get_clipboard_text
|
||||
from deluge.ui.gtkui.dialogs import AuthenticationDialog, ErrorDialog
|
||||
from deluge.ui.hostlist import DEFAULT_PORT, HostList
|
||||
|
||||
|
@ -68,7 +68,6 @@ def cell_render_status(column, cell, model, row, data):
|
|||
pixbuf = None
|
||||
if status in HOSTLIST_STATUS:
|
||||
pixbuf = HOSTLIST_PIXBUFS[HOSTLIST_STATUS.index(status)]
|
||||
|
||||
cell.set_property('pixbuf', pixbuf)
|
||||
|
||||
|
||||
|
@ -76,7 +75,7 @@ class ConnectionManager(component.Component):
|
|||
def __init__(self):
|
||||
component.Component.__init__(self, 'ConnectionManager')
|
||||
self.gtkui_config = ConfigManager('gtkui.conf')
|
||||
|
||||
self.hostlist = HostList()
|
||||
self.running = False
|
||||
|
||||
# Component overrides
|
||||
|
@ -93,92 +92,408 @@ class ConnectionManager(component.Component):
|
|||
|
||||
# Public methods
|
||||
def show(self):
|
||||
"""
|
||||
Show the ConnectionManager dialog.
|
||||
"""
|
||||
# Get the gtk builder file for the connection manager
|
||||
"""Show the ConnectionManager dialog."""
|
||||
self.builder = gtk.Builder()
|
||||
# The main dialog
|
||||
self.builder.add_from_file(resource_filename(
|
||||
'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')))
|
||||
# The ask password dialog
|
||||
self.builder.add_from_file(resource_filename(
|
||||
'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.askpassword_dialog = self.builder.get_object('askpassword_dialog')
|
||||
self.askpassword_dialog.set_transient_for(self.connection_manager)
|
||||
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
|
||||
if not HOSTLIST_PIXBUFS:
|
||||
for stock_id in (gtk.STOCK_NO, gtk.STOCK_YES, gtk.STOCK_CONNECT):
|
||||
HOSTLIST_PIXBUFS.append(
|
||||
self.connection_manager.render_icon(
|
||||
stock_id, gtk.ICON_SIZE_MENU
|
||||
)
|
||||
)
|
||||
self.connection_manager.render_icon(stock_id, gtk.ICON_SIZE_MENU))
|
||||
|
||||
# Create the host list gtkliststore
|
||||
# id-hash, hostname, port, username, password, status, version
|
||||
self.liststore = gtk.ListStore(str, str, int, str, str, str, str)
|
||||
# Setup the hostlist liststore and treeview
|
||||
self.treeview = self.builder.get_object('treeview_hostlist')
|
||||
self.liststore = self.builder.get_object('liststore_hostlist')
|
||||
|
||||
# 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, HOSTLIST_COL_STATUS)
|
||||
self.hostlist.append_column(column)
|
||||
self.treeview.append_column(column)
|
||||
|
||||
render = gtk.CellRendererText()
|
||||
column = gtk.TreeViewColumn(_('Host'), render, text=HOSTLIST_COL_HOST)
|
||||
column.set_cell_data_func(
|
||||
render, cell_render_host, (HOSTLIST_COL_HOST, HOSTLIST_COL_PORT, HOSTLIST_COL_USER))
|
||||
host_data = (HOSTLIST_COL_HOST, HOSTLIST_COL_PORT, HOSTLIST_COL_USER)
|
||||
column.set_cell_data_func(render, cell_render_host, host_data)
|
||||
column.set_expand(True)
|
||||
self.hostlist.append_column(column)
|
||||
render = gtk.CellRendererText()
|
||||
column = gtk.TreeViewColumn(_('Version'), render, text=HOSTLIST_COL_VERSION)
|
||||
self.hostlist.append_column(column)
|
||||
self.treeview.append_column(column)
|
||||
|
||||
column = gtk.TreeViewColumn(_('Version'), gtk.CellRendererText(), text=HOSTLIST_COL_VERSION)
|
||||
self.treeview.append_column(column)
|
||||
|
||||
# Load any saved host entries
|
||||
self._load_liststore()
|
||||
# Set widgets to values from gtkui config.
|
||||
self._load_widget_config()
|
||||
self._update_widget_buttons()
|
||||
|
||||
# Connect the signals to the handlers
|
||||
self.builder.connect_signals(self)
|
||||
self.hostlist.get_selection().connect(
|
||||
'changed', self.on_hostlist_selection_changed
|
||||
)
|
||||
|
||||
# Load any saved host entries
|
||||
self.__load_hostlist()
|
||||
self.__load_options()
|
||||
self.__update_list()
|
||||
self.treeview.get_selection().connect('changed', self.on_hostlist_selection_changed)
|
||||
|
||||
# Set running True before update status call.
|
||||
self.running = True
|
||||
# Trigger the on_selection_changed code and select the first host
|
||||
# if possible
|
||||
self.hostlist.get_selection().unselect_all()
|
||||
if len(self.liststore) > 0:
|
||||
self.hostlist.get_selection().select_path(0)
|
||||
|
||||
if windows_check():
|
||||
# Call to simulate() required to workaround showing daemon status (see #2813)
|
||||
reactor.simulate()
|
||||
self._update_host_status()
|
||||
|
||||
# Trigger the on_selection_changed code and select the first host if possible
|
||||
self.treeview.get_selection().unselect_all()
|
||||
if len(self.liststore):
|
||||
self.treeview.get_selection().select_path(0)
|
||||
|
||||
# Run the dialog
|
||||
self.connection_manager.run()
|
||||
|
||||
# Dialog closed so cleanup.
|
||||
self.running = False
|
||||
|
||||
# Save the toggle options
|
||||
self.__save_options()
|
||||
|
||||
self.connection_manager.destroy()
|
||||
del self.builder
|
||||
del self.connection_manager
|
||||
del self.liststore
|
||||
del self.hostlist
|
||||
del self.treeview
|
||||
|
||||
def _load_liststore(self):
|
||||
"""Load saved host entries"""
|
||||
for host_entry in self.hostlist.get_hosts_info():
|
||||
host_id, host, port, username = host_entry
|
||||
self.liststore.append([host_id, host, port, username, '', '', ''])
|
||||
|
||||
def _load_widget_config(self):
|
||||
"""Set the widgets to show the correct options from the config."""
|
||||
self.builder.get_object('chk_autoconnect').set_active(
|
||||
self.gtkui_config['autoconnect'])
|
||||
self.builder.get_object('chk_autostart').set_active(
|
||||
self.gtkui_config['autostart_localhost'])
|
||||
self.builder.get_object('chk_donotshow').set_active(
|
||||
not self.gtkui_config['show_connection_manager_on_start'])
|
||||
|
||||
def _update_host_status(self):
|
||||
"""Updates the host status"""
|
||||
if not self.running:
|
||||
# Callback likely fired after the window closed.
|
||||
return
|
||||
|
||||
def on_host_status(status_info, row):
|
||||
if self.running and row:
|
||||
row[HOSTLIST_COL_STATUS] = status_info[1]
|
||||
row[HOSTLIST_COL_VERSION] = status_info[2]
|
||||
self._update_widget_buttons()
|
||||
|
||||
deferreds = []
|
||||
for row in self.liststore:
|
||||
host_id = row[HOSTLIST_COL_ID]
|
||||
d = self.hostlist.get_host_status(host_id)
|
||||
try:
|
||||
d.addCallback(on_host_status, row)
|
||||
except AttributeError:
|
||||
on_host_status(d, row)
|
||||
else:
|
||||
deferreds.append(d)
|
||||
defer.DeferredList(deferreds)
|
||||
|
||||
def _update_widget_buttons(self):
|
||||
"""Updates the dialog button states."""
|
||||
self.builder.get_object('button_refresh').set_sensitive(len(self.liststore))
|
||||
self.builder.get_object('button_startdaemon').set_sensitive(False)
|
||||
self.builder.get_object('button_connect').set_sensitive(False)
|
||||
self.builder.get_object('button_connect').set_label(_('C_onnect'))
|
||||
self.builder.get_object('button_edithost').set_sensitive(False)
|
||||
self.builder.get_object('button_removehost').set_sensitive(False)
|
||||
self.builder.get_object('button_startdaemon').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_with_mnemonic('_Start Daemon')
|
||||
|
||||
model, row = self.treeview.get_selection().get_selected()
|
||||
if row:
|
||||
self.builder.get_object('button_edithost').set_sensitive(True)
|
||||
self.builder.get_object('button_removehost').set_sensitive(True)
|
||||
else:
|
||||
return
|
||||
|
||||
# Get selected host info.
|
||||
__, host, port, __, __, status, __ = model[row]
|
||||
try:
|
||||
gethostbyname(host)
|
||||
except gaierror as ex:
|
||||
log.error('Error resolving host %s to ip: %s', row[HOSTLIST_COL_HOST], ex.args[1])
|
||||
self.builder.get_object('button_connect').set_sensitive(False)
|
||||
return
|
||||
|
||||
log.debug('Host Status: %s, %s', host, status)
|
||||
|
||||
# Check to see if the host is online
|
||||
if status == 'Connected' or status == 'Online':
|
||||
self.builder.get_object('button_connect').set_sensitive(True)
|
||||
self.builder.get_object('image_startdaemon').set_from_stock(
|
||||
gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
|
||||
self.builder.get_object('label_startdaemon').set_text_with_mnemonic(_('_Stop Daemon'))
|
||||
self.builder.get_object('button_startdaemon').set_sensitive(True)
|
||||
if status == 'Connected':
|
||||
# Display a disconnect button if we're connected to this host
|
||||
self.builder.get_object('button_connect').set_label(_('_Disconnect'))
|
||||
self.builder.get_object('button_removehost').set_sensitive(False)
|
||||
elif host in LOCALHOST:
|
||||
# If localhost we can start the dameon.
|
||||
self.builder.get_object('button_startdaemon').set_sensitive(True)
|
||||
|
||||
def start_daemon(self, port, config):
|
||||
"""Attempts to start local daemon process and will show an ErrorDialog if not.
|
||||
|
||||
Args:
|
||||
port (int): Port for the daemon to listen on.
|
||||
config (str): Config path to pass to daemon.
|
||||
|
||||
Returns:
|
||||
bool: True is successfully started the daemon, False otherwise.
|
||||
|
||||
"""
|
||||
if client.start_daemon(port, config):
|
||||
log.debug('Localhost daemon started')
|
||||
reactor.callLater(0.5, self._update_host_status)
|
||||
return True
|
||||
else:
|
||||
ErrorDialog(
|
||||
_('Unable to start daemon!'),
|
||||
_('Check deluged package is installed and logs for further details')).run()
|
||||
return False
|
||||
|
||||
# Signal handlers
|
||||
def _connect(self, host_id, username=None, password=None, try_counter=0):
|
||||
def do_connect(result, username=None, password=None, *args):
|
||||
log.debug('Attempting to connect to daemon...')
|
||||
for host_entry in self.hostlist.config['hosts']:
|
||||
if host_entry[0] == host_id:
|
||||
__, host, port, host_user, host_pass = host_entry
|
||||
|
||||
username = username if username else host_user
|
||||
password = password if password else host_pass
|
||||
|
||||
d = client.connect(host, port, username, password)
|
||||
d.addCallback(self._on_connect, host_id)
|
||||
d.addErrback(self._on_connect_fail, host_id, try_counter)
|
||||
return d
|
||||
|
||||
if client.connected():
|
||||
return client.disconnect().addCallback(do_connect, username, password)
|
||||
else:
|
||||
return do_connect(None, username, password)
|
||||
|
||||
def _on_connect(self, daemon_info, host_id):
|
||||
log.debug('Connected to daemon: %s', host_id)
|
||||
if self.gtkui_config['autoconnect']:
|
||||
self.gtkui_config['autoconnect_host_id'] = host_id
|
||||
if self.running:
|
||||
# When connected to a client, and then trying to connect to another,
|
||||
# this component will be stopped(while the connect deferred is
|
||||
# running), so, self.connection_manager will be deleted.
|
||||
# If that's not the case, close the dialog.
|
||||
self.connection_manager.response(gtk.RESPONSE_OK)
|
||||
component.start()
|
||||
|
||||
def _on_connect_fail(self, reason, host_id, try_counter):
|
||||
log.debug('Failed to connect: %s', reason.value)
|
||||
|
||||
if reason.check(AuthenticationRequired, BadLoginError):
|
||||
log.debug('PasswordRequired exception')
|
||||
dialog = AuthenticationDialog(reason.value.message, reason.value.username)
|
||||
|
||||
def dialog_finished(response_id):
|
||||
if response_id == gtk.RESPONSE_OK:
|
||||
self.__connect(host_id, dialog.get_username(), dialog.get_password())
|
||||
return dialog.run().addCallback(dialog_finished)
|
||||
|
||||
elif reason.trap(IncompatibleClient):
|
||||
return ErrorDialog(_('Incompatible Client'), reason.value.message).run()
|
||||
|
||||
if try_counter:
|
||||
log.info('Retrying connection.. Retries left: %s', try_counter)
|
||||
return reactor.callLater(0.8, self._connect, host_id, try_counter=try_counter - 1)
|
||||
|
||||
msg = str(reason.value)
|
||||
if not self.gtkui_config['autostart_localhost']:
|
||||
msg += '\n' + _('Auto-starting the daemon locally is not enabled. '
|
||||
'See "Options" on the "Connection Manager".')
|
||||
ErrorDialog(_('Failed To Connect'), msg).run()
|
||||
|
||||
def on_button_connect_clicked(self, widget=None):
|
||||
"""Button handler for connect to or disconnect from daemon."""
|
||||
model, row = self.treeview.get_selection().get_selected()
|
||||
if not row:
|
||||
return
|
||||
|
||||
host_id, host, port, __, __, status, __ = model[row]
|
||||
# If status is connected then connect button disconnects instead.
|
||||
if status == 'Connected':
|
||||
def on_disconnect(reason):
|
||||
self._update_host_status()
|
||||
return client.disconnect().addCallback(on_disconnect)
|
||||
|
||||
try_counter = 0
|
||||
auto_start = self.builder.get_object('chk_autostart').get_active()
|
||||
if auto_start and host in LOCALHOST and status == 'Offline':
|
||||
# Start the local daemon and then connect with retries set.
|
||||
if self.start_daemon(port, get_config_dir()):
|
||||
try_counter = 4
|
||||
else:
|
||||
# Don't attempt to connect to offline daemon.
|
||||
return
|
||||
|
||||
self._connect(host_id, try_counter=try_counter)
|
||||
|
||||
def on_button_close_clicked(self, widget):
|
||||
self.connection_manager.response(gtk.RESPONSE_CLOSE)
|
||||
|
||||
def _run_addhost_dialog(self, edit_host_info=None):
|
||||
"""Create and runs the add host dialog.
|
||||
|
||||
Supplying edit_host_info changes the dialog to an edit dialog.
|
||||
|
||||
Args:
|
||||
edit_host_info (list): A list of (host, port, user, pass) to edit.
|
||||
|
||||
Returns:
|
||||
list: The new host info values (host, port, user, pass).
|
||||
|
||||
"""
|
||||
self.builder.add_from_file(resource_filename(
|
||||
'deluge.ui.gtkui', os.path.join('glade', 'connection_manager.addhost.ui')))
|
||||
dialog = self.builder.get_object('addhost_dialog')
|
||||
dialog.set_transient_for(self.connection_manager)
|
||||
hostname_entry = self.builder.get_object('entry_hostname')
|
||||
port_spinbutton = self.builder.get_object('spinbutton_port')
|
||||
username_entry = self.builder.get_object('entry_username')
|
||||
password_entry = self.builder.get_object('entry_password')
|
||||
|
||||
if edit_host_info:
|
||||
dialog.set_title(_('Edit Host'))
|
||||
hostname_entry.set_text(edit_host_info[0])
|
||||
port_spinbutton.set_value(edit_host_info[1])
|
||||
username_entry.set_text(edit_host_info[2])
|
||||
password_entry.set_text(edit_host_info[3])
|
||||
|
||||
response = dialog.run()
|
||||
new_host_info = []
|
||||
if response:
|
||||
new_host_info.append(hostname_entry.get_text())
|
||||
new_host_info.append(port_spinbutton.get_value_as_int())
|
||||
new_host_info.append(username_entry.get_text())
|
||||
new_host_info.append(password_entry.get_text())
|
||||
|
||||
dialog.destroy()
|
||||
return new_host_info
|
||||
|
||||
def on_button_addhost_clicked(self, widget):
|
||||
log.debug('on_button_addhost_clicked')
|
||||
host_info = self._run_addhost_dialog()
|
||||
if host_info:
|
||||
hostname, port, username, password = host_info
|
||||
try:
|
||||
host_id = self.hostlist.add_host(hostname, port, username, password)
|
||||
except ValueError as ex:
|
||||
ErrorDialog(_('Error Adding Host'), ex).run()
|
||||
else:
|
||||
self.liststore.append([host_id, hostname, port, username, password, 'Offline', ''])
|
||||
self._update_host_status()
|
||||
|
||||
def on_button_edithost_clicked(self, widget=None):
|
||||
log.debug('on_button_edithost_clicked')
|
||||
model, row = self.treeview.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_host_status()
|
||||
client.disconnect().addCallback(on_disconnect)
|
||||
return
|
||||
|
||||
host_info = [
|
||||
self.liststore[row][HOSTLIST_COL_HOST],
|
||||
self.liststore[row][HOSTLIST_COL_PORT],
|
||||
self.liststore[row][HOSTLIST_COL_USER],
|
||||
self.liststore[row][HOSTLIST_COL_PASS]]
|
||||
new_host_info = self._run_addhost_dialog(edit_host_info=host_info)
|
||||
if new_host_info:
|
||||
hostname, port, username, password = new_host_info
|
||||
try:
|
||||
self.hostlist.update_host(host_id, hostname, port, username, password)
|
||||
except ValueError as ex:
|
||||
ErrorDialog(_('Error Updating Host'), ex).run()
|
||||
else:
|
||||
self.liststore[row] = host_id, hostname, port, username, password, '', ''
|
||||
self._update_host_status()
|
||||
|
||||
def on_button_removehost_clicked(self, widget):
|
||||
log.debug('on_button_removehost_clicked')
|
||||
# Get the selected rows
|
||||
model, row = self.treeview.get_selection().get_selected()
|
||||
self.hostlist.remove_host(model[row][HOSTLIST_COL_ID])
|
||||
self.liststore.remove(row)
|
||||
# Update the hostlist
|
||||
self._update_host_status()
|
||||
|
||||
def on_button_startdaemon_clicked(self, widget):
|
||||
log.debug('on_button_startdaemon_clicked')
|
||||
if not self.liststore.iter_n_children(None):
|
||||
# There is nothing in the list, so lets create a localhost entry
|
||||
try:
|
||||
self.hostlist.add_default_host()
|
||||
except ValueError as ex:
|
||||
log.error('Error adding default host: %s', ex)
|
||||
else:
|
||||
self.start_daemon(DEFAULT_PORT, get_config_dir())
|
||||
finally:
|
||||
return
|
||||
|
||||
paths = self.treeview.get_selection().get_selected_rows()[1]
|
||||
if len(paths):
|
||||
__, host, port, user, password, status, __ = self.liststore[paths[0]]
|
||||
else:
|
||||
return
|
||||
|
||||
if host not in LOCALHOST:
|
||||
return
|
||||
|
||||
def on_daemon_status_change(d):
|
||||
"""Daemon start/stop callback"""
|
||||
reactor.callLater(0.7, self._update_host_status)
|
||||
|
||||
if status in ('Online', 'Connected'):
|
||||
# Button will stop the daemon if status is online or connected.
|
||||
def on_connect(d, c):
|
||||
"""Client callback to call daemon shutdown"""
|
||||
c.daemon.shutdown().addCallback(on_daemon_status_change)
|
||||
|
||||
if client.connected() and (host, port, user) == client.connection_info():
|
||||
client.daemon.shutdown().addCallback(on_daemon_status_change)
|
||||
elif user and password:
|
||||
c = Client()
|
||||
c.connect(host, port, user, password).addCallback(on_connect, c)
|
||||
else:
|
||||
# Otherwise button will start the daemon.
|
||||
self.start_daemon(port, get_config_dir()).addCallback(on_daemon_status_change)
|
||||
|
||||
def on_button_refresh_clicked(self, widget):
|
||||
self._update_host_status()
|
||||
|
||||
def on_hostlist_row_activated(self, tree, path, view_column):
|
||||
self.on_button_connect_clicked()
|
||||
|
||||
def on_hostlist_selection_changed(self, treeselection):
|
||||
self._update_widget_buttons()
|
||||
|
||||
def on_chk_toggled(self, widget):
|
||||
self.gtkui_config['autoconnect'] = self.builder.get_object('chk_autoconnect').get_active()
|
||||
self.gtkui_config['autostart_localhost'] = self.builder.get_object('chk_autostart').get_active()
|
||||
self.gtkui_config['show_connection_manager_on_start'] = not self.builder.get_object(
|
||||
'chk_donotshow').get_active()
|
||||
|
||||
def on_entry_host_paste_clipboard(self, widget):
|
||||
text = get_clipboard_text()
|
||||
|
@ -194,456 +509,3 @@ class ConnectionManager(component.Component):
|
|||
self.builder.get_object('entry_username').set_text(parsed.username)
|
||||
if parsed.password:
|
||||
self.builder.get_object('entry_password').set_text(parsed.password)
|
||||
|
||||
def __load_hostlist(self):
|
||||
"""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):
|
||||
"""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:
|
||||
if host_id == row[HOSTLIST_COL_ID]:
|
||||
return row
|
||||
return None
|
||||
|
||||
def __update_list(self):
|
||||
"""Updates the host status"""
|
||||
if not hasattr(self, 'liststore'):
|
||||
# This callback was probably fired after the window closed
|
||||
return
|
||||
|
||||
def on_connect(result, c, host_id):
|
||||
# Return if the deferred callback was done after the dialog was closed
|
||||
if not self.running:
|
||||
return
|
||||
row = self.__get_host_row(host_id)
|
||||
|
||||
def on_info(info, c):
|
||||
if not self.running:
|
||||
return
|
||||
if row:
|
||||
row[HOSTLIST_COL_STATUS] = 'Online'
|
||||
row[HOSTLIST_COL_VERSION] = info
|
||||
self.__update_buttons()
|
||||
c.disconnect()
|
||||
|
||||
def on_info_fail(reason, c):
|
||||
if not self.running:
|
||||
return
|
||||
if row:
|
||||
row[HOSTLIST_COL_STATUS] = 'Offline'
|
||||
self.__update_buttons()
|
||||
c.disconnect()
|
||||
|
||||
d = c.daemon.info()
|
||||
d.addCallback(on_info, c)
|
||||
d.addErrback(on_info_fail, c)
|
||||
|
||||
def on_connect_failed(reason, host_id):
|
||||
if not self.running:
|
||||
return
|
||||
row = self.__get_host_row(host_id)
|
||||
if row:
|
||||
row[HOSTLIST_COL_STATUS] = 'Offline'
|
||||
row[HOSTLIST_COL_VERSION] = ''
|
||||
self.__update_buttons()
|
||||
|
||||
for row in self.liststore:
|
||||
host_id = row[HOSTLIST_COL_ID]
|
||||
host = row[HOSTLIST_COL_HOST]
|
||||
port = row[HOSTLIST_COL_PORT]
|
||||
user = row[HOSTLIST_COL_USER]
|
||||
|
||||
try:
|
||||
ip = gethostbyname(host)
|
||||
except gaierror as ex:
|
||||
log.error('Error resolving host %s to ip: %s', host, ex.args[1])
|
||||
continue
|
||||
|
||||
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
|
||||
log.debug('Client connected, query info: %s', info)
|
||||
row[HOSTLIST_COL_VERSION] = info
|
||||
self.__update_buttons()
|
||||
|
||||
row[HOSTLIST_COL_STATUS] = 'Connected'
|
||||
log.debug('Query daemon info')
|
||||
client.daemon.info().addCallback(on_info, row)
|
||||
continue
|
||||
|
||||
# Create a new Client instance
|
||||
c = Client()
|
||||
d = c.connect(host, port, skip_authentication=True)
|
||||
d.addCallback(on_connect, c, host_id)
|
||||
d.addErrback(on_connect_failed, host_id)
|
||||
|
||||
def __load_options(self):
|
||||
"""
|
||||
Set the widgets to show the correct options from the config.
|
||||
"""
|
||||
self.builder.get_object('chk_autoconnect').set_active(
|
||||
self.gtkui_config['autoconnect'])
|
||||
self.builder.get_object('chk_autostart').set_active(
|
||||
self.gtkui_config['autostart_localhost'])
|
||||
self.builder.get_object('chk_donotshow').set_active(
|
||||
not self.gtkui_config['show_connection_manager_on_start'])
|
||||
|
||||
def __save_options(self):
|
||||
"""
|
||||
Set options in gtkui config from the toggle buttons.
|
||||
"""
|
||||
self.gtkui_config['autoconnect'] = self.builder.get_object('chk_autoconnect').get_active()
|
||||
self.gtkui_config['autostart_localhost'] = self.builder.get_object('chk_autostart').get_active()
|
||||
self.gtkui_config['show_connection_manager_on_start'] = not self.builder.get_object(
|
||||
'chk_donotshow').get_active()
|
||||
|
||||
def __update_buttons(self):
|
||||
"""Updates the buttons states."""
|
||||
if len(self.liststore) == 0:
|
||||
# There is nothing in the list
|
||||
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_with_mnemonic('_Start Daemon')
|
||||
|
||||
model, row = self.hostlist.get_selection().get_selected()
|
||||
if not row:
|
||||
self.builder.get_object('button_edithost').set_sensitive(False)
|
||||
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
|
||||
__, 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
|
||||
self.builder.get_object('button_connect').set_label('gtk-disconnect')
|
||||
self.builder.get_object('button_removehost').set_sensitive(False)
|
||||
else:
|
||||
self.builder.get_object('button_connect').set_label('gtk-connect')
|
||||
if status == 'Offline' and not localhost:
|
||||
self.builder.get_object('button_connect').set_sensitive(False)
|
||||
|
||||
# Check to see if the host is online
|
||||
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_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_with_mnemonic(_('_Start Daemon'))
|
||||
|
||||
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 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)
|
||||
|
||||
def start_daemon(self, port, config):
|
||||
"""
|
||||
Attempts to start a daemon process and will show an ErrorDialog if unable
|
||||
to.
|
||||
"""
|
||||
try:
|
||||
return client.start_daemon(port, config)
|
||||
except OSError as ex:
|
||||
from errno import ENOENT
|
||||
if ex.errno == ENOENT:
|
||||
ErrorDialog(
|
||||
_('Unable to start daemon!'),
|
||||
_('Deluge cannot find the `deluged` executable, check that '
|
||||
'the deluged package is installed, or added to your PATH.')).run()
|
||||
|
||||
return False
|
||||
else:
|
||||
raise ex
|
||||
except Exception:
|
||||
import traceback
|
||||
import sys
|
||||
tb = sys.exc_info()
|
||||
ErrorDialog(
|
||||
_('Unable to start daemon!'),
|
||||
_('Please examine the details for more information.'),
|
||||
details=traceback.format_exc(tb[2])).run()
|
||||
|
||||
# Signal handlers
|
||||
def __connect(self, host_id, host, port, username, password,
|
||||
skip_authentication=False, try_counter=0):
|
||||
def do_connect(*args):
|
||||
d = client.connect(host, port, username, password, skip_authentication)
|
||||
d.addCallback(self.__on_connected, host_id)
|
||||
d.addErrback(self.__on_connected_failed, host_id, host, port,
|
||||
username, password, try_counter)
|
||||
return d
|
||||
|
||||
if client.connected():
|
||||
return client.disconnect().addCallback(do_connect)
|
||||
else:
|
||||
return do_connect()
|
||||
|
||||
def __on_connected(self, daemon_info, host_id):
|
||||
if self.gtkui_config['autoconnect']:
|
||||
self.gtkui_config['autoconnect_host_id'] = host_id
|
||||
if self.running:
|
||||
# When connected to a client, and then trying to connect to another,
|
||||
# this component will be stopped(while the connect deferred is
|
||||
# running), so, self.connection_manager will be deleted.
|
||||
# If that's not the case, close the dialog.
|
||||
self.connection_manager.response(gtk.RESPONSE_OK)
|
||||
component.start()
|
||||
|
||||
def __on_connected_failed(self, reason, host_id, host, port, user, password,
|
||||
try_counter):
|
||||
log.debug('Failed to connect: %s', reason.value)
|
||||
|
||||
if reason.check(AuthenticationRequired, BadLoginError):
|
||||
log.debug('PasswordRequired exception')
|
||||
dialog = AuthenticationDialog(reason.value.message, reason.value.username)
|
||||
|
||||
def dialog_finished(response_id, host, port, user):
|
||||
if response_id == gtk.RESPONSE_OK:
|
||||
self.__connect(host_id, host, port,
|
||||
user and user or dialog.get_username(),
|
||||
dialog.get_password())
|
||||
d = dialog.run().addCallback(dialog_finished, host, port, user)
|
||||
return d
|
||||
|
||||
elif reason.trap(IncompatibleClient):
|
||||
return ErrorDialog(_('Incompatible Client'), reason.value.message).run()
|
||||
|
||||
if try_counter:
|
||||
log.info('Retrying connection.. Retries left: %s', try_counter)
|
||||
return reactor.callLater(
|
||||
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():
|
||||
msg += '\n' + _('Auto-starting the daemon locally is not enabled. '
|
||||
'See "Options" on the "Connection Manager".')
|
||||
ErrorDialog(_('Failed To Connect'), msg).run()
|
||||
|
||||
def on_button_connect_clicked(self, widget=None):
|
||||
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, 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
|
||||
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)
|
||||
|
||||
def on_button_addhost_clicked(self, widget):
|
||||
log.debug('on_button_addhost_clicked')
|
||||
dialog = self.builder.get_object('addhost_dialog')
|
||||
dialog.set_transient_for(self.connection_manager)
|
||||
dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
|
||||
hostname_entry = self.builder.get_object('entry_hostname')
|
||||
port_spinbutton = self.builder.get_object('spinbutton_port')
|
||||
username_entry = self.builder.get_object('entry_username')
|
||||
password_entry = self.builder.get_object('entry_password')
|
||||
button_addhost_save = self.builder.get_object('button_addhost_save')
|
||||
button_addhost_save.hide()
|
||||
button_addhost_add = self.builder.get_object('button_addhost_add')
|
||||
button_addhost_add.show()
|
||||
response = dialog.run()
|
||||
if response == 1:
|
||||
username = username_entry.get_text()
|
||||
password = password_entry.get_text()
|
||||
hostname = hostname_entry.get_text()
|
||||
port = port_spinbutton.get_value_as_int()
|
||||
|
||||
try:
|
||||
host_id = self.hostlist_config.add_host(hostname, port, username, password)
|
||||
except ValueError as ex:
|
||||
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(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()
|
||||
client.disconnect().addCallback(on_disconnect)
|
||||
return
|
||||
|
||||
dialog = self.builder.get_object('addhost_dialog')
|
||||
dialog.set_transient_for(self.connection_manager)
|
||||
dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
|
||||
hostname_entry = self.builder.get_object('entry_hostname')
|
||||
port_spinbutton = self.builder.get_object('spinbutton_port')
|
||||
username_entry = self.builder.get_object('entry_username')
|
||||
password_entry = self.builder.get_object('entry_password')
|
||||
button_addhost_save = self.builder.get_object('button_addhost_save')
|
||||
button_addhost_save.show()
|
||||
button_addhost_add = self.builder.get_object('button_addhost_add')
|
||||
button_addhost_add.hide()
|
||||
|
||||
username_entry.set_text(self.liststore[row][HOSTLIST_COL_USER])
|
||||
password_entry.set_text(self.liststore[row][HOSTLIST_COL_PASS])
|
||||
hostname_entry.set_text(self.liststore[row][HOSTLIST_COL_HOST])
|
||||
port_spinbutton.set_value(self.liststore[row][HOSTLIST_COL_PORT])
|
||||
|
||||
response = dialog.run()
|
||||
|
||||
if response == 2:
|
||||
username = username_entry.get_text()
|
||||
password = password_entry.get_text()
|
||||
hostname = hostname_entry.get_text()
|
||||
port = port_spinbutton.get_value_as_int()
|
||||
|
||||
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()
|
||||
|
||||
username_entry.set_text('')
|
||||
password_entry.set_text('')
|
||||
hostname_entry.set_text('')
|
||||
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
|
||||
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()
|
||||
|
||||
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
|
||||
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())
|
||||
return
|
||||
|
||||
paths = self.hostlist.get_selection().get_selected_rows()[1]
|
||||
if len(paths) < 1:
|
||||
return
|
||||
|
||||
__, host, port, user, password, status, __ = self.liststore[paths[0]]
|
||||
|
||||
if host not in LOCALHOST:
|
||||
return
|
||||
|
||||
if status in ('Online', 'Connected'):
|
||||
# We need to stop this daemon
|
||||
# Call the shutdown method on the daemon
|
||||
def on_daemon_shutdown(d):
|
||||
# Update display to show change
|
||||
reactor.callLater(0.8, self.__update_list)
|
||||
if client.connected() and client.connection_info() == (host, port, user):
|
||||
client.daemon.shutdown().addCallback(on_daemon_shutdown)
|
||||
elif user and password:
|
||||
# Create a new client instance
|
||||
c = Client()
|
||||
|
||||
def on_connect(d, c):
|
||||
log.debug('on_connect')
|
||||
c.daemon.shutdown().addCallback(on_daemon_shutdown)
|
||||
|
||||
c.connect(host, port, user, password).addCallback(on_connect, c)
|
||||
|
||||
elif status == 'Offline':
|
||||
self.start_daemon(port, get_config_dir())
|
||||
reactor.callLater(0.8, self.__update_list)
|
||||
|
||||
def on_button_refresh_clicked(self, widget):
|
||||
self.__update_list()
|
||||
|
||||
def on_hostlist_row_activated(self, tree, path, view_column):
|
||||
self.on_button_connect_clicked()
|
||||
|
||||
def on_hostlist_selection_changed(self, treeselection):
|
||||
self.__update_buttons()
|
||||
|
||||
def on_askpassword_dialog_connect_button_clicked(self, widget):
|
||||
log.debug('on on_askpassword_dialog_connect_button_clicked')
|
||||
self.askpassword_dialog.response(gtk.RESPONSE_OK)
|
||||
|
||||
def on_askpassword_dialog_entry_activate(self, entry):
|
||||
self.askpassword_dialog.response(gtk.RESPONSE_OK)
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<property name="border_width">5</property>
|
||||
<property name="title" translatable="yes">Add Host</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="window_position">center</property>
|
||||
<property name="window_position">center-on-parent</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<child internal-child="vbox">
|
||||
|
@ -43,14 +43,14 @@
|
|||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button_addhost_add">
|
||||
<property name="label">gtk-add</property>
|
||||
<property name="label">_Save</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can_default">True</property>
|
||||
<property name="has_default">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="use_underline">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
@ -58,20 +58,6 @@
|
|||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button_addhost_save">
|
||||
<property name="label">gtk-save</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
@ -114,7 +100,7 @@
|
|||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="primary_icon_sensitive">True</property>
|
||||
<property name="secondary_icon_sensitive">True</property>
|
||||
<signal name="paste-clipboard" handler="on_entry_host_paste_clipboard" />
|
||||
<signal name="paste-clipboard" handler="on_entry_host_paste_clipboard" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -253,15 +239,11 @@
|
|||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<action-widgets>
|
||||
<action-widget response="0">button_addhost_cancel</action-widget>
|
||||
<action-widget response="1">button_addhost_add</action-widget>
|
||||
<action-widget response="2">button_addhost_save</action-widget>
|
||||
<action-widget response="0">button_addhost_add</action-widget>
|
||||
</action-widgets>
|
||||
</object>
|
||||
</interface>
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<requires lib="gtk+" version="2.24"/>
|
||||
<!-- interface-naming-policy project-wide -->
|
||||
<object class="GtkDialog" id="askpassword_dialog">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="border_width">5</property>
|
||||
<property name="title" translatable="yes">Password Required</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="window_position">center-on-parent</property>
|
||||
<property name="default_width">320</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkVBox" id="dialog-vbox7">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">2</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkHButtonBox" id="dialog-action_area7">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="askpassword_dialog_connect_button">
|
||||
<property name="label">gtk-connect</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<signal name="clicked" handler="on_askpassword_dialog_connect_button_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkHBox" id="hbox1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="askpassword_dialog_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-dialog-authentication</property>
|
||||
<property name="icon-size">6</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="askpassword_dialog_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="visibility">False</property>
|
||||
<property name="invisible_char">●</property>
|
||||
<property name="truncate_multiline">True</property>
|
||||
<property name="invisible_char_set">True</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="secondary_icon_activatable">False</property>
|
||||
<property name="primary_icon_sensitive">True</property>
|
||||
<property name="secondary_icon_sensitive">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<action-widgets>
|
||||
<action-widget response="1">askpassword_dialog_connect_button</action-widget>
|
||||
</action-widgets>
|
||||
</object>
|
||||
</interface>
|
|
@ -46,10 +46,11 @@
|
|||
<property name="hscrollbar_policy">automatic</property>
|
||||
<property name="vscrollbar_policy">automatic</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="hostlist">
|
||||
<object class="GtkTreeView" id="treeview_hostlist">
|
||||
<property name="height_request">80</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="model">liststore_hostlist</property>
|
||||
<signal name="row-activated" handler="on_hostlist_row_activated" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
|
@ -242,12 +243,12 @@
|
|||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="button_close">
|
||||
<property name="label">gtk-close</property>
|
||||
<property name="label">_Close</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="clicked" handler="on_button_close_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
|
@ -258,12 +259,12 @@
|
|||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button_connect">
|
||||
<property name="label">gtk-connect</property>
|
||||
<property name="label">C_onnect</property>
|
||||
<property name="use_action_appearance">False</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="clicked" handler="on_button_connect_clicked" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
|
@ -303,6 +304,7 @@
|
|||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<signal name="toggled" handler="on_chk_toggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
|
@ -318,6 +320,7 @@
|
|||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<signal name="toggled" handler="on_chk_toggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
|
@ -333,6 +336,7 @@
|
|||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<signal name="toggled" handler="on_chk_toggled" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
|
@ -368,4 +372,22 @@
|
|||
<action-widget response="0">button_connect</action-widget>
|
||||
</action-widgets>
|
||||
</object>
|
||||
<object class="GtkListStore" id="liststore_hostlist">
|
||||
<columns>
|
||||
<!-- column-name host_id -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name hostname -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name port -->
|
||||
<column type="gint"/>
|
||||
<!-- column-name username -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name password -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name status -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name version -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
</object>
|
||||
</interface>
|
||||
|
|
|
@ -21,7 +21,7 @@ pygtk.require('2.0') # NOQA: E402
|
|||
|
||||
# isort:imports-thirdparty
|
||||
from gobject import set_prgname
|
||||
from gtk import RESPONSE_OK, RESPONSE_YES
|
||||
from gtk import RESPONSE_YES
|
||||
from gtk.gdk import WINDOWING, threads_enter, threads_init, threads_leave
|
||||
from twisted.internet import defer, gtk2reactor
|
||||
from twisted.internet.error import ReactorAlreadyInstalledError
|
||||
|
@ -38,12 +38,12 @@ except ReactorAlreadyInstalledError as ex:
|
|||
import deluge.component as component
|
||||
from deluge.common import fsize, fspeed, get_default_download_dir, osx_check, windows_check
|
||||
from deluge.configmanager import ConfigManager, get_config_dir
|
||||
from deluge.error import AuthenticationRequired, BadLoginError, DaemonRunningError
|
||||
from deluge.error import DaemonRunningError
|
||||
from deluge.ui.client import client
|
||||
from deluge.ui.gtkui.addtorrentdialog import AddTorrentDialog
|
||||
from deluge.ui.gtkui.common import associate_magnet_links
|
||||
from deluge.ui.gtkui.connectionmanager import ConnectionManager
|
||||
from deluge.ui.gtkui.dialogs import AuthenticationDialog, ErrorDialog, YesNoDialog
|
||||
from deluge.ui.gtkui.dialogs import YesNoDialog
|
||||
from deluge.ui.gtkui.filtertreeview import FilterTreeView
|
||||
from deluge.ui.gtkui.ipcinterface import IPCInterface, process_args
|
||||
from deluge.ui.gtkui.mainwindow import MainWindow
|
||||
|
@ -61,7 +61,7 @@ from deluge.ui.sessionproxy import SessionProxy
|
|||
from deluge.ui.tracker_icons import TrackerIcons
|
||||
from deluge.ui.translations_util import set_language, setup_translations
|
||||
|
||||
set_prgname(b'deluge')
|
||||
set_prgname('deluge'.encode('utf8'))
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
|
@ -220,7 +220,7 @@ class GtkUI(object):
|
|||
# Setup RPC stats logging
|
||||
# daemon_bps: time, bytes_sent, bytes_recv
|
||||
self.daemon_bps = (0, 0, 0)
|
||||
self.rpc_stats = LoopingCall(self.print_rpc_stats)
|
||||
self.rpc_stats = LoopingCall(self.log_rpc_stats)
|
||||
self.closing = False
|
||||
|
||||
# Twisted catches signals to terminate, so have it call a pre_shutdown method.
|
||||
|
@ -238,8 +238,8 @@ class GtkUI(object):
|
|||
# Initialize gdk threading
|
||||
threads_enter()
|
||||
reactor.run()
|
||||
# Reactor is not running. Any async callbacks (Deferreds) can no longer
|
||||
# be processed from this point on.
|
||||
# Reactor no longer running so async callbacks (Deferreds) cannot be
|
||||
# processed after this point.
|
||||
threads_leave()
|
||||
|
||||
def shutdown(self, *args, **kwargs):
|
||||
|
@ -267,11 +267,12 @@ class GtkUI(object):
|
|||
|
||||
reactor.stop()
|
||||
|
||||
# Restart the application after closing if MainWindow attribute set.
|
||||
# Restart the application after closing if MainWindow restart attribute set.
|
||||
if component.get('MainWindow').restart:
|
||||
os.execv(sys.argv[0], sys.argv)
|
||||
|
||||
def print_rpc_stats(self):
|
||||
def log_rpc_stats(self):
|
||||
"""Log RPC statistics for thinclient mode."""
|
||||
if not client.connected():
|
||||
return
|
||||
|
||||
|
@ -290,144 +291,73 @@ class GtkUI(object):
|
|||
log.debug('_on_reactor_start')
|
||||
self.mainwindow.first_show()
|
||||
|
||||
if self.config['standalone']:
|
||||
def on_dialog_response(response):
|
||||
if response != RESPONSE_YES:
|
||||
# The user does not want to turn Standalone Mode off, so just quit
|
||||
self.mainwindow.quit()
|
||||
return
|
||||
if not self.config['standalone']:
|
||||
return self._start_thinclient()
|
||||
|
||||
err_msg = ''
|
||||
try:
|
||||
client.start_standalone()
|
||||
except DaemonRunningError:
|
||||
err_msg = _('A Deluge daemon (deluged) is already running.\n'
|
||||
'To use Standalone mode, stop local daemon and restart Deluge.')
|
||||
except ImportError as ex:
|
||||
if 'No module named libtorrent' in ex.message:
|
||||
err_msg = _('Only Thin Client mode is available because libtorrent is not installed.\n'
|
||||
'To use Standalone mode, please install libtorrent package.')
|
||||
else:
|
||||
log.exception(ex)
|
||||
err_msg = _('Only Thin Client mode is available due to unknown Import Error.\n'
|
||||
'To use Standalone mode, please see logs for error details.')
|
||||
except Exception as ex:
|
||||
log.exception(ex)
|
||||
err_msg = _('Only Thin Client mode is available due to unknown Import Error.\n'
|
||||
'To use Standalone mode, please see logs for error details.')
|
||||
else:
|
||||
component.start()
|
||||
return
|
||||
|
||||
def on_dialog_response(response):
|
||||
"""User response to switching mode dialog."""
|
||||
if response == RESPONSE_YES:
|
||||
# Turning off standalone
|
||||
self.config['standalone'] = False
|
||||
self.__start_thinclient()
|
||||
self._start_thinclient()
|
||||
else:
|
||||
# User want keep Standalone Mode so just quit.
|
||||
self.mainwindow.quit()
|
||||
|
||||
try:
|
||||
try:
|
||||
client.start_standalone()
|
||||
except DaemonRunningError:
|
||||
d = YesNoDialog(
|
||||
_('Switch to Thin Client Mode?'),
|
||||
_('A Deluge daemon process (deluged) is already running. '
|
||||
'To use Standalone mode, stop this daemon and restart Deluge.'
|
||||
'\n\n'
|
||||
'Continue in Thin Client mode?')).run()
|
||||
d.addCallback(on_dialog_response)
|
||||
except ImportError as ex:
|
||||
if 'No module named libtorrent' in ex.message:
|
||||
d = YesNoDialog(
|
||||
_('Switch to Thin Client Mode?'),
|
||||
_('Only Thin Client mode is available because libtorrent is not installed.'
|
||||
'\n\n'
|
||||
'To use Deluge Standalone mode, please install libtorrent.')).run()
|
||||
d.addCallback(on_dialog_response)
|
||||
else:
|
||||
raise ex
|
||||
else:
|
||||
component.start()
|
||||
return
|
||||
except Exception:
|
||||
import traceback
|
||||
tb = sys.exc_info()
|
||||
ed = ErrorDialog(
|
||||
_('Error Starting Core'),
|
||||
_('An error occurred starting the core component required to run Deluge in Standalone mode.'
|
||||
'\n\n'
|
||||
'Please see the details below for more information.'), details=traceback.format_exc(tb[2])).run()
|
||||
# An error occurred so ask user to switch from Standalone to Thin Client mode.
|
||||
err_msg += '\n\n' + _('Continue in Thin Client mode?')
|
||||
d = YesNoDialog(_('Change User Interface Mode'), err_msg).run()
|
||||
d.addCallback(on_dialog_response)
|
||||
|
||||
def on_ed_response(response):
|
||||
d = YesNoDialog(
|
||||
_('Switch to Thin Client Mode?'),
|
||||
_('Unable to start Standalone mode would you like to continue in Thin Client mode?')
|
||||
).run()
|
||||
d.addCallback(on_dialog_response)
|
||||
ed.addCallback(on_ed_response)
|
||||
else:
|
||||
def _start_thinclient(self):
|
||||
"""Start the gtkui in thinclient mode"""
|
||||
if log.isEnabledFor(logging.DEBUG):
|
||||
self.rpc_stats.start(10)
|
||||
self.__start_thinclient()
|
||||
|
||||
def __start_thinclient(self):
|
||||
# Check to see if we need to start the localhost daemon
|
||||
if self.config['autostart_localhost']:
|
||||
port = 0
|
||||
for host_config in self.connectionmanager.hostlist.config['hosts']:
|
||||
if host_config[1] in self.connectionmanager.LOCALHOST:
|
||||
port = host_config[2]
|
||||
log.debug('Autostarting localhost: %s', host_config[0:3])
|
||||
|
||||
if port:
|
||||
self.connectionmanager.start_daemon(port, get_config_dir())
|
||||
|
||||
# Autoconnect to a host
|
||||
if self.config['autoconnect']:
|
||||
|
||||
def update_connection_manager():
|
||||
if not self.connectionmanager.running:
|
||||
return
|
||||
self.connectionmanager.builder.get_object('button_refresh').emit('clicked')
|
||||
|
||||
def close_connection_manager():
|
||||
if not self.connectionmanager.running:
|
||||
return
|
||||
self.connectionmanager.builder.get_object('button_close').emit('clicked')
|
||||
|
||||
for host_config in self.connectionmanager.config['hosts']:
|
||||
hostid, host, port, user, passwd = host_config
|
||||
if hostid == self.config['autoconnect_host_id']:
|
||||
try_connect = True
|
||||
# Check to see if we need to start the localhost daemon
|
||||
if self.config['autostart_localhost'] and host in ('localhost', '127.0.0.1'):
|
||||
log.debug('Autostarting localhost:%s', host)
|
||||
try_connect = client.start_daemon(
|
||||
port, get_config_dir()
|
||||
)
|
||||
log.debug('Localhost started: %s', try_connect)
|
||||
if not try_connect:
|
||||
ErrorDialog(
|
||||
_('Error Starting Daemon'),
|
||||
_('There was an error starting the daemon '
|
||||
'process. Try running it from a console '
|
||||
'to see if there is an error.')
|
||||
).run()
|
||||
|
||||
# Daemon Started, let's update it's info
|
||||
reactor.callLater(0.5, update_connection_manager)
|
||||
|
||||
def on_connect(connector):
|
||||
component.start()
|
||||
reactor.callLater(0.2, update_connection_manager)
|
||||
reactor.callLater(0.5, close_connection_manager)
|
||||
|
||||
def on_connect_fail(reason, try_counter,
|
||||
host, port, user, passwd):
|
||||
if not try_counter:
|
||||
return
|
||||
|
||||
if reason.check(AuthenticationRequired, BadLoginError):
|
||||
log.debug('PasswordRequired exception')
|
||||
dialog = AuthenticationDialog(reason.value.message, reason.value.username)
|
||||
|
||||
def dialog_finished(response_id, host, port):
|
||||
if response_id == RESPONSE_OK:
|
||||
reactor.callLater(
|
||||
0.5, do_connect, try_counter - 1,
|
||||
host, port, dialog.get_username(),
|
||||
dialog.get_password())
|
||||
dialog.run().addCallback(dialog_finished, host, port)
|
||||
return
|
||||
|
||||
log.info('Connection to host failed..')
|
||||
log.info('Retrying connection.. Retries left: '
|
||||
'%s', try_counter)
|
||||
reactor.callLater(0.5, update_connection_manager)
|
||||
reactor.callLater(0.5, do_connect, try_counter - 1,
|
||||
host, port, user, passwd)
|
||||
|
||||
def do_connect(try_counter, host, port, user, passwd):
|
||||
log.debug('Trying to connect to %s@%s:%s',
|
||||
user, host, port)
|
||||
d = client.connect(host, port, user, passwd)
|
||||
d.addCallback(on_connect)
|
||||
d.addErrback(on_connect_fail, try_counter,
|
||||
host, port, user, passwd)
|
||||
|
||||
if try_connect:
|
||||
reactor.callLater(
|
||||
0.5, do_connect, 6, host, port, user, passwd
|
||||
)
|
||||
for host_config in self.connectionmanager.hostlist.config['hosts']:
|
||||
host_id, host, port, user, __ = host_config
|
||||
if host_id == self.config['autoconnect_host_id']:
|
||||
log.debug('Trying to connect to %s@%s:%s', user, host, port)
|
||||
reactor.callLater(0.3, self.connectionmanager._connect, host_id, try_counter=6)
|
||||
break
|
||||
|
||||
if self.config['show_connection_manager_on_start']:
|
||||
if windows_check():
|
||||
# Call to simulate() required to workaround showing daemon status (see #2813)
|
||||
reactor.simulate()
|
||||
# Dialog is blocking so call last.
|
||||
self.connectionmanager.show()
|
||||
|
||||
def __on_disconnect(self):
|
||||
|
|
|
@ -7,19 +7,19 @@
|
|||
# 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 twisted.internet import defer
|
||||
|
||||
from deluge.common import get_localhost_auth
|
||||
from deluge.config import Config
|
||||
from deluge.configmanager import get_config_dir
|
||||
from deluge.ui.client import Client, client
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -29,45 +29,12 @@ LOCALHOST = ('127.0.0.1', 'localhost')
|
|||
|
||||
|
||||
def default_hostlist():
|
||||
"""Create a new hosts for hostlist with a localhost entry"""
|
||||
"""Create a new hosts key 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.
|
||||
|
||||
|
@ -84,25 +51,25 @@ def validate_host_info(hostname, port):
|
|||
except gaierror as ex:
|
||||
raise ValueError('Host %s: %s', hostname, ex.args[1])
|
||||
|
||||
try:
|
||||
int(port)
|
||||
except ValueError:
|
||||
if not isinstance(port, int):
|
||||
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
|
||||
"""Mirgrates old hostlist config files to new format"""
|
||||
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):
|
||||
"""This class contains methods for adding, removing and looking up hosts in hostlist.conf."""
|
||||
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)
|
||||
|
@ -152,31 +119,87 @@ class HostList(object):
|
|||
def get_host_info(self, host_id):
|
||||
"""Get the host details for host_id.
|
||||
|
||||
Includes password details!
|
||||
Args:
|
||||
host_id (str): The host id to get info on.
|
||||
|
||||
Returns:
|
||||
list: A list of (host_id, hostname, port, username).
|
||||
|
||||
"""
|
||||
for host_entry in self.config['hosts']:
|
||||
if host_entry[0] == host_id:
|
||||
return host_entry
|
||||
return host_entry[0:4]
|
||||
else:
|
||||
return []
|
||||
|
||||
def get_hosts_info(self):
|
||||
"""Get all the hosts in the hostlist
|
||||
"""Get information of all the hosts in the hostlist.
|
||||
|
||||
Returns:
|
||||
list of lists: Host information in the format [(host_id, hostname, port, username)].
|
||||
|
||||
Excluding password details.
|
||||
"""
|
||||
return [host[0:4 + 1] for host in self.config['hosts']]
|
||||
return [host_entry[0:4] for host_entry in self.config['hosts']]
|
||||
|
||||
def get_hosts_info2(self):
|
||||
"""Get all the hosts in the hostlist
|
||||
def get_host_status(self, host_id):
|
||||
"""Gets the current status (online/offline) of the host
|
||||
|
||||
Args:
|
||||
host_id (str): The host id to check status of.
|
||||
|
||||
Returns:
|
||||
tuple: A tuple of strings (host_id, status, version).
|
||||
|
||||
Excluding password details.
|
||||
"""
|
||||
return [host for host in self.config['hosts']]
|
||||
status_offline = (host_id, 'Offline', '')
|
||||
|
||||
def on_connect(result, c, host_id):
|
||||
"""Successfully connected to a daemon"""
|
||||
def on_info(info, c):
|
||||
c.disconnect()
|
||||
return host_id, 'Online', info
|
||||
|
||||
def on_info_fail(reason, c):
|
||||
c.disconnect()
|
||||
return status_offline
|
||||
|
||||
return c.daemon.info().addCallback(on_info, c).addErrback(on_info_fail, c)
|
||||
|
||||
def on_connect_failed(reason, host_id):
|
||||
"""Connection to daemon failed"""
|
||||
log.debug('Host status failed for %s: %s', host_id, reason)
|
||||
return status_offline
|
||||
|
||||
try:
|
||||
host_id, host, port, user = self.get_host_info(host_id)
|
||||
except ValueError:
|
||||
log.warning('Problem getting host_id info from hostlist')
|
||||
return status_offline
|
||||
|
||||
try:
|
||||
ip = gethostbyname(host)
|
||||
except gaierror as ex:
|
||||
log.error('Error resolving host %s to ip: %s', host, ex.args[1])
|
||||
return status_offline
|
||||
|
||||
host_conn_info = (ip, port, 'localclient' if not user and host in LOCALHOST else user)
|
||||
if client.connected() and host_conn_info == client.connection_info():
|
||||
# Currently connected to host_id daemon.
|
||||
def on_info(info, host_id):
|
||||
log.debug('Client connected, query info: %s', info)
|
||||
return host_id, 'Connected', info
|
||||
|
||||
return client.daemon.info().addCallback(on_info, host_id)
|
||||
else:
|
||||
# Attempt to connect to daemon with host_id details.
|
||||
c = Client()
|
||||
d = c.connect(host, port, skip_authentication=True)
|
||||
d.addCallback(on_connect, c, host_id)
|
||||
d.addErrback(on_connect_failed, host_id)
|
||||
return d
|
||||
|
||||
def update_host(self, host_id, hostname, port, username, password):
|
||||
"""Update the host with new details.
|
||||
"""Update the supplied host id with new connection details.
|
||||
|
||||
Args:
|
||||
host_id (str): The host id to update.
|
||||
|
@ -192,13 +215,23 @@ class HostList(object):
|
|||
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']:
|
||||
for idx, host_entry in enumerate(self.config['hosts']):
|
||||
if host_id == host_entry[0]:
|
||||
host_entry = host_id, hostname, port, username, password
|
||||
self.config['hosts'][idx] = host_id, hostname, port, username, password
|
||||
self.config.save()
|
||||
return True
|
||||
return False
|
||||
|
||||
def remove_host(self, host_id):
|
||||
"""Removes the host entry from hostlist config.
|
||||
|
||||
Args:
|
||||
host_id (str): The host id to remove.
|
||||
|
||||
Returns:
|
||||
bool: True is successfully removed, False otherwise.
|
||||
|
||||
"""
|
||||
for host_entry in self.config['hosts']:
|
||||
if host_id == host_entry[0]:
|
||||
self.config['hosts'].remove(host_entry)
|
||||
|
@ -209,3 +242,12 @@ class HostList(object):
|
|||
|
||||
def add_default_host(self):
|
||||
self.add_host(DEFAULT_HOST, DEFAULT_PORT, *get_localhost_auth())
|
||||
|
||||
def connect_host(self, host_id):
|
||||
"""Connect to host daemon"""
|
||||
for host_entry in self.config['hosts']:
|
||||
if host_entry[0] == host_id:
|
||||
__, host, port, username, password = host_entry
|
||||
return client.connect(host, port, username, password)
|
||||
|
||||
return defer.fail(Exception('Bad host id'))
|
||||
|
|
|
@ -78,7 +78,7 @@ Deluge.AddConnectionWindow = Ext.extend(Ext.Window, {
|
|||
|
||||
onAddClick: function() {
|
||||
var values = this.form.getForm().getValues();
|
||||
deluge.client.web.add_host(values.host, values.port, values.username, values.password, {
|
||||
deluge.client.web.add_host(values.host, Number(values.port), values.username, values.password, {
|
||||
success: function(result) {
|
||||
if (!result[0]) {
|
||||
Ext.MessageBox.show({
|
||||
|
|
|
@ -91,6 +91,13 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, {
|
|||
iconCls: 'icon-add',
|
||||
handler: this.onAddClick,
|
||||
scope: this
|
||||
}, {
|
||||
id: 'cm-edit',
|
||||
cls: 'x-btn-text-icon',
|
||||
text: _('Edit'),
|
||||
iconCls: 'icon-edit',
|
||||
handler: this.onEditClick,
|
||||
scope: this
|
||||
}, {
|
||||
id: 'cm-remove',
|
||||
cls: 'x-btn-text-icon',
|
||||
|
@ -164,27 +171,25 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, {
|
|||
var button = this.buttons[1], status = record.get('status');
|
||||
|
||||
// Update the Connect/Disconnect button
|
||||
if (status == 'Connected') {
|
||||
button.enable();
|
||||
button.enable();
|
||||
if (status.toLowerCase() == 'connected') {
|
||||
button.setText(_('Disconnect'));
|
||||
} else if (status == 'Offline') {
|
||||
button.disable();
|
||||
} else {
|
||||
button.enable();
|
||||
button.setText(_('Connect'));
|
||||
if (status.toLowerCase() != 'online') button.disable();
|
||||
}
|
||||
|
||||
// Update the Stop/Start Daemon button
|
||||
if (status == 'Offline') {
|
||||
if (status.toLowerCase() == 'connected' || status.toLowerCase() == 'online') {
|
||||
this.stopHostButton.enable();
|
||||
this.stopHostButton.setText(_('Stop Daemon'));
|
||||
} else {
|
||||
if (record.get('host') == '127.0.0.1' || record.get('host') == 'localhost') {
|
||||
this.stopHostButton.enable();
|
||||
this.stopHostButton.setText(_('Start Daemon'));
|
||||
} else {
|
||||
this.stopHostButton.disable();
|
||||
}
|
||||
} else {
|
||||
this.stopHostButton.enable();
|
||||
this.stopHostButton.setText(_('Stop Daemon'));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -192,13 +197,25 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, {
|
|||
onAddClick: function(button, e) {
|
||||
if (!this.addWindow) {
|
||||
this.addWindow = new Deluge.AddConnectionWindow();
|
||||
this.addWindow.on('hostadded', this.onHostAdded, this);
|
||||
this.addWindow.on('hostadded', this.onHostChange, this);
|
||||
}
|
||||
this.addWindow.show();
|
||||
},
|
||||
|
||||
// private
|
||||
onHostAdded: function() {
|
||||
onEditClick: function(button, e) {
|
||||
var connection = this.list.getSelectedRecords()[0];
|
||||
if (!connection) return;
|
||||
|
||||
if (!this.editWindow) {
|
||||
this.editWindow = new Deluge.EditConnectionWindow();
|
||||
this.editWindow.on('hostedited', this.onHostChange, this);
|
||||
}
|
||||
this.editWindow.show(connection);
|
||||
},
|
||||
|
||||
// private
|
||||
onHostChange: function() {
|
||||
this.loadHosts();
|
||||
},
|
||||
|
||||
|
@ -212,7 +229,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, {
|
|||
var selected = this.list.getSelectedRecords()[0];
|
||||
if (!selected) return;
|
||||
|
||||
if (selected.get('status') == 'Connected') {
|
||||
if (selected.get('status').toLowerCase() == 'connected') {
|
||||
deluge.client.web.disconnect({
|
||||
success: function(result) {
|
||||
this.update(this);
|
||||
|
@ -248,8 +265,8 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, {
|
|||
// private
|
||||
onGetHostStatus: function(host) {
|
||||
var record = this.list.getStore().getById(host[0]);
|
||||
record.set('status', host[3])
|
||||
record.set('version', host[4])
|
||||
record.set('status', host[1])
|
||||
record.set('version', host[2])
|
||||
record.commit();
|
||||
if (this.list.getSelectedRecords()[0] == record) this.updateButtons(record);
|
||||
},
|
||||
|
@ -312,11 +329,13 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, {
|
|||
// private
|
||||
onSelectionChanged: function(list, selections) {
|
||||
if (selections[0]) {
|
||||
this.editHostButton.enable();
|
||||
this.removeHostButton.enable();
|
||||
this.stopHostButton.enable();
|
||||
this.stopHostButton.setText(_('Stop Daemon'));
|
||||
this.updateButtons(this.list.getRecord(selections[0]));
|
||||
} else {
|
||||
this.editHostButton.disable();
|
||||
this.removeHostButton.disable();
|
||||
this.stopHostButton.disable();
|
||||
}
|
||||
|
@ -328,6 +347,7 @@ Deluge.ConnectionManager = Ext.extend(Ext.Window, {
|
|||
if (!this.addHostButton) {
|
||||
var bbar = this.panel.getBottomToolbar();
|
||||
this.addHostButton = bbar.items.get('cm-add');
|
||||
this.editHostButton = bbar.items.get('cm-edit');
|
||||
this.removeHostButton = bbar.items.get('cm-remove');
|
||||
this.stopHostButton = bbar.items.get('cm-stop');
|
||||
}
|
||||
|
|
114
deluge/ui/web/js/deluge-all/EditConnectionWindow.js
Normal file
114
deluge/ui/web/js/deluge-all/EditConnectionWindow.js
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*!
|
||||
* Deluge.EditConnectionWindow.js
|
||||
*
|
||||
* Copyright (c) Damien Churchill 2009-2010 <damoxc@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.
|
||||
*/
|
||||
Ext.ns('Deluge');
|
||||
|
||||
/**
|
||||
* @class Deluge.EditConnectionWindow
|
||||
* @extends Ext.Window
|
||||
*/
|
||||
Deluge.EditConnectionWindow = Ext.extend(Ext.Window, {
|
||||
|
||||
title: _('Edit Connection'),
|
||||
iconCls: 'x-deluge-add-window-icon',
|
||||
|
||||
layout: 'fit',
|
||||
width: 300,
|
||||
height: 195,
|
||||
constrainHeader: true,
|
||||
bodyStyle: 'padding: 10px 5px;',
|
||||
closeAction: 'hide',
|
||||
|
||||
initComponent: function() {
|
||||
Deluge.EditConnectionWindow.superclass.initComponent.call(this);
|
||||
|
||||
this.addEvents('hostedited');
|
||||
|
||||
this.addButton(_('Close'), this.hide, this);
|
||||
this.addButton(_('Edit'), this.onEditClick, this);
|
||||
|
||||
this.on('hide', this.onHide, this);
|
||||
|
||||
this.form = this.add({
|
||||
xtype: 'form',
|
||||
defaultType: 'textfield',
|
||||
baseCls: 'x-plain',
|
||||
labelWidth: 60,
|
||||
items: [{
|
||||
fieldLabel: _('Host:'),
|
||||
labelSeparator : '',
|
||||
name: 'host',
|
||||
anchor: '75%',
|
||||
value: ''
|
||||
}, {
|
||||
xtype: 'spinnerfield',
|
||||
fieldLabel: _('Port:'),
|
||||
labelSeparator : '',
|
||||
name: 'port',
|
||||
strategy: {
|
||||
xtype: 'number',
|
||||
decimalPrecision: 0,
|
||||
minValue: 0,
|
||||
maxValue: 65535
|
||||
},
|
||||
anchor: '40%',
|
||||
value: 58846
|
||||
}, {
|
||||
fieldLabel: _('Username:'),
|
||||
labelSeparator : '',
|
||||
name: 'username',
|
||||
anchor: '75%',
|
||||
value: ''
|
||||
}, {
|
||||
fieldLabel: _('Password:'),
|
||||
labelSeparator : '',
|
||||
anchor: '75%',
|
||||
name: 'password',
|
||||
inputType: 'password',
|
||||
value: ''
|
||||
}]
|
||||
});
|
||||
},
|
||||
|
||||
show: function(connection) {
|
||||
Deluge.EditConnectionWindow.superclass.show.call(this);
|
||||
|
||||
this.form.getForm().findField('host').setValue(connection.get('host'));
|
||||
this.form.getForm().findField('port').setValue(connection.get('port'));
|
||||
this.form.getForm().findField('username').setValue(connection.get('user'));
|
||||
this.host_id = connection.id
|
||||
},
|
||||
|
||||
onEditClick: function() {
|
||||
var values = this.form.getForm().getValues();
|
||||
deluge.client.web.edit_host(this.host_id, values.host, Number(values.port), values.username, values.password, {
|
||||
success: function(result) {
|
||||
if (!result) {
|
||||
console.log(result)
|
||||
Ext.MessageBox.show({
|
||||
title: _('Error'),
|
||||
msg: String.format(_('Unable to edit host')),
|
||||
buttons: Ext.MessageBox.OK,
|
||||
modal: false,
|
||||
icon: Ext.MessageBox.ERROR,
|
||||
iconCls: 'x-deluge-icon-error'
|
||||
});
|
||||
} else {
|
||||
this.fireEvent('hostedited');
|
||||
}
|
||||
this.hide();
|
||||
},
|
||||
scope: this
|
||||
});
|
||||
},
|
||||
|
||||
onHide: function() {
|
||||
this.form.getForm().reset();
|
||||
}
|
||||
});
|
|
@ -164,7 +164,8 @@ class JSON(resource.Resource, component.Component):
|
|||
except AuthError:
|
||||
error = {'message': 'Not authenticated', 'code': 1}
|
||||
except Exception as ex:
|
||||
log.error('Error calling method `%s`', method)
|
||||
log.error('Error calling method `%s`: %s', method, ex)
|
||||
log.exception(ex)
|
||||
error = {'message': '%s: %s' % (ex.__class__.__name__, str(ex)), 'code': 3}
|
||||
|
||||
return request_id, result, error
|
||||
|
@ -404,11 +405,10 @@ class WebApi(JSONComponent):
|
|||
self.sessionproxy.stop()
|
||||
return defer.succeed(True)
|
||||
|
||||
def _connect_daemon(self, host='localhost', port=58846, username='', password=''):
|
||||
def _connect_daemon(self, host_id):
|
||||
"""
|
||||
Connects the client to a daemon
|
||||
"""
|
||||
d = client.connect(host, port, username, password)
|
||||
|
||||
def on_client_connected(connection_id):
|
||||
"""
|
||||
|
@ -420,7 +420,7 @@ class WebApi(JSONComponent):
|
|||
self.start()
|
||||
return d
|
||||
|
||||
return d.addCallback(on_client_connected)
|
||||
return self.hostlist.connect_host(host_id).addCallback(on_client_connected)
|
||||
|
||||
@export
|
||||
def connect(self, host_id):
|
||||
|
@ -432,10 +432,7 @@ class WebApi(JSONComponent):
|
|||
:returns: the methods the daemon supports
|
||||
:rtype: list
|
||||
"""
|
||||
host = self._get_host(host_id)
|
||||
if host:
|
||||
return self._connect_daemon(*host[1:])
|
||||
return defer.fail(Exception('Bad host id'))
|
||||
return self._connect_daemon(host_id)
|
||||
|
||||
@export
|
||||
def connected(self):
|
||||
|
@ -716,7 +713,7 @@ class WebApi(JSONComponent):
|
|||
Return the hosts in the hostlist.
|
||||
"""
|
||||
log.debug('get_hosts called')
|
||||
return self.hostlist.get_hosts() + ['']
|
||||
return self.hostlist.get_hosts_info()
|
||||
|
||||
@export
|
||||
def get_host_status(self, host_id):
|
||||
|
@ -725,46 +722,13 @@ class WebApi(JSONComponent):
|
|||
|
||||
:param host_id: the hash id of the host
|
||||
:type host_id: string
|
||||
|
||||
"""
|
||||
def response(status, info=None):
|
||||
return host_id, host, port, status, info
|
||||
def response(result):
|
||||
log.critical('%s', result)
|
||||
return result
|
||||
|
||||
try:
|
||||
host_id, host, port, user, password = self._get_host(host_id)
|
||||
except TypeError:
|
||||
host = None
|
||||
port = None
|
||||
return response('Offline')
|
||||
|
||||
def on_connect(connected, c, host_id):
|
||||
def on_info(info, c):
|
||||
c.disconnect()
|
||||
return response('Online', info)
|
||||
|
||||
def on_info_fail(reason, c):
|
||||
c.disconnect()
|
||||
return response('Offline')
|
||||
|
||||
if not connected:
|
||||
return response('Offline')
|
||||
|
||||
return c.daemon.info().addCallback(on_info, c).addErrback(on_info_fail, c)
|
||||
|
||||
def on_connect_failed(reason, host_id):
|
||||
return response('Offline')
|
||||
|
||||
if client.connected() and (host, port, 'localclient' if not
|
||||
user and host in ('127.0.0.1', 'localhost') else
|
||||
user) == client.connection_info():
|
||||
def on_info(info):
|
||||
return response('Connected', info)
|
||||
|
||||
return client.daemon.info().addCallback(on_info)
|
||||
else:
|
||||
c = Client()
|
||||
d = c.connect(host, port, user, password)
|
||||
d.addCallback(on_connect, c, host_id).addErrback(on_connect_failed, host_id)
|
||||
return d
|
||||
return self.hostlist.get_host_status(host_id).addCallback(response)
|
||||
|
||||
@export
|
||||
def add_host(self, host, port, username='', password=''):
|
||||
|
@ -787,15 +751,33 @@ class WebApi(JSONComponent):
|
|||
else:
|
||||
return True, host_id
|
||||
|
||||
@export
|
||||
def edit_host(self, host_id, host, port, username='', password=''):
|
||||
"""Edit host details in the hostlist.
|
||||
|
||||
Args:
|
||||
host_id (str): The host identifying hash.
|
||||
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:
|
||||
bool: True if succesful, False otherwise.
|
||||
|
||||
"""
|
||||
return self.hostlist.update_host(host_id, host, port, username, password)
|
||||
|
||||
@export
|
||||
def remove_host(self, host_id):
|
||||
"""Removes a host from the list.
|
||||
"""Removes a host from the hostlist.
|
||||
|
||||
Args:
|
||||
host_id (str): The host identifying hash.
|
||||
|
||||
Returns:
|
||||
bool: True if succesful, False otherwise.
|
||||
|
||||
"""
|
||||
return self.hostlist.remove_host(host_id)
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue