#
# connectionmanager.py
#
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
#
# Deluge is free software.
#
# You may redistribute it and/or modify it under the terms of the
# GNU General Public License, as published by the Free Software
# Foundation; either version 3 of the License, or (at your option)
# any later version.
#
# deluge is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with deluge.    If not, write to:
#   The Free Software Foundation, Inc.,
#   51 Franklin Street, Fifth Floor
#   Boston, MA  02110-1301, USA.
#
#    In addition, as a special exception, the copyright holders give
#    permission to link the code of portions of this program with the OpenSSL
#    library.
#    You must obey the GNU General Public License in all respects for all of
#    the code used other than OpenSSL. If you modify file(s) with this
#    exception, you may extend this exception to your version of the file(s),
#    but you are not obligated to do so. If you do not wish to do so, delete
#    this exception statement from your version. If you delete this exception
#    statement from all source files in the program, then also delete it here.
#
#

import os
import gtk
import time
import hashlib
import logging
from twisted.internet import reactor

import deluge.common
import deluge.component as component
import common
import deluge.configmanager
from deluge.ui.common import get_localhost_auth
from deluge.ui.client import client
import deluge.ui.client
from deluge.configmanager import ConfigManager
from deluge.error import AuthenticationRequired, BadLoginError, IncompatibleClient
import dialogs

log = logging.getLogger(__name__)

DEFAULT_HOST = "127.0.0.1"
DEFAULT_PORT = 58846

HOSTLIST_COL_ID = 0
HOSTLIST_COL_HOST = 1
HOSTLIST_COL_PORT = 2
HOSTLIST_COL_STATUS = 3
HOSTLIST_COL_USER = 4
HOSTLIST_COL_PASS = 5
HOSTLIST_COL_VERSION = 6


HOSTLIST_PIXBUFS = [
    # This is populated in ConnectionManager.show
]

HOSTLIST_STATUS = [
    "Offline",
    "Online",
    "Connected"
]

def cell_render_host(column, cell, model, row, data):
    host, port, username = model.get(row, *data)
    text = host + ":" + str(port)
    if username:
        text = username + "@" + text
    cell.set_property('text', text)

def cell_render_status(column, cell, model, row, data):
    status = model[row][data]
    pixbuf = None
    if status in HOSTLIST_STATUS:
        pixbuf = HOSTLIST_PIXBUFS[HOSTLIST_STATUS.index(status)]

    cell.set_property("pixbuf", pixbuf)

