This file is indexed.

/usr/lib/python3/dist-packages/MythbuntuControlCentre/backend.py is in mythbuntu-control-centre 0.64.1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# «backend» - Backend Manager.  Handles install actions that would require root
#
# Copyright (C) 2009, Mario Limonciello
#           (C) 2008 Canonical Ltd.
#
# Author:
#  - Mario Limonciello <superm1@ubuntu.com>
#
# This 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; either version 2 of the License, or at your option)
# any later version.
#
# 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 application; if not, write to the Free Software Foundation, Inc., 51
# Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
##################################################################################

import logging, os, os.path, signal, sys

from gi.repository import GObject
import dbus
import dbus.service
import dbus.mainloop.glib

import getopt
import tempfile
import subprocess
import shutil

from MythbuntuControlCentre.plugin import MCCPlugin

DBUS_BUS_NAME = 'com.mythbuntu.ControlCentre'

#Translation Support
from gettext import gettext as _

#--------------------------------------------------------------------#

class UnknownHandlerException(dbus.DBusException):
    _dbus_error_name = 'com.mythbuntu.ControlCentre.UnknownHandlerException'

class PermissionDeniedByPolicy(dbus.DBusException):
    _dbus_error_name = 'com.mythbuntu.ControlCentre.PermissionDeniedByPolicy'

class BackendCrashError(SystemError):
    pass

#--------------------------------------------------------------------#

def dbus_sync_call_signal_wrapper(dbus_iface, fn, handler_map, *args, **kwargs):
    '''Run a D-BUS method call while receiving signals.

    This function is an Ugly Hack™, since a normal synchronous dbus_iface.fn()
    call does not cause signals to be received until the method returns. Thus
    it calls fn asynchronously and sets up a temporary main loop to receive
    signals and call their handlers; these are assigned in handler_map (signal
    name → signal handler).
    '''
    if not hasattr(dbus_iface, 'connect_to_signal'):
        # not a D-BUS object
        return getattr(dbus_iface, fn)(*args, **kwargs)

    def _h_reply(result=None):
        global _h_reply_result
        _h_reply_result = result
        loop.quit()

    def _h_error(exception=None):
        global _h_exception_exc
        _h_exception_exc = exception
        loop.quit()

    loop = GObject.MainLoop()
    global _h_reply_result, _h_exception_exc
    _h_reply_result = None
    _h_exception_exc = None
    kwargs['reply_handler'] = _h_reply
    kwargs['error_handler'] = _h_error
    kwargs['timeout'] = 86400
    for signame, sighandler in list(handler_map.items()):
        dbus_iface.connect_to_signal(signame, sighandler)
    dbus_iface.get_dbus_method(fn)(*args, **kwargs)
    loop.run()
    if _h_exception_exc:
        raise _h_exception_exc
    return _h_reply_result

#--------------------------------------------------------------------#

