File: core.py

package info (click to toggle)
software-center 5.1.2debian3.1
  • links: PTS
  • area: main
  • in suites: wheezy
  • size: 8,708 kB
  • sloc: python: 28,999; xml: 379; sh: 127; makefile: 28
file content (196 lines) | stat: -rw-r--r-- 8,216 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# -*- coding: utf-8 -*-
# Copyright (C) 2011 Canonical
#
# Authors:
#  Didier Roche
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; version 3.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA


from oneconf.dbusconnect import DbusConnect
from oneconf.enums import MIN_TIME_WITHOUT_ACTIVITY

from softwarecenter.backend.login_sso import get_sso_backend
from softwarecenter.backend.restfulclient import get_ubuntu_sso_backend
from softwarecenter.utils import clear_token_from_ubuntu_sso

import datetime
from gi.repository import GObject
import logging

from gettext import gettext as _

LOG = logging.getLogger(__name__)

class OneConfHandler(GObject.GObject):

    __gsignals__ = {
        "show-oneconf-changed" : (GObject.SIGNAL_RUN_LAST,
                                  GObject.TYPE_NONE,
                                  (GObject.TYPE_PYOBJECT,),
                                 ),
        "last-time-sync-changed" : (GObject.SIGNAL_RUN_LAST,
                                    GObject.TYPE_NONE,
                                    (GObject.TYPE_PYOBJECT,),
                                   ),
        }
        
        
    def __init__(self, oneconfviewpickler):
        '''Controller of the installed pane'''
    
        LOG.debug("OneConf Handler init")
        super(OneConfHandler, self).__init__()
        
        # FIXME: should be an enum common to OneConf and here
        self.appname = "Ubuntu Software Center"
        
        # OneConf stuff
        self.oneconf = DbusConnect()
        self.oneconf.hosts_dbus_object.connect_to_signal('hostlist_changed',
                                                         self.refresh_hosts)
        self.oneconf.hosts_dbus_object.connect_to_signal('packagelist_changed',
                                                         self._on_store_packagelist_changed)
        self.oneconf.hosts_dbus_object.connect_to_signal('latestsync_changed',
                                                          self.on_new_latest_oneconf_sync_timestamp)
        self.already_registered_hostids = []
        self.is_current_registered = False
        
        self.oneconfviewpickler = oneconfviewpickler
        
        # refresh host list
        self._refreshing_hosts = False
        GObject.timeout_add_seconds(MIN_TIME_WITHOUT_ACTIVITY, self.get_latest_oneconf_sync)
        GObject.idle_add(self.refresh_hosts)

    def refresh_hosts(self):
        """refresh hosts list in the panel view"""
        LOG.debug('oneconf: refresh hosts')

        # this function can be called in different threads
        if self._refreshing_hosts:
            return
        self._refreshing_hosts = True

        #view_switcher = self.app.view_switcher
        #model = view_switcher.get_model()
        #previous_iter = model.installed_iter

        all_hosts = self.oneconf.get_all_hosts()
        for hostid in all_hosts:
            current, hostname, share_inventory = all_hosts[hostid]
            if not hostid in self.already_registered_hostids and not current:
                self.oneconfviewpickler.register_computer(hostid, hostname)
                self.already_registered_hostids.append(hostid)
            if current:
                is_current_registered = share_inventory
        
        # ensure we are logged to ubuntu sso to activate the view
        if self.is_current_registered != is_current_registered:
            self.sync_between_computers(is_current_registered)
        
        self._refreshing_hosts = False

    def get_latest_oneconf_sync(self):
        '''Get latest sync state in OneConf.
        
        This function is also the "ping" letting OneConf service alive'''
        LOG.debug("get latest sync state")
        timestamp = self.oneconf.get_last_sync_date()
        self.on_new_latest_oneconf_sync_timestamp(timestamp)
        return True
        
    def on_new_latest_oneconf_sync_timestamp(self, timestamp):
        '''Callback computing the right message for latest sync time'''
        try:
            last_sync = datetime.datetime.fromtimestamp(float(timestamp))
            today = datetime.datetime.strptime(str(datetime.date.today()), '%Y-%m-%d')
            the_daybefore = today - datetime.timedelta(days=1)

            if last_sync > today:
                msg = _("Last sync %s") % last_sync.strftime('%H:%M')
            elif last_sync < today and last_sync > the_daybefore:
                msg = _("Last sync yesterday %s") % last_sync.strftime('%H:%M')
            else:
                msg = _("Last sync %s") % last_sync.strftime('%Y-%m-%d  %H:%M')                    
        except (TypeError, ValueError):
            msg = _("To sync with another computer, choose “Sync Between Computers” from that computer.")
        self.emit("last-time-sync-changed", msg)

    def _share_inventory(self, share_inventory):
        '''set oneconf state and emit signal for installed view to show or not oneconf'''

        if share_inventory == self.is_current_registered:
            return
        self.is_current_registered = share_inventory
        LOG.debug("change share inventory state to %s", share_inventory)        
        self.oneconf.set_share_inventory(share_inventory)
        self.get_latest_oneconf_sync()
        self.emit("show-oneconf-changed", share_inventory)

    def sync_between_computers(self, sync_on):
        '''toggle the sync on and off if needed between computers'''
        LOG.debug("Toggle sync between computers: %s", sync_on)
        
        if sync_on:
            self._try_login()
        else:
            self._share_inventory(False)
            
    def _on_store_packagelist_changed(self, hostid):
        '''pass the message to the view controller'''
        self.oneconfviewpickler.store_packagelist_changed(hostid)
    

    # SSO login part    
        
    def _try_login(self):
        '''Try to get the credential or login on ubuntu sso'''
        logging.debug("OneConf login()")
        help_text = _("With multiple Ubuntu computers, you can publish their inventories online to compare the software installed on each\n"
                      "No-one else will be able to see what you have installed.")
        self.sso = get_sso_backend(0,
                                   self.appname, help_text)
        self.sso.connect("login-successful", self._maybe_login_successful)
        self.sso.connect("login-canceled", self._login_canceled)
        self.sso.login_or_register()
        
    def _login_canceled(self, sso):
        self._share_inventory(False)
        
    def _maybe_login_successful(self, sso, oauth_result):
        """ called after we have the token, then we go and figure out our name """
        logging.debug("_maybe_login_successful")
        token = oauth_result
        self.ssoapi = get_ubuntu_sso_backend(token)
        self.ssoapi.connect("whoami", self._whoami_done)
        self.ssoapi.connect("error", self._whoami_error)
        self.ssoapi.whoami()

    def _whoami_done(self, ssologin, result):
        logging.debug("_whoami_done")
        self._share_inventory(True)

    def _whoami_error(self, ssologin, e):
        logging.error("whoami error '%s'" % e)
        # HACK: clear the token from the keyring assuming that it expired
        #       or got deauthorized by the user on the website
        # this really should be done by ubuntu-sso-client itself
        import lazr.restfulclient.errors
        errortype = lazr.restfulclient.errors.HTTPError
        if (type(e) == errortype):
            LOG.warn("authentication error, resetting token and retrying")
            clear_token_from_ubuntu_sso(self.appname)
            self._share_inventory(False)
            return