~ubuntu-branches/ubuntu/quantal/ubuntuone-control-panel/quantal-proposed

« back to all changes in this revision

Viewing changes to ubuntuone/controlpanel/dbus_service.py

  • Committer: Bazaar Package Importer
  • Author(s): Natalia Bidart (nessita)
  • Date: 2010-12-06 12:27:11 UTC
  • Revision ID: james.westby@ubuntu.com-20101206122711-0wvvlliao34bjztf
Tags: upstream-0.0.9
ImportĀ upstreamĀ versionĀ 0.0.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
 
 
3
# Authors: Alejandro J. Cura <alecu@canonical.com>
 
4
# Authors: Natalia B. Bidart <nataliabidart@canonical.com>
 
5
#
 
6
# Copyright 2010 Canonical Ltd.
 
7
#
 
8
# This program is free software: you can redistribute it and/or modify it
 
9
# under the terms of the GNU General Public License version 3, as published
 
10
# by the Free Software Foundation.
 
11
#
 
12
# This program is distributed in the hope that it will be useful, but
 
13
# WITHOUT ANY WARRANTY; without even the implied warranties of
 
14
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 
15
# PURPOSE.  See the GNU General Public License for more details.
 
16
#
 
17
# You should have received a copy of the GNU General Public License along
 
18
# with this program.  If not, see <http://www.gnu.org/licenses/>.
 
19
 
 
20
"""Export the control backend thru DBus."""
 
21
 
 
22
import dbus.service
 
23
import gobject
 
24
 
 
25
from dbus.mainloop.glib import DBusGMainLoop
 
26
from dbus.service import method, signal
 
27
 
 
28
from twisted.python.failure import Failure
 
29
 
 
30
from ubuntuone.controlpanel import (DBUS_BUS_NAME, DBUS_PREFERENCES_PATH,
 
31
    DBUS_PREFERENCES_IFACE, utils)
 
32
from ubuntuone.controlpanel.backend import ControlBackend
 
33
from ubuntuone.controlpanel.logger import setup_logging
 
34
 
 
35
 
 
36
logger = setup_logging('dbus_service')
 
37
 
 
38
 
 
39
def make_unicode(anything):
 
40
    """Transform 'anything' on an unicode."""
 
41
    if not isinstance(anything, unicode):
 
42
        anything = str(anything).decode('utf8', 'replace')
 
43
 
 
44
    return anything
 
45
 
 
46
 
 
47
def error_handler(error):
 
48
    """Handle 'error' properly to be able to call a dbus error signal.
 
49
     If 'error' is a Failure, then transform the exception in it to a error
 
50
    dict. If 'error' is a regular Exception, transform it.
 
51
 
 
52
    If 'error' is already a string-string dict, just pass it along. Build a
 
53
    generic error dict in any other case.
 
54
 
 
55
    """
 
56
    result = {}
 
57
    if isinstance(error, Failure):
 
58
        result = utils.failure_to_error_dict(error)
 
59
    elif isinstance(error, Exception):
 
60
        result = utils.exception_to_error_dict(error)
 
61
    elif isinstance(error, dict):
 
62
        # ensure that both keys and values are unicodes
 
63
        result = dict(map(make_unicode, i) for i in error.iteritems())
 
64
    else:
 
65
        msg = 'Got unexpected error argument %r' % error
 
66
        result = {utils.ERROR_TYPE: 'UnknownError', utils.ERROR_MESSAGE: msg}
 
67
 
 
68
    return result
 
69
 
 
70
 
 
71
def transform_failure(f):
 
72
    """Decorator to apply to DBus error signals.
 
73
 
 
74
    With this call, a Failure is transformed into a string-string dict.
 
75
 
 
76
    """
 
77
    def inner(error, *a):
 
78
        """Do the Failure transformation."""
 
79
        error_dict = error_handler(error)
 
80
        return f(error_dict)
 
81
 
 
82
    return inner
 
83
 
 
84
 
 
85
class ControlPanelBackend(dbus.service.Object):
 
86
    """Export the Control Panel backend thru DBus."""
 
