The GtkUi's connection manager now has the ability to edit existing host entries besides adding and deleting them.

It also asks for a password prior to attemting to connect in case the password is null, this alows host entries not to store the passwords on file like it has done so far.
NOTE: This is not yet the desired behaviour, ie, the daemon should simply complain if the authentication details are incomplete and the client should act accordingly. I had an issue with this though, I catched the errback the daemon was sending, asked the user for the password and re-tried to authenticate again. However, twisted always locked when I tried this. I'm investigating it.
This commit is contained in:
Pedro Algarvio 2010-12-15 17:45:34 +00:00
parent 1794f09b21
commit 6c99204828
5 changed files with 337 additions and 50 deletions

View File

@ -54,6 +54,9 @@ AUTH_LEVEL_DEFAULT = AUTH_LEVEL_NORMAL
class BadLoginError(deluge.error.DelugeError):
pass
class PasswordRequired(BadLoginError):
pass
class AuthManager(component.Component):
def __init__(self):
component.Component.__init__(self, "AuthManager")
@ -68,6 +71,16 @@ class AuthManager(component.Component):
def shutdown(self):
pass
def peek(self, username):
if username not in self.__auth:
# Let's try to re-load the file.. Maybe it's been updated
self.__load_auth_file()
if username not in self.__auth:
raise BadLoginError("Username does not exist")
return int(self.__auth[username][1])
def authorize(self, username, password):
"""
Authorizes users based on username and password
@ -80,16 +93,12 @@ class AuthManager(component.Component):
:raises BadLoginError: if the username does not exist or password does not match
"""
if username not in self.__auth:
# Let's try to re-load the file.. Maybe it's been updated
self.__load_auth_file()
if username not in self.__auth:
raise BadLoginError("Username does not exist")
auth_level = self.peek(username)
if self.__auth[username][0] == password:
# Return the users auth level
return int(self.__auth[username][1])
return auth_level
elif not password and self.__auth[username][0]:
raise PasswordRequired("Password is required")
else:
raise BadLoginError("Password does not match")

View File

@ -253,9 +253,25 @@ class DelugeRPCProtocol(Protocol):
"".join(traceback.format_tb(exceptionTraceback)))
))
if method == "daemon.login":
if method == "daemon.peek":
try:
ret = component.get("AuthManager").peek(*args, **kwargs)
if ret:
self.factory.authorized_sessions[self.transport.sessionno] = (ret, args[0])
self.factory.session_protocols[self.transport.sessionno] = self
except Exception, e:
sendError()
log.exception(e)
else:
self.sendData((RPC_RESPONSE, request_id, ret))
if not ret:
self.transport.loseConnection()
finally:
return
elif method == "daemon.login":
# This is a special case and used in the initial connection process
# We need to authenticate the user here
log.debug("RPC dispatch daemon.login")
try:
ret = component.get("AuthManager").authorize(*args, **kwargs)
if ret:
@ -271,6 +287,7 @@ class DelugeRPCProtocol(Protocol):
finally:
return
elif method == "daemon.set_event_interest" and self.transport.sessionno in self.factory.authorized_sessions:
log.debug("RPC dispatch daemon.set_event_interest")
# This special case is to allow clients to set which events they are
# interested in receiving.
# We are expecting a sequence from the client.
@ -286,6 +303,7 @@ class DelugeRPCProtocol(Protocol):
return
if method in self.factory.methods and self.transport.sessionno in self.factory.authorized_sessions:
log.debug("RPC dispatch %s", method)
try:
method_auth_requirement = self.factory.methods[method]._rpcserver_auth_level
auth_level = self.factory.authorized_sessions[self.transport.sessionno][0]

View File