class ConnectionManager(component.Component):
    def __init__(self):
        component.Component.__init__(self, "ConnectionManager")
        self.gtkui_config = ConfigManager("gtkui.conf")
        self.config = self.__load_config()
        self.running = False

    # Component overrides
    def start(self):
        pass

    def stop(self):
        # Close this dialog when we are shutting down
        if self.running:
            self.connection_manager.response(gtk.RESPONSE_CLOSE)

    def shutdown(self):
        pass

    def __load_config(self):
        auth_file = deluge.configmanager.get_config_dir("auth")
        if not os.path.exists(auth_file):
            from deluge.common import create_localclient_account
            create_localclient_account()

        localclient_username, localclient_password = get_localhost_auth()
        DEFAULT_CONFIG = {
            "hosts": [(
                hashlib.sha1(str(time.time())).hexdigest(),
                 DEFAULT_HOST,
                 DEFAULT_PORT,
                 localclient_username,
                 localclient_password
            )]
        }
        config = ConfigManager("hostlist.conf.1.2", DEFAULT_CONFIG)
        config.run_converter((0, 1), 2, self.__migrate_config_1_to_2)
        return config

    # Public methods
    def show(self):
        """
        Show the ConnectionManager dialog.
        """
        self.config = self.__load_config()
        # Get the gtk builder file for the connection manager
        self.builder = gtk.Builder()
        # The main dialog
        self.builder.add_from_file(deluge.common.resource_filename(
            "deluge.ui.gtkui", os.path.join("glade", "connection_manager.ui")
        ))
        # The add host dialog
        self.builder.add_from_file(deluge.common.resource_filename(
            "deluge.ui.gtkui", os.path.join("glade", "connection_manager.addhost.ui")
        ))
        # The ask password dialog
        self.builder.add_from_file(deluge.common.resource_filename(
            "deluge.ui.gtkui", os.path.join("glade", "connection_manager.askpassword.ui")
        ))
        self.window = component.get("MainWindow")

        # Setup the ConnectionManager dialog
        self.connection_manager = self.builder.get_object("connection_manager")
        self.connection_manager.set_transient_for(self.window.window)

        self.connection_manager.set_icon(common.get_deluge_icon())

        self.builder.get_object("image1").set_from_pixbuf(common.get_logo(32))

        self.askpassword_dialog = self.builder.get_object("askpassword_dialog")
        self.askpassword_dialog.set_transient_for(self.connection_manager)
        self.askpassword_dialog.set_icon(common.get_deluge_icon())
        self.askpassword_dialog_entry = self.builder.get_object("askpassword_dialog_entry")

        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
                    )
                )

        # Create the host list gtkliststore
        # id-hash, hostname, port, status, username, password, version
        self.liststore = gtk.ListStore(str, str, int, str, str, str, str)

        # Setup host list treeview
        self.hostlist.set_model(self.liststore)
        render = gtk.CellRendererPixbuf()
        column = gtk.TreeViewColumn(_("Status"), render)
        column.set_cell_data_func(render, cell_render_status, 3)
        self.hostlist.append_column(column)
        render = gtk.CellRendererText()
        column = gtk.TreeViewColumn(_("Host"), render, text=HOSTLIST_COL_HOST)
        column.set_cell_data_func(render, cell_render_host, (1, 2, 4))
        column.set_expand(True)
        self.hostlist.append_column(column)
        render = gtk.CellRendererText()
        column = gtk.TreeViewColumn(_("Version"), render, text=HOSTLIST_COL_VERSION)
        self.hostlist.append_column(column)

        # 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.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")

        # Run the dialog
        response = self.connection_manager.run()
        self.running = False

        # Save the toggle options
        self.__save_options()
        self.__save_hostlist()

        self.connection_manager.destroy()
        del self.builder
        del self.window
        del self.connection_manager
        del self.liststore
        del self.hostlist

    def add_host(self, host, port, username="", password=""):
        """
        Adds a host to the list.

        :param host: str, the hostname
        :param port: int, the port
        :param username: str, the username to login as
        :param password: str, the password to login with

        """
        # Check to see if there is already an entry for this host and return
        # if thats the case
        for entry in self.liststore:
            if [entry[HOSTLIST_COL_HOST], entry[HOSTLIST_COL_PORT],
                entry[HOSTLIST_COL_USER]] == [host, port, username]:
                raise Exception("Host already in list!")

        # Host isn't in the list, so lets add it
        row = self.liststore.append()
        import time
        import hashlib
        self.liststore[row][HOSTLIST_COL_ID] = hashlib.sha1(str(time.time())).hexdigest()
        self.liststore[row][HOSTLIST_COL_HOST] = host
        self.liststore[row][HOSTLIST_COL_PORT] = port
        self.liststore[row][HOSTLIST_COL_USER] = username
        self.liststore[row][HOSTLIST_COL_PASS] = password
        self.liststore[row][HOSTLIST_COL_STATUS] = "Offline"

        # Save the host list to file
        self.__save_hostlist()

        # Update the status of the hosts
        self.__update_list()

    # Private methods
    def __save_hostlist(self):
        """
        Save the current hostlist to the config file.
        """
        # Grab the hosts from the liststore
        self.config["hosts"] = []
        for row in self.liststore:
            self.config["hosts"].append((row[HOSTLIST_COL_ID],
                                         row[HOSTLIST_COL_HOST],
                                         row[HOSTLIST_COL_PORT],
                                         row[HOSTLIST_COL_USER],
                                         row[HOSTLIST_COL_PASS]))

        self.config.save()

    def __load_hostlist(self):
        """
        Load saved host entries
        """
        for host in self.config["hosts"]:
            new_row = self.liststore.append()
            self.liststore[new_row][HOSTLIST_COL_ID] = host[0]
            self.liststore[new_row][HOSTLIST_COL_HOST] = host[1]
            self.liststore[new_row][HOSTLIST_COL_PORT] = host[2]
            self.liststore[new_row][HOSTLIST_COL_USER] = host[3]
            self.liststore[new_row][HOSTLIST_COL_PASS] = host[4]
            self.liststore[new_row][HOSTLIST_COL_STATUS] = "Offline"
            self.liststore[new_row][HOSTLIST_COL_VERSION] = ""

    def __get_host_row(self, host_id):
        """
        Returns the row in the liststore for `:param:host_id` or None

        """
        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]

            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):
                    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's info")
                client.daemon.info().addCallback(on_info)
                continue

            # Create a new Client instance
            c = deluge.ui.client.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(True)
            self.builder.get_object("button_connect").set_sensitive(False)
            self.builder.get_object("button_removehost").set_sensitive(False)
            self.builder.get_object("image_startdaemon").set_from_stock(
                gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU)
            self.builder.get_object("label_startdaemon").set_text("_Start Daemon")

        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)

        # Get some values about the selected host
        status = model[row][HOSTLIST_COL_STATUS]
        host = model[row][HOSTLIST_COL_HOST]
        port = model[row][HOSTLIST_COL_PORT]
        user = model[row][HOSTLIST_COL_USER]
        passwd = model[row][HOSTLIST_COL_PASS]

        log.debug("Status: %s", status)
        # Check to see if we have a localhost entry selected
        localhost = False
        if host in ("127.0.0.1", "localhost"):
            localhost = True

        # Make sure buttons are sensitive at start
        self.builder.get_object("button_startdaemon").set_sensitive(True)
        self.builder.get_object("button_connect").set_sensitive(True)
        self.builder.get_object("button_removehost").set_sensitive(True)

        # 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(
                _("_Stop Daemon"))

        # Update the start daemon button if the selected host is localhost
        if localhost and status == "Offline":
            # The localhost is not online
            self.builder.get_object("image_startdaemon").set_from_stock(
                gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU)
            self.builder.get_object("label_startdaemon").set_text(
                _("_Start Daemon"))

        if client.connected() and (host, port, user) == client.connection_info():
            # If we're connected, we can stop the dameon
            self.builder.get_object("button_startdaemon").set_sensitive(True)
        elif user and passwd:
            # In this case we also have all the info to shutdown the dameon
            self.builder.get_object("button_startdaemon").set_sensitive(True)
        else:
            # Can't stop non localhost daemons, specially without the necessary info
            self.builder.get_object("button_startdaemon").set_sensitive(False)

        # Make sure label is displayed correctly using mnemonics
        self.builder.get_object("label_startdaemon").set_use_underline(True)

    def start_daemon(self, port, config):
        """
        Attempts to start a daemon process and will show an ErrorDialog if unable
        to.
        """
        try:
            return client.start_daemon(port, config)
        except OSError, e:
            from errno import ENOENT
            if e.errno == ENOENT:
                dialogs.ErrorDialog(
                    _("Unable to start daemon!"),
                    _("Deluge cannot find the 'deluged' executable, it is "
                      "likely that you forgot to install the deluged package "
                      "or it's not in your PATH.")).run()
                return False
            else:
                raise e
        except Exception, e:
            import traceback
            import sys
            tb = sys.exc_info()
            dialogs.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, passwd,
                              try_counter):
        log.debug("Failed to connect: %s", reason.value)

        if reason.check(AuthenticationRequired, BadLoginError):
            log.debug("PasswordRequired exception")
            dialog = dialogs.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):
            dialog = dialogs.ErrorDialog(
                _("Incompatible Client"), reason.value.message
            )
            return dialog.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, passwd,
                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\".")
        dialogs.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 == "Connected":
            def on_disconnect(reason):
                self.__update_list()
            client.disconnect().addCallback(on_disconnect)
            return

        host_id = model[row][HOSTLIST_COL_ID]
        host = model[row][HOSTLIST_COL_HOST]
        port = model[row][HOSTLIST_COL_PORT]
        user = model[row][HOSTLIST_COL_USER]
        password = model[row][HOSTLIST_COL_PASS]

        if status == "Offline" and \
                    self.builder.get_object("chk_autostart").get_active() and \
                    host in ("127.0.0.1", "localhost"):
            if not self.start_daemon(port, deluge.configmanager.get_config_dir()):
                log.debug("Failed to auto-start daemon")
                return
            return self.__connect(
                host_id, host, port, user, password, try_counter=6
            )
        return self.__connect(host_id, host, port, user, password)

    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()

            # We add the host
            try:
                self.add_host(hostname, port_spinbutton.get_value_as_int(),
                              username, password)
            except Exception, e:
                from deluge.ui.gtkui.dialogs import ErrorDialog
                ErrorDialog(_("Error Adding Host"), e).run()

        username_entry.set_text("")
        password_entry.set_text("")
        hostname_entry.set_text("")
        port_spinbutton.set_value(58846)
        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]
        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:
            self.liststore[row][HOSTLIST_COL_HOST] = hostname_entry.get_text()
            self.liststore[row][HOSTLIST_COL_PORT] = port_spinbutton.get_value_as_int()
            self.liststore[row][HOSTLIST_COL_USER] = username_entry.get_text()
            self.liststore[row][HOSTLIST_COL_PASS] = password_entry.get_text()
            self.liststore[row][HOSTLIST_COL_STATUS] = "Offline"

        # Save the host list to file
        self.__save_hostlist()

        # Update the status of the hosts
        self.__update_list()

        username_entry.set_text("")
        password_entry.set_text("")
        hostname_entry.set_text("")
        port_spinbutton.set_value(58846)
        dialog.hide()

    def on_button_removehost_clicked(self, widget):
        log.debug("on_button_removehost_clicked")
        # Get the selected rows
        paths = self.hostlist.get_selection().get_selected_rows()[1]
        for path in paths:
            self.liststore.remove(self.liststore.get_iter(path))

        # Update the hostlist
        self.__update_list()

        # Save the host list
        self.__save_hostlist()

    def on_button_startdaemon_clicked(self, widget):
        log.debug("on_button_startdaemon_clicked")
        if self.liststore.iter_n_children(None) < 1:
            # There is nothing in the list, so lets create a localhost entry
            self.add_host(DEFAULT_HOST, DEFAULT_PORT, *get_localhost_auth())
            # ..and start the daemon.
            self.start_daemon(
                DEFAULT_PORT, deluge.configmanager.get_config_dir()
            )
            return

        paths = self.hostlist.get_selection().get_selected_rows()[1]
        if len(paths) < 1:
            return

        status = self.liststore[paths[0]][HOSTLIST_COL_STATUS]
        host = self.liststore[paths[0]][HOSTLIST_COL_HOST]
        port = self.liststore[paths[0]][HOSTLIST_COL_PORT]
        user = self.liststore[paths[0]][HOSTLIST_COL_USER]
        password = self.liststore[paths[0]][HOSTLIST_COL_PASS]

        if host not in ("127.0.0.1", "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 = deluge.ui.client.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, deluge.configmanager.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)

    def __migrate_config_1_to_2(self, config):
        localclient_username, localclient_password = get_localhost_auth()
        if not localclient_username:
            # Nothing to do here, there's no auth file
            return
        for idx, (_, host, _, username, _) in enumerate(config["hosts"][:]):
            if host in ("127.0.0.1", "localhost"):
                if not username:
                    config["hosts"][idx][3] = localclient_username
                    config["hosts"][idx][4] = localclient_password
        return config