87
 
 
88
    def __init__(self, backend, *args, **kwargs):
 
89
        """Create this instance of the backend."""
 
90
        super(ControlPanelBackend, self).__init__(*args, **kwargs)
 
91
        self.backend = backend
 
92
        logger.debug('ControlPanelBackend created with %r, %r', args, kwargs)
 
93
 
 
94
    # pylint: disable=C0103
 
95
 
 
96
    @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="")
 
97
    def account_info(self):
 
98
        """Find out the account info for the current logged in user."""
 
99
        d = self.backend.account_info()
 
100
        d.addCallback(self.AccountInfoReady)
 
101
        d.addErrback(transform_failure(self.AccountInfoError))
 
102
 
 
103
    @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}")
 
104
    def AccountInfoReady(self, info):
 
105
        """The info for the current user is available right now."""
 
106
 
 
107
    @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}")
 
108
    def AccountInfoError(self, error):
 
109
        """The info for the current user is currently unavailable."""
 
110
 
 
111
    #---
 
112
 
 
113
    @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="")
 
114
    def devices_info(self):
 
115
        """Find out the devices info for the logged in user."""
 
116
        d = self.backend.devices_info()
 
117
        d.addCallback(self.DevicesInfoReady)
 
118
        d.addErrback(transform_failure(self.DevicesInfoError))
 
119
 
 
120
    @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="aa{ss}")
 
121
    def DevicesInfoReady(self, info):
 
122
        """The info for the devices is available right now."""
 
123
 
 
124
    @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}")
 
125
    def DevicesInfoError(self, error):
 
126
        """The info for the devices is currently unavailable."""
 
127
 
 
128
    #---
 
129
 
 
130
    @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="sa{ss}")
 
131
    def change_device_settings(self, token, settings):
 
132
        """Configure a given device."""
 
133
        d = self.backend.change_device_settings(token, settings)
 
134
        d.addCallback(self.DeviceSettingsChanged)
 
135
        d.addErrback(transform_failure(self.DeviceSettingsChangeError))
 
136
 
 
137
    @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s")
 
138
    def DeviceSettingsChanged(self, token):
 
139
        """The settings for the device were changed."""
 
140
 
 
141
    @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}")
 
142
    def DeviceSettingsChangeError(self, error):
 
143
        """Problem changing settings for the device."""
 
144
 
 
145
    #---
 
146
 
 
147
    @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="s")
 
148
    def remove_device(self, token):
 
149
        """Remove a given device."""
 
150
        d = self.backend.remove_device(token)
 
151
        d.addCallback(self.DeviceRemoved)
 
152
        d.addErrback(transform_failure(self.DeviceRemovalError))
 
153
 
 
154
    @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s")
 
155
    def DeviceRemoved(self, token):
 
156
        """The removal for the device was completed."""
 
157
 
 
158
    @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}")
 
159
    def DeviceRemovalError(self, error):
 
160
        """Problem removing the device."""
 
161
 
 
162
    #---
 
163
 
 
164
    @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="")
 
165
    def file_sync_status(self):
 
166
        """Get the status of the file sync service."""
 
167
        d = self.backend.file_sync_status()
 
168
        d.addCallback(lambda args: self.FileSyncStatusReady(*args))
 
169
        d.addErrback(transform_failure(self.FileSyncStatusError))
 
170
 
 
171
    @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="bs")
 
172
    def FileSyncStatusReady(self, enabled, status):
 
173
        """The new file sync status is available."""
 
174
 
 
175
    @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}")
 
176
    def FileSyncStatusError(self, error):
 
177
        """Problem getting the file sync status."""
 
178
 
 
179
    #---
 
180
 
 
181
    @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="")
 
182
    def volumes_info(self):
 
183
        """Find out the volumes info for the logged in user."""
 
184
        d = self.backend.volumes_info()
 
185
        d.addCallback(self.VolumesInfoReady)
 
186
        d.addErrback(transform_failure(self.VolumesInfoError))
 
187
 
 
188
    @utils.log_call(logger.info)
 
189
    @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="aa{ss}")
 
190
    def VolumesInfoReady(self, info):
 