@ -261,6 +261,26 @@ class DaemonSSLProxy(DaemonProxy):
self.disconnect_deferred = None
self.disconnect_callback = None
def peek(self, host, port, username):
self.host = host
self.port = port
self.__connector = reactor.connectSSL(self.host, self.port, self.__factory, ssl.ClientContextFactory())
self.connect_deferred = defer.Deferred()
self.peek_deferred = defer.Deferred()
def on_connect(result, username):
self.__login_deferred = self.call("daemon.peek", username)
self.__login_deferred.addCallback(self.__on_peek, username)
self.__login_deferred.addErrback(self.__on_peek_fail)
def on_connect_fail(reason):
log.debug("connect_fail: %s", reason)
self.peek_deferred.errback(reason)
self.connect_deferred.addCallback(on_connect, username)
self.connect_deferred.addErrback(on_connect_fail)
return self.peek_deferred
def connect(self, host, port, username, password):
"""
Connects to a daemon at host:port
@ -273,6 +293,7 @@ class DaemonSSLProxy(DaemonProxy):
:returns: twisted.Deferred
"""
log.debug("sslproxy.connect()")
self.host = host
self.port = port
self.__connector = reactor.connectSSL(self.host, self.port, self.__factory, ssl.ClientContextFactory())
@ -286,6 +307,7 @@ class DaemonSSLProxy(DaemonProxy):
return self.login_deferred
def disconnect(self):
log.debug("sslproxy.disconnect()")
self.disconnect_deferred = defer.Deferred()
self.__connector.disconnect()
return self.disconnect_deferred
@ -397,15 +419,18 @@ class DaemonSSLProxy(DaemonProxy):
return error_data
def __on_connect(self, result, username, password):
log.debug("__on_connect called")
self.__login_deferred = self.call("daemon.login", username, password)
self.__login_deferred.addCallback(self.__on_login, username)
self.__login_deferred.addErrback(self.__on_login_fail)
def __on_connect_fail(self, reason):
log.debug("__on_connect_fail called")
log.debug("connect_fail: %s", reason)
self.login_deferred.errback(reason)
def __on_login(self, result, username):
log.debug("__on_login called")
self.username = username
# We need to tell the daemon what events we're interested in receiving
if self.__factory.event_handlers:
@ -416,6 +441,15 @@ class DaemonSSLProxy(DaemonProxy):
log.debug("_on_login_fail(): %s", result)
self.login_deferred.errback(result)
def __on_peek(self, result, username):
log.debug("__on_peek called. result: %s", result)
self.username = username
self.peek_deferred.callback(result)
def __on_peek_fail(self, result):
log.debug("__on_peek_fail called. result: %s", result)
self.peek_deferred.errback(result)
def set_disconnect_callback(self, cb):
"""
Set a function to be called when the connection to the daemon is lost
@ -531,6 +565,7 @@ class Client(object):
:returns: a Deferred object that will be called once the connection
has been established or fails
"""
log.debug("real client connect")
if not username and host in ("127.0.0.1", "localhost"):
# No username was provided and it's the localhost, so we can try
# to grab the credentials from the auth file.
@ -548,6 +583,24 @@ class Client(object):
d.addErrback(on_connect_fail)
return d
def peek(self, host="127.0.0.1", port=58846, username=""):
if not username and host in ("127.0.0.1", "localhost"):
# No username was provided and it's the localhost, so we can try
# to grab the credentials from the auth file.
import common
username, password = common.get_localhost_auth()
self._daemon_proxy = DaemonSSLProxy(dict(self.__event_handlers))
self._daemon_proxy.set_disconnect_callback(self.__on_disconnect)
d = self._daemon_proxy.peek(host, port, username)
def on_connect_fail(result):
log.debug("on_connect_fail: %s", result)
self.disconnect()
return result
d.addErrback(on_connect_fail)
return d
def disconnect(self):
"""
Disconnects from the daemon.

View File

@ -133,6 +133,11 @@ class ConnectionManager(component.Component):
self.glade.get_widget("image1").set_from_pixbuf(common.get_logo(32))
self.askpassword_dialog = self.glade.get_widget("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.glade.get_widget("askpassword_dialog_entry")
self.hostlist = self.glade.get_widget("hostlist")
# Create status pixbufs
@ -317,7 +322,7 @@ class ConnectionManager(component.Component):
# Create a new Client instance
c = deluge.ui.client.Client()
d = c.connect(host, port, user, password)
d = c.peek(host, port, user)
d.addCallback(on_connect, c, host_id)
d.addErrback(on_connect_failed, host_id)
@ -352,8 +357,11 @@ class ConnectionManager(component.Component):
model, row = self.hostlist.get_selection().get_selected()
if not row:
self.glade.get_widget("button_edithost").set_sensitive(False)
return
self.glade.get_widget("button_edithost").set_sensitive(True)
# Get some values about the selected host
status = model[row][HOSTLIST_COL_STATUS]
host = model[row][HOSTLIST_COL_HOST]
@ -428,6 +436,7 @@ that you forgot to install the deluged package or it's not in your PATH.")).run(
# Signal handlers
def __on_connected(self, connector, host_id):
log.debug("__on_connected called")
if self.gtkui_config["autoconnect"]:
self.gtkui_config["autoconnect_host_id"] = host_id
@ -450,6 +459,10 @@ that you forgot to install the deluged package or it's not in your PATH.")).run(
user = model[row][HOSTLIST_COL_USER]
password = model[row][HOSTLIST_COL_PASS]
if not password:
self.askpassword_dialog.run()
password = self.askpassword_dialog_entry.get_text()
if status == _("Offline") and self.glade.get_widget("chk_autostart").get_active() and\
host in ("127.0.0.1", "localhost"):
# We need to start this localhost
@ -475,7 +488,9 @@ that you forgot to install the deluged package or it's not in your PATH.")).run(
def do_connect(*args):
client.connect(host, port, user, password).addCallback(self.__on_connected, host_id)
d = client.connect(host, port, user, password)
d.addCallback(self.__on_connected, host_id)
d.addErrback(self.__on_connected_failed, host_id, host, port, user)
if client.connected():
client.disconnect().addCallback(do_connect)
@ -496,6 +511,10 @@ that you forgot to install the deluged package or it's not in your PATH.")).run(
port_spinbutton = self.glade.get_widget("spinbutton_port")
username_entry = self.glade.get_widget("entry_username")
password_entry = self.glade.get_widget("entry_password")
button_addhost_save = self.glade.get_widget("button_addhost_save")
button_addhost_save.hide()
button_addhost_add = self.glade.get_widget("button_addhost_add")
button_addhost_add.show()
response = dialog.run()
if response == 1:
username = username_entry.get_text()
@ -515,6 +534,54 @@ that you forgot to install the deluged package or it's not in your PATH.")).run(
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.glade.get_widget("addhost_dialog")
dialog.set_transient_for(self.connection_manager)
dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
hostname_entry = self.glade.get_widget("entry_hostname")
port_spinbutton = self.glade.get_widget("spinbutton_port")
username_entry = self.glade.get_widget("entry_username")
password_entry = self.glade.get_widget("entry_password")
button_addhost_save = self.glade.get_widget("button_addhost_save")
button_addhost_save.show()
button_addhost_add = self.glade.get_widget("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
@ -579,3 +646,17 @@ that you forgot to install the deluged package or it's not in your PATH.")).run(
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 __on_connected_failed(self, reason, host_id, host, port, user):
log.exception(reason)
log.debug(reason.value)
log.debug(reason.value.__dict__)
dialogs.ErrorDialog(_("Failed To Authenticate"),
reason.value.exception_msg).run()

View File

@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.4.5 on Mon Jan 26 23:25:11 2009 -->
<?xml version="1.0"?>
<glade-interface>
<!-- interface-requires gtk+ 2.6 -->
<!-- interface-naming-policy toplevel-contextual -->
<widget class="GtkDialog" id="addhost_dialog">
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="border_width">5</property>
<property name="title" translatable="yes">Add Host</property>
<property name="modal">True</property>
<property name="window_position">GTK_WIN_POS_CENTER</property>
<property name="window_position">center</property>
<property name="destroy_with_parent">True</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<property name="type_hint">dialog</property>
<property name="has_separator">False</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox3">
@ -30,6 +30,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
@ -159,38 +160,59 @@
<widget class="GtkHButtonBox" id="dialog-action_area3">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<property name="layout_style">end</property>
<child>
<widget class="GtkButton" id="button_addhost_cancel">
<property name="label">gtk-cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="button_addhost_add">
<property name="label">gtk-add</property>
<property name="response_id">1</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="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label">gtk-add</property>
<property name="use_stock">True</property>
<property name="response_id">1</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="button_addhost_save">
<property name="label">gtk-save</property>
<property name="response_id">2</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="pack_type">GTK_PACK_END</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
</widget>
@ -203,11 +225,11 @@
<property name="border_width">5</property>
<property name="title" translatable="yes">Connection Manager</property>
<property name="modal">True</property>
<property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
<property name="window_position">center-on-parent</property>
<property name="default_width">350</property>
<property name="default_height">300</property>
<property name="destroy_with_parent">True</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<property name="type_hint">dialog</property>
<property name="has_separator">False</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox2">
@ -228,6 +250,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
@ -258,14 +281,14 @@
<widget class="GtkViewport" id="viewport1">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="resize_mode">GTK_RESIZE_QUEUE</property>
<property name="resize_mode">queue</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="hscrollbar_policy">automatic</property>
<property name="vscrollbar_policy">automatic</property>
<child>
<widget class="GtkTreeView" id="hostlist">
<property name="visible">True</property>
@ -277,6 +300,9 @@
</widget>
</child>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox3">
@ -286,48 +312,69 @@
<widget class="GtkHButtonBox" id="hbuttonbox2">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="layout_style">GTK_BUTTONBOX_START</property>
<property name="layout_style">start</property>
<child>
<widget class="GtkButton" id="button_addhost">
<property name="label">gtk-add</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label">gtk-add</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_button_addhost_clicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="button_edithost">
<property name="label">gtk-edit</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<signal name="clicked" handler="on_button_edithost_clicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="button_removehost">
<property name="label">gtk-remove</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label">gtk-remove</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_button_removehost_clicked"/>
</widget>
<packing>
<property name="position">1</property>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="button_refresh">
<property name="label">gtk-refresh</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="no">gtk-refresh</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_button_refresh_clicked"/>
</widget>
<packing>
@ -342,7 +389,6 @@
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_button_startdaemon_clicked"/>
<child>
<widget class="GtkHBox" id="hbox4">
@ -355,6 +401,9 @@
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="stock">gtk-execute</property>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label_startdaemon">
@ -373,7 +422,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">GTK_PACK_END</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
@ -409,22 +458,25 @@
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child>
<widget class="GtkCheckButton" id="chk_autoconnect">
<property name="label" translatable="yes">Automatically connect to selected host on start-up</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Automatically connect to selected host on start-up</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_chk_autoconnect_toggled"/>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="chk_autostart">
<property name="label" translatable="yes">Automatically start localhost if needed</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Automatically start localhost if needed</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_chk_autostart_toggled"/>
</widget>
@ -434,11 +486,11 @@
</child>
<child>
<widget class="GtkCheckButton" id="chk_donotshow">
<property name="label" translatable="yes">Do not show this dialog on start-up</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Do not show this dialog on start-up</property>
<property name="response_id">0</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_chk_donotshow_toggled"/>
</widget>
@ -469,31 +521,30 @@
<child>
<widget class="GtkHButtonBox" id="hbuttonbox1">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<property name="layout_style">end</property>
<child>
<widget class="GtkButton" id="button_close">
<property name="label">gtk-close</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label">gtk-close</property>
<property name="use_stock">True</property>
<property name="response_id">-7</property>
<signal name="clicked" handler="on_button_close_clicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="button_connect">
<property name="label">gtk-connect</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="label" translatable="yes">gtk-connect</property>
<property name="use_stock">True</property>
<property name="response_id">0</property>
<signal name="clicked" handler="on_button_connect_clicked"/>
</widget>
<packing>
@ -513,12 +564,87 @@
<widget class="GtkHButtonBox" id="dialog-action_area2">
<property name="sensitive">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<property name="layout_style">end</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">GTK_PACK_END</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
</widget>
</child>
</widget>
<widget class="GtkDialog" id="askpassword_dialog">
<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>
<property name="has_separator">False</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox5">
<property name="visible">True</property>
<property name="spacing">2</property>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<child>
<widget class="GtkImage" id="askpassword_dialog_image">
<property name="visible">True</property>
<property name="stock">gtk-dialog-authentication</property>
<property name="icon-size">6</property>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget 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">&#x25CF;</property>
<signal name="activate" handler="on_askpassword_dialog_entry_activate"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area5">
<property name="visible">True</property>
<property name="layout_style">end</property>
<child>
<widget class="GtkButton" id="askpassword_dialog_connect_button">
<property name="label">gtk-connect</property>
<property name="response_id">1</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"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
</widget>