1 | # -*- coding: utf-8 -*-
|
---|
2 | #
|
---|
3 | # Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
|
---|
4 | #
|
---|
5 | # This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
---|
6 | # the additional special exception to link portions of this program with the OpenSSL library.
|
---|
7 | # See LICENSE for more details.
|
---|
8 | #
|
---|
9 |
|
---|
10 | import hashlib
|
---|
11 | import logging
|
---|
12 | import os
|
---|
13 | import time
|
---|
14 | from socket import gethostbyname
|
---|
15 |
|
---|
16 | import gtk
|
---|
17 | from twisted.internet import reactor
|
---|
18 |
|
---|
19 | import deluge.component as component
|
---|
20 | from deluge.common import resource_filename
|
---|
21 | from deluge.configmanager import ConfigManager, get_config_dir
|
---|
22 | from deluge.error import AuthenticationRequired, BadLoginError, IncompatibleClient
|
---|
23 | from deluge.ui.client import Client, client
|
---|
24 | from deluge.ui.common import get_localhost_auth
|
---|
25 | from deluge.ui.gtkui.common import get_deluge_icon, get_logo
|
---|
26 | from deluge.ui.gtkui.dialogs import AuthenticationDialog, ErrorDialog
|
---|
27 |
|
---|
28 | log = logging.getLogger(__name__)
|
---|
29 |
|
---|
30 | DEFAULT_HOST = '127.0.0.1'
|
---|
31 | DEFAULT_PORT = 58846
|
---|
32 |
|
---|
33 | HOSTLIST_COL_ID = 0
|
---|
34 | HOSTLIST_COL_HOST = 1
|
---|
35 | HOSTLIST_COL_PORT = 2
|
---|
36 | HOSTLIST_COL_STATUS = 3
|
---|
37 | HOSTLIST_COL_USER = 4
|
---|
38 | HOSTLIST_COL_PASS = 5
|
---|
39 | HOSTLIST_COL_VERSION = 6
|
---|
40 |
|
---|
41 |
|
---|
42 | HOSTLIST_PIXBUFS = [
|
---|
43 | # This is populated in ConnectionManager.show
|
---|
44 | ]
|
---|
45 |
|
---|
46 | HOSTLIST_STATUS = [
|
---|
47 | 'Offline',
|
---|
48 | 'Online',
|
---|
49 | 'Connected'
|
---|
50 | ]
|
---|
51 |
|
---|
52 |
|
---|
53 | def cell_render_host(column, cell, model, row, data):
|
---|
54 | host, port, username = model.get(row, *data)
|
---|
55 | text = host + ':' + str(port)
|
---|
56 | if username:
|
---|
57 | text = username + '@' + text
|
---|
58 | cell.set_property('text', text)
|
---|
59 |
|
---|
60 |
|
---|
61 | def cell_render_status(column, cell, model, row, data):
|
---|
62 | status = model[row][data]
|
---|
63 | pixbuf = None
|
---|
64 | if status in HOSTLIST_STATUS:
|
---|
65 | pixbuf = HOSTLIST_PIXBUFS[HOSTLIST_STATUS.index(status)]
|
---|
66 |
|
---|
67 | cell.set_property('pixbuf', pixbuf)
|
---|
68 |
|
---|
69 |
|
---|
70 | class ConnectionManager(component.Component):
|
---|
71 | def __init__(self):
|
---|
72 | component.Component.__init__(self, 'ConnectionManager')
|
---|
73 | self.gtkui_config = ConfigManager('gtkui.conf')
|
---|
74 | self.config = self.__load_config()
|
---|
75 | self.running = False
|
---|
76 |
|
---|
77 | # Component overrides
|
---|
78 | def start(self):
|
---|
79 | pass
|
---|
80 |
|
---|
81 | def stop(self):
|
---|
82 | # Close this dialog when we are shutting down
|
---|
83 | if self.running:
|
---|
84 | self.connection_manager.response(gtk.RESPONSE_CLOSE)
|
---|
85 |
|
---|
86 | def shutdown(self):
|
---|
87 | pass
|
---|
88 |
|
---|
89 | def __load_config(self):
|
---|
90 | auth_file = get_config_dir('auth')
|
---|
91 | if not os.path.exists(auth_file):
|
---|
92 | from deluge.common import create_localclient_account
|
---|
93 | create_localclient_account()
|
---|
94 |
|
---|
95 | localclient_username, localclient_password = get_localhost_auth()
|
---|
96 | default_config = {
|
---|
97 | 'hosts': [(
|
---|
98 | hashlib.sha1(str(time.time())).hexdigest(),
|
---|
99 | DEFAULT_HOST,
|
---|
100 | DEFAULT_PORT,
|
---|
101 | localclient_username,
|
---|
102 | localclient_password
|
---|
103 | )]
|
---|
104 | }
|
---|
105 | config = ConfigManager('hostlist.conf.1.2', defaults=default_config, file_version=2)
|
---|
106 | config.run_converter((0, 1), 2, self.__migrate_config_1_to_2)
|
---|
107 | return config
|
---|
108 |
|
---|
109 | # Public methods
|
---|
110 | def show(self):
|
---|
111 | """
|
---|
112 | Show the ConnectionManager dialog.
|
---|
113 | """
|
---|
114 | self.config = self.__load_config()
|
---|
115 | # Get the gtk builder file for the connection manager
|
---|
116 | self.builder = gtk.Builder()
|
---|
117 | # The main dialog
|
---|
118 | self.builder.add_from_file(resource_filename(
|
---|
119 | 'deluge.ui.gtkui', os.path.join('glade', 'connection_manager.ui')
|
---|
120 | ))
|
---|
121 | # The add host dialog
|
---|
122 | self.builder.add_from_file(resource_filename(
|
---|
123 | 'deluge.ui.gtkui', os.path.join('glade', 'connection_manager.addhost.ui')
|
---|
124 | ))
|
---|
125 | # The ask password dialog
|
---|
126 | self.builder.add_from_file(resource_filename(
|
---|
127 | 'deluge.ui.gtkui', os.path.join('glade', 'connection_manager.askpassword.ui')
|
---|
128 | ))
|
---|
129 | self.window = component.get('MainWindow')
|
---|
130 |
|
---|
131 | # Setup the ConnectionManager dialog
|
---|
132 | self.connection_manager = self.builder.get_object('connection_manager')
|
---|
133 | self.connection_manager.set_transient_for(self.window.window)
|
---|
134 |
|
---|
135 | self.connection_manager.set_icon(get_deluge_icon())
|
---|
136 |
|
---|
137 | self.builder.get_object('image1').set_from_pixbuf(get_logo(32))
|
---|
138 |
|
---|
139 | self.askpassword_dialog = self.builder.get_object('askpassword_dialog')
|
---|
140 | self.askpassword_dialog.set_transient_for(self.connection_manager)
|
---|
141 | self.askpassword_dialog.set_icon(get_deluge_icon())
|
---|
142 | self.askpassword_dialog_entry = self.builder.get_object('askpassword_dialog_entry')
|
---|
143 |
|
---|
144 | self.hostlist = self.builder.get_object('hostlist')
|
---|
145 |
|
---|
146 | # Create status pixbufs
|
---|
147 | if not HOSTLIST_PIXBUFS:
|
---|
148 | for stock_id in (gtk.STOCK_NO, gtk.STOCK_YES, gtk.STOCK_CONNECT):
|
---|
149 | HOSTLIST_PIXBUFS.append(
|
---|
150 | self.connection_manager.render_icon(
|
---|
151 | stock_id, gtk.ICON_SIZE_MENU
|
---|
152 | )
|
---|
153 | )
|
---|
154 |
|
---|
155 | # Create the host list gtkliststore
|
---|
156 | # id-hash, hostname, port, status, username, password, version
|
---|
157 | self.liststore = gtk.ListStore(str, str, int, str, str, str, str)
|
---|
158 |
|
---|
159 | # Setup host list treeview
|
---|
160 | self.hostlist.set_model(self.liststore)
|
---|
161 | render = gtk.CellRendererPixbuf()
|
---|
162 | column = gtk.TreeViewColumn(_('Status'), render)
|
---|
163 | column.set_cell_data_func(render, cell_render_status, 3)
|
---|
164 | self.hostlist.append_column(column)
|
---|
165 | render = gtk.CellRendererText()
|
---|
166 | column = gtk.TreeViewColumn(_('Host'), render, text=HOSTLIST_COL_HOST)
|
---|
167 | column.set_cell_data_func(render, cell_render_host, (1, 2, 4))
|
---|
168 | column.set_expand(True)
|
---|
169 | self.hostlist.append_column(column)
|
---|
170 | render = gtk.CellRendererText()
|
---|
171 | column = gtk.TreeViewColumn(_('Version'), render, text=HOSTLIST_COL_VERSION)
|
---|
172 | self.hostlist.append_column(column)
|
---|
173 |
|
---|
174 | # Connect the signals to the handlers
|
---|
175 | self.builder.connect_signals(self)
|
---|
176 | self.hostlist.get_selection().connect(
|
---|
177 | 'changed', self.on_hostlist_selection_changed
|
---|
178 | )
|
---|
179 |
|
---|
180 | # Load any saved host entries
|
---|
181 | self.__load_hostlist()
|
---|
182 | self.__load_options()
|
---|
183 | self.__update_list()
|
---|
184 |
|
---|
185 | self.running = True
|
---|
186 | # Trigger the on_selection_changed code and select the first host
|
---|
187 | # if possible
|
---|
188 | self.hostlist.get_selection().unselect_all()
|
---|
189 | if len(self.liststore) > 0:
|
---|
190 | self.hostlist.get_selection().select_path('0')
|
---|
191 |
|
---|
192 | # Run the dialog
|
---|
193 | self.connection_manager.run()
|
---|
194 | self.running = False
|
---|
195 |
|
---|
196 | # Save the toggle options
|
---|
197 | self.__save_options()
|
---|
198 | self.__save_hostlist()
|
---|
199 |
|
---|
200 | self.connection_manager.destroy()
|
---|
201 | del self.builder
|
---|
202 | del self.window
|
---|
203 | del self.connection_manager
|
---|
204 | del self.liststore
|
---|
205 | del self.hostlist
|
---|
206 |
|
---|
207 | def add_host(self, host, port, username='', password=''):
|
---|
208 | """
|
---|
209 | Adds a host to the list.
|
---|
210 |
|
---|
211 | :param host: str, the hostname
|
---|
212 | :param port: int, the port
|
---|
213 | :param username: str, the username to login as
|
---|
214 | :param password: str, the password to login with
|
---|
215 |
|
---|
216 | """
|
---|
217 | # Check to see if there is already an entry for this host and return
|
---|
218 | # if thats the case
|
---|
219 | for entry in self.liststore:
|
---|
220 | if [entry[HOSTLIST_COL_HOST], entry[HOSTLIST_COL_PORT], entry[HOSTLIST_COL_USER]] == [host, port, username]:
|
---|
221 | raise Exception('Host already in list!')
|
---|
222 |
|
---|
223 | # Host isn't in the list, so lets add it
|
---|
224 | row = self.liststore.append()
|
---|
225 | self.liststore[row][HOSTLIST_COL_ID] = hashlib.sha1(str(time.time())).hexdigest()
|
---|
226 | self.liststore[row][HOSTLIST_COL_HOST] = host
|
---|
227 | self.liststore[row][HOSTLIST_COL_PORT] = port
|
---|
228 | self.liststore[row][HOSTLIST_COL_USER] = username
|
---|
229 | self.liststore[row][HOSTLIST_COL_PASS] = password
|
---|
230 | self.liststore[row][HOSTLIST_COL_STATUS] = 'Offline'
|
---|
231 |
|
---|
232 | # Save the host list to file
|
---|
233 | self.__save_hostlist()
|
---|
234 |
|
---|
235 | # Update the status of the hosts
|
---|
236 | self.__update_list()
|
---|
237 |
|
---|
238 | # Private methods
|
---|
239 | def __save_hostlist(self):
|
---|
240 | """
|
---|
241 | Save the current hostlist to the config file.
|
---|
242 | """
|
---|
243 | # Grab the hosts from the liststore
|
---|
244 | self.config['hosts'] = []
|
---|
245 | for row in self.liststore:
|
---|
246 | self.config['hosts'].append((row[HOSTLIST_COL_ID],
|
---|
247 | row[HOSTLIST_COL_HOST],
|
---|
248 | row[HOSTLIST_COL_PORT],
|
---|
249 | row[HOSTLIST_COL_USER],
|
---|
250 | row[HOSTLIST_COL_PASS]))
|
---|
251 |
|
---|
252 | self.config.save()
|
---|
253 |
|
---|
254 | def __load_hostlist(self):
|
---|
255 | """
|
---|
256 | Load saved host entries
|
---|
257 | """
|
---|
258 | for host in self.config['hosts']:
|
---|
259 | new_row = self.liststore.append()
|
---|
260 | self.liststore[new_row][HOSTLIST_COL_ID] = host[0]
|
---|
261 | self.liststore[new_row][HOSTLIST_COL_HOST] = host[1]
|
---|
262 | self.liststore[new_row][HOSTLIST_COL_PORT] = host[2]
|
---|
263 | self.liststore[new_row][HOSTLIST_COL_USER] = host[3]
|
---|
264 | self.liststore[new_row][HOSTLIST_COL_PASS] = host[4]
|
---|
265 | self.liststore[new_row][HOSTLIST_COL_STATUS] = 'Offline'
|
---|
266 | self.liststore[new_row][HOSTLIST_COL_VERSION] = ''
|
---|
267 |
|
---|
268 | def __get_host_row(self, host_id):
|
---|
269 | """
|
---|
270 | Returns the row in the liststore for `:param:host_id` or None
|
---|
271 |
|
---|
272 | """
|
---|
273 | for row in self.liststore:
|
---|
274 | if host_id == row[HOSTLIST_COL_ID]:
|
---|
275 | return row
|
---|
276 | return None
|
---|
277 |
|
---|
278 | def __update_list(self):
|
---|
279 | """Updates the host status"""
|
---|
280 | if not hasattr(self, 'liststore'):
|
---|
281 | # This callback was probably fired after the window closed
|
---|
282 | return
|
---|
283 |
|
---|
284 | def on_connect(result, c, host_id):
|
---|
285 | # Return if the deferred callback was done after the dialog was closed
|
---|
286 | if not self.running:
|
---|
287 | return
|
---|
288 | row = self.__get_host_row(host_id)
|
---|
289 |
|
---|
290 | def on_info(info, c):
|
---|
291 | if not self.running:
|
---|
292 | return
|
---|
293 | if row:
|
---|
294 | row[HOSTLIST_COL_STATUS] = 'Online'
|
---|
295 | row[HOSTLIST_COL_VERSION] = info
|
---|
296 | self.__update_buttons()
|
---|
297 | c.disconnect()
|
---|
298 |
|
---|
299 | def on_info_fail(reason, c):
|
---|
300 | if not self.running:
|
---|
301 | return
|
---|
302 | if row:
|
---|
303 | row[HOSTLIST_COL_STATUS] = 'Offline'
|
---|
304 | self.__update_buttons()
|
---|
305 | c.disconnect()
|
---|
306 |
|
---|
307 | d = c.daemon.info()
|
---|
308 | d.addCallback(on_info, c)
|
---|
309 | d.addErrback(on_info_fail, c)
|
---|
310 |
|
---|
311 | def on_connect_failed(reason, host_id):
|
---|
312 | if not self.running:
|
---|
313 | return
|
---|
314 | row = self.__get_host_row(host_id)
|
---|
315 | if row:
|
---|
316 | row[HOSTLIST_COL_STATUS] = 'Offline'
|
---|
317 | row[HOSTLIST_COL_VERSION] = ''
|
---|
318 | self.__update_buttons()
|
---|
319 |
|
---|
320 | for row in self.liststore:
|
---|
321 | host_id = row[HOSTLIST_COL_ID]
|
---|
322 | host = row[HOSTLIST_COL_HOST]
|
---|
323 | port = row[HOSTLIST_COL_PORT]
|
---|
324 | user = row[HOSTLIST_COL_USER]
|
---|
325 |
|
---|
326 | if client.connected() and (
|
---|
327 | gethostbyname(host),
|
---|
328 | port,
|
---|
329 | 'localclient' if not user and host in ('127.0.0.1', 'localhost') else user
|
---|
330 | ) == client.connection_info():
|
---|
331 | def on_info(info, row):
|
---|
332 | if not self.running:
|
---|
333 | return
|
---|
334 | log.debug('Client connected, query info: %s', info)
|
---|
335 | row[HOSTLIST_COL_VERSION] = info
|
---|
336 | self.__update_buttons()
|
---|
337 |
|
---|
338 | row[HOSTLIST_COL_STATUS] = 'Connected'
|
---|
339 | log.debug("Query daemon's info")
|
---|
340 | client.daemon.info().addCallback(on_info, row)
|
---|
341 | continue
|
---|
342 |
|
---|
343 | # Create a new Client instance
|
---|
344 | c = Client()
|
---|
345 | d = c.connect(host, port, skip_authentication=True)
|
---|
346 | d.addCallback(on_connect, c, host_id)
|
---|
347 | d.addErrback(on_connect_failed, host_id)
|
---|
348 |
|
---|
349 | def __load_options(self):
|
---|
350 | """
|
---|
351 | Set the widgets to show the correct options from the config.
|
---|
352 | """
|
---|
353 | self.builder.get_object('chk_autoconnect').set_active(
|
---|
354 | self.gtkui_config['autoconnect']
|
---|
355 | )
|
---|
356 | self.builder.get_object('chk_autostart').set_active(
|
---|
357 | self.gtkui_config['autostart_localhost']
|
---|
358 | )
|
---|
359 | self.builder.get_object('chk_donotshow').set_active(
|
---|
360 | not self.gtkui_config['show_connection_manager_on_start']
|
---|
361 | )
|
---|
362 |
|
---|
363 | def __save_options(self):
|
---|
364 | """
|
---|
365 | Set options in gtkui config from the toggle buttons.
|
---|
366 | """
|
---|
367 | self.gtkui_config['autoconnect'] = self.builder.get_object('chk_autoconnect').get_active()
|
---|
368 | self.gtkui_config['autostart_localhost'] = self.builder.get_object('chk_autostart').get_active()
|
---|
369 | self.gtkui_config['show_connection_manager_on_start'] = not self.builder.get_object(
|
---|
370 | 'chk_donotshow').get_active()
|
---|
371 |
|
---|
372 | def __update_buttons(self):
|
---|
373 | """
|
---|
374 | Updates the buttons states.
|
---|
375 | """
|
---|
376 | if len(self.liststore) == 0:
|
---|
377 | # There is nothing in the list
|
---|
378 | self.builder.get_object('button_startdaemon').set_sensitive(True)
|
---|
379 | self.builder.get_object('button_connect').set_sensitive(False)
|
---|
380 | self.builder.get_object('button_removehost').set_sensitive(False)
|
---|
381 | self.builder.get_object('image_startdaemon').set_from_stock(
|
---|
382 | gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU)
|
---|
383 | self.builder.get_object('label_startdaemon').set_text('_Start Daemon')
|
---|
384 |
|
---|
385 | model, row = self.hostlist.get_selection().get_selected()
|
---|
386 | if not row:
|
---|
387 | self.builder.get_object('button_edithost').set_sensitive(False)
|
---|
388 | return
|
---|
389 |
|
---|
390 | self.builder.get_object('button_edithost').set_sensitive(True)
|
---|
391 |
|
---|
392 | # Get some values about the selected host
|
---|
393 | status = model[row][HOSTLIST_COL_STATUS]
|
---|
394 | host = model[row][HOSTLIST_COL_HOST]
|
---|
395 | port = model[row][HOSTLIST_COL_PORT]
|
---|
396 | user = model[row][HOSTLIST_COL_USER]
|
---|
397 | passwd = model[row][HOSTLIST_COL_PASS]
|
---|
398 |
|
---|
399 | log.debug('Status: %s', status)
|
---|
400 | # Check to see if we have a localhost entry selected
|
---|
401 | localhost = False
|
---|
402 | if host in ('127.0.0.1', 'localhost'):
|
---|
403 | localhost = True
|
---|
404 |
|
---|
405 | # Make sure buttons are sensitive at start
|
---|
406 | self.builder.get_object('button_startdaemon').set_sensitive(True)
|
---|
407 | self.builder.get_object('button_connect').set_sensitive(True)
|
---|
408 | self.builder.get_object('button_removehost').set_sensitive(True)
|
---|
409 |
|
---|
410 | # See if this is the currently connected host
|
---|
411 | if status == 'Connected':
|
---|
412 | # Display a disconnect button if we're connected to this host
|
---|
413 | self.builder.get_object('button_connect').set_label('gtk-disconnect')
|
---|
414 | self.builder.get_object('button_removehost').set_sensitive(False)
|
---|
415 | else:
|
---|
416 | self.builder.get_object('button_connect').set_label('gtk-connect')
|
---|
417 | if status == 'Offline' and not localhost:
|
---|
418 | self.builder.get_object('button_connect').set_sensitive(False)
|
---|
419 |
|
---|
420 | # Check to see if the host is online
|
---|
421 | if status == 'Connected' or status == 'Online':
|
---|
422 | self.builder.get_object('image_startdaemon').set_from_stock(
|
---|
423 | gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
|
---|
424 | self.builder.get_object('label_startdaemon').set_text(
|
---|
425 | _('_Stop Daemon'))
|
---|
426 |
|
---|
427 | # Update the start daemon button if the selected host is localhost
|
---|
428 | if localhost and status == 'Offline':
|
---|
429 | # The localhost is not online
|
---|
430 | self.builder.get_object('image_startdaemon').set_from_stock(
|
---|
431 | gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU)
|
---|
432 | self.builder.get_object('label_startdaemon').set_text(
|
---|
433 | _('_Start Daemon'))
|
---|
434 |
|
---|
435 | if client.connected() and (host, port, user) == client.connection_info():
|
---|
436 | # If we're connected, we can stop the dameon
|
---|
437 | self.builder.get_object('button_startdaemon').set_sensitive(True)
|
---|
438 | elif user and passwd:
|
---|
439 | # In this case we also have all the info to shutdown the daemon
|
---|
440 | self.builder.get_object('button_startdaemon').set_sensitive(True)
|
---|
441 | else:
|
---|
442 | # Can't stop non localhost daemons, specially without the necessary info
|
---|
443 | self.builder.get_object('button_startdaemon').set_sensitive(False)
|
---|
444 |
|
---|
445 | # Make sure label is displayed correctly using mnemonics
|
---|
446 | self.builder.get_object('label_startdaemon').set_use_underline(True)
|
---|
447 |
|
---|
448 | def start_daemon(self, port, config):
|
---|
449 | """
|
---|
450 | Attempts to start a daemon process and will show an ErrorDialog if unable
|
---|
451 | to.
|
---|
452 | """
|
---|
453 | try:
|
---|
454 | return client.start_daemon(port, config)
|
---|
455 | except OSError as ex:
|
---|
456 | from errno import ENOENT
|
---|
457 | if ex.errno == ENOENT:
|
---|
458 | ErrorDialog(
|
---|
459 | _('Unable to start daemon!'),
|
---|
460 | _("Deluge cannot find the 'deluged' executable, it is "
|
---|
461 | 'likely that you forgot to install the deluged package '
|
---|
462 | "or it's not in your PATH.")).run()
|
---|
463 | return False
|
---|
464 | else:
|
---|
465 | raise ex
|
---|
466 | except Exception:
|
---|
467 | import traceback
|
---|
468 | import sys
|
---|
469 | tb = sys.exc_info()
|
---|
470 | ErrorDialog(
|
---|
471 | _('Unable to start daemon!'),
|
---|
472 | _('Please examine the details for more information.'),
|
---|
473 | details=traceback.format_exc(tb[2])).run()
|
---|
474 |
|
---|
475 | # Signal handlers
|
---|
476 | def __connect(self, host_id, host, port, username, password,
|
---|
477 | skip_authentication=False, try_counter=0):
|
---|
478 | def do_connect(*args):
|
---|
479 | d = client.connect(host, port, username, password, skip_authentication)
|
---|
480 | d.addCallback(self.__on_connected, host_id)
|
---|
481 | d.addErrback(self.__on_connected_failed, host_id, host, port,
|
---|
482 | username, password, try_counter)
|
---|
483 | return d
|
---|
484 |
|
---|
485 | if client.connected():
|
---|
486 | return client.disconnect().addCallback(do_connect)
|
---|
487 | else:
|
---|
488 | return do_connect()
|
---|
489 |
|
---|
490 | def __on_connected(self, daemon_info, host_id):
|
---|
491 | if self.gtkui_config['autoconnect']:
|
---|
492 | self.gtkui_config['autoconnect_host_id'] = host_id
|
---|
493 | if self.running:
|
---|
494 | # When connected to a client, and then trying to connect to another,
|
---|
495 | # this component will be stopped(while the connect deferred is
|
---|
496 | # running), so, self.connection_manager will be deleted.
|
---|
497 | # If that's not the case, close the dialog.
|
---|
498 | self.connection_manager.response(gtk.RESPONSE_OK)
|
---|
499 | component.start()
|
---|
500 |
|
---|
501 | def __on_connected_failed(self, reason, host_id, host, port, user, passwd,
|
---|
502 | try_counter):
|
---|
503 | log.debug('Failed to connect: %s', reason.value)
|
---|
504 |
|
---|
505 | if reason.check(AuthenticationRequired, BadLoginError):
|
---|
506 | log.debug('PasswordRequired exception')
|
---|
507 | dialog = AuthenticationDialog(reason.value.message, reason.value.username)
|
---|
508 |
|
---|
509 | def dialog_finished(response_id, host, port, user):
|
---|
510 | if response_id == gtk.RESPONSE_OK:
|
---|
511 | self.__connect(host_id, host, port,
|
---|
512 | user and user or dialog.get_username(),
|
---|
513 | dialog.get_password())
|
---|
514 | d = dialog.run().addCallback(dialog_finished, host, port, user)
|
---|
515 | return d
|
---|
516 |
|
---|
517 | elif reason.trap(IncompatibleClient):
|
---|
518 | dialog = ErrorDialog(
|
---|
519 | _('Incompatible Client'), reason.value.message
|
---|
520 | )
|
---|
521 | return dialog.run()
|
---|
522 |
|
---|
523 | if try_counter:
|
---|
524 | log.info('Retrying connection.. Retries left: %s', try_counter)
|
---|
525 | return reactor.callLater(
|
---|
526 | 0.5, self.__connect, host_id, host, port, user, passwd,
|
---|
527 | try_counter=try_counter - 1
|
---|
528 | )
|
---|
529 |
|
---|
530 | msg = str(reason.value)
|
---|
531 | if not self.builder.get_object('chk_autostart').get_active():
|
---|
532 | msg += '\n' + _('Auto-starting the daemon locally is not enabled. '
|
---|
533 | 'See "Options" on the "Connection Manager".')
|
---|
534 | ErrorDialog(_('Failed To Connect'), msg).run()
|
---|
535 |
|
---|
536 | def on_button_connect_clicked(self, widget=None):
|
---|
537 | model, row = self.hostlist.get_selection().get_selected()
|
---|
538 | if not row:
|
---|
539 | return
|
---|
540 | status = model[row][HOSTLIST_COL_STATUS]
|
---|
541 | if status == 'Connected':
|
---|
542 | def on_disconnect(reason):
|
---|
543 | self.__update_list()
|
---|
544 | client.disconnect().addCallback(on_disconnect)
|
---|
545 | return
|
---|
546 |
|
---|
547 | host_id = model[row][HOSTLIST_COL_ID]
|
---|
548 | host = model[row][HOSTLIST_COL_HOST]
|
---|
549 | port = model[row][HOSTLIST_COL_PORT]
|
---|
550 | user = model[row][HOSTLIST_COL_USER]
|
---|
551 | password = model[row][HOSTLIST_COL_PASS]
|
---|
552 |
|
---|
553 | if (status == 'Offline' and self.builder.get_object('chk_autostart').get_active() and
|
---|
554 | host in ('127.0.0.1', 'localhost')):
|
---|
555 | if not self.start_daemon(port, get_config_dir()):
|
---|
556 | log.debug('Failed to auto-start daemon')
|
---|
557 | return
|
---|
558 | return self.__connect(
|
---|
559 | host_id, host, port, user, password, try_counter=6
|
---|
560 | )
|
---|
561 | return self.__connect(host_id, host, port, user, password)
|
---|
562 |
|
---|
563 | def on_button_close_clicked(self, widget):
|
---|
564 | self.connection_manager.response(gtk.RESPONSE_CLOSE)
|
---|
565 |
|
---|
566 | def on_button_addhost_clicked(self, widget):
|
---|
567 | log.debug('on_button_addhost_clicked')
|
---|
568 | dialog = self.builder.get_object('addhost_dialog')
|
---|
569 | dialog.set_transient_for(self.connection_manager)
|
---|
570 | dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
|
---|
571 | hostname_entry = self.builder.get_object('entry_hostname')
|
---|
572 | port_spinbutton = self.builder.get_object('spinbutton_port')
|
---|
573 | username_entry = self.builder.get_object('entry_username')
|
---|
574 | password_entry = self.builder.get_object('entry_password')
|
---|
575 | button_addhost_save = self.builder.get_object('button_addhost_save')
|
---|
576 | button_addhost_save.hide()
|
---|
577 | button_addhost_add = self.builder.get_object('button_addhost_add')
|
---|
578 | button_addhost_add.show()
|
---|
579 | response = dialog.run()
|
---|
580 | if response == 1:
|
---|
581 | username = username_entry.get_text()
|
---|
582 | password = password_entry.get_text()
|
---|
583 | hostname = hostname_entry.get_text()
|
---|
584 |
|
---|
585 | if (not password and not username or username == 'localclient') and hostname in ['127.0.0.1', 'localhost']:
|
---|
586 | username, password = get_localhost_auth()
|
---|
587 |
|
---|
588 | # We add the host
|
---|
589 | try:
|
---|
590 | self.add_host(hostname, port_spinbutton.get_value_as_int(),
|
---|
591 | username, password)
|
---|
592 | except Exception as ex:
|
---|
593 | ErrorDialog(_('Error Adding Host'), ex).run()
|
---|
594 |
|
---|
595 | username_entry.set_text('')
|
---|
596 | password_entry.set_text('')
|
---|
597 | hostname_entry.set_text('')
|
---|
598 | port_spinbutton.set_value(58846)
|
---|
599 | dialog.hide()
|
---|
600 |
|
---|
601 | def on_button_edithost_clicked(self, widget=None):
|
---|
602 | log.debug('on_button_edithost_clicked')
|
---|
603 | model, row = self.hostlist.get_selection().get_selected()
|
---|
604 | status = model[row][HOSTLIST_COL_STATUS]
|
---|
605 | if status == 'Connected':
|
---|
606 | def on_disconnect(reason):
|
---|
607 | self.__update_list()
|
---|
608 | client.disconnect().addCallback(on_disconnect)
|
---|
609 | return
|
---|
610 |
|
---|
611 | dialog = self.builder.get_object('addhost_dialog')
|
---|
612 | dialog.set_transient_for(self.connection_manager)
|
---|
613 | dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
|
---|
614 | hostname_entry = self.builder.get_object('entry_hostname')
|
---|
615 | port_spinbutton = self.builder.get_object('spinbutton_port')
|
---|
616 | username_entry = self.builder.get_object('entry_username')
|
---|
617 | password_entry = self.builder.get_object('entry_password')
|
---|
618 | button_addhost_save = self.builder.get_object('button_addhost_save')
|
---|
619 | button_addhost_save.show()
|
---|
620 | button_addhost_add = self.builder.get_object('button_addhost_add')
|
---|
621 | button_addhost_add.hide()
|
---|
622 |
|
---|
623 | username_entry.set_text(self.liststore[row][HOSTLIST_COL_USER])
|
---|
624 | password_entry.set_text(self.liststore[row][HOSTLIST_COL_PASS])
|
---|
625 | hostname_entry.set_text(self.liststore[row][HOSTLIST_COL_HOST])
|
---|
626 | port_spinbutton.set_value(self.liststore[row][HOSTLIST_COL_PORT])
|
---|
627 |
|
---|
628 | response = dialog.run()
|
---|
629 |
|
---|
630 | if response == 2:
|
---|
631 | username = username_entry.get_text()
|
---|
632 | password = password_entry.get_text()
|
---|
633 | hostname = hostname_entry.get_text()
|
---|
634 |
|
---|
635 | if (not password and not username or username == 'localclient') and hostname in ['127.0.0.1', 'localhost']:
|
---|
636 | username, password = get_localhost_auth()
|
---|
637 |
|
---|
638 | self.liststore[row][HOSTLIST_COL_HOST] = hostname
|
---|
639 | self.liststore[row][HOSTLIST_COL_PORT] = port_spinbutton.get_value_as_int()
|
---|
640 | self.liststore[row][HOSTLIST_COL_USER] = username
|
---|
641 | self.liststore[row][HOSTLIST_COL_PASS] = password
|
---|
642 | self.liststore[row][HOSTLIST_COL_STATUS] = 'Offline'
|
---|
643 |
|
---|
644 | # Save the host list to file
|
---|
645 | self.__save_hostlist()
|
---|
646 |
|
---|
647 | # Update the status of the hosts
|
---|
648 | self.__update_list()
|
---|
649 |
|
---|
650 | username_entry.set_text('')
|
---|
651 | password_entry.set_text('')
|
---|
652 | hostname_entry.set_text('')
|
---|
653 | port_spinbutton.set_value(58846)
|
---|
654 | dialog.hide()
|
---|
655 |
|
---|
656 | def on_button_removehost_clicked(self, widget):
|
---|
657 | log.debug('on_button_removehost_clicked')
|
---|
658 | # Get the selected rows
|
---|
659 | paths = self.hostlist.get_selection().get_selected_rows()[1]
|
---|
660 | for path in paths:
|
---|
661 | self.liststore.remove(self.liststore.get_iter(path))
|
---|
662 |
|
---|
663 | # Update the hostlist
|
---|
664 | self.__update_list()
|
---|
665 |
|
---|
666 | # Save the host list
|
---|
667 | self.__save_hostlist()
|
---|
668 |
|
---|
669 | def on_button_startdaemon_clicked(self, widget):
|
---|
670 | log.debug('on_button_startdaemon_clicked')
|
---|
671 | if self.liststore.iter_n_children(None) < 1:
|
---|
672 | # There is nothing in the list, so lets create a localhost entry
|
---|
673 | self.add_host(DEFAULT_HOST, DEFAULT_PORT, *get_localhost_auth())
|
---|
674 | # ..and start the daemon.
|
---|
675 | self.start_daemon(
|
---|
676 | DEFAULT_PORT, get_config_dir()
|
---|
677 | )
|
---|
678 | return
|
---|
679 |
|
---|
680 | paths = self.hostlist.get_selection().get_selected_rows()[1]
|
---|
681 | if len(paths) < 1:
|
---|
682 | return
|
---|
683 |
|
---|
684 | status = self.liststore[paths[0]][HOSTLIST_COL_STATUS]
|
---|
685 | host = self.liststore[paths[0]][HOSTLIST_COL_HOST]
|
---|
686 | port = self.liststore[paths[0]][HOSTLIST_COL_PORT]
|
---|
687 | user = self.liststore[paths[0]][HOSTLIST_COL_USER]
|
---|
688 | password = self.liststore[paths[0]][HOSTLIST_COL_PASS]
|
---|
689 |
|
---|
690 | if host not in ('127.0.0.1', 'localhost'):
|
---|
691 | return
|
---|
692 |
|
---|
693 | if status in ('Online', 'Connected'):
|
---|
694 | # We need to stop this daemon
|
---|
695 | # Call the shutdown method on the daemon
|
---|
696 | def on_daemon_shutdown(d):
|
---|
697 | # Update display to show change
|
---|
698 | reactor.callLater(0.8, self.__update_list)
|
---|
699 | if client.connected() and client.connection_info() == (host, port, user):
|
---|
700 | client.daemon.shutdown().addCallback(on_daemon_shutdown)
|
---|
701 | elif user and password:
|
---|
702 | # Create a new client instance
|
---|
703 | c = Client()
|
---|
704 |
|
---|
705 | def on_connect(d, c):
|
---|
706 | log.debug('on_connect')
|
---|
707 | c.daemon.shutdown().addCallback(on_daemon_shutdown)
|
---|
708 |
|
---|
709 | c.connect(host, port, user, password).addCallback(on_connect, c)
|
---|
710 |
|
---|
711 | elif status == 'Offline':
|
---|
712 | self.start_daemon(port, get_config_dir())
|
---|
713 | reactor.callLater(0.8, self.__update_list)
|
---|
714 |
|
---|
715 | def on_button_refresh_clicked(self, widget):
|
---|
716 | self.__update_list()
|
---|
717 |
|
---|
718 | def on_hostlist_row_activated(self, tree, path, view_column):
|
---|
719 | self.on_button_connect_clicked()
|
---|
720 |
|
---|
721 | def on_hostlist_selection_changed(self, treeselection):
|
---|
722 | self.__update_buttons()
|
---|
723 |
|
---|
724 | def on_askpassword_dialog_connect_button_clicked(self, widget):
|
---|
725 | log.debug('on on_askpassword_dialog_connect_button_clicked')
|
---|
726 | self.askpassword_dialog.response(gtk.RESPONSE_OK)
|
---|
727 |
|
---|
728 | def on_askpassword_dialog_entry_activate(self, entry):
|
---|
729 | self.askpassword_dialog.response(gtk.RESPONSE_OK)
|
---|
730 |
|
---|
731 | def __migrate_config_1_to_2(self, config):
|
---|
732 | localclient_username, localclient_password = get_localhost_auth()
|
---|
733 | if not localclient_username:
|
---|
734 | # Nothing to do here, there's no auth file
|
---|
735 | return
|
---|
736 | for idx, (_, host, _, username, _) in enumerate(config['hosts'][:]):
|
---|
737 | if host in ('127.0.0.1', 'localhost'):
|
---|
738 | if not username:
|
---|
739 | config['hosts'][idx][3] = localclient_username
|
---|
740 | config['hosts'][idx][4] = localclient_password
|
---|
741 | return config
|
---|