191
        """The info for the volumes is available right now."""
 
192
 
 
193
    @utils.log_call(logger.error)
 
194
    @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}")
 
195
    def VolumesInfoError(self, error):
 
196
        """The info for the volumes is currently unavailable."""
 
197
 
 
198
    #---
 
199
 
 
200
    @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="sa{ss}")
 
201
    def change_volume_settings(self, volume_id, settings):
 
202
        """Configure a given volume."""
 
203
        d = self.backend.change_volume_settings(volume_id, settings)
 
204
        d.addCallback(self.VolumeSettingsChanged)
 
205
        d.addErrback(transform_failure(self.VolumeSettingsChangeError))
 
206
 
 
207
    @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s")
 
208
    def VolumeSettingsChanged(self, token):
 
209
        """The settings for the volume were changed."""
 
210
 
 
211
    @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}")
 
212
    def VolumeSettingsChangeError(self, error):
 
213
        """Problem changing settings for the volume."""
 
214
 
 
215
    #---
 
216
 
 
217
    @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="")
 
218
    def query_bookmark_extension(self):
 
219
        """Check if the extension to sync bookmarks is installed."""
 
220
        d = self.backend.query_bookmark_extension()
 
221
        d.addCallback(self.QueryBookmarksResult)
 
222
        d.addErrback(transform_failure(self.QueryBookmarksError))
 
223
 
 
224
    @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="b")
 
225
    def QueryBookmarksResult(self, enabled):
 
226
        """The bookmark extension is or is not installed."""
 
227
 
 
228
    @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}")
 
229
    def QueryBookmarksError(self, error):
 
230
        """Problem getting the status of the extension."""
 
231
 
 
232
    #---
 
233
 
 
234
    @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="")
 
235
    def install_bookmarks_extension(self):
 
236
        """Install the extension to sync bookmarks."""
 
237
        d = self.backend.install_bookmarks_extension()
 
238
        d.addCallback(lambda _: self.InstallBookmarksSuccess())
 
239
        d.addErrback(transform_failure(self.InstallBookmarksError))
 
240
 
 
241
    @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="")
 
242
    def InstallBookmarksSuccess(self):
 
243
        """The extension to sync bookmarks has been installed."""
 
244
 
 
245
    @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}")
 
246
    def InstallBookmarksError(self, error):
 
247
        """Problem installing the extension to sync bookmarks."""
 
248
 
 
249
 
 
250
def init_mainloop():
 
251
    """Start the DBus mainloop."""
 
252
    DBusGMainLoop(set_as_default=True)
 
253
 
 
254
 
 
255
def run_mainloop():
 
256
    """Run the gobject main loop."""
 
257
    loop = gobject.MainLoop()
 
258
    loop.run()
 
259
 
 
260
 
 
261
def register_service():
 
262
    """Try to register DBus service for making sure we run only one instance.
 
263
 
 
264
    Return True if succesfully registered, False if already running.
 
265
    """
 
266
    session_bus = dbus.SessionBus()
 
267
    name = session_bus.request_name(DBUS_BUS_NAME,
 
268
                                    dbus.bus.NAME_FLAG_DO_NOT_QUEUE)
 
269
    return name != dbus.bus.REQUEST_NAME_REPLY_EXISTS
 
270
 
 
271
 
 
272
def get_busname():
 
273
    """Build the DBus BusName."""
 
274
    return dbus.service.BusName(DBUS_BUS_NAME, bus=dbus.SessionBus())
 
275
 
 
276
 
 
277
def publish_backend(backend=ControlBackend()):
 
278
    """Publish the backend on the DBus."""
 
279
    return ControlPanelBackend(backend=backend,
 
280
                               object_path=DBUS_PREFERENCES_PATH,
 
281
                               bus_name=get_busname())
 
282
 
 
283
 
 
284
def main():
 
285
    """Hook the DBus listeners and start the main loop."""
 
286
    init_mainloop()
 
287
    if register_service():
 
288
        publish_backend()
 
289
        run_mainloop()
 
290
    else:
 
291
        print "Control panel backend already running."