class Backend(dbus.service.Object):
    '''Backend manager.

    This encapsulates all services calls of the backend. It
    is implemented as a dbus.service.Object, so that it can be called through
    D-BUS as well (on the /ControlCentre object path).
    '''
    DBUS_INTERFACE_NAME = 'com.mythbuntu.ControlCentre'

    #
    # D-BUS control API
    #

    def __init__(self):
        # cached D-BUS interfaces for _check_polkit_privilege()
        self.dbus_info = None
        self.polkit = None
        self.enforce_polkit = True

        #TODO:
        # debug support

    def run_dbus_service(self, timeout=None, send_usr1=False):
        '''Run D-BUS server.

        If no timeout is given, the server will run forever, otherwise it will
        return after the specified number of seconds.

        If send_usr1 is True, this will send a SIGUSR1 to the parent process
        once the server is ready to take requests.
        '''
        dbus.service.Object.__init__(self, self.bus, '/ControlCentre')
        main_loop = GObject.MainLoop()
        self._timeout = False
        if timeout:
            def _t():
                main_loop.quit()
                return True
            GObject.timeout_add(timeout * 1000, _t)

        # send parent process a signal that we are ready now
        if send_usr1:
            os.kill(os.getppid(), signal.SIGUSR1)

        # run until we time out
        while not self._timeout:
            if timeout:
                self._timeout = True
            main_loop.run()

    @classmethod
    def create_dbus_server(klass):
        '''Return a D-BUS server backend instance.
        '''
        import dbus.mainloop.glib

        backend = Backend()
        dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
        backend.bus = dbus.SystemBus()
        backend.dbus_name = dbus.service.BusName(DBUS_BUS_NAME, backend.bus)
        return backend

    #
    # Internal methods
    #

    def _reset_timeout(self):
        '''Reset the D-BUS server timeout.'''

        self._timeout = False

    def _check_polkit_privilege(self, sender, conn, privilege):
        '''Verify that sender has a given PolicyKit privilege.

        sender is the sender's (private) D-BUS name, such as ":1:42"
        (sender_keyword in @dbus.service.methods). conn is
        the dbus.Connection object (connection_keyword in
        @dbus.service.methods). privilege is the PolicyKit privilege string.

        This method returns if the caller is privileged, and otherwise throws a
        PermissionDeniedByPolicy exception.
        '''
        if sender is None and conn is None:
            # called locally, not through D-BUS
            return
        if not self.enforce_polkit:
            #yeah, i guess that sounds sensible to do..
            return

        # get peer PID
        if self.dbus_info is None:
            self.dbus_info = dbus.Interface(conn.get_object('org.freedesktop.DBus',
                '/org/freedesktop/DBus/Bus', False), 'org.freedesktop.DBus')
        pid = self.dbus_info.GetConnectionUnixProcessID(sender)

        # query PolicyKit
        if self.polkit is None:
            self.polkit = dbus.Interface(dbus.SystemBus().get_object(
                'org.freedesktop.PolicyKit1',
                '/org/freedesktop/PolicyKit1/Authority', False),
                'org.freedesktop.PolicyKit1.Authority')
        try:
            # we don't need is_challenge return here, since we call with AllowUserInteraction
            (is_auth, _, details) = self.polkit.CheckAuthorization(
                    ('unix-process', {'pid': dbus.UInt32(pid, variant_level=1),
                     'start-time': dbus.UInt64(0, variant_level=1)}), 
                     privilege, {'': ''}, dbus.UInt32(1), '', timeout=600)
        except dbus.DBusException as e:
            if e._dbus_error_name == 'org.freedesktop.DBus.Error.ServiceUnknown':
                # polkitd timed out, connect again
                self.polkit = None
                return self._check_polkit_privilege(sender, conn, privilege)
            else:
                raise
        if not is_auth:
            logging.debug('_check_polkit_privilege: sender %s on connection %s pid %i is not authorized for %s: %s' %
                    (sender, conn, pid, privilege, str(details)))
            raise PermissionDeniedByPolicy(privilege)

    #
    # Internal API for calling from Handlers (not exported through D-BUS)
    #

        # none for now

    #
    # Client API (through D-BUS)
    #

    @dbus.service.method(DBUS_INTERFACE_NAME,
        in_signature='a{sa{sv}}s', out_signature='b', sender_keyword='sender',
        connection_keyword='conn')
    def scriptedchanges(self, plugin_dictionary, plugin_root_path, sender=None, conn=None):
        '''Processes changes that can't be represented by debian packages
           easily.  This function is sent a dictionary with key values of
           each plugin that has things to be processed.

           The matching data to each key plugin is a dictionary of
           {"item":"value"} of things to change within that particular
           key plugin.
        '''

        self._reset_timeout()
        self._check_polkit_privilege(sender, conn, 'com.mythbuntu.controlcentre.scriptedchanges')

        plugin_path = plugin_root_path + '/python'
        plugin_instances = {}

        logging.debug("scriptedchanges: using plugin_path of: %s" % plugin_path)
        if plugin_path not in sys.path:
            sys.path.insert(0, plugin_path)

        self.report_progress(_('Importing necessary plugins'),'0.0')
        for item in plugin_dictionary:
            #load plugin
            logging.debug("scriptedchanges: attempting to import plugin: %s" % item)
            try:
                __import__(item, None, None, [''])
            except:
                logging.warning("scriptedchanges: error importing plugin: %s " % item)
                del plugin_dictionary[item]
                continue

        self.report_progress(_('Instantiating necessary plugins'),'0.0')
        for item in MCCPlugin.__subclasses__():
            #instantiate
            logging.debug("scriptedchanges: attempting to instantiate plugin: %s" % item)
            try:
                plugin_instances[item] = item()
                plugin_instances[item].emit_progress=self.report_progress
            except:
                logging.warning("scriptedchanges: error instantiating plugin %s " % item)

        self.report_progress(_('Processing plugins'),'0.0')
        #process each plugin individually
        count=float(0)
        for plugin in plugin_dictionary:
            for instance in plugin_instances:
                if plugin_instances[instance].__class__.__module__ == plugin:
                    self.report_progress("Processing %s" % plugin, count/len(plugin_dictionary))
                    logging.debug("scriptedchanges: processing %s plugin " % plugin)
                    plugin_instances[instance].root_scripted_changes(plugin_dictionary[plugin])
                    count += 1
                    break

    @dbus.service.signal(DBUS_INTERFACE_NAME)
    def report_error(self, error_str, secondary=None):
        '''Reports an error to the UI'''
        return True

    @dbus.service.signal(DBUS_INTERFACE_NAME)
    def report_progress(self, progress, percent):
        '''Report package or script progress'''
        #if we are reporting progress, we shouldn't
        #ever let the dbus backend timeout
        self._reset_timeout()
        return True