~didrocks/ubuntuone-client/use_result_var

« back to all changes in this revision

Viewing changes to bin/ubuntuone-preferences

  • Committer: Bazaar Package Importer
  • Author(s): Rodney Dawes
  • Date: 2011-02-11 16:18:11 UTC
  • mfrom: (1.1.44 upstream)
  • Revision ID: james.westby@ubuntu.com-20110211161811-lbelxu332e8r2dov
Tags: 1.5.4-0ubuntu1
* New upstream release.
  - Files still get downloaded from unsubscribed folder (LP: #682878)
  - Add subscription capabilities to shares (LP: #708335)
  - Nautilus offers Publish option within other's shares (LP: #712674)
  - Shares dir name may not be unique (LP: #715776)
  - Send a notification when new Volume is available (LP: #702055)
  - Add messaging menu entry for new Volumes (LP: #702075)
  - Aggregate notifications for completed operations (LP: #702128)
  - Send a notification for new Share offers (LP: #702138)
  - Add messaging menu entry for new Share offers (LP: #702144)
* Remove ubuntuone-client-tools as u1sync has been moved out.
* Simplify python package as we don't have multiple python packages now.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python
2
 
# -*- coding: utf-8 -*-
3
 
 
4
 
# ubuntuone-client-applet - Tray icon applet for managing Ubuntu One
5
 
#
6
 
# Authors: Rodney Dawes <rodney.dawes@canonical.com>
7
 
#          Rodrigo Moya <rodrigo.moya@canonical.com>
8
 
#
9
 
# Copyright 2009-2010 Canonical Ltd.
10
 
#
11
 
# This program is free software: you can redistribute it and/or modify it
12
 
# under the terms of the GNU General Public License version 3, as published
13
 
# by the Free Software Foundation.
14
 
#
15
 
# This program is distributed in the hope that it will be useful, but
16
 
# WITHOUT ANY WARRANTY; without even the implied warranties of
17
 
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
18
 
# PURPOSE.  See the GNU General Public License for more details.
19
 
#
20
 
# You should have received a copy of the GNU General Public License along
21
 
# with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 
 
23
 
from __future__ import division, with_statement
24
 
 
25
 
import pygtk
26
 
pygtk.require('2.0')
27
 
import gobject
28
 
import gtk
29
 
import os
30
 
import sys
31
 
import time
32
 
import gettext
33
 
import subprocess
34
 
from multiprocessing import Pipe, Process
35
 
from threading import Thread
36
 
from oauth import oauth
37
 
 
38
 
from ubuntu_sso import (DBUS_BUS_NAME, DBUS_CREDENTIALS_IFACE,
39
 
    DBUS_CREDENTIALS_PATH)
40
 
from ubuntu_sso.credentials import (HELP_TEXT_KEY, PING_URL_KEY, TC_URL_KEY,
41
 
    WINDOW_ID_KEY)
42
 
 
43
 
from ubuntuone import clientdefs
44
 
from ubuntuone.platform.linux.tools import SyncDaemonTool
45
 
from ubuntuone.logger import (basic_formatter, LOGFOLDER,
46
 
                              CustomRotatingFileHandler)
47
 
 
48
 
import logging
49
 
 
50
 
import dbus.service
51
 
from dbus.exceptions import DBusException
52
 
from dbus.mainloop.glib import DBusGMainLoop
53
 
 
54
 
LOG_LEVEL = logging.INFO
55
 
 
56
 
logger = logging.getLogger("ubuntuone-preferences")
57
 
logger.setLevel(LOG_LEVEL)
58
 
handler = CustomRotatingFileHandler(filename=os.path.join(LOGFOLDER,
59
 
                                                          'u1-prefs.log'))
60
 
handler.setFormatter(basic_formatter)
61
 
handler.setLevel(LOG_LEVEL)
62
 
logger.addHandler(handler)
63
 
 
64
 
_ = gettext.gettext
65
 
 
66
 
dcouch = None
67
 
# Try importing the Ubuntu One desktopcouch enablement api
68
 
try:
69
 
    from desktopcouch.application.replication_services import (
70
 
        ubuntuone as dcouch)
71
 
except ImportError:
72
 
    logger.error("DesktopCouch replication API not found")
73
 
 
74
 
DBusGMainLoop(set_as_default=True)
75
 
 
76
 
DBUS_IFACE_NAME = "com.ubuntuone.SyncDaemon"
77
 
DBUS_IFACE_CONFIG_NAME = DBUS_IFACE_NAME + ".Config"
78
 
DBUS_IFACE_STATUS_NAME = DBUS_IFACE_NAME + ".Status"
79
 
 
80
 
# Our own DBus interface
81
 
PREFS_BUS_NAME = "com.ubuntuone.Preferences"
82
 
 
83
 
# This is where thy music lies
84
 
U1MSPATH = os.path.expanduser("~/.ubuntuone/Purchased from Ubuntu One")
85
 
 
86
 
# Why thank you GTK+ for enforcing style-set and breaking API
87
 
RCSTYLE = """
88
 
style 'dialogs' {
89
 
  GtkDialog::action-area-border = 12
90
 
  GtkDialog::button-spacing = 6
91
 
  GtkDialog::content-area-border = 0
92
 
}
93
 
widget_class '*Dialog*' style 'dialogs'
94
 
"""
95
 
 
96
 
# Some defines for the bindwood installation magic
97
 
BW_PKG_NAME = "xul-ext-bindwood"
98
 
BW_INST_ARGS = ['apturl', 'apt:%s?section=universe' % BW_PKG_NAME]
99
 
BW_CHCK_ARGS = ['dpkg', '-l', BW_PKG_NAME]
100
 
 
101
 
# This is a global so we can avoid creating multiple instances in some cases
102
 
oauth_token = None
103
 
oauth_consumer = None
104
 
 
105
 
def dbus_async(*args, **kwargs):
106
 
    """Simple handler to make dbus do stuff async."""
107
 
    pass
108
 
 
109
 
 
110
 
def do_login_request(bus, error_handler):
111
 
    """Make a login request to the login handling daemon."""
112
 
    try:
113
 
        client = bus.get_object(DBUS_BUS_NAME,
114
 
                                DBUS_CREDENTIALS_PATH,
115
 
                                follow_name_owner_changes=True)
116
 
        iface = dbus.Interface(client, DBUS_CREDENTIALS_IFACE)
117
 
        params = {HELP_TEXT_KEY: clientdefs.DESCRIPTION,
118
 
                  TC_URL_KEY: clientdefs.TC_URL,
119
 
                  PING_URL_KEY: clientdefs.PING_URL,
120
 
                  WINDOW_ID_KEY: '0'}
121
 
        iface.register(clientdefs.APP_NAME, params,
122
 
                       reply_handler=dbus_async,
123
 
                       error_handler=error_handler)
124
 
    except DBusException, e:
125
 
        error_handler(e)
126
 
 
127
 
 
128
 
def really_do_rest_request(url, method, conn):
129
 
    """Second-order helper that does the REST request.
130
 
 
131
 
    Necessary because of libproxy's orneriness WRT threads: LP:633241.
132
 
    """
133
 
    from ubuntuone.api.restclient import RestClient
134
 
    logger.debug("really_do_rest_request (%s:%s)", method, url)
135
 
    rest_client = RestClient(url)
136
 
    result = rest_client.call(url, method, oauth_consumer, oauth_token)
137
 
    logger.debug("got result for really_do_rest_request (%s:%s)", method, url)
138
 
    conn.send(result)
139
 
    logger.debug("end really_do_rest_request (%s:%s)", method, url)
140
 
 
141
 
 
142
 
def do_rest_request(proc, conn, callback):
143
 
    """Helper that handles the REST request."""
144
 
    pid = os.getpid()
145
 
    logger.debug("do_rest_request (%s)", pid)
146
 
    proc.join()
147
 
 
148
 
    result = conn.recv()
149
 
    logger.debug("got result for do_rest_request (%d)", pid)
150
 
    if callback is not None:
151
 
        with gtk.gdk.lock:
152
 
            callback(result)
153
 
    logger.debug("end do_rest_request (%d)", pid)
154
 
 
155
 
 
156
 
def make_rest_request(url=None, method='GET', callback=None):
157
 
    """Helper that makes an oauth-wrapped REST request."""
158
 
    logger.debug("make_rest_request (%s:%s)", method, url)
159
 
    conn1, conn2 = Pipe(False)
160
 
    p = Process(target=really_do_rest_request, args=(url, method, conn2))
161
 
    p.start()
162
 
    logger.debug("make_rest_request (%s:%s) started %d", method, url, p.pid)
163
 
    Thread(target=do_rest_request, args=(p, conn1, callback)).start()
164
 
    logger.debug("end make_rest_request (%s:%s)", method, url)
165
 
 
166
 
 
167
 
class DevicesWidget(gtk.Table):
168
 
    """
169
 
    the Devices tab.
170
 
    """
171
 
    def __init__(self,
172
 
                 bus,
173
 
                 url='https://one.ubuntu.com/api/1.0/devices/'):
174
 
        super(DevicesWidget, self).__init__(rows=2, columns=3)
175
 
        self.bus = bus
176
 
        self.sdtool = SyncDaemonTool(bus)
177
 
        self.set_border_width(6)
178
 
        self.set_row_spacings(6)
179
 
        self.set_col_spacings(6)
180
 
        self.devices = None
181
 
        self.base_url = url
182
 
        self.conn = None
183
 
        self.consumer = None
184
 
        self.table_widgets = []
185
 
 
186
 
        self.connected = None  # i.e. unknown
187
 
        self.conn_btn = None
188
 
        self.up_spin = None
189
 
        self.dn_spin = None
190
 
        self.bw_chk = None
191
 
        self.bw_limited = False
192
 
        self.up_limit = 2097152
193
 
        self.dn_limit = 2097152
194
 
        self.got_limits = False
195
 
 
196
 
        self._update_id = 0
197
 
 
198
 
        self.status_label = gtk.Label("")
199
 
        self.attach(self.status_label, 0, 3, 2, 3)
200
 
 
201
 
        self.description = gtk.Label(_("The devices connected to your personal"
202
 
                                       " cloud network are listed below"))
203
 
        self.description.set_alignment(0., .5)
204
 
        self.description.set_line_wrap(True)
205
 
        self.attach(self.description, 0, 3, 0, 1, xpadding=12, ypadding=12)
206
 
 
207
 
    def update_bw_settings(self):
208
 
        """
209
 
        Push the bandwidth settings at syncdaemon.
210
 
        """
211
 
        if self.got_limits:
212
 
            if self.sdtool.is_files_sync_enabled():
213
 
                self.sdtool.set_throttling_limits(self.dn_limit or -1,
214
 
                                                  self.up_limit or -1)
215
 
                self.sdtool.enable_throttling(self.bw_limited)
216
 
 
217
 
    def handle_bw_controls_changed(self, *a):
218
 
        """
219
 
        Sync the bandwidth throttling model with the view.
220
 
 
221
 
        Start a timer to sync with syncdaemon too.
222
 
        """
223
 
        if not self.sdtool.is_files_sync_enabled():
224
 
            return
225
 
        # Remove the timeout ...
226
 
        if self._update_id != 0:
227
 
            gobject.source_remove(self._update_id)
228
 
 
229
 
        # sync the model ...
230
 
        self.bw_limited = self.bw_chk.get_active()
231
 
        self.up_limit = self.up_spin.get_value_as_int() * 1024
232
 
        self.dn_limit = self.dn_spin.get_value_as_int() * 1024
233
 
 
234
 
        # ... and add the timeout back
235
 
        self._update_id = gobject.timeout_add_seconds(
236
 
              1, self.update_bw_settings)
237
 
 
238
 
    def handle_bw_checkbox_toggled(self, checkbox, *widgets):
239
 
        """
240
 
        Callback for the bandwidth togglebutton.
241
 
        """
242
 
        active = checkbox.get_active()
243
 
        for widget in widgets:
244
 
            widget.set_sensitive(active)
245
 
        self.handle_bw_controls_changed()
246
 
 
247
 
    def handle_limits(self, limits):
248
 
        """
249
 
        Callback for when syncdaemon tells us its throttling limits.
250
 
        """
251
 
        self.got_limits = True
252
 
        self.up_limit = int(limits['upload'])
253
 
        self.dn_limit = int(limits['download'])
254
 
        if self.up_spin is not None and self.dn_spin is not None:
255
 
            self.up_spin.set_value(self.up_limit / 1024)
256
 
            self.dn_spin.set_value(self.dn_limit / 1024)
257
 
 
258
 
            # Now we can connect to spin buttons' signals
259
 
            self.up_spin.connect("value-changed", self.handle_bw_controls_changed)
260
 
            self.dn_spin.connect("value-changed", self.handle_bw_controls_changed)
261
 
 
262
 
    def handle_throttling_enabled(self, enabled):
263
 
        """
264
 
        Callback for when syncdaemon tells us whether throttling is enabled.
265
 
        """
266
 
        self.bw_limited = enabled
267
 
        if self.bw_chk is not None:
268
 
            self.bw_chk.set_active(enabled)
269
 
 
270
 
    def handle_state_change(self, new_state):
271
 
        """
272
 
        Callback for when syncdaemon's state changes.
273
 
        """
274
 
        if new_state['is_error']:
275
 
            # this syncdaemon isn't going to connect no more
276
 
            self.connected = None
277
 
        else:
278
 
            self.connected = new_state['is_connected']
279
 
        if self.conn_btn is not None:
280
 
            if self.connected:
281
 
                self.conn_btn.set_label(_("Disconnect"))
282
 
            else:
283
 
                self.conn_btn.set_label(_("Connect"))
284
 
            if self.connected is None:
285
 
                self.conn_btn.set_sensitive(False)
286
 
            else:
287
 
                self.conn_btn.set_sensitive(True)
288
 
 
289
 
    def error(self, msg):
290
 
        """
291
 
        Clear the table and show the error message in its place.
292
 
 
293
 
        This might be better as an error dialog.
294
 
        """
295
 
        logger.error(msg)
296
 
        dialog = gtk.MessageDialog(self.get_toplevel(),
297
 
                                   gtk.DIALOG_DESTROY_WITH_PARENT |
298
 
                                   gtk.DIALOG_MODAL,
299
 
                                   gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
300
 
                                   'Error')
301
 
        dialog.format_secondary_text(str(msg))
302
 
        try:
303
 
            dialog.run()
304
 
        finally:
305
 
            dialog.destroy()
306
 
        while gtk.events_pending():
307
 
            gtk.main_iteration()
308
 
 
309
 
    def get_devices(self):
310
 
        """
311
 
        Ask the server for a list of devices
312
 
 
313
 
        Hook up parse_devices to run on the result (when it gets here).
314
 
        """
315
 
        make_rest_request(url=self.base_url, callback=self.parse_devices)
316
 
 
317
 
    def parse_devices(self, result):
318
 
        """
319
 
        Parse the list of devices, and hook up list_devices if it worked.
320
 
        """
321
 
        error = None
322
 
        if result and isinstance(result, list):
323
 
            self.devices = result
324
 
        elif isinstance(result, dict):
325
 
            error = result.get('error', None)
326
 
            if not error and result.get('status', None):
327
 
                error = result.get('reason', None)
328
 
            else:
329
 
                error = "Invalid response getting devices list."
330
 
        else:
331
 
            error = "Got empty result for devices list."
332
 
 
333
 
        if error:
334
 
            self.devices = []
335
 
            logger.error(error)
336
 
 
337
 
        gobject.idle_add(self.list_devices)
338
 
 
339
 
    def clear_devices_view(self):
340
 
        """
341
 
        Clear out almost all the widgets.
342
 
 
343
 
        All except from the table, the description and the
344
 
        status_label get destroyed.
345
 
        """
346
 
        for i in self.get_children():
347
 
            if i not in (self.description, self.status_label):
348
 
                i.destroy()
349
 
        self.conn_btn = None
350
 
        self.up_spin = None
351
 
        self.dn_spin = None
352
 
        self.bw_chk = None
353
 
 
354
 
    def list_devices(self):
355
 
        """
356
 
        Populate the table with the list of devices.
357
 
 
358
 
        If the list of devices is empty, make a fake one that refers
359
 
        to the local machine (to get the connect/restart buttons).
360
 
        """
361
 
        self.clear_devices_view()
362
 
 
363
 
        fsync_enabled = self.sdtool.is_files_sync_enabled()
364
 
 
365
 
        if not self.devices:
366
 
            if fsync_enabled:
367
 
                # a stopgap device so you can at least try to connect
368
 
                self.devices = [{'kind': 'Computer',
369
 
                                 'description': _("<LOCAL MACHINE>"),
370
 
                                 'token': oauth_token.key if oauth_token else '',
371
 
                                 'FAKE': 'YES'}]
372
 
        else:
373
 
            self.resize(len(self.devices)+1, 3)
374
 
 
375
 
        self.status_label.set_label("")
376
 
 
377
 
        i = 0
378
 
        for row in self.devices or ():
379
 
            i += 1
380
 
            img = gtk.Image()
381
 
            img.set_from_icon_name(row['kind'].lower(), gtk.ICON_SIZE_DND)
382
 
            desc = gtk.Label(row['description'])
383
 
            desc.set_alignment(0., .5)
384
 
            self.attach(img, 0, 1, i, i+1)
385
 
            self.attach(desc, 1, 2, i, i+1)
386
 
            if 'FAKE' not in row:
387
 
                # we don't include the "Remove" button for the fake entry :)
388
 
                butn = gtk.Button(_("Remove"))
389
 
                butn.connect('clicked', self.remove,
390
 
                             row['kind'], row.get('token'))
391
 
                self.attach(butn, 2, 3, i, i+1, xoptions=0, yoptions=0)
392
 
            if ((oauth_token and row.get('token') == oauth_token.key or 'FAKE' in row)
393
 
                and fsync_enabled):
394
 
                self.bw_chk = ck_btn = gtk.CheckButton(
395
 
                    _("_Limit bandwidth"))
396
 
                ck_btn.set_active(self.bw_limited)
397
 
                up_lbl = gtk.Label(_("Maximum _upload speed (KB/s):"))
398
 
                up_lbl.set_alignment(0., .5)
399
 
                up_lbl.set_use_underline(True)
400
 
                adj = gtk.Adjustment(value=self.up_limit/1024.,
401
 
                                     lower=0.0, upper=4096.0,
402
 
                                     step_incr=1.0, page_incr=16.0)
403
 
                self.up_spin = up_btn = gtk.SpinButton(adj)
404
 
                up_lbl.set_mnemonic_widget(up_btn)
405
 
                dn_lbl = gtk.Label(_("Maximum _download speed (KB/s):"))
406
 
                dn_lbl.set_alignment(0., .5)
407
 
                dn_lbl.set_use_underline(True)
408
 
                adj = gtk.Adjustment(value=self.dn_limit/1024.,
409
 
                                     lower=0.0, upper=4096.0,
410
 
                                     step_incr=1.0, page_incr=16.0)
411
 
                self.dn_spin = dn_btn = gtk.SpinButton(adj)
412
 
                dn_lbl.set_mnemonic_widget(dn_btn)
413
 
                ck_btn.connect('toggled', self.handle_bw_checkbox_toggled,
414
 
                               up_lbl, up_btn, dn_lbl, dn_btn)
415
 
                if fsync_enabled:
416
 
                    for w in up_lbl, up_btn, dn_lbl, dn_btn:
417
 
                        w.set_sensitive(self.bw_limited)
418
 
 
419
 
                self.conn_btn = gtk.Button(_("Connect"))
420
 
                if self.connected is None:
421
 
                    self.conn_btn.set_sensitive(False)
422
 
                elif self.connected:
423
 
                    self.conn_btn.set_label(_("Disconnect"))
424
 
                self.conn_btn.connect('clicked', self.handle_connect_button)
425
 
                restart_btn = gtk.Button(_("Restart"))
426
 
                restart_btn.connect('clicked', self.handle_restart_button)
427
 
                btn_box = gtk.HButtonBox()
428
 
                btn_box.add(self.conn_btn)
429
 
                btn_box.add(restart_btn)
430
 
 
431
 
                i += 1
432
 
                self.attach(ck_btn, 1, 3, i, i+1)
433
 
                i += 1
434
 
                self.attach(up_lbl, 1, 2, i, i+1)
435
 
                self.attach(up_btn, 2, 3, i, i+1)
436
 
                i += 1
437
 
                self.attach(dn_lbl, 1, 2, i, i+1)
438
 
                self.attach(dn_btn, 2, 3, i, i+1)
439
 
                i += 1
440
 
                self.attach(btn_box, 1, 3, i, i+1)
441
 
            i += 2
442
 
        self.show_all()
443
 
 
444
 
    def handle_connect_button(self, *a):
445
 
        """
446
 
        Callback for the Connect/Disconnect button.
447
 
        """
448
 
        self.conn_btn.set_sensitive(False)
449
 
        if self.connected:
450
 
            self.sdtool.disconnect()
451
 
        else:
452
 
            self.sdtool.connect()
453
 
 
454
 
    def handle_restart_button(self, *a):
455
 
        """
456
 
        Callback for the Restart button.
457
 
        """
458
 
        self.sdtool.quit().addCallbacks(lambda _: self.sdtool.start())
459
 
 
460
 
    def remove(self, button, kind, token):
461
 
        """
462
 
        Callback for the Remove button.
463
 
 
464
 
        Starts an async request to remove a device.
465
 
        """
466
 
        make_rest_request(url=('%sremove/%s/%s' % (self.base_url,
467
 
                                                   kind.lower(), token)),
468
 
                          callback=self.parse_devices)
469
 
        def local_removal_cb(*args, **kwargs):
470
 
            """Try to get a new token if we remove the local one."""
471
 
            do_login_request(self.bus, self.error)
472
 
 
473
 
        if token == oauth_token.key:
474
 
            try:
475
 
                client = self.bus.get_object(DBUS_BUS_NAME,
476
 
                                             DBUS_CREDENTIALS_PATH,
477
 
                                             follow_name_owner_changes=True)
478
 
                iface = dbus.Interface(client, DBUS_CREDENTIALS_IFACE)
479
 
                iface.clear_credentials(clientdefs.APP_NAME, {},
480
 
                                        reply_handler=local_removal_cb,
481
 
                                        error_handler=self.error)
482
 
            except DBusException, e:
483
 
                self.error(e)
484
 
 
485
 
 
486
 
class UbuntuOneDialog(gtk.Dialog):
487
 
    """Preferences dialog."""
488
 
 
489
 
    def __init__(self, config=None, *args, **kw):
490
 
        """Initializes our config dialog."""
491
 
        super(UbuntuOneDialog, self).__init__(*args, **kw)
492
 
        self.set_title(_("Ubuntu One Preferences"))
493
 
        self.set_has_separator(False)
494
 
        self.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
495
 
        self.set_default_response(gtk.RESPONSE_CLOSE)
496
 
        self.set_icon_name("ubuntuone")
497
 
        self.set_default_size(400, 300)
498
 
 
499
 
        self.connect("close", self.__handle_response, gtk.RESPONSE_CLOSE)
500
 
        self.connect("response", self.__handle_response)
501
 
 
502
 
        # desktopcouch ReplicationExclusion, or None
503
 
        self.dcouch = None
504
 
 
505
 
        # folder id for dealing with the Music folder
506
 
        self.ums_id = None
507
 
 
508
 
        self.__bus = dbus.SessionBus()
509
 
 
510
 
        # Timeout ID to avoid spamming DBus from spinbutton changes
511
 
        self.__update_id = 0
512
 
 
513
 
        # Build the dialog
514
 
        self.__construct()
515
 
 
516
 
        # SD Tool object
517
 
        self.sdtool = SyncDaemonTool(self.__bus)
518
 
        if self.sdtool.is_files_sync_enabled():
519
 
            self._hookup_dbus()
520
 
        else:
521
 
            self.status_label.set_text(_("Disconnected"))
522
 
 
523
 
        logger.info("Starting Ubuntu One Preferences")
524
 
 
525
 
    def _hookup_dbus(self):
526
 
        """
527
 
        Hook up dbus
528
 
        """
529
 
        self.sdtool.get_status().addCallbacks(self.__got_state,
530
 
                                              self.__sd_error)
531
 
 
532
 
        # hook dbus up
533
 
        self.__bus.add_signal_receiver(
534
 
            handler_function=self.__got_state,
535
 
            signal_name='StatusChanged',
536
 
            dbus_interface=DBUS_IFACE_STATUS_NAME)
537
 
 
538
 
        try:
539
 
            client = self.__bus.get_object(DBUS_IFACE_NAME, "/config",
540
 
                                           follow_name_owner_changes=True)
541
 
            iface = dbus.Interface(client, DBUS_IFACE_CONFIG_NAME)
542
 
            iface.get_throttling_limits(
543
 
                reply_handler=self.__got_limits,
544
 
                error_handler=self.__dbus_error)
545
 
            iface.bandwidth_throttling_enabled(
546
 
                reply_handler=self.__got_enabled,
547
 
                error_handler=self.__dbus_error)
548
 
        except DBusException, e:
549
 
            self.__dbus_error(e)
550
 
 
551
 
 
552
 
    def __dbus_error(self, error):
553
 
        """Error getting throttling config."""
554
 
        logger.error(error)
555
 
 
556
 
    def __sd_error(self, error):
557
 
        """Error talking to syncdaemon."""
558
 
        logger.error(error)
559
 
 
560
 
    def __got_state(self, state):
561
 
        """Got the state of syncdaemon."""
562
 
        if not state['is_connected']:
563
 
            self.status_label.set_text(_("Disconnected"))
564
 
        else:
565
 
            try:
566
 
                status = state['name']
567
 
                queues = state['queues']
568
 
            except KeyError:
569
 
                status = None
570
 
                queues = None
571
 
            if status == u'QUEUE_MANAGER' and queues == u'IDLE':
572
 
                self.status_label.set_text(_("Synchronization complete"))
573
 
            else:
574
 
                self.status_label.set_text(_(u"Synchronization in progress…"))
575
 
 
576
 
        # Update the stuff in the devices tab
577
 
        self.devices.handle_state_change(state)
578
 
 
579
 
    def __got_limits(self, limits):
580
 
        """Got the throttling limits."""
581
 
        logger.debug("got limits: %s" % (limits,))
582
 
        self.devices.handle_limits(limits)
583
 
 
584
 
    def __got_enabled(self, enabled):
585
 
        """Got the throttling enabled config."""
586
 
        self.devices.handle_throttling_enabled(enabled)
587
 
 
588
 
    def __handle_response(self, dialog, response):
589
 
        """Handle the dialog's response."""
590
 
        self.hide()
591
 
        while gtk.events_pending():
592
 
            gtk.main_iteration()
593
 
        self.devices.handle_bw_controls_changed()
594
 
        gobject.timeout_add_seconds(5, gtk.main_quit)
595
 
 
596
 
    def _format_for_gb_display(self, bytes):
597
 
        """Format bytes into reasonable gb display."""
598
 
        gb = bytes / 1024 / 1024 / 1024
599
 
        if gb < 1.0:
600
 
            mb = bytes / 1024 / 1024
601
 
            if mb < 1.0:
602
 
                return (bytes / 1024, 'KB')
603
 
            return (mb, 'MB')
604
 
        return (gb, 'GB')
605
 
 
606
 
    def update_quota_display(self, used, total):
607
 
        """Update the quota display."""
608
 
        percent = (float(used) / float(total)) * 100
609
 
        real_used, real_type = self._format_for_gb_display(used)
610
 
        self.usage_label.set_text(
611
 
            _("%(used)0.1f %(type)s Used (%(percent)0.1f%%)") % {
612
 
                'used' : real_used,
613
 
                'type' : real_type,
614
 
                'percent' : percent })
615
 
        self.usage_graph.set_fraction(percent / 100)
616
 
 
617
 
        if percent >= 100.:
618
 
            self.overquota_img.set_from_icon_name('dialog-warning',
619
 
                                                  gtk.ICON_SIZE_DIALOG)
620
 
            self.overquota.show_all()
621
 
            label = _("You are using all of your Ubuntu One storage.")
622
 
            desc = _("Synchronization is now disabled. Remove files from"
623
 
                     " synchronization or upgrade your subscription.")
624
 
        elif percent >= 95.:
625
 
            self.overquota_img.set_from_icon_name('dialog-information',
626
 
                                                  gtk.ICON_SIZE_DIALOG)
627
 
            self.overquota.show_all()
628
 
            label = _("You are near your Ubuntu One storage limit.")
629
 
            desc = _("Automatic synchronization will stop when you reach"
630
 
                     " your storage limit. Please consider upgrading to"
631
 
                     " avoid a service interruption.")
632
 
        else:
633
 
            self.overquota_img.clear()
634
 
            label = desc = ""
635
 
            self.overquota.hide_all()
636
 
        self.overquota_label.set_markup("%s\n<small>%s</small>" % (label, desc))
637
 
 
638
 
    def got_quota_info(self, quota):
639
 
        """Handle the result from the quota REST call."""
640
 
        if quota:
641
 
            used = quota.get('used', 0)
642
 
            total = quota.get('total', 2)
643
 
            self.update_quota_display(used, total)
644
 
 
645
 
    def request_quota_info(self):
646
 
        """Request new quota info from server, and update display."""
647
 
        make_rest_request(url='https://one.ubuntu.com/api/quota/',
648
 
                          callback=self.got_quota_info)
649
 
 
650
 
    def got_account_info(self, user):
651
 
        """Handle the result from the account REST call."""
652
 
        if user:
653
 
            self.name_label.set_text(user.get('nickname', _("Unknown")))
654
 
            self.mail_label.set_text(user.get('email', _("Unknown")))
655
 
            if "current_plan" in user:
656
 
                desc = user["current_plan"]
657
 
            elif "subscription" in user \
658
 
                     and "description" in user["subscription"]:
659
 
                desc = user["subscription"]["description"]
660
 
            else:
661
 
                desc = _("Unknown")
662
 
            self.plan_label.set_label(desc)
663
 
 
664
 
    def request_account_info(self):
665
 
        """Request account info from server, and update display."""
666
 
        make_rest_request(url='https://one.ubuntu.com/api/account/',
667
 
                          callback=self.got_account_info)
668
 
 
669
 
    def __bw_inst_cb(self, button):
670
 
        """Pops off the bindwood installer to a thread."""
671
 
        Thread(target=self.__install_bindwood).start()
672
 
 
673
 
    def __install_bindwood(self):
674
 
        """Runs the tool to intall bindwood and updates the UI."""
675
 
        installed = subprocess.call(BW_INST_ARGS)
676
 
        gtk.gdk.threads_enter()
677
 
        if installed == 0:
678
 
            self.bw_inst_box.hide()
679
 
        else:
680
 
            self.bw_inst_box.show()
681
 
            logger.error("There was an error installing bindwood.")
682
 
        gtk.gdk.threads_leave()
683
 
 
684
 
    def __check_for_bindwood(self):
685
 
        """
686
 
        Method run in a thread to check that bindwood exists, and
687
 
        update the interface appropriately.
688
 
        """
689
 
        exists = True
690
 
        p = subprocess.Popen(BW_CHCK_ARGS, bufsize=4096,
691
 
                             stdout=subprocess.PIPE, stderr=subprocess.PIPE,
692
 
                             stdin=subprocess.PIPE, env=os.environ)
693
 
        result = p.wait()
694
 
        if result == 0:
695
 
            for line in p.stdout.readlines():
696
 
                if line.startswith('un'):
697
 
                    exists = False
698
 
        else:
699
 
            exists = False
700
 
 
701
 
        gtk.gdk.threads_enter()
702
 
        if exists:
703
 
            self.bw_inst_box.hide()
704
 
        else:
705
 
            self.bw_inst_btn.connect("clicked",
706
 
                                     self.__bw_inst_cb)
707
 
            self.bw_inst_box.show()
708
 
        gtk.gdk.threads_leave()
709
 
 
710
 
    def connect_desktopcouch_exclusion(self):
711
 
        """Hook up to desktopcouch enablement API, or disable the UI."""
712
 
        # Hook up to desktopcouch, or die trying
713
 
        try:
714
 
            if not dcouch:
715
 
                raise ValueError('Replication service not available.')
716
 
 
717
 
            # Check bindwood availability
718
 
            Thread(target=self.__check_for_bindwood).start()
719
 
 
720
 
            # Get the exclusion settings
721
 
            self.dcouch = dcouch.ReplicationExclusion()
722
 
            exclusions = self.dcouch.all_exclusions()
723
 
            for check in [self.bookmarks_check,
724
 
                          self.abook_check,
725
 
                          self.gwib_check]:
726
 
                if check.get_data('dbname') in exclusions:
727
 
                    check.set_active(False)
728
 
                else:
729
 
                    check.set_active(True)
730
 
        except ValueError, e:
731
 
            # Replication is not avaialble, so disable the UI
732
 
            for check in [self.bookmarks_check,
733
 
                          self.abook_check,
734
 
                          self.gwib_check]:
735
 
                check.set_sensitive(False)
736
 
            logger.warning(e)
737
 
 
738
 
    def toggle_db_sync(self, dbname, disable=False):
739
 
        """
740
 
        Toggle whether a db in desktopcouch is synchronized to the
741
 
        Ubuntu One couchdb server.
742
 
        """
743
 
        if self.dcouch:
744
 
            if disable:
745
 
                self.dcouch.exclude(dbname)
746
 
            else:
747
 
                self.dcouch.replicate(dbname)
748
 
        else:
749
 
            logger.error("Database enablement is unavailable.")
750
 
 
751
 
    def __db_check_toggled(self, checkbutton):
752
 
        """Handle toggling a desktopcouch service toggling."""
753
 
        dbname = checkbutton.get_data('dbname')
754
 
        self.toggle_db_sync(dbname, not checkbutton.get_active())
755
 
 
756
 
    def files_check_toggled(self, checkbutton):
757
 
        """Handle toggling the files service."""
758
 
        enabled = checkbutton.get_active()
759
 
        self.sdtool.enable_files_sync(enabled).addCallbacks(
760
 
            lambda _: dbus_async,
761
 
            self.__sd_error)
762
 
        def sd_enabled(_):
763
 
            self._hookup_dbus()
764
 
            self.sdtool.connect()
765
 
            self.devices.list_devices()
766
 
        def sd_error(error):
767
 
            self.files_check.set_active(False)
768
 
            self.files_check.set_sensitive(False)
769
 
            self.music_check.set_sensitive(False)
770
 
            self.__sd_error(error)
771
 
 
772
 
        if enabled:
773
 
            self.sdtool.start().addCallbacks(sd_enabled,
774
 
                                             sd_error)
775
 
            if self.ums_id:
776
 
                self.music_check.set_sensitive(True)
777
 
                if self.music_check.get_active():
778
 
                    self.music_check_toggled(self.music_check)
779
 
        else:
780
 
            self.sdtool.quit().addCallback(
781
 
                lambda _: self.devices.list_devices())
782
 
            self.music_check.set_sensitive(False)
783
 
 
784
 
    def music_check_toggled(self, checkbutton):
785
 
        """Handle toggling the music download service."""
786
 
        if not self.files_check.get_active() or not self.ums_id:
787
 
            checkbutton.set_sensitive(False)
788
 
            checkbutton.set_active(False)
789
 
            return
790
 
        enabled = checkbutton.get_active()
791
 
        def got_error(error):
792
 
            checkbutton.set_sensitive(False)
793
 
            checkbutton.set_active(not enabled)
794
 
            self.__sd_error(error)
795
 
 
796
 
        if enabled:
797
 
            d = self.sdtool.subscribe_folder(self.ums_id)
798
 
        else:
799
 
            d = self.sdtool.unsubscribe_folder(self.ums_id)
800
 
        d.addErrback(got_error)
801
 
 
802
 
    def get_syncdaemon_sync_config(self):
803
 
        """Update the files/music sync config from syncdaemon."""
804
 
        def got_ms_err(error):
805
 
            self.music_check.set_sensitive(False)
806
 
            self.__sd_error(error)
807
 
 
808
 
        def got_info(info):
809
 
            if not info:
810
 
                self.music_check.set_active(False)
811
 
                self.music_check.set_sensitive(False)
812
 
            else:
813
 
                self.ums_id = info['volume_id']
814
 
                self.music_check.set_sensitive(True)
815
 
                if info['subscribed'] == 'True':
816
 
                    self.music_check.set_active(True)
817
 
                else:
818
 
                    self.music_check.set_active(False)
819
 
        if os.path.exists(U1MSPATH):
820
 
            self.sdtool.get_folder_info(
821
 
                U1MSPATH).addCallbacks(got_info, got_ms_err)
822
 
        self.files_check.set_active(self.sdtool.is_files_sync_enabled())
823
 
 
824
 
    def connect_file_sync_callbacks(self):
825
 
        """Connect the file sync checkbox callbacks."""
826
 
        self.files_check.connect('toggled', self.files_check_toggled)
827
 
        self.music_check.connect('toggled', self.music_check_toggled)
828
 
 
829
 
    def __construct(self):
830
 
        """Construct the dialog's layout."""
831
 
        area = self.get_content_area()
832
 
 
833
 
        vbox = gtk.VBox(spacing=12)
834
 
        vbox.set_border_width(12)
835
 
        area.add(vbox)
836
 
        vbox.show()
837
 
 
838
 
        # Usage text/progress bar
839
 
        rbox = gtk.VBox(spacing=12)
840
 
        vbox.pack_start(rbox, False, False)
841
 
        rbox.show()
842
 
 
843
 
        hbox = gtk.HBox(spacing=6)
844
 
        rbox.pack_start(hbox, False, False)
845
 
        hbox.show()
846
 
 
847
 
        self.usage_graph = gtk.ProgressBar()
848
 
        hbox.pack_start(self.usage_graph, False, False)
849
 
        self.usage_graph.show()
850
 
 
851
 
        self.usage_label = gtk.Label(_("Unknown"))
852
 
        self.usage_label.set_alignment(0.0, 0.5)
853
 
        hbox.pack_start(self.usage_label, False, False)
854
 
        self.usage_label.show()
855
 
 
856
 
        self.status_label = gtk.Label(_("Unknown"))
857
 
        self.status_label.set_alignment(0.0, 0.5)
858
 
        rbox.pack_start(self.status_label, False, False)
859
 
        self.status_label.show()
860
 
 
861
 
        # Notebook
862
 
        self.notebook = gtk.Notebook()
863
 
        vbox.add(self.notebook)
864
 
        self.notebook.show()
865
 
 
866
 
        # Account tab
867
 
        account = gtk.VBox(spacing=12)
868
 
        account.set_border_width(6)
869
 
        self.notebook.append_page(account)
870
 
        self.notebook.set_tab_label_text(account, _("Account"))
871
 
        account.show()
872
 
 
873
 
        # overquota notice in account tab
874
 
        self.overquota = gtk.HBox(spacing=6)
875
 
        self.overquota.set_border_width(6)
876
 
        account.pack_start(self.overquota, False, False)
877
 
 
878
 
        self.overquota_img = gtk.Image()
879
 
        self.overquota.pack_start(self.overquota_img, False, False)
880
 
        self.overquota_img.show()
881
 
 
882
 
        self.overquota_label = label = gtk.Label("\n\n")
883
 
        label.set_line_wrap(True)
884
 
        label.set_alignment(0.0, 0.5)
885
 
        self.overquota.pack_start(label, False, False)
886
 
        self.overquota_label.show()
887
 
 
888
 
 
889
 
        # User info in account tab
890
 
        table = gtk.Table(rows=6, columns=2)
891
 
        table.set_row_spacings(6)
892
 
        table.set_col_spacings(6)
893
 
        account.pack_start(table, False, False)
894
 
        table.show()
895
 
 
896
 
        label = gtk.Label(_("_Name:"))
897
 
        label.set_use_underline(True)
898
 
        label.set_alignment(0.0, 0.5)
899
 
        table.attach(label, 0, 1, 0, 1)
900
 
        label.show()
901
 
 
902
 
        self.name_label = gtk.Label(_("Unknown"))
903
 
        self.name_label.set_use_underline(True)
904
 
        self.name_label.set_alignment(0.0, 0.5)
905
 
        table.attach(self.name_label, 1, 2, 0, 1)
906
 
        self.name_label.show()
907
 
 
908
 
        label = gtk.Label(_("_E-mail:"))
909
 
        label.set_use_underline(True)
910
 
        label.set_alignment(0.0, 0.5)
911
 
        table.attach(label, 0, 1, 1, 2)
912
 
        label.show()
913
 
 
914
 
        self.mail_label = gtk.Label(_("Unknown"))
915
 
        self.mail_label.set_use_underline(True)
916
 
        self.mail_label.set_alignment(0.0, 0.5)
917
 
        table.attach(self.mail_label, 1, 2, 1, 2)
918
 
        self.mail_label.show()
919
 
 
920
 
        label = gtk.Label(_("Current plan:"))
921
 
        label.set_alignment(0.0, 0.5)
922
 
        table.attach(label, 0, 1, 2, 3)
923
 
        label.show()
924
 
 
925
 
        self.plan_label = gtk.Label(_("Unknown"))
926
 
        self.plan_label.set_alignment(0.0, 0.5)
927
 
        table.attach(self.plan_label, 1, 2, 2, 3)
928
 
        self.plan_label.show()
929
 
 
930
 
        for n, (url, label) in enumerate([
931
 
                ("http://one.ubuntu.com/support", _("Support options")),
932
 
                ("http://one.ubuntu.com/account", _("Manage account")),
933
 
                ("http://one.ubuntu.com/upgrade",
934
 
                 _("Upgrade your subscription")),
935
 
            ]):
936
 
            link = gtk.LinkButton(url, label)
937
 
            link.set_relief(gtk.RELIEF_NONE)
938
 
            link.set_alignment(0.0, 0.5)
939
 
            table.attach(link, 0, 2, 5-n, 6-n)
940
 
            link.show()
941
 
 
942
 
        # Devices tab
943
 
        sw = gtk.ScrolledWindow()
944
 
        sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
945
 
        self.notebook.append_page(sw)
946
 
        self.notebook.set_tab_label_text(sw, _("Devices"))
947
 
        sw.show()
948
 
        self.devices = DevicesWidget(self.__bus)
949
 
        sw.add_with_viewport(self.devices)
950
 
        self.devices.list_devices()
951
 
        self.devices.show_all()
952
 
 
953
 
        # Services tab
954
 
        services = gtk.VBox(spacing=12)
955
 
        services.set_border_width(6)
956
 
        self.notebook.append_page(services)
957
 
        self.notebook.set_tab_label_text(services, _("Services"))
958
 
        services.show()
959
 
 
960
 
        label = gtk.Label()
961
 
        label.set_markup(
962
 
            _("Ubuntu One sync options\n"
963
 
              "<small>Choose services to synchronize"
964
 
              " with this computer</small>"))
965
 
        label.set_alignment(0., .5)
966
 
        label.set_padding(12, 6)
967
 
        label.set_line_wrap(True)
968
 
        services.pack_start(label, False, False)
969
 
        label.show()
970
 
 
971
 
        self.bookmarks_check = gtk.CheckButton(_("_Bookmarks"))
972
 
        self.bookmarks_check.set_data('dbname', 'bookmarks')
973
 
        self.bookmarks_check.connect('toggled', self.__db_check_toggled)
974
 
        services.pack_start(self.bookmarks_check, False, False)
975
 
        self.bookmarks_check.show()
976
 
 
977
 
        # This box is shown in the event bindwood is not installed
978
 
        self.bw_inst_box = gtk.HBox(spacing=12)
979
 
        services.pack_start(self.bw_inst_box, False, False)
980
 
 
981
 
        label = gtk.Label("")
982
 
        self.bw_inst_box.pack_start(label, False, False)
983
 
        label.show()
984
 
 
985
 
        label = gtk.Label("<i>%s</i>" % _("Firefox extension not installed."))
986
 
        label.set_use_markup(True)
987
 
        label.set_alignment(0.0, 0.5)
988
 
        self.bw_inst_box.pack_start(label, False, False)
989
 
        label.show()
990
 
 
991
 
        self.bw_inst_btn = gtk.Button(_("_Install"))
992
 
        self.bw_inst_box.pack_start(self.bw_inst_btn, False, False)
993
 
        self.bw_inst_btn.show()
994
 
 
995
 
        self.gwib_check = gtk.CheckButton(_("Broadcast messages _archive"))
996
 
        self.gwib_check.set_data('dbname', 'gwibber_messages')
997
 
        self.gwib_check.connect('toggled', self.__db_check_toggled)
998
 
        services.pack_start(self.gwib_check, False, False)
999
 
        self.gwib_check.show()
1000
 
 
1001
 
        self.abook_check = gtk.CheckButton(_("C_ontacts"))
1002
 
        self.abook_check.set_data('dbname', 'contacts')
1003
 
        self.abook_check.connect('toggled', self.__db_check_toggled)
1004
 
        services.pack_start(self.abook_check, False, False)
1005
 
        self.abook_check.show()
1006
 
 
1007
 
        fbox = gtk.VBox(spacing=6)
1008
 
        services.pack_start(fbox, False, False)
1009
 
        fbox.show()
1010
 
 
1011
 
        self.files_check = gtk.CheckButton(_("_Files"))
1012
 
        self.files_check.set_active(False)
1013
 
        fbox.pack_start(self.files_check, False, False)
1014
 
        self.files_check.show()
1015
 
 
1016
 
        alignment = gtk.Alignment(0.0, 0.5)
1017
 
        alignment.set_padding(0, 0, 12, 0)
1018
 
        fbox.pack_start(alignment, False, False)
1019
 
        alignment.show()
1020
 
 
1021
 
        self.music_check = gtk.CheckButton(_("Ubuntu One _Music Store"
1022
 
                                             " downloads"))
1023
 
        self.music_check.set_active(True)
1024
 
        self.music_check.set_sensitive(False)
1025
 
        alignment.add(self.music_check)
1026
 
        self.music_check.show()
1027
 
 
1028
 
 
1029
 
class UbuntuoneLoginHandler(dbus.service.Object):
1030
 
    """Class to handle registration/login, in case we aren't already."""
1031
 
 
1032
 
    def __init__(self, dialog, *args, **kw):
1033
 
        self.bus = dbus.SessionBus()
1034
 
 
1035
 
        self.dialog = dialog
1036
 
 
1037
 
        # DBus object magic
1038
 
        self.path = '/'
1039
 
        bus_name = dbus.service.BusName(PREFS_BUS_NAME, bus=self.bus)
1040
 
        dbus.service.Object.__init__(self, bus_name=bus_name,
1041
 
                                     object_path=self.path)
1042
 
 
1043
 
 
1044
 
    @dbus.service.method(PREFS_BUS_NAME, in_signature='', out_signature='')
1045
 
    def present(self):
1046
 
        """Raise the dialog window."""
1047
 
        self.dialog.connect_desktopcouch_exclusion()
1048
 
        self.dialog.get_syncdaemon_sync_config()
1049
 
        self.dialog.connect_file_sync_callbacks()
1050
 
        self.dialog.request_quota_info()
1051
 
        self.dialog.request_account_info()
1052
 
        self.dialog.devices.get_devices()
1053
 
        # watch it! jaunty has no 'get_visible' method
1054
 
        if self.dialog.get_property('visible'):
1055
 
            self.dialog.present_with_time(int(time.time()))
1056
 
 
1057
 
    def got_newcredentials(self, app_name, credentials):
1058
 
        """Show our dialog, since we can do stuff now."""
1059
 
        global oauth_consumer
1060
 
        global oauth_token
1061
 
 
1062
 
        if app_name == clientdefs.APP_NAME:
1063
 
            oauth_consumer = oauth.OAuthConsumer(credentials['consumer_key'],
1064
 
                                                 credentials['consumer_secret'])
1065
 
            oauth_token = oauth.OAuthToken(credentials['token'],
1066
 
                                           credentials['token_secret'])
1067
 
            self.present()
1068
 
            logger.info("Got credentials for %s", app_name)
1069
 
 
1070
 
    def got_credentialserror(self, app_name, error_dict):
1071
 
        """Got an error during authentication."""
1072
 
        message = error_dict.get('error_msg', '')
1073
 
        detailed_error = error_dict.get('detailed_error', '')
1074
 
        if app_name == clientdefs.APP_NAME:
1075
 
            logger.error("Credentials error for %s: %s - %s" %
1076
 
                         (app_name, message, detailed_error))
1077
 
 
1078
 
    def got_authdenied(self, app_name):
1079
 
        """User denied access."""
1080
 
        if app_name == clientdefs.APP_NAME:
1081
 
            logger.error("Authorization was denied for %s" % app_name)
1082
 
 
1083
 
    def got_dbus_error(self, error):
1084
 
        """Got a DBusError."""
1085
 
        logger.error(error)
1086
 
 
1087
 
    def register_signal_handlers(self):
1088
 
        """Register the dbus signal handlers."""
1089
 
        self.bus.add_signal_receiver(
1090
 
            handler_function=self.got_newcredentials,
1091
 
            signal_name='CredentialsFound',
1092
 
            dbus_interface=DBUS_CREDENTIALS_IFACE)
1093
 
        self.bus.add_signal_receiver(
1094
 
            handler_function=self.got_credentialserror,
1095
 
            signal_name='CredentialsError',
1096
 
            dbus_interface=DBUS_CREDENTIALS_IFACE)
1097
 
        self.bus.add_signal_receiver(
1098
 
            handler_function=self.got_authdenied,
1099
 
            signal_name='AuthorizationDenied',
1100
 
            dbus_interface=DBUS_CREDENTIALS_IFACE)
1101
 
 
1102
 
 
1103
 
if __name__ == "__main__":
1104
 
    gettext.bindtextdomain(clientdefs.GETTEXT_PACKAGE, clientdefs.LOCALEDIR)
1105
 
    gettext.textdomain(clientdefs.GETTEXT_PACKAGE)
1106
 
 
1107
 
    gobject.threads_init()
1108
 
    gtk.gdk.threads_init()
1109
 
 
1110
 
    gtk.rc_parse_string(RCSTYLE)
1111
 
    gobject.set_application_name("ubuntuone-preferences")
1112
 
 
1113
 
    bus = dbus.SessionBus()
1114
 
    result = bus.request_name(PREFS_BUS_NAME,
1115
 
                              dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
1116
 
    if result == dbus.bus.REQUEST_NAME_REPLY_EXISTS:
1117
 
        try:
1118
 
            client = bus.get_object(PREFS_BUS_NAME, '/',
1119
 
                                    follow_name_owner_changes=True)
1120
 
            iface= dbus.Interface(client, PREFS_BUS_NAME)
1121
 
            iface.present()
1122
 
        except DBusException, e:
1123
 
            logger.error(e)
1124
 
        sys.exit(0)
1125
 
 
1126
 
    try:
1127
 
        # The prefs dialog
1128
 
        gtk.gdk.threads_enter()
1129
 
 
1130
 
        prefs_dialog = UbuntuOneDialog()
1131
 
        prefs_dialog.show()
1132
 
 
1133
 
        login = UbuntuoneLoginHandler(dialog=prefs_dialog)
1134
 
        login.register_signal_handlers()
1135
 
        gobject.timeout_add_seconds(1, do_login_request,
1136
 
                                    bus, login.got_dbus_error)
1137
 
        gtk.main()
1138
 
        gtk.gdk.threads_leave()
1139
 
    except KeyboardInterrupt:
1140
 
        pass