Exemplo n.º 1
0
    def run(self, cmd, realTime=True, returnAsList=True, outputTreeView=None):
        tvHandler = None
        if outputTreeView is not None:
            tvHandler = TreeViewHandler(outputTreeView)
            tvHandler.fillTreeview([], columnTypesList=['str'])
        msg = "Command to execute: %(cmd)s" % { "cmd": cmd }
        if self.log:
            self.log.write(msg, 'execcmd.run', 'debug')
        else:
            print(msg)

        p = subprocess.Popen([cmd], shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        lstOut = []
        while True:
            line = p.stdout.readline()
            if not line:
                break
            # Strip the line, also from null spaces (strip() only strips white spaces)
            line = line.decode('utf-8').rstrip().rstrip("\0")
            lstOut.append(line)
            if realTime:
                if tvHandler is not None:
                    tvHandler.fillTreeview([line], columnTypesList=['str'], appendToExisting=True, setCursor=tvHandler.getRowCount())
                if self.log:
                    self.log.write(line, 'execcmd.run', 'info')
                else:
                    print(line)
            sys.stdout.flush()
            while Gtk.events_pending():
                Gtk.main_iteration()

        ret = lstOut
        if not returnAsList:
            ret = "\n".join(lstOut).strip()
        return ret
Exemplo n.º 2
0
 def rtobjectWrite(self, message):
     if self.rtobject is not None and self.typeString != '':
         if self.typeString == 'gtk.Label':
             self.rtobject.set_text(message)
         elif self.typeString == 'gtk.TreeView':
             tvHandler = TreeViewHandler(None, self.rtobject)
             tvHandler.fillTreeview([message], ['str'], [-1], 0, 400, False, True, True, fontSize=10000)
         elif self.typeString == 'gtk.Statusbar':
             functions.pushMessage(self.rtobject, message)
         else:
             # For obvious reasons: do not log this...
             print 'Return object type not implemented: %s' % self.typeString
Exemplo n.º 3
0
 def rtobjectWrite(self, message):
     if self.rtobject is not None and self.typeString != '':
         if 'label' in self.typeString.lower():
             self.rtobject.set_text(message)
         elif 'treeview' in self.typeString.lower():
             tvHandler = TreeViewHandler(self.rtobject)
             tvHandler.fillTreeview([message], ['str'], [-1], 0, 400, False, True, True, fontSize=10000)
         elif 'statusbar' in self.typeString.lower():
             self.pushMessage(message)
         else:
             # For obvious reasons: do not log this...
             print(('Return object type not implemented: %s' % self.typeString))
Exemplo n.º 4
0
    def run(self, cmd, realTime=True, returnAsList=True, outputTreeView=None):
        tvHandler = None
        if outputTreeView is not None:
            tvHandler = TreeViewHandler(outputTreeView)
            tvHandler.fillTreeview([], columnTypesList=['str'])
        msg = "Command to execute: %(cmd)s" % {"cmd": cmd}
        if self.log:
            self.log.write(msg, 'execcmd.run', 'debug')
        else:
            print(msg)

        p = subprocess.Popen([cmd],
                             shell=True,
                             stdin=subprocess.PIPE,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.STDOUT)
        lstOut = []
        while True:
            line = p.stdout.readline()
            if not line:
                break
            # Strip the line, also from null spaces (strip() only strips white spaces)
            line = line.decode('utf-8').rstrip().rstrip("\0")
            lstOut.append(line)
            if realTime:
                if tvHandler is not None:
                    tvHandler.fillTreeview([line],
                                           columnTypesList=['str'],
                                           appendToExisting=True,
                                           setCursor=tvHandler.getRowCount())
                if self.log:
                    self.log.write(line, 'execcmd.run', 'info')
                else:
                    print(line)
            try:
                sys.stdout.flush()
            except:
                pass
            while Gtk.events_pending():
                Gtk.main_iteration()

        ret = lstOut
        if not returnAsList:
            ret = "\n".join(lstOut).strip()
        return ret
Exemplo n.º 5
0
 def rtobjectWrite(self, message):
     if self.rtobject is not None and self.typeString != '':
         if 'label' in self.typeString.lower():
             self.rtobject.set_text(message)
         elif 'treeview' in self.typeString.lower():
             tvHandler = TreeViewHandler(self.rtobject)
             tvHandler.fillTreeview([message], ['str'], [-1],
                                    0,
                                    400,
                                    False,
                                    True,
                                    True,
                                    fontSize=10000)
         elif 'statusbar' in self.typeString.lower():
             functions.pushMessage(self.rtobject, message)
         else:
             # For obvious reasons: do not log this...
             print('Return object type not implemented: %s' %
                   self.typeString)
Exemplo n.º 6
0
class SambaShare(object):
    def __init__(self):
        self.scriptDir = abspath(dirname(__file__))

        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(
            join(self.scriptDir, '../../share/sambashare/sambashare.glade'))

        go = self.builder.get_object
        self.window = go('sambashareWindow')
        self.windowAdd = go('sambashareWindowAdd')
        self.lblTitle = go('lblTitle')
        self.tvShares = go('tvShares')
        self.btnAdd = go('btnAdd')
        self.btnRemove = go('btnRemove')
        self.txtShareDetails = go('txtShareDetails')
        self.lblName = go('lblName')
        self.lblPath = go('lblPath')
        self.lblComment = go('lblComment')
        self.lblPublic = go('lblPublic')
        self.lblReadOnly = go('lblReadOnly')
        self.txtName = go('txtName')
        self.txtPath = go('txtPath')
        self.txtComment = go('txtComment')
        self.chkPublic = go('chkPublic')
        self.chkReadOnly = go('chkReadOnly')
        self.btnOk = go('btnOk')
        self.btnCancel = go('btnCancel')

        # Translations
        self.window.set_title(_("Samba share"))
        self.windowAdd.set_title(_("Create samba share"))
        self.lblTitle.set_text(self.window.get_title())
        self.lblName.set_text(_("Name"))
        self.lblPath.set_text(_("Path"))
        self.lblComment.set_text(_("Comment"))
        self.lblReadOnly.set_text(_("Read only"))
        self.lblPublic.set_text(_("Public"))

        # Init
        self.ec = ExecCmd()
        self.us = UserShare()
        self.shareName = None
        self.sharePath = None
        self.startAddNow = False

        # Fill treeview with shares
        self.tvHandler = TreeViewHandler(self.tvShares)
        self.refreshShares()

        # Command arguments
        args = sys.argv[1:]
        for arg in args:
            if "/" in arg:
                self.sharePath = arg
                self.startAddNow = True
            else:
                self.shareName = arg

        # Connect the signals and show the window
        self.builder.connect_signals(self)
        self.window.show_all()
        if self.startAddNow:
            self.on_btnAdd_clicked(None)

    # ===============================================
    # Menu section functions
    # ===============================================

    def on_tvShares_cursor_changed(self, widget, event=None):
        # Show share details
        self.setDetailText()

    def on_btnAdd_clicked(self, widget):
        # Show add share window
        if self.sharePath is None:
            self.sharePath = os.getcwd()
            self.shareName = basename(self.sharePath)
        if self.sharePath is None:
            self.sharePath = ""
        if self.shareName is None:
            self.shareName = ""
        self.txtName.set_text(self.shareName)
        self.txtPath.set_text(self.sharePath)
        self.windowAdd.show_all()

    def on_btnBrowse_clicked(self, widget):
        directory = SelectDirectoryDialog(_('Select directory to share'),
                                          self.txtPath.get_text(),
                                          self.window).show()
        if directory is not None:
            self.sharePath = directory
            self.txtPath.set_text(self.sharePath)

    def on_btnRemove_clicked(self, widget):
        # TODO: remove selected share
        title = _("Remove share")
        qd = QuestionDialog(
            title,
            _("Are you sure you want to remove the following share:\n\n'%(share)s'"
              ) % {"share": self.shareName}, self.window)
        answer = qd.show()
        if answer:
            ret = self.us.removeShare(self.shareName)
            self.showUserFeedback(ret, title, "remove", self.window)
            self.refreshShares()

    def on_btnOk_clicked(self, widget):
        # Create network share, and close add share window
        title = _("Create share")
        self.shareName = self.txtName.get_text()
        comment = self.txtComment.get_text()
        public = self.chkPublic.get_active()
        readonly = self.chkReadOnly.get_active()
        ret = self.us.createShare(self.sharePath, self.shareName, comment,
                                  public, readonly)
        closeWin = self.showUserFeedback(ret, title, "create", self.windowAdd)
        if closeWin:
            self.windowAdd.hide()
            self.refreshShares()

    def on_txtPath_changed(self, widget):
        self.sharePath = self.txtPath.get_text()
        name = basename(self.sharePath)
        if name == "":
            name = basename(dirname(self.sharePath))
        self.txtName.set_text(name)

    def on_txtName_changed(self, widget):
        name = self.txtName.get_text()
        if name in self.us.systemNames:
            name += "_"
            self.txtName.set_text(name)
        self.shareName = name

    def showUserFeedback(self, returnList, title, action, parent):
        msg = ""
        closeWin = False
        for line in returnList:
            msg += "%s\n" % line
        if msg != "":
            MessageDialogSafe(title, msg, Gtk.MessageType.ERROR, parent).show()
        else:
            shareExists = self.us.doesShareExist(self.shareName)
            if action == "removed" and shareExists:
                msg = _("Could not remove share: '%(share)s'") % {
                    "share": self.shareName
                }
            elif action == "created" and not shareExists:
                msg = _("Could not create share: '%(share)s'") % {
                    "share": self.shareName
                }
            else:
                msg = _(
                    "Share successfully %(action)s:\n\n'%(share)s' on %(path)s"
                ) % {
                    "action": action,
                    "share": self.shareName,
                    "path": self.sharePath
                }
                closeWin = True
            MessageDialogSafe(title, msg, Gtk.MessageType.INFO, parent).show()
        return closeWin

    def refreshShares(self):
        self.shareName = None
        self.sharePath = None
        self.shares = self.us.getShares()
        self.shareDetail = self.us.getShares(True, True)
        self.fillTreeView()
        self.setDetailText()
        print("Refresh done")

    def on_btnCancel_clicked(self, widget):
        # Close add share window without saving
        self.windowAdd.hide()

    def on_sambashareWindowAdd_delete_event(self, widget, data=None):
        self.windowAdd.hide()
        return True

    def fillTreeView(self):
        # Very dirty hack: add and remove a dummy row to the treeview when treeview is empty
        # GtkTreeView has a problem refreshing when there's no data present
        if self.tvHandler.getRowCount() == 0:
            self.tvHandler.fillTreeview('-', ['str'])
            self.tvHandler.clearTreeView()
        self.tvHandler.fillTreeview(self.shares, ['str'])

    def setDetailText(self):
        buf = Gtk.TextBuffer()
        txt = ""
        try:
            self.shareName = self.tvHandler.getSelectedValue()
            if self.shareName is not None:
                if self.shareDetail[self.shareName]:
                    txt = "Name:\t\t%s\n" % self.shareName
                    for line in self.shareDetail[self.shareName]:
                        line = line.strip()
                        if line != "" and line != "[%s]" % self.shareName:
                            if line.startswith("path="):
                                self.sharePath = line.replace("path=", "")
                                txt += "Path:\t\t%s\n" % self.sharePath
                            elif line.startswith("comment="):
                                txt += "Comment:\t%s\n" % line.replace(
                                    "comment=", "")
                            elif ':R' in line:
                                txt += "Share:\t\tRead permission"
                            elif ':F' in line:
                                txt += "Share:\t\tRead/write permission"
                            elif line == "guest_ok=y":
                                txt += ", public\n"
                            elif line == "guest_ok=n":
                                txt += ", private\n"
                else:
                    self.sharePath = None
        except:
            # Best effort
            pass

        buf.set_text(txt)
        self.txtShareDetails.set_buffer(buf)

    # Close the gui
    def on_sambashareWindow_destroy(self, widget):
        # Close the app
        Gtk.main_quit()
Exemplo n.º 7
0
class DPM:
    def __init__(self):
        self.scriptDir = dirname(realpath(__file__))
        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(
            join(
                self.scriptDir,
                '../../share/debian-plymouth-manager/debian-plymouth-manager.glade'
            ))

        # Main window objects
        go = self.builder.get_object
        self.window = go('dpmWindow')
        self.tv1 = go('tv1')
        self.tv2 = go('tv2')
        self.sw2 = go('sw2')
        self.statusbar = go('statusbar')
        self.btnPlymouth = go('btnPlymouth')
        self.btnThemes = go('btnThemes')
        self.btnGrub = go('btnGrub')
        self.pbDPM = go('pbDPM')
        self.btn1 = go('btn1')
        self.btn2 = go('btn2')
        self.lblTitle1 = go('lblTitle1')
        self.lblTitle2 = go('lblTitle2')

        # Translations
        title = _("Debian Plymouth Manager")
        self.window.set_title(title)
        self.btnPlymouth.set_label("_" + _("Plymouth"))
        self.btnThemes.set_label("_" + _("Themes"))
        self.btnGrub.set_label("_" + _("Grub"))

        self.selectedMenuItem = None
        self.selectedAvailableTheme = None
        self.selectedRemoveTheme = None
        self.threadPackage = None
        self.queue = Queue()
        self.noPlymouth = _('None: no plymouth splash')

        # Set some variables
        self.logFile = '/var/log/dpm.log'
        self.log = Logger(self.logFile, addLogTime=False, maxSizeKB=1024)
        self.version = utils.getPackageVersion('debian-plymouth-manager')
        self.plymouth = Plymouth(self.log)
        self.grub = Grub(self.log)
        self.resolutions = utils.getResolutions('800x600', '', True, False)
        self.currentResolution = self.plymouth.getCurrentResolution()
        self.selectedResolution = self.currentResolution
        self.currentGrubResolution = self.grub.getCurrentResolution()
        self.selectedGrubResolution = self.currentGrubResolution
        self.currentTheme = self.plymouth.getCurrentTheme()
        self.selectedTheme = self.currentTheme
        self.installedThemes = self.plymouth.getInstalledThemes()
        self.availableThemes = self.plymouth.getAvailableThemes()
        self.tv1Handler = TreeViewHandler(self.tv1, self.log)
        self.tv2Handler = TreeViewHandler(self.tv2, self.log)
        self.force = utils.get_apt_force()

        self.on_btnPlymouth_clicked()

        # Connect builder signals and show window
        self.builder.connect_signals(self)
        self.window.show_all()

        # TODO: Hide the tool bar for now
        go('tlbMain').set_visible(False)

    # ===============================================
    # Menu section functions
    # ===============================================

    def on_btnPlymouth_clicked(self, widget=None, event=None, refresh=False):
        if self.selectedMenuItem != menuItems[0] or refresh:
            self.selectedMenuItem = menuItems[0]

            # Clear treeviews
            self.tv1Handler.clearTreeView()
            self.tv2Handler.clearTreeView()

            # Set object properties
            self.btn1.set_label(_("Set Plymouth Theme"))
            self.btn2.set_label(_("Preview"))
            self.btn2.show()
            self.lblTitle2.set_visible(True)
            self.sw2.show()

            # Show Installed Themes
            self.lblTitle1.set_label(_("Installed Themes"))
            # Clone the installedThemes list
            listInst = list(self.installedThemes)
            listInst.append(self.noPlymouth)
            # Get current theme and set setcursor
            ind = -1
            if self.currentTheme:
                try:
                    ind = listInst.index(self.currentTheme)
                except:
                    # Theme is set but removed from system
                    ind = 0
            else:
                ind = len(listInst) - 1

            if len(listInst) > 0:
                self.tv1Handler.fillTreeview(listInst, ['str'], ind, 700)

            # Show Resolutios
            self.lblTitle2.set_label(_("Resolutions"))
            ind = -1
            if self.currentResolution:
                try:
                    ind = self.resolutions.index(self.currentResolution)
                except:
                    ind = 0

            if len(self.resolutions) > 0:
                self.tv2Handler.fillTreeview(self.resolutions, ['str'], ind,
                                             700)

    def on_btnThemes_clicked(self, widget=None, event=None, refresh=False):
        if self.selectedMenuItem != menuItems[1] or refresh:
            self.selectedMenuItem = menuItems[1]

            # Clear treeviews
            self.tv1Handler.clearTreeView()
            self.tv2Handler.clearTreeView()

            # Set object properties
            self.btn1.set_label(_("Install Theme"))
            self.btn2.set_label(_("Remove Theme"))
            self.btn2.show()
            self.lblTitle2.set_visible(True)
            self.sw2.show()

            # Show Available Themes
            self.lblTitle1.set_label(_("Available Themes"))
            if len(self.availableThemes) > 0:
                self.tv1Handler.fillTreeview(self.availableThemes, ['str'], 0)

            # Show Installed Themes
            self.lblTitle2.set_label(_("Installed Themes"))
            if len(self.installedThemes) > 0:
                self.tv2Handler.fillTreeview(self.installedThemes, ['str'], 0)

    def on_btnGrub_clicked(self, widget=None, event=None, refresh=False):
        if self.selectedMenuItem != menuItems[2] or refresh:
            self.selectedMenuItem = menuItems[2]

            # Clear treeviews
            self.tv1Handler.clearTreeView()
            self.tv2Handler.clearTreeView()

            # Set object properties
            self.btn1.set_label(_("Set Grub Resolution"))
            self.btn2.hide()
            self.lblTitle2.set_visible(False)
            self.sw2.hide()

            # Show Resolutios
            self.lblTitle1.set_text(_("Grub Resolutions"))
            ind = -1
            if self.currentGrubResolution:
                try:
                    ind = self.resolutions.index(self.currentGrubResolution)
                except:
                    ind = 0

            if len(self.resolutions) > 0:
                self.tv1Handler.fillTreeview(self.resolutions, ['str'], ind,
                                             700)

    # ===============================================
    # Treeview functions
    # ===============================================

    def on_tv1_cursor_changed(self, widget):
        if self.selectedMenuItem == menuItems[0]:
            # Themes Menu
            self.selectedTheme = self.tv1Handler.getSelectedValue()
            self.log.write(
                "Themes menu - seleceted theme: %(theme)s" %
                {"theme": self.selectedTheme}, 'dpm.tv1Changed')
        elif self.selectedMenuItem == menuItems[1]:
            # Install Menu
            self.selectedAvailableTheme = self.tv1Handler.getSelectedValue()
            self.log.write(
                "Install menu - seleceted available theme: %(theme)s" %
                {"theme": self.selectedAvailableTheme}, 'dpm.tv1Changed')
        elif self.selectedMenuItem == menuItems[2]:
            # Grub Menu
            self.selectedGrubResolution = self.tv1Handler.getSelectedValue()
            self.log.write(
                "Grub menu - seleceted grub resolution: %(res)s" %
                {"res": self.selectedGrubResolution}, 'dpm.tv1Changed')

    def on_tv2_cursor_changed(self, widget):
        if self.selectedMenuItem == menuItems[0]:
            # Themes Menu
            self.selectedResolution = self.tv2Handler.getSelectedValue()
            self.log.write(
                "Themes menu - seleceted resolution: %(res)s" %
                {"res": self.selectedResolution}, 'dpm.tv2Changed')
        elif self.selectedMenuItem == menuItems[1]:
            # Install Menu
            self.selectedRemoveTheme = self.tv2Handler.getSelectedValue()
            self.log.write(
                "Install menu - seleceted theme to remove: %(theme)s" %
                {"theme": self.selectedRemoveTheme}, 'dpm.tv2Changed')

    # ===============================================
    # Button functions
    # ===============================================

    def on_btn1_clicked(self, widget):
        if self.selectedMenuItem == menuItems[0]:
            # Themes
            self.setTheme()
        elif self.selectedMenuItem == menuItems[1]:
            # Install
            self.installTheme()
        elif self.selectedMenuItem == menuItems[2]:
            # Grub
            self.setGrubResolution()

    def on_btn2_clicked(self, widget):
        if self.selectedMenuItem == menuItems[0]:
            # Themes
            self.preview()
        elif self.selectedMenuItem == menuItems[1]:
            # Install
            self.removeTheme()
        elif self.selectedMenuItem == menuItems[2]:
            # Grub
            pass

    # ===============================================
    # Themes section functions
    # ===============================================

    def preview(self):
        # Check if the selected have been saved
        if self.currentTheme == self.selectedTheme and self.currentResolution == self.selectedResolution:
            self.plymouth.previewPlymouth()
        else:
            title = _("Preview")
            msg = _(
                "You must save before you can preview:\n\nTheme: %(theme)s\nResolution: %(res)s"
            ) % {
                "theme": self.selectedTheme,
                "res": self.selectedResolution
            }
            MessageDialogSafe(title, msg, Gtk.MessageType.INFO,
                              self.window).show()

    def setTheme(self):
        self.toggleGuiElements(True)
        if self.selectedTheme != self.noPlymouth:
            if self.selectedResolution is None:
                self.selectedResolution = self.tv2Handler.getValue(
                    self.tv2Handler.getRowCount() - 1)
        else:
            self.selectedTheme = None
            self.selectedResolution = None
        self.log.write(
            _("Save setting: %(theme)s (%(res)s)") % {
                "theme": self.selectedTheme,
                "res": self.selectedResolution
            }, 'dpm.setTheme', 'info')
        # Start saving in a separate thread
        t = PlymouthSave(self.log, self.selectedTheme, self.selectedResolution)
        t.start()
        GObject.timeout_add(250, self.checkSaveThread)

    def checkSaveThread(self):
        #print 'Thread count = ' + str(threading.active_count())
        # As long there's a thread active, keep spinning
        if threading.active_count() > 1:
            self.pbDPM.pulse()
            return True

        # Get the new data
        self.pbDPM.set_fraction(0)
        self.currentTheme = self.plymouth.getCurrentTheme()
        self.currentResolution = self.plymouth.getCurrentResolution()
        self.installedThemes = self.plymouth.getInstalledThemes()
        self.availableThemes = self.plymouth.getAvailableThemes()
        if self.selectedMenuItem == menuItems[0]:
            self.on_btnPlymouth_clicked(None, None, True)

        # Thread is done: make button sensitive again
        self.toggleGuiElements(False)
        self.log.write(
            _("Done saving settings: %(theme)s (%(res)s)") % {
                "theme": self.currentTheme,
                "res": self.currentResolution
            }, 'dpm.checkSaveThread', 'info')

        title = _("Save settings")
        msg = _("Theme: %(theme)s\nResolution: %(res)s\n\nDone") % {
            "theme": self.currentTheme,
            "res": str(self.currentResolution)
        }
        self.log.write(msg, 'dpm.checkSaveThread')
        MessageDialogSafe(title, msg, Gtk.MessageType.INFO, self.window).show()
        return False

    def toggleGuiElements(self, startSave):
        if startSave:
            self.btn1.set_sensitive(False)
            self.btn2.set_sensitive(False)
        else:
            self.btn1.set_sensitive(True)
            self.btn2.set_sensitive(True)

    # ===============================================
    # Install section functions
    # ===============================================

    def installTheme(self):
        self.threadAction = _("install")
        self.threadPackage = self.plymouth.getPackageName(
            self.selectedAvailableTheme)
        if self.threadPackage:
            dialog = QuestionDialog(
                _("Install theme"),
                _("Continue installing theme:\n%(theme)s") %
                {"theme": self.threadPackage}, self.window)
            go = dialog.show()
            if (go):
                self.toggleGuiElements(True)
                self.log.write(
                    _("Start installing theme: %(theme)s") %
                    {"theme": self.threadPackage}, 'dpm.installTheme', 'info')

                #  Start apt in a separate thread
                cmd = 'apt-get install -y %s %s' % (self.force,
                                                    self.threadPackage)
                t = ExecuteApt(self.log, cmd, self.queue)
                t.daemon = True
                t.start()
                self.queue.join()

                #self.log.write("Check every 250 miliseconds if thread is still active", 'dpm.installTheme')
                GObject.timeout_add(250, self.checkAptThread)
            else:
                self.log.write(
                    _("User cancel install theme: %(theme)s") %
                    {"theme": self.threadPackage}, 'dpm.installTheme', 'info')
        else:
            title = _("%(act1)s%(act2)s theme") % {
                "act1": self.threadAction[0].capitalize(),
                "act2": self.threadAction[1:]
            }
            msg = _("The package cannot be installed: %(pck)s\nTry apt instead"
                    ) % {
                        "pck": self.threadPackage
                    }
            self.log.write(msg, 'dpm.installTheme')
            MessageDialogSafe(title, msg, Gtk.MessageType.INFO,
                              self.window).show()

    def removeTheme(self):
        self.threadAction = _("remove")
        self.threadPackage = self.plymouth.getRemovablePackageName(
            self.selectedRemoveTheme)
        if self.threadPackage:
            dialog = QuestionDialog(
                _("Remove theme"),
                _("Continue removing theme:\n%(theme)s") %
                {"theme": self.threadPackage}, self.window)
            go = dialog.show()
            if (go):
                self.toggleGuiElements(True)

                # Start apt in a separate thread
                self.log.write(
                    _("Start removing theme: %(theme)s") %
                    {"theme": self.threadPackage}, 'dpm.removeTheme', 'info')
                cmd = 'apt-get purge -y %s %s' % (self.force,
                                                  self.threadPackage)
                t = ExecuteApt(self.log, cmd, self.queue)
                t.daemon = True
                t.start()
                self.queue.join()

                #self.log.write("Check every 250 miliseconds if thread is still active", 'dpm.removeTheme')
                GObject.timeout_add(250, self.checkAptThread)
            else:
                self.log.write(
                    _("User cancel remove theme: %(theme)s") %
                    {"theme": self.threadPackage}, 'dpm.removeTheme', 'info')
        else:
            title = _("%(act1)s%(act2)s theme") % {
                "act1": self.threadAction[0].capitalize(),
                "act2": self.threadAction[1:]
            }
            msg = _(
                "The package cannot be removed: %(pck)s\nIt is part of a meta package.\nTry apt instead"
            ) % {
                "pck": self.selectedRemoveTheme
            }
            self.log.write(msg, 'dpm.removeTheme')
            MessageDialogSafe(title, msg, Gtk.MessageType.INFO,
                              self.window).show()

    def checkAptThread(self):
        # As long there's a thread active, keep spinning
        if threading.active_count() > 1:
            self.pbDPM.pulse()
            return True

        # Thread is done
        self.pbDPM.set_fraction(0)

        # Get the error data from the queue
        aptError = self.queue.get()

        # Get the new data
        self.installedThemes = self.plymouth.getInstalledThemes()
        self.availableThemes = self.plymouth.getAvailableThemes()
        if self.selectedMenuItem == menuItems[1]:
            self.on_btnThemes_clicked(None, None, True)

        self.toggleGuiElements(False)
        title = _("%(act1)s%(act2)s theme") % {
            "act1": self.threadAction[0].capitalize(),
            "act2": self.threadAction[1:]
        }
        if aptError:
            msg = _(
                "Could not %(action)s theme:\n%(theme)s\nTry apt instead.\n\nError message:\n%(err)s"
            ) % {
                "action": self.threadAction,
                "theme": self.threadPackage,
                "err": aptError
            }
        else:
            msg = _("%(action)s successfully of:\n%(pck)s") % {
                "action":
                self.threadAction[0].capitalize() + self.threadAction[1:],
                "pck": self.threadPackage
            }

        self.log.write(msg, 'dpm.checkAptThread')
        MessageDialogSafe(title, msg, Gtk.MessageType.INFO, self.window).show()
        return False

    # ===============================================
    # Grub section functions
    # ===============================================

    def setGrubResolution(self):
        self.toggleGuiElements(True)
        self.log.write(
            _("Save grub resolution: %(res)s") %
            {"res": self.selectedGrubResolution}, 'dpm.setGrubResolution',
            'info')
        # Start saving in a separate thread
        t = GrubSave(self.log, self.selectedGrubResolution)
        t.start()
        GObject.timeout_add(250, self.checkGrubThread)

    def checkGrubThread(self):
        # As long there's a thread active, keep spinning
        if threading.active_count() > 1:
            self.pbDPM.pulse()
            return True

        # Thread is done
        self.pbDPM.set_fraction(0)
        self.currentGrubResolution = self.grub.getCurrentResolution()
        if self.selectedMenuItem == menuItems[2]:
            self.on_btnGrub_clicked(None, None, True)

        self.toggleGuiElements(False)
        title = _("Grub resolution")
        msg = _("Grub resolution saved: %(res)s") % {
            "res": self.selectedGrubResolution
        }
        self.log.write(msg, 'dpm.setGrubResolution', 'info')
        MessageDialogSafe(title, msg, Gtk.MessageType.INFO, self.window).show()
        return False

    def on_dpmWindow_destroy(self, widget, data=None):
        # Close the app
        Gtk.main_quit()
Exemplo n.º 8
0
class DPM:

    def __init__(self):
        self.scriptDir = dirname(realpath(__file__))
        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(join(self.scriptDir, '../../share/debian-plymouth-manager/debian-plymouth-manager.glade'))

        # Main window objects
        go = self.builder.get_object
        self.window = go('dpmWindow')
        self.tv1 = go('tv1')
        self.tv2 = go('tv2')
        self.sw2 = go('sw2')
        self.statusbar = go('statusbar')
        self.btnPlymouth = go('btnPlymouth')
        self.btnThemes = go('btnThemes')
        self.btnGrub = go('btnGrub')
        self.pbDPM = go('pbDPM')
        self.btn1 = go('btn1')
        self.btn2 = go('btn2')
        self.lblTitle1 = go('lblTitle1')
        self.lblTitle2 = go('lblTitle2')

        # Translations
        title = _("Debian Plymouth Manager")
        self.window.set_title(title)
        self.btnPlymouth.set_label("_" + _("Plymouth"))
        self.btnThemes.set_label("_" + _("Themes"))
        self.btnGrub.set_label("_" + _("Grub"))

        self.selectedMenuItem = None
        self.selectedAvailableTheme = None
        self.selectedRemoveTheme = None
        self.threadPackage = None
        self.queue = Queue()
        self.noPlymouth = _('None: no plymouth splash')

        # Set some variables
        self.logFile = '/var/log/dpm.log'
        self.log = Logger(self.logFile, addLogTime=False, maxSizeKB=1024)
        self.version = utils.getPackageVersion('debian-plymouth-manager')
        self.plymouth = Plymouth(self.log)
        self.grub = Grub(self.log)
        self.resolutions = utils.getResolutions('800x600', '', True, False)
        self.currentResolution = self.plymouth.getCurrentResolution()
        self.selectedResolution = self.currentResolution
        self.currentGrubResolution = self.grub.getCurrentResolution()
        self.selectedGrubResolution = self.currentGrubResolution
        self.currentTheme = self.plymouth.getCurrentTheme()
        self.selectedTheme = self.currentTheme
        self.installedThemes = self.plymouth.getInstalledThemes()
        self.availableThemes = self.plymouth.getAvailableThemes()
        self.tv1Handler = TreeViewHandler(self.tv1, self.log)
        self.tv2Handler = TreeViewHandler(self.tv2, self.log)
        self.force = utils.get_apt_force()

        self.on_btnPlymouth_clicked()

        # Connect builder signals and show window
        self.builder.connect_signals(self)
        self.window.show_all()

        # TODO: Hide the tool bar for now
        go('tlbMain').set_visible(False)

    # ===============================================
    # Menu section functions
    # ===============================================

    def on_btnPlymouth_clicked(self, widget=None, event=None, refresh=False):
        if self.selectedMenuItem != menuItems[0] or refresh:
            self.selectedMenuItem = menuItems[0]

            # Clear treeviews
            self.tv1Handler.clearTreeView()
            self.tv2Handler.clearTreeView()

            # Set object properties
            self.btn1.set_label(_("Set Plymouth Theme"))
            self.btn2.set_label(_("Preview"))
            self.btn2.show()
            self.lblTitle2.set_visible(True)
            self.sw2.show()

            # Show Installed Themes
            self.lblTitle1.set_label(_("Installed Themes"))
            # Clone the installedThemes list
            listInst = list(self.installedThemes)
            listInst.append(self.noPlymouth)
            # Get current theme and set setcursor
            ind = -1
            if self.currentTheme:
                try:
                    ind = listInst.index(self.currentTheme)
                except:
                    # Theme is set but removed from system
                    ind = 0
            else:
                ind = len(listInst) - 1

            if len(listInst) > 0:
                self.tv1Handler.fillTreeview(listInst, ['str'], ind, 700)

            # Show Resolutios
            self.lblTitle2.set_label(_("Resolutions"))
            ind = -1
            if self.currentResolution:
                try:
                    ind = self.resolutions.index(self.currentResolution)
                except:
                    ind = 0

            if len(self.resolutions) > 0:
                self.tv2Handler.fillTreeview(self.resolutions, ['str'], ind, 700)

    def on_btnThemes_clicked(self, widget=None, event=None, refresh=False):
        if self.selectedMenuItem != menuItems[1] or refresh:
            self.selectedMenuItem = menuItems[1]

            # Clear treeviews
            self.tv1Handler.clearTreeView()
            self.tv2Handler.clearTreeView()

            # Set object properties
            self.btn1.set_label(_("Install Theme"))
            self.btn2.set_label(_("Remove Theme"))
            self.btn2.show()
            self.lblTitle2.set_visible(True)
            self.sw2.show()

            # Show Available Themes
            self.lblTitle1.set_label(_("Available Themes"))
            if len(self.availableThemes) > 0:
                self.tv1Handler.fillTreeview(self.availableThemes, ['str'], 0)

            # Show Installed Themes
            self.lblTitle2.set_label(_("Installed Themes"))
            if len(self.installedThemes) > 0:
                self.tv2Handler.fillTreeview(self.installedThemes, ['str'], 0)

    def on_btnGrub_clicked(self, widget=None, event=None, refresh=False):
        if self.selectedMenuItem != menuItems[2] or refresh:
            self.selectedMenuItem = menuItems[2]

            # Clear treeviews
            self.tv1Handler.clearTreeView()
            self.tv2Handler.clearTreeView()

            # Set object properties
            self.btn1.set_label(_("Set Grub Resolution"))
            self.btn2.hide()
            self.lblTitle2.set_visible(False)
            self.sw2.hide()

            # Show Resolutios
            self.lblTitle1.set_text(_("Grub Resolutions"))
            ind = -1
            if self.currentGrubResolution:
                try:
                    ind = self.resolutions.index(self.currentGrubResolution)
                except:
                    ind = 0

            if len(self.resolutions) > 0:
                self.tv1Handler.fillTreeview(self.resolutions, ['str'], ind, 700)

    # ===============================================
    # Treeview functions
    # ===============================================

    def on_tv1_cursor_changed(self, widget):
        if self.selectedMenuItem == menuItems[0]:
            # Themes Menu
            self.selectedTheme = self.tv1Handler.getSelectedValue()
            self.log.write("Themes menu - seleceted theme: %(theme)s" % { "theme": self.selectedTheme }, 'dpm.tv1Changed')
        elif self.selectedMenuItem == menuItems[1]:
            # Install Menu
            self.selectedAvailableTheme = self.tv1Handler.getSelectedValue()
            self.log.write("Install menu - seleceted available theme: %(theme)s" % { "theme": self.selectedAvailableTheme }, 'dpm.tv1Changed')
        elif self.selectedMenuItem == menuItems[2]:
            # Grub Menu
            self.selectedGrubResolution = self.tv1Handler.getSelectedValue()
            self.log.write("Grub menu - seleceted grub resolution: %(res)s" % { "res": self.selectedGrubResolution }, 'dpm.tv1Changed')

    def on_tv2_cursor_changed(self, widget):
        if self.selectedMenuItem == menuItems[0]:
            # Themes Menu
            self.selectedResolution = self.tv2Handler.getSelectedValue()
            self.log.write("Themes menu - seleceted resolution: %(res)s" % { "res": self.selectedResolution }, 'dpm.tv2Changed')
        elif self.selectedMenuItem == menuItems[1]:
            # Install Menu
            self.selectedRemoveTheme = self.tv2Handler.getSelectedValue()
            self.log.write("Install menu - seleceted theme to remove: %(theme)s" % { "theme": self.selectedRemoveTheme }, 'dpm.tv2Changed')

    # ===============================================
    # Button functions
    # ===============================================

    def on_btn1_clicked(self, widget):
        if self.selectedMenuItem == menuItems[0]:
            # Themes
            self.setTheme()
        elif self.selectedMenuItem == menuItems[1]:
            # Install
            self.installTheme()
        elif self.selectedMenuItem == menuItems[2]:
            # Grub
            self.setGrubResolution()

    def on_btn2_clicked(self, widget):
        if self.selectedMenuItem == menuItems[0]:
            # Themes
            self.preview()
        elif self.selectedMenuItem == menuItems[1]:
            # Install
            self.removeTheme()
        elif self.selectedMenuItem == menuItems[2]:
            # Grub
            pass

    # ===============================================
    # Themes section functions
    # ===============================================

    def preview(self):
        # Check if the selected have been saved
        if self.currentTheme == self.selectedTheme and self.currentResolution == self.selectedResolution:
            self.plymouth.previewPlymouth()
        else:
            title = _("Preview")
            msg = _("You must save before you can preview:\n\nTheme: %(theme)s\nResolution: %(res)s") % { "theme": self.selectedTheme, "res": self.selectedResolution }
            MessageDialogSafe(title, msg, Gtk.MessageType.INFO, self.window).show()

    def setTheme(self):
        self.toggleGuiElements(True)
        if self.selectedTheme != self.noPlymouth:
            if self.selectedResolution is None:
                self.selectedResolution = self.tv2Handler.getValue(self.tv2Handler.getRowCount() - 1)
        else:
            self.selectedTheme = None
            self.selectedResolution = None
        self.log.write(_("Save setting: %(theme)s (%(res)s)") % { "theme": self.selectedTheme, "res": self.selectedResolution }, 'dpm.setTheme', 'info')
        # Start saving in a separate thread
        t = PlymouthSave(self.log, self.selectedTheme, self.selectedResolution)
        t.start()
        GObject.timeout_add(250, self.checkSaveThread)

    def checkSaveThread(self):
        #print 'Thread count = ' + str(threading.active_count())
        # As long there's a thread active, keep spinning
        if threading.active_count() > 1:
            self.pbDPM.pulse()
            return True

        # Get the new data
        self.pbDPM.set_fraction(0)
        self.currentTheme = self.plymouth.getCurrentTheme()
        self.currentResolution = self.plymouth.getCurrentResolution()
        self.installedThemes = self.plymouth.getInstalledThemes()
        self.availableThemes = self.plymouth.getAvailableThemes()
        if self.selectedMenuItem == menuItems[0]:
            self.on_btnPlymouth_clicked(None, None, True)

        # Thread is done: make button sensitive again
        self.toggleGuiElements(False)
        self.log.write(_("Done saving settings: %(theme)s (%(res)s)") % { "theme": self.currentTheme, "res": self.currentResolution }, 'dpm.checkSaveThread', 'info')

        title = _("Save settings")
        msg = _("Theme: %(theme)s\nResolution: %(res)s\n\nDone") % { "theme": self.currentTheme, "res": str(self.currentResolution) }
        self.log.write(msg, 'dpm.checkSaveThread')
        MessageDialogSafe(title, msg, Gtk.MessageType.INFO, self.window).show()
        return False

    def toggleGuiElements(self, startSave):
        if startSave:
            self.btn1.set_sensitive(False)
            self.btn2.set_sensitive(False)
        else:
            self.btn1.set_sensitive(True)
            self.btn2.set_sensitive(True)

    # ===============================================
    # Install section functions
    # ===============================================

    def installTheme(self):
        self.threadAction = _("install")
        self.threadPackage = self.plymouth.getPackageName(self.selectedAvailableTheme)
        if self.threadPackage:
            dialog = QuestionDialog(_("Install theme"), _("Continue installing theme:\n%(theme)s") % { "theme": self.threadPackage }, self.window)
            go = dialog.show()
            if (go):
                self.toggleGuiElements(True)
                self.log.write(_("Start installing theme: %(theme)s") % { "theme": self.threadPackage }, 'dpm.installTheme', 'info')

                #  Start apt in a separate thread
                cmd = 'apt-get install -y %s %s' % (self.force, self.threadPackage)
                t = ExecuteApt(self.log, cmd, self.queue)
                t.daemon = True
                t.start()
                self.queue.join()

                #self.log.write("Check every 250 miliseconds if thread is still active", 'dpm.installTheme')
                GObject.timeout_add(250, self.checkAptThread)
            else:
                self.log.write(_("User cancel install theme: %(theme)s") % { "theme": self.threadPackage }, 'dpm.installTheme', 'info')
        else:
            title = _("%(act1)s%(act2)s theme") % { "act1": self.threadAction[0].capitalize(), "act2": self.threadAction[1:] }
            msg = _("The package cannot be installed: %(pck)s\nTry apt instead") % { "pck": self.threadPackage }
            self.log.write(msg, 'dpm.installTheme')
            MessageDialogSafe(title, msg, Gtk.MessageType.INFO, self.window).show()

    def removeTheme(self):
        self.threadAction = _("remove")
        self.threadPackage = self.plymouth.getRemovablePackageName(self.selectedRemoveTheme)
        if self.threadPackage:
            dialog = QuestionDialog(_("Remove theme"), _("Continue removing theme:\n%(theme)s") % { "theme": self.threadPackage }, self.window)
            go = dialog.show()
            if (go):
                self.toggleGuiElements(True)

                # Start apt in a separate thread
                self.log.write(_("Start removing theme: %(theme)s") % { "theme": self.threadPackage }, 'dpm.removeTheme', 'info')
                cmd = 'apt-get purge -y %s %s' % (self.force, self.threadPackage)
                t = ExecuteApt(self.log, cmd, self.queue)
                t.daemon = True
                t.start()
                self.queue.join()

                #self.log.write("Check every 250 miliseconds if thread is still active", 'dpm.removeTheme')
                GObject.timeout_add(250, self.checkAptThread)
            else:
                self.log.write(_("User cancel remove theme: %(theme)s") % { "theme": self.threadPackage }, 'dpm.removeTheme', 'info')
        else:
            title = _("%(act1)s%(act2)s theme") % { "act1": self.threadAction[0].capitalize(), "act2": self.threadAction[1:] }
            msg = _("The package cannot be removed: %(pck)s\nIt is part of a meta package.\nTry apt instead") % { "pck": self.selectedRemoveTheme }
            self.log.write(msg, 'dpm.removeTheme')
            MessageDialogSafe(title, msg, Gtk.MessageType.INFO, self.window).show()

    def checkAptThread(self):
        # As long there's a thread active, keep spinning
        if threading.active_count() > 1:
            self.pbDPM.pulse()
            return True

        # Thread is done
        self.pbDPM.set_fraction(0)

        # Get the error data from the queue
        aptError = self.queue.get()

        # Get the new data
        self.installedThemes = self.plymouth.getInstalledThemes()
        self.availableThemes = self.plymouth.getAvailableThemes()
        if self.selectedMenuItem == menuItems[1]:
            self.on_btnThemes_clicked(None, None, True)

        self.toggleGuiElements(False)
        title = _("%(act1)s%(act2)s theme") % { "act1": self.threadAction[0].capitalize(), "act2": self.threadAction[1:] }
        if aptError:
            msg = _("Could not %(action)s theme:\n%(theme)s\nTry apt instead.\n\nError message:\n%(err)s") % { "action": self.threadAction, "theme": self.threadPackage, "err": aptError }
        else:
            msg = _("%(action)s successfully of:\n%(pck)s") % { "action": self.threadAction[0].capitalize() + self.threadAction[1:], "pck": self.threadPackage }

        self.log.write(msg, 'dpm.checkAptThread')
        MessageDialogSafe(title, msg, Gtk.MessageType.INFO, self.window).show()
        return False

    # ===============================================
    # Grub section functions
    # ===============================================

    def setGrubResolution(self):
        self.toggleGuiElements(True)
        self.log.write(_("Save grub resolution: %(res)s") % { "res": self.selectedGrubResolution }, 'dpm.setGrubResolution', 'info')
        # Start saving in a separate thread
        t = GrubSave(self.log, self.selectedGrubResolution)
        t.start()
        GObject.timeout_add(250, self.checkGrubThread)

    def checkGrubThread(self):
        # As long there's a thread active, keep spinning
        if threading.active_count() > 1:
            self.pbDPM.pulse()
            return True

        # Thread is done
        self.pbDPM.set_fraction(0)
        self.currentGrubResolution = self.grub.getCurrentResolution()
        if self.selectedMenuItem == menuItems[2]:
            self.on_btnGrub_clicked(None, None, True)

        self.toggleGuiElements(False)
        title = _("Grub resolution")
        msg = _("Grub resolution saved: %(res)s") % { "res": self.selectedGrubResolution }
        self.log.write(msg, 'dpm.setGrubResolution', 'info')
        MessageDialogSafe(title, msg, Gtk.MessageType.INFO, self.window).show()
        return False

    def on_dpmWindow_destroy(self, widget, data=None):
        # Close the app
        Gtk.main_quit()
Exemplo n.º 9
0
class LightDMManager:
    def __init__(self):
        self.scriptDir = os.path.dirname(os.path.realpath(__file__))

        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(
            join(self.scriptDir,
                 '../../share/lightdm-manager/lightdm-manager.glade'))
        # Main window objects
        go = self.builder.get_object
        self.window = go('ldmWindow')
        self.swUsers = go('swUsers')
        self.tvUsers = go('tvUsers')
        self.btnSave = go('btnSave')
        self.imgBackground = go('imgBackground')
        self.btnUsers = go('btnUsers')
        self.btnAppearance = go('btnAppearance')
        self.chkHideUsers = go('chkHideUsers')
        self.ebFace = go('ebFace')
        self.imgFace = go('imgFace')
        self.nbLightDM = go('nbLightDM')
        self.cmbThemes = go('cmbThemes')

        # Read from config file
        self.cfg = Config('lightdm-manager.conf')
        self.lightdmConf = self.cfg.getValue('CONFIG', 'lightdmConf')
        self.desktopbaseDir = self.cfg.getValue('CONFIG', 'desktopbaseDir')
        gktGreeterConf = self.cfg.getValue('CONFIG', 'gtkGreeterConf')
        kdeGreeterConf = self.cfg.getValue('CONFIG', 'kdeGreeterConf')
        if exists(gktGreeterConf):
            self.greeterConf = gktGreeterConf
        else:
            self.greeterConf = kdeGreeterConf

        # Translations
        title = _("LightDM Manager")
        self.window.set_title(title)
        self.btnUsers.set_label("_{}".format(_("Users")))
        self.btnAppearance.set_label("_{}".format(_("Appearance")))
        go('lblBackground').set_label(_("Background"))
        go('lblTheme').set_label(_("Theme"))
        go('lblLightDmMenu').set_label(_("Menu"))
        self.chkHideUsers.set_label(_("Hide users"))
        go('lblUsersFace').set_label(_("User icon"))
        go('lblUsersAutologin').set_label(_("Auto-login"))

        # Get current background image
        self.cfgGreeter = Config(self.greeterConf)
        try:
            self.curBgPath = self.cfgGreeter.getValue('greeter', 'background')
            self.curTheme = self.cfgGreeter.getValue('greeter', 'theme-name')
        except:
            self.curBgPath = None
            self.curTheme = None

        # Get current auto-login user
        self.cfgLightdm = Config(self.lightdmConf)
        try:
            self.curAutoUser = self.cfgLightdm.getValue(
                'SeatDefaults', 'autologin-user').strip()
            self.curHideUsers = False
            ghu = self.cfgLightdm.getValue('SeatDefaults',
                                           'greeter-hide-users').strip()
            if 'true' in ghu:
                self.curHideUsers = True
        except:
            self.curAutoUser = None
            self.curHideUsers = False

        # Init
        self.usr = User()
        self.newbgImg = self.curBgPath
        self.newAutoUser = self.curAutoUser
        self.newTheme = self.curTheme
        self.themes = []
        self.selectedMenuItem = None
        self.debug = False
        self.logPath = ''
        self.prevPath = None
        self.tempFace = "/tmp/face"
        self.newFaces = []
        self.loggedUser = functions.getUserLoginName()
        self.curUser = self.loggedUser
        self.selectImg = join(self.scriptDir,
                              '../../share/lightdm-manager/select.png')

        # Handle arguments
        try:
            opts, args = getopt.getopt(sys.argv[1:], 'dl:', ['debug', 'log='])
        except getopt.GetoptError:
            print(("Arguments cannot be parsed: %s" % str(sys.argv[1:])))
            sys.exit(1)

        for opt, arg in opts:
            if opt in ('-d', '--debug'):
                self.debug = True
            elif opt in ('-l', '--log'):
                self.logPath = arg

        # Initialize logging
        if self.debug:
            if not self.logPath:
                self.logPath = 'lightdm-manager.log'
        self.log = Logger(self.logPath, 'debug', True, None, self.window)

        # Backup config files because ConfigParser does not preserve commented lines
        if not exists("%s.org" % self.greeterConf):
            copy(self.greeterConf, "%s.org" % self.greeterConf)
            self.log.write(
                "%(conf1)s copied to %(conf2)s.org" % {
                    "conf1": self.greeterConf,
                    "conf2": self.greeterConf
                }, 'LightDMManager.main', 'debug')
        if not exists("%s.org" % self.lightdmConf):
            copy(self.lightdmConf, "%s.org" % self.lightdmConf)
            self.log.write(
                "%(conf1)s copied to %(conf2)s.org" % {
                    "conf1": self.lightdmConf,
                    "conf2": self.lightdmConf
                }, 'LightDMManager.main', 'debug')

        # Initiate the treeview handler and connect the custom toggle event with usersCheckBoxToggled
        self.tvHandler = TreeViewHandler(self.tvUsers, self.log)
        self.tvHandler.connect('checkbox-toggled', self.usersCheckBoxToggled)

        # Get users
        self.users = self.usr.getUsers()
        self.fillUsers()
        self.tvHandler.selectValue(self.curUser, 1)
        self.setBackground(self.curBgPath)
        self.cmbHandlerThemes = ComboBoxHandler(self.cmbThemes)
        self.listThemes()
        self.chkHideUsers.set_active(self.curHideUsers)

        # Show users menu
        self.on_btnUsers_clicked(None)
        self.on_tvUsers_cursor_changed(None)

        self.version = functions.getPackageVersion('lightdm-manager')

        # Connect the signals and show the window
        self.builder.connect_signals(self)
        self.window.show()

    # ===============================================
    # Menu section functions
    # ===============================================

    def on_btnUsers_clicked(self, widget, event=None):
        if self.selectedMenuItem != menuItems[0]:
            self.selectedMenuItem = menuItems[0]
            self.nbLightDM.set_current_page(0)

    def on_btnAppearance_clicked(self, widget, event=None):
        if self.selectedMenuItem != menuItems[1]:
            self.selectedMenuItem = menuItems[1]
            self.nbLightDM.set_current_page(1)

    # ===============================================
    # Functions
    # ===============================================

    def on_ebFace_enter_notify_event(self, widget, event):
        self.window.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))

    def on_ebFace_leave_notify_event(self, widget, event):
        self.window.get_window().set_cursor(None)

    def on_ebFace_button_release_event(self, widget, event):
        home = self.usr.getUserHomeDir(self.curUser)
        primaryGroup = self.usr.getUserPrimaryGroupName(self.curUser)
        imagePath = SelectImageDialog(_('Select user image'), home,
                                      self.window).show()
        if imagePath is not None:
            tempUserImg = "%(tempFace)s.%(curUser)s" % {
                "tempFace": self.tempFace,
                "curUser": self.curUser
            }
            self.newFaces.append(
                [tempUserImg,
                 join(home, ".face"), self.curUser, primaryGroup])
            print((">>> self.newFaces = %s" % self.newFaces))
            ih = ImageHandler(imagePath)
            ih.makeFaceImage(tempUserImg)
            if exists(tempUserImg):
                self.imgFace.set_from_pixbuf(ih.pixbuf)
            else:
                # This should never happen
                self.imgFace.set_from_file(self.selectImg)

    def on_tvUsers_cursor_changed(self, widget):
        self.curUser = self.tvHandler.getSelectedValue(1)
        showFace = None
        if self.newFaces:
            for face in self.newFaces:
                if face[2] == self.curUser and exists(face[0]):
                    showFace = GdkPixbuf.Pixbuf.new_from_file(face[0])
        if showFace is None:
            showFace = self.usr.getUserFacePixbuf(self.curUser)
        if showFace is None:
            # Still no user icon found: show select image
            self.imgFace.set_from_file(self.selectImg)
        else:
            self.imgFace.set_from_pixbuf(showFace)

    def on_btnSave_clicked(self, widget):
        saved = False
        saveHideUsers = False
        saveAutoUser = False
        saveFaces = False
        saveBackground = False
        saveTheme = False
        if self.chkHideUsers.get_active() != self.curHideUsers:
            saveHideUsers = True
        if self.curAutoUser != self.newAutoUser:
            saveAutoUser = True
        if self.newFaces:
            saveFaces = True
        if self.curBgPath != self.newbgImg:
            saveBackground = True
        self.newTheme = self.cmbHandlerThemes.getValue()
        if self.curTheme != self.newTheme:
            saveTheme = True

        if saveHideUsers or saveAutoUser or saveFaces or saveBackground or saveTheme:
            qd = QuestionDialog(
                _("LightDM settings"),
                _("Settings have changed\n\nDo you want to save the new settings?"
                  ), self.window)
            answer = qd.show()
            if answer:
                if saveAutoUser:
                    if self.newAutoUser is not None:
                        # Save the auto-login user
                        self.cfgLightdm.setValue('SeatDefaults',
                                                 'autologin-user',
                                                 self.newAutoUser)
                        self.cfgLightdm.setValue('SeatDefaults',
                                                 'autologin-user-timeout', '0')
                        self.curAutoUser = self.newAutoUser
                        self.log.write(
                            "New auto-login user: %(usr)s" %
                            {"usr": self.curAutoUser},
                            'LightDMManager.saveSettings', 'debug')
                    else:
                        self.cfgLightdm.removeOption('SeatDefaults',
                                                     'autologin-user')
                        self.cfgLightdm.removeOption('SeatDefaults',
                                                     'autologin-user-timeout')
                        self.curAutoUser = None
                        self.log.write("Auto-login disabled",
                                       'LightDMManager.saveSettings', 'debug')
                if saveHideUsers:
                    hideUsers = str(self.chkHideUsers.get_active()).lower()
                    self.cfgLightdm.setValue('SeatDefaults',
                                             'greeter-hide-users', hideUsers)
                    self.log.write(
                        "Hide users saved: %(users)s" % {"users": hideUsers},
                        'LightDMManager.saveSettings', 'debug')
                if saveFaces:
                    for face in self.newFaces:
                        if exists(face[0]):
                            copy(face[0], face[1])
                            if exists(face[1]):
                                os.system(
                                    "chown %(owner)s:%(group)s %(path)s" % {
                                        "owner": face[2],
                                        "group": face[3],
                                        "path": face[1]
                                    })
                    self.log.write("User icons saved",
                                   'LightDMManager.saveSettings', 'debug')
                if saveTheme:
                    self.cfgGreeter.setValue('greeter', 'theme-name',
                                             self.newTheme)
                    self.curTheme = self.newTheme
                    self.log.write(
                        "Theme saved: %(theme)s" % {"theme": self.curTheme},
                        'LightDMManager.saveSettings', 'debug')
                if saveBackground:
                    if os.path.exists(self.newbgImg):
                        self.cfgGreeter.setValue('greeter', 'background',
                                                 self.newbgImg)
                        self.curBgPath = self.newbgImg
                        self.log.write(
                            "Background saved: %(background)s" %
                            {"background": self.curBgPath},
                            'LightDMManager.saveSettings', 'debug')
                saved = True
            else:
                if os.path.exists(self.curBgPath):
                    self.setBackground(self.curBgPath)
                    self.log.write(
                        "Current background: %(background)s" %
                        {"background": self.curBgPath},
                        'LightDMManager.saveSettings', 'debug')
                else:
                    self.imgBackground.set_from_file(
                        join(self.scriptDir,
                             '../../share/lightdm-manager/select.png'))
                    self.log.write("No background set",
                                   'LightDMManager.saveSettings', 'debug')
                self.fillUsers()

        if saved:
            self.curHideUsers = self.chkHideUsers.get_active()
            self.curAutoUser = self.newAutoUser
            self.newFaces = []
            self.curBgPath = self.newbgImg
            self.curTheme = self.newTheme
            MessageDialogSafe(_("Saved"),
                              _("LightDM settings saved successfully."),
                              Gtk.MessageType.INFO, self.window).show()

    def on_ebBackground_button_release_event(self, widget, event):
        self.newbgImg = SelectImageDialog(_("Choose background image"),
                                          self.desktopbaseDir,
                                          self.window).show()
        if exists(self.newbgImg) and self.newbgImg != self.curBgPath:
            self.setBackground(self.newbgImg)
            self.log.write(
                _("New background: %(bg)s") % {"bg": self.newbgImg},
                'LightDMManager.chooseFile', 'info')

    def on_ebBackground_enter_notify_event(self, widget, event):
        self.window.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))

    def on_ebBackground_leave_notify_event(self, widget, event):
        self.window.get_window().set_cursor(None)

    # This method is fired by the TreeView.checkbox-toggled event
    def usersCheckBoxToggled(self, obj, path, colNr, toggleValue):
        path = int(path)
        model = self.tvUsers.get_model()
        itr = model.get_iter(path)
        user = model[itr][1]

        if self.prevPath != path or toggleValue:
            # Only one toggle box can be selected (or none)
            self.tvHandler.treeviewToggleAll([0], False, 1, user)
            # Save current path
            self.prevPath = path
            # Save selected user
            self.newAutoUser = user
            self.log.write(
                _("Auto-login user selected: %(usr)s") % {"usr": user},
                'LightDMManager.usersCheckBoxToggled', 'info')
        elif self.prevPath == path and not toggleValue:
            self.newAutoUser = None

    def listThemes(self):
        themeDir = '/usr/share/themes'
        themeDirLocal = '~/.local/share/themes'
        dirs = functions.locate('gtk-*', themeDir, True) + functions.locate(
            'gtk-*', themeDirLocal, True)
        for path in dirs:
            dirList = path.split('/')
            for d in dirList:
                if 'gtk-' in d:
                    break
                themeName = d
            if themeName not in self.themes:
                self.themes.append(themeName)
        if self.themes:
            self.cmbHandlerThemes.fillComboBox(self.themes)
            if self.curTheme in self.themes:
                self.cmbHandlerThemes.selectValue(self.curTheme)

    def fillUsers(self):
        selUsr = False
        contentList = []
        i = 0
        for usr in self.users:
            if usr == self.curAutoUser:
                selUsr = True
                self.prevPath = i
            else:
                selUsr = False
            contentList.append([selUsr, usr])
            i += 1

        # Fill treeview with users
        #fillTreeview(contentList, columnTypesList, columnHideList=[-1], setCursor=0, setCursorWeight=400, firstItemIsColName=False, appendToExisting=False, appendToTop=False)
        columnTypesList = ['bool', 'str']
        self.tvHandler.fillTreeview(contentList, columnTypesList)

    def setBackground(self, path):
        # Set Background
        if path is not None:
            if exists(path):
                ih = ImageHandler(path)
                ih.resizeImage(height=200)
                self.imgBackground.set_from_pixbuf(ih.pixbuf)
            else:
                self.imgBackground.set_from_file(self.selectImg)
        else:
            self.imgBackground.set_from_file(self.selectImg)

    # ===============================================
    # General functions
    # ===============================================

    def on_ldmWindow_destroy(self, widget, data=None):
        # Close the app
        self.on_btnSave_clicked(None)
        for tmp in self.newFaces:
            os.remove(tmp[0])
        Gtk.main_quit()
Exemplo n.º 10
0
class UpdateManager(object):
    def __init__(self):
        # Check if script is running
        self.scriptName = basename(__file__)
        self.umglobal = UmGlobal()
        self.user = self.umglobal.getLoginName()

        # Initiate logging
        self.logFile = join('/var/log', self.umglobal.settings['log'])
        print(("UM log = %s" % self.logFile))
        self.log = Logger(self.logFile, maxSizeKB=5120)

        # Remove scripts
        self.deleteScripts(self.umglobal.localUpdVersion)

        # Initialize
        self.ec = ExecCmd(loggerObject=self.log)
        self.apt = UmApt(self.umglobal)
        self.kernelVersion = self.umglobal.getKernelVersion()
        self.upgradables = []
        self.upgradableUM = []
        self.window = None

        # Handle arguments
        parser = argparse.ArgumentParser(description='SolydXK Update Manager')
        parser.add_argument('-q',
                            '--quick',
                            action="store_true",
                            help='Quick upgrade')
        parser.add_argument('-r', '--reload', action="store_true", help='')
        args, extra = parser.parse_known_args()

        self.quickUpdate = False
        if args.quick and not self.umglobal.newUpd:
            self.quickUpdate = True
        if args.reload:
            pids = self.umglobal.getProcessPids("updatemanager.py")
            if len(pids) > 1:
                print(("updatemanager.py already running - kill pid {}".format(
                    pids[0])))
                os.system("kill {}".format(pids[0]))

        # Set some global translations
        self.aptErrorText = _("Apt error")
        self.upgradablePackagesText = _(
            "The following packages will be upgraded:")
        self.newPackagesText = _(
            "The following NEW packages will be installed:")
        self.removedPackagesText = _("The following packages will be REMOVED:")
        self.heldbackPackagesText = _(
            "The following packages have been kept back:")
        self.downgradePackagesText = _(
            "The following packages are going to be downgraded:")

        # Cleanup first
        for fle in glob(join(self.umglobal.filesDir, '.um*')):
            remove(fle)

        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(
            join(self.umglobal.shareDir, 'updatemanager.glade'))
        go = self.builder.get_object

        # Quick update
        if self.quickUpdate:
            # Refresh data
            self.refresh()
            self.umglobal.collectData()
            self.apt.createPackagesInfoList()
            self.apt.createPackageLists()
            self.fillTreeView()

            # Run upgrade
            nid = self.run_upgrade()

            if nid != "":
                self.on_command_done(None, 0, nid)

            sys.exit(2)

        # Make sure the files directory is set correctly
        self.checkFilesDir()

        # Main window objects
        self.window = go("windowMain")
        #self.window.set_icon_from_file(join(self.umglobal.iconsDir, self.umglobal.settings["icon-connected"]))
        self.tvPck = go("tvPck")
        self.swTerminal = go("swTerminal")
        self.statusbar = go("statusbar")
        self.btnInstall = go("btnInstall")
        self.btnRefresh = go("btnRefresh")
        self.btnPackages = go("btnPackages")
        self.btnOutput = go("btnOutput")
        self.btnInfo = go("btnInfo")
        self.btnPreferences = go("btnPreferences")
        self.nbMain = go("nbMain")
        self.swInfo = go("swInfo")
        self.btnMaintenance = go("btnMaintenance")
        self.lblMaintenance = go("lblMaintenance")
        self.tvMaintenance = go("tvMaintenance")
        self.btnMaintenanceExecute = go("btnMaintenanceExecute")
        self.chkMaintenanceSelectAll = go("chkMaintenanceSelectAll")
        self.radUnneeded = go("radUnneeded")
        self.radCleanCache = go("radCleanCache")
        self.radDowngradable = go("radDowngradable")
        self.radNotavailable = go("radNotavailable")
        self.radOldKernel = go("radOldKernel")
        self.lblMaintenanceHelp = go("lblMaintenanceHelp")

        # Translations
        self.window.set_title(_("SolydXK Update Manager"))
        self.btnInstall.set_label(_("Install"))
        self.btnRefresh.set_label(_("Refresh"))
        self.btnOutput.set_label(_("Output"))
        self.btnInfo.set_label(_("Information"))
        self.btnPreferences.set_label(_("Preferences"))
        self.btnMaintenance.set_label(_("Maintenance"))
        self.btnPackages.set_label(_("Packages"))
        self.uptodateText = self.umglobal.connectedText
        self.lblMaintenance.set_label(self.btnMaintenance.get_label())
        self.btnMaintenanceExecute.set_label(_("Execute"))
        self.chkMaintenanceSelectAll.set_label(_("Select all"))
        self.radCleanCache.set_label(_("Clean up the apt cache"))
        self.radUnneeded.set_label(_("Remove unneeded packages"))
        self.radNotavailable.set_label(
            _("Remove packages not available\nin the repositories"))
        self.radOldKernel.set_label(_("Remove old kernels"))
        self.radDowngradable.set_label(
            _("Downgrade packages with\nonly lower versions available"))
        self.lblMaintenanceHelp.set_label(
            _("Make sure you create\n"
              "a system image before you\n"
              "continue (e.g. Clonezilla)."))

        # VTE Terminal
        self.terminal = VirtualTerminal(userInputAllowed=self.umglobal.
                                        settings["allow-terminal-user-input"])
        self.swTerminal.add(self.terminal)
        self.terminal.set_vexpand(True)
        self.terminal.set_hexpand(True)
        self.terminal.connect('command-done', self.on_command_done)
        self.terminal.connect('line-added', self.on_line_added)
        palletList = [
            '#4A4A4A', '#BD1919', '#118011', '#CE6800', '#1919BC', '#8D138D',
            '#139494', '#A7A7A7'
        ]
        self.terminal.setTerminalColors("#000000", "#FFFFFF", palletList)
        self.swTerminal.modify_bg(Gtk.StateType.NORMAL,
                                  Gdk.color_parse("#FFFFFF"))

        # Disable all buttons
        self.btnInfo.set_sensitive(False)
        self.btnPreferences.set_sensitive(False)
        self.btnOutput.set_sensitive(False)
        self.btnRefresh.set_sensitive(False)
        self.btnInstall.set_sensitive(False)
        self.btnPackages.set_sensitive(False)
        self.btnMaintenance.set_sensitive(False)

        # Hide tabs if needed
        for tab in self.umglobal.settings["hide-tabs"]:
            if tab == "packages":
                self.nbMain.get_nth_page(0).set_visible(False)
                self.btnPackages.set_visible(False)
            elif tab == "output":
                self.nbMain.get_nth_page(1).set_visible(False)
                self.btnOutput.set_visible(False)
            elif tab == "info":
                self.nbMain.get_nth_page(2).set_visible(False)
                self.btnInfo.set_visible(False)
            elif tab == "maintenance":
                self.nbMain.get_nth_page(3).set_visible(False)
                self.btnMaintenance.set_visible(False)

        # Connect the signals and show the window
        self.builder.connect_signals(self)
        self.window.show()

        # Force the window to show
        while Gtk.events_pending():
            Gtk.main_iteration()

        # Just show something that we're busy
        msg = _("Gathering information...")
        self.terminal.executeCommand('echo "%s"' % msg, 'init')
        self.showOutput()

        # Treeview handlers
        self.tvHandler = TreeViewHandler(self.tvPck)
        self.tvMaintenanceHandler = TreeViewHandler(self.tvMaintenance)

        # Version information
        ver = _("Version")
        pckVer = self.apt.getPackageVersion('updatemanager')
        versionInfo = "%(ver)s: %(pckVer)s" % {"ver": ver, "pckVer": pckVer}
        if self.umglobal.localUpdVersion != "2000.01.01":
            versionInfo = "%(ver)s: %(pckVer)s" % {
                "ver": ver,
                "pckVer": pckVer
            }
        self.pushMessage(versionInfo)

        # Log basic information
        self.log.write("==============================================",
                       "UM.init", "debug")
        self.log.write("UM version = %s" % versionInfo, "UM.init", "debug")
        self.log.write("==============================================",
                       "UM.init", "debug")
        mirrorsList = join(self.umglobal.filesDir,
                           basename(self.umglobal.settings["mirrors-list"]))
        if exists(mirrorsList):
            self.log.write("Mirrors list", "UM.init", "debug")
            with open(mirrorsList, 'r') as f:
                for line in f.readlines():
                    self.log.write(line, "UM.init", "debug")
            self.log.write("==============================================",
                           "UM.init", "debug")

        # Refresh apt cache
        self.refresh()

        # Initialize maintenance screen
        self.fillTreeViewMaintenance()

    # ===============================================
    # Main window functions
    # ===============================================

    def on_btnInstall_clicked(self, widget):
        self.run_upgrade()

    def run_upgrade(self):
        nid = ""
        aptHasErrors = self.apt.aptHasErrors()
        if aptHasErrors is not None:
            MessageDialog(self.aptErrorText, aptHasErrors)
        elif self.upgradables:

            if self.apt.upgradablePackages:
                self.log.write(
                    "=================== upgradable pacages ====================",
                    "UM.run_upgrade", "debug")
                self.log.write(
                    self.createLogString(self.apt.upgradablePackages),
                    "UM.run_upgrade", "debug")
            if self.apt.removedPackages:
                self.log.write(
                    "==================== removed packages =====================",
                    "UM.run_upgrade", "debug")
                self.log.write(self.createLogString(self.apt.removedPackages),
                               "UM.run_upgrade", "debug")
            if self.apt.newPackages:
                self.log.write(
                    "======================= new packages =======================",
                    "UM.run_upgrade", "debug")
                self.log.write(self.createLogString(self.apt.newPackages),
                               "UM.run_upgrade", "debug")
            if self.apt.heldbackPackages:
                self.log.write(
                    "=================== kept back packages =====================",
                    "UM.run_upgrade", "debug")
                self.log.write(self.createLogString(self.apt.heldbackPackages),
                               "UM.run_upgrade", "debug")

            if not self.quickUpdate:
                self.showOutput()
            contMsg = _("Continue installation?")
            if self.upgradableUM:
                cmd = "%s install updatemanager" % self.umglobal.settings[
                    'apt-get-string']
                cmd += "; %s install %s" % (
                    self.umglobal.settings['apt-get-string'], " ".join(
                        self.apt.getPackageDependencies('updatemanager')))
                nid = 'uminstallum'
                self.prepForCommand(nid)
                if self.quickUpdate:
                    self.ec.run(cmd)
                else:
                    self.terminal.executeCommand(cmd, nid)
                self.log.write("Execute command: %s (%s)" % (cmd, nid),
                               "UM.on_btnInstall_clicked", "debug")
            else:
                msg = self.getDistUpgradeInfo()
                answer = True
                if msg != "":
                    answer = self.showConfirmationDlg(contMsg, msg)
                if answer:
                    cmd = "%s dist-upgrade" % self.umglobal.settings[
                        'apt-get-string']
                    #if self.umglobal.newUpd:
                    pre = join(
                        self.umglobal.filesDir,
                        self.umglobal.settings['pre-upd'].replace(
                            "[VERSION]", self.umglobal.serverUpdVersion))
                    post = join(
                        self.umglobal.filesDir,
                        self.umglobal.settings['post-upd'].replace(
                            "[VERSION]", self.umglobal.serverUpdVersion))
                    if exists(pre):
                        cmd = "/bin/bash %(pre)s; %(cmd)s" % {
                            "pre": pre,
                            "cmd": cmd
                        }
                    if exists(post):
                        cmd = "%(cmd)s; /bin/bash %(post)s" % {
                            "cmd": cmd,
                            "post": post
                        }
                    nid = 'umupd'
                    self.prepForCommand(nid)
                    if self.quickUpdate:
                        self.ec.run(cmd)
                    else:
                        self.terminal.executeCommand(cmd, nid)
                    self.log.write("Execute command: %s (%s)" % (cmd, nid),
                                   "UM.on_btnInstall_clicked", "debug")

        else:
            if not self.quickUpdate:
                MessageDialog(self.btnInstall.get_label(), self.uptodateText)

        return nid

    def on_btnRefresh_clicked(self, widget):
        self.refresh()

    def on_btnPackages_clicked(self, widget):
        self.showPackages()

    def on_btnOutput_clicked(self, widget):
        self.showOutput()

    def on_btnInfo_clicked(self, widget):
        self.showInfo()

    def on_btnPreferences_clicked(self, widget):
        # Run preferences in its own thread
        pref_thread = threading.Thread(target=self.openPreferences)
        pref_thread.setDaemon(True)
        pref_thread.start()

    def openPreferences(self):
        os.system("updatemanager -p")

    def on_btnMaintenance_clicked(self, widget):
        self.showMaintenance()

    def on_radCleanCache_toggled(self, widget):
        if widget.get_active():
            self.fillTreeViewMaintenance()

    def on_radUnneeded_toggled(self, widget):
        if widget.get_active():
            message = "%s\n\n%s" % (
                _("You might need to run this several times."),
                self.lblMaintenanceHelp.get_label().replace("\n", " "))
            WarningDialog(self.btnMaintenance.get_label(), message)
            self.fillTreeViewMaintenance()

    def on_radNotavailable_toggled(self, widget):
        if widget.get_active():
            message = "%s\n\n%s" % (
                _("Removing not available packages may break your system!"),
                self.lblMaintenanceHelp.get_label().replace("\n", " "))
            WarningDialog(self.btnMaintenance.get_label(), message)
            self.fillTreeViewMaintenance()

    def on_radOldKernel_toggled(self, widget):
        if widget.get_active():
            message = "%s\n\n%s" % (
                _("Once removed you will not be able to boot these kernels!"),
                self.lblMaintenanceHelp.get_label().replace("\n", " "))
            WarningDialog(self.btnMaintenance.get_label(), message)
            self.fillTreeViewMaintenance()

    def on_radDowngradable_toggled(self, widget):
        if widget.get_active():
            message = "%s\n\n%s" % (
                _("Downgrading packages may break your system!"),
                self.lblMaintenanceHelp.get_label().replace("\n", " "))
            WarningDialog(self.btnMaintenance.get_label(), message)
            self.fillTreeViewMaintenance()

    def on_btnMaintenanceExecute_clicked(self, widget):
        self.executeMaintenance()

    def on_chkMaintenanceSelectAll_toggled(self, widget):
        self.tvMaintenanceHandler.treeviewToggleAll(
            toggleColNrList=[0], toggleValue=widget.get_active())

    # ===============================================
    # Maintenance functions
    # ===============================================

    def enableMaintenance(self, enable=True):
        self.btnMaintenanceExecute.set_sensitive(enable)
        self.radUnneeded.set_sensitive(enable)
        self.radCleanCache.set_sensitive(enable)
        self.radDowngradable.set_sensitive(enable)
        self.radOldKernel.set_sensitive(enable)
        self.radNotavailable.set_sensitive(enable)
        self.chkMaintenanceSelectAll.set_sensitive(enable)
        if enable:
            self.chkMaintenanceSelectAll.set_active(False)

    def fillTreeViewMaintenance(self):
        blnCleanCache = self.radCleanCache.get_active()
        blnUnneeded = self.radUnneeded.get_active()
        blnDowngradable = self.radDowngradable.get_active()
        blnNotavailable = self.radNotavailable.get_active()
        blnOldKernels = self.radOldKernel.get_active()

        self.enableMaintenance(False)

        columnTypesList = ['bool', 'str', 'str', 'str']
        packages = [["", _("Package"), _("Installed"), _("Available")]]
        # Clear the treeview first
        self.tvMaintenanceHandler.fillTreeview(packages, columnTypesList, 0,
                                               400, True)

        msg = ""
        if blnCleanCache:
            msg = _("Hit the Execute button to clean the cache.")
            columnTypesList = ['str']
            packages.append([msg])
        elif blnUnneeded:
            msg = self.radUnneeded.get_label()
            self.apt.createPackageLists("apt-get autoremove")
            for pck in self.apt.removedPackages:
                packages.append([False, pck[0], pck[1], pck[2]])
            self.apt.createPackageLists()
            # Add orphaned files
            self.apt.fillOrphanedPackages()
            for pck in self.apt.orphanedPackages:
                packages.append([False, pck[0], pck[1], pck[2]])
        elif blnNotavailable:
            msg = self.radNotavailable.get_label()
            self.apt.fillNotAvailablePackages()
            for pck in self.apt.notavailablePackages:
                if pck[0][0:6] != "linux-":
                    packages.append([False, pck[0], pck[1], ""])
        elif blnOldKernels:
            msg = self.radOldKernel.get_label()
            self.apt.fillKernelPackages()
            for pck in self.apt.kernelPackages:
                if "headers-486" not in pck[0] \
                    and "headers-586" not in pck[0] \
                    and "headers-686" not in pck[0] \
                    and "headers-amd64" not in pck[0] \
                    and "image-486" not in pck[0] \
                    and "image-586" not in pck[0] \
                    and "image-686" not in pck[0] \
                    and "image-amd64" not in pck[0]:

                    checkVersion = self.kernelVersion
                    if "kbuild" in pck[0]:
                        indMinus = self.kernelVersion.index("-")
                        indZero = self.kernelVersion.index("0")
                        if indZero > 0 and indZero < indMinus:
                            ind = indZero - 1
                        else:
                            ind = indMinus
                        if ind > 0:
                            checkVersion = self.kernelVersion[0:ind]
                    if checkVersion not in pck[0]:
                        packages.append([False, pck[0], pck[1], ""])
        elif blnDowngradable:
            msg = self.radDowngradable.get_label()
            self.apt.fillDowngradablePackages()
            for pck in self.apt.downgradablePackages:
                packages.append([False, pck[0], pck[1], pck[2]])

        if len(packages) > 1:
            self.tvMaintenanceHandler.fillTreeview(packages, columnTypesList,
                                                   0, 400, True)
        else:
            if not blnCleanCache:
                msg = _("\"%s\"\n did not return any results.") % msg
                MessageDialog(self.btnMaintenance.get_label(), msg)

        self.enableMaintenance(True)

    def executeMaintenance(self):
        blnCleanCache = self.radCleanCache.get_active()
        blnDowngradable = self.radDowngradable.get_active()
        blnNotNeeded = self.radUnneeded.get_active()
        downgradeString = ""
        deleteString = ""
        updateGrub = False
        cmd = ""

        self.enableMaintenance(False)

        if blnCleanCache:
            safe = False
            msg = _(
                "Do you want to completely clean the apt cache?\n\n"
                "When No, only unavailable installation packages are removed.")
            answer = QuestionDialog(self.radCleanCache.get_label(), msg)
            if answer:
                safe = True
            self.apt.cleanCache(safe)
            msg = _("Apt cache has been cleaned.")
            MessageDialog(self.radCleanCache.get_label(), msg)
        else:
            # Get user selected packages
            model = self.tvMaintenance.get_model()
            itr = model.get_iter_first()
            while itr is not None:
                sel = model.get_value(itr, 0)
                if sel:
                    pck = model.get_value(itr, 1)
                    avVer = model.get_value(itr, 3)
                    if blnDowngradable:
                        downgradeString += " %(pck)s=%(avVer)s" % {
                            "pck": pck,
                            "avVer": avVer
                        }
                    else:
                        deleteString += " %s" % pck
                        if "linux-image" in pck:
                            updateGrub = True
                itr = model.iter_next(itr)

            if downgradeString != "":
                cmd = "%s install %s" % (
                    self.umglobal.settings['apt-get-string'], downgradeString)
            elif deleteString != "":
                cmd = "%s purge %s" % (
                    self.umglobal.settings['apt-get-string'], deleteString)

        if cmd != "":
            self.apt.createPackageLists(cmd)
            msg = self.getDistUpgradeInfo()
            answer = True
            if msg != "":
                contMsg = _("Execute maintenance changes?")
                answer = self.showConfirmationDlg(contMsg, msg)
            if answer:
                if updateGrub:
                    cmd += "; update-grub"
                if blnNotNeeded:
                    cmd += "; %s purge $(COLUMNS=132 dpkg -l | grep ^rc | awk '{ print $2 }')" % self.umglobal.settings[
                        'apt-get-string']
                self.showOutput()
                nid = 'ummaintenance'
                self.prepForCommand(nid)
                self.terminal.executeCommand(cmd, nid)
                self.log.write("Execute command: %s (%s)" % (cmd, nid),
                               "UM.executeMaintenance", "debug")

        self.enableMaintenance(True)

    # ===============================================
    # General functions
    # ===============================================

    def prepForCommand(self, nid):
        os.system("touch %s" % self.umglobal.umfiles[nid])
        if not self.quickUpdate:
            self.btnRefresh.set_sensitive(False)
            self.btnInstall.set_sensitive(False)
            self.btnMaintenance.set_sensitive(False)

    def on_line_added(self, terminal, line):
        if line.strip()[0:2].upper() == "E:":
            self.log.write(line, "UM.on_line_added", "error")
        else:
            self.log.write(line, "UM.on_line_added", "info")

    def on_command_done(self, terminal, pid, nid):
        if nid != "init":
            self.log.write("Command finished (pid=%s, nid=%s)" % (pid, nid),
                           "UM.on_command_done", "info")
            if nid == "uminstallum":
                # Reload UM
                self.log.write(
                    "Updating UM: kill process of updatemanagerpref.py",
                    "UM.on_command_done", "debug")
                self.umglobal.killScriptProcess("updatemanagerpref.py")
                # Reload tray as user
                self.log.write(
                    "Updating UM: kill process of updatemanagertray.py",
                    "UM.on_command_done", "debug")
                self.umglobal.killScriptProcess("updatemanagertray.py")
                cmd = "sudo -u {} updatemanager -t -r".format(self.user)
                os.system(cmd)
                self.log.write(
                    "UM updated: reload tray as user {}".format(self.user),
                    "UM.on_command_done", "debug")
                # Reload UM window
                cmd = join(self.umglobal.scriptDir, "updatemanager.py")
                if self.quickUpdate:
                    cmd = join(self.umglobal.scriptDir, "updatemanager.py -q")
                self.umglobal.reloadWindow(cmd, self.user)
                self.log.write(
                    "UM updated: reload {0} as user {1}".format(
                        cmd, self.user), "UM.on_command_done", "debug")
            elif nid == "umrefresh":
                # Build installed packages info list
                self.apt.createPackagesInfoList()
                # Run post update when needed
                self.postUpdate()
            elif nid == "ummaintenance":
                self.enableMaintenance(True)
                self.fillTreeViewMaintenance()
                self.btnInstall.set_sensitive(True)
                self.btnRefresh.set_sensitive(True)
                self.btnPackages.set_sensitive(True)
                self.btnMaintenance.set_sensitive(True)
                self.showMaintenance()
            elif nid == 'umupd':
                # Save update version in hist file
                self.umglobal.saveHistVersion("upd",
                                              self.umglobal.serverUpdVersion)
                self.log.write(
                    "Save history upd=%s" % self.umglobal.serverUpdVersion,
                    "UM.on_command_done", "debug")
                self.deleteScripts()

            # Refresh data after install or update
            self.umglobal.collectData()
            self.apt.createPackageLists()
            self.fillTreeView()

            # Enable the buttons and load the info page
            self.log.write(
                "Re-initiate window after terminal command: %s" %
                str(not self.quickUpdate), "UM.on_command_done", "debug")
            if not self.quickUpdate:
                self.btnInstall.set_sensitive(True)
                self.btnRefresh.set_sensitive(True)
                self.btnPackages.set_sensitive(True)
                self.btnMaintenance.set_sensitive(True)
                self.btnInfo.set_sensitive(True)
                self.loadInfo()

                if self.umglobal.newUpd:
                    self.showInfo()
                else:
                    # This throws a lock file error in Plasma5
                    #aptHasErrors = self.apt.aptHasErrors()
                    #if aptHasErrors is not None:
                    #MessageDialog(self.aptErrorText, aptHasErrors)
                    #el
                    if self.upgradables:
                        self.showPackages()
                    else:
                        self.showInfo()
                        MessageDialog(self.btnInfo.get_label(),
                                      self.uptodateText)
                self.log.write("Re-initiate window complete",
                               "UM.on_command_done", "debug")

            # Cleanup name file(s)
            for fle in glob(join(self.umglobal.filesDir, '.um*')):
                remove(fle)

    def createLogString(self, packagesList):
        lst = []
        for data in packagesList:
            lst.append(data[0])
        return ' '.join(lst)

    def refresh(self):
        # Refresh server info
        print((self.umglobal.hasInternet))
        self.umglobal.getServerInfo()
        print((self.umglobal.hasInternet))

        # Check of programs locking apt
        prog = self.apt.getAptCacheLockedProgram(
            self.umglobal.settings["apt-packages"])
        if prog is not None:
            msg = _("Another program is locking the apt cache\n\n"
                    "Please, close the program before refreshing:\n"
                    "* %s" % prog)
            MessageDialog(self.btnRefresh.get_label(), msg)
            self.log.write("%s is locking the apt cache" % prog, "UM.refresh",
                           "warning")
        elif self.umglobal.hasInternet:
            if not self.quickUpdate:
                # Update the apt cache
                self.btnPreferences.set_sensitive(True)
                self.btnOutput.set_sensitive(True)
                self.btnRefresh.set_sensitive(False)
                self.btnInstall.set_sensitive(False)
                self.btnMaintenance.set_sensitive(False)
                self.btnPackages.set_sensitive(True)
                self.showOutput()
            cmd = "dpkg --configure -a; %s -f install; apt-get update" % self.umglobal.settings[
                'apt-get-string']
            nid = 'umrefresh'
            self.prepForCommand(nid)
            if self.quickUpdate:
                self.ec.run(cmd)
            else:
                self.terminal.executeCommand(cmd, nid)
            self.apt.initAptShowVersions()
            self.log.write("Execute command: %s (%s)" % (cmd, nid),
                           "UM.refresh", "debug")
        else:
            if not self.quickUpdate:
                # No internet connection
                self.btnInstall.set_sensitive(True)
                self.btnRefresh.set_sensitive(True)
                self.btnPackages.set_sensitive(True)
                self.btnMaintenance.set_sensitive(True)
                self.btnInfo.set_sensitive(True)
                self.btnOutput.set_sensitive(True)
                self.btnPreferences.set_sensitive(True)
                self.loadInfo()
                self.showInfo()

    def postUpdate(self):
        # Check for changed version information
        if self.umglobal.newUpd and self.umglobal.serverUpdVersion is not None:
            self.getScripts([
                self.umglobal.settings['pre-upd'].replace(
                    "[VERSION]", self.umglobal.serverUpdVersion),
                self.umglobal.settings['post-upd'].replace(
                    "[VERSION]", self.umglobal.serverUpdVersion)
            ])

    def fillTreeView(self):
        # First check if this application is upgradable
        self.upgradableUM = self.getUpgradablePackages(
            packageNames=["updatemanager"])
        if self.upgradableUM:
            self.upgradables = self.upgradableUM
        else:
            # Get a list of packages that can be upgraded
            self.upgradableUM = []
            self.upgradables = self.getUpgradablePackages()
            #if not self.upgradables:
            ## Check for black listed packages
            #cmd = "dpkg --get-selections | grep hold$ | awk '{print $1}'"
            #lst = self.ec.run(cmd, False)
            #for pck in lst:
            #self.upgradables.append([pck.strip(), _("blacklisted"), ""])

        if not self.quickUpdate:
            contentList = [[
                _("Package"),
                _("Current version"),
                _("New version")
            ]] + self.upgradables
            self.tvHandler.fillTreeview(contentList=contentList,
                                        columnTypesList=['str', 'str', 'str'],
                                        firstItemIsColName=True)

    def getUpgradablePackages(self, packageNames=[]):
        upckList = []
        if packageNames:
            upckList = []
            for packageName in packageNames:
                for upck in self.apt.upgradablePackages:
                    if upck[0] == packageName:
                        upckList.append(upck)
                        break
        else:
            upckList = self.apt.upgradablePackages
        return upckList

    def getDistUpgradeInfo(self, upgradablesOnly=False):
        info = ""
        if upgradablesOnly:
            if self.apt.upgradablePackages:
                info = "<strong>%s</strong><br>" % self.apt.upgradablePackagesText
                for pck in self.apt.upgradablePackages:
                    info += "%s " % pck[0]
        else:
            if self.apt.removedPackages:
                info = "<strong>%s</strong><br>" % self.removedPackagesText
                for pck in self.apt.removedPackages:
                    info += "%s " % pck[0]
            if self.apt.newPackages:
                if info != "":
                    info += "<p>&nbsp;</p>"
                info += "<strong>%s</strong><br>" % self.newPackagesText
                for pck in self.apt.newPackages:
                    info += "%s " % pck[0]
            if self.apt.heldbackPackages:
                if info != "":
                    info += "<p>&nbsp;</p>"
                info += "<strong>%s</strong><br>" % self.heldbackPackagesText
                for pck in self.apt.heldbackPackages:
                    info += "%s " % pck[0]
            if self.apt.downgradablePackages:
                if info != "":
                    info += "<p>&nbsp;</p>"
                info += "<strong>%s</strong><br>" % self.downgradePackagesText
                for pck in self.apt.downgradablePackages:
                    info += "%s " % pck[0]
        return info

    def showPackages(self):
        self.nbMain.set_current_page(0)

    def showOutput(self):
        self.nbMain.set_current_page(1)
        self.terminal.grab_focus()

    def showInfo(self):
        self.nbMain.set_current_page(2)

    def showMaintenance(self):
        self.nbMain.set_current_page(3)

    def showConfirmationDlg(self, title, message):
        head = "<html><head><style>body { font-family: Arial, Helvetica, Verdana, Sans-serif; font-size: 12px; color: #555555; background: #ffffff; }</style></head><body>"
        end = "</body></html>"
        html = "%s%s%s" % (head, message, end)
        sw = Gtk.ScrolledWindow()
        sw.add(SimpleBrowser(html))
        return CustomQuestionDialog(title, sw, 550, 300, self.window).show()

    # Get pre-install script and post-install script from the server
    def getScripts(self, files):
        for fle in files:
            flePath = join(self.umglobal.filesDir, fle)
            if not exists(flePath):
                # Get the new scripts if they exist
                url = "%s/%s/%s" % (self.umglobal.settings['solydxk'],
                                    self.umglobal.settings["umfilesdir"], fle)
                try:
                    txt = urlopen(url).read().decode('utf-8')
                    if txt != '':
                        # Save to a file and make executable
                        self.log.write("Save script = %s" % flePath,
                                       "UM.getScripts", "debug")
                        with open(flePath, 'w') as f:
                            f.write(txt)
                        chmod(flePath, 0o755)
                except:
                    pass

    def loadInfo(self):
        languageDir = self.get_language_dir()
        url = join("file://%s" % languageDir,
                   self.umglobal.settings['up-to-date'])
        self.btnInfo.set_icon_name("help-about")
        if self.upgradables:
            url = join("file://%s" % languageDir,
                       self.umglobal.settings['updates'])
            if self.umglobal.newUpd:
                url = "%s/%s/%s" % (self.umglobal.settings['solydxk'],
                                    self.umglobal.settings["umfilesdir"],
                                    self.umglobal.settings['upd-info'])
        elif self.umglobal.serverUpdVersion is None:
            url = join("file://%s" % languageDir,
                       self.umglobal.settings['not-found'])

        self.log.write("Load info url: %s" % url, "UM.loadInfo", "debug")

        children = self.swInfo.get_children()
        if children:
            children[0].openUrl(url)
        else:
            self.swInfo.add(SimpleBrowser(url))

    def get_language_dir(self):
        # First test if full locale directory exists, e.g. html/pt_BR,
        # otherwise perhaps at least the language is there, e.g. html/pt
        # and if that doesn't work, try html/pt_PT
        lang = self.get_current_language()
        path = join(self.umglobal.htmlDir, lang)
        if not isdir(path):
            base_lang = lang.split('_')[0].lower()
            path = join(self.umglobal.htmlDir, base_lang)
            if not isdir(path):
                path = join(self.umglobal.htmlDir,
                            "{}_{}".format(base_lang, base_lang.upper()))
                if not isdir(path):
                    path = join(self.umglobal.htmlDir, 'en')
        return path

    def get_current_language(self):
        lang = os.environ.get('LANG', 'US').split('.')[0]
        if lang == '':
            lang = 'en'
        return lang

    def pushMessage(self, message):
        if message is not None:
            context = self.statusbar.get_context_id('message')
            self.statusbar.push(context, message)

    def checkFilesDir(self):
        if not exists(self.umglobal.filesDir):
            makedirs(self.umglobal.filesDir)
        oldFiles = glob(join(self.umglobal.scriptDir, 'pre-*')) + \
                   glob(join(self.umglobal.scriptDir, 'post-*')) + \
                   [join(self.umglobal.scriptDir, 'updatemanager.hist')] + \
                   [join(self.umglobal.scriptDir, 'mirrors.list')]
        for fle in oldFiles:
            if exists(fle):
                fleName = basename(fle)
                if not exists(join(self.umglobal.filesDir, fleName)):
                    move(fle, self.umglobal.filesDir)
                else:
                    remove(fle)
        chmod(self.umglobal.filesDir, 0o777)

    def deleteScripts(self, UpdVersion=None):
        UpdVersion = "*"
        if UpdVersion is not None:
            UpdVersion = "*%s" % UpdVersion

        oldFiles = glob(join(
            self.umglobal.filesDir, "pre-%s" % UpdVersion)) + glob(
                join(self.umglobal.filesDir, "post-%s" % UpdVersion))
        for fle in oldFiles:
            remove(fle)
            self.log.write("Cleanup file: %s" % fle, "UM.deleteScripts",
                           "debug")

    # Close the gui
    def on_windowMain_destroy(self, widget):
        # Close the app
        Gtk.main_quit()
Exemplo n.º 11
0
class Constructor(object):

    def __init__(self):
        self.scriptDir = abspath(dirname(__file__))
        self.shareDir = join(self.scriptDir, '../../../share/solydxk/constructor')

        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(join(self.shareDir, 'constructor.glade'))

        # Main window objects
        go = self.builder.get_object
        self.window = go('constructorWindow')
        self.tvDistros = go('tvDistros')
        self.lblOutput = go('lblOutput')
        self.statusbar = go('statusbar')
        self.btnAdd = go('btnAdd')
        self.chkSelectAll = go('chkSelectAll')
        self.btnRemove = go('btnRemove')
        self.btnEdit = go('btnEdit')
        self.btnUpgrade = go('btnUpgrade')
        self.btnLocalize = go('btnLocalize')
        self.btnBuildIso = go('btnBuildIso')

        # Add iso window objects
        self.windowAddDistro = go('addDistroWindow')
        self.txtIso = go('txtIso')
        self.txtDir = go('txtDir')
        self.btnDir = go('btnDir')
        self.btnSave = go('btnSave')
        self.btnHelp = go('btnHelp')
        self.lblIso = go('lblIso')
        self.boxIso = go('boxIso')
        self.lblDir = go('lblDir')
        self.chkFromIso = go('chkFromIso')

        # Main window translations
        self.window.set_title(_("SolydXK Constructor"))
        self.chkSelectAll.set_label(_("Select all"))
        self.btnAdd.set_label("_{}".format(_("Add")))
        self.btnRemove.set_label("_{}".format(_("Remove")))
        self.btnEdit.set_label("_{}".format(_("Edit")))
        self.btnUpgrade.set_label("_{}".format(_("Upgrade")))
        self.btnLocalize.set_label("_{}".format(_("Localize")))
        self.btnBuildIso.set_label("_{}".format(_("Build")))
        self.btnHelp.set_label("_{}".format(_("Help")))

        # Add iso window translations
        self.lblIso.set_text(_("ISO"))
        go('btnCancel').set_label("_{}".format(_("Cancel")))

        # Init
        self.ec = ExecCmd()
        self.ec.run("modprobe loop", False)
        self.queue = Queue()
        self.mountDir = "/mnt/constructor"
        self.distroFile = join(self.scriptDir, "distros.list")
        self.distroAdded = False
        self.iso = None
        self.dir = None
        self.isoName = None
        self.doneWav = join(self.shareDir, 'done.wav')
        self.help = join(self.shareDir, 'help.html')
        self.chkFromIso.set_active(True)
        self.toggleGuiElements(False)
        self.hostEfiArchitecture = functions.getHostEfiArchitecture()

        # Treeviews
        self.tvHandlerDistros = TreeViewHandler(self.tvDistros)
        self.fillTreeViewDistros()

        # Version information
        ver = _("Version")
        self.version = "%s: %s" % (ver, functions.getPackageVersion('solydxk-constructor'))
        self.showOutput(self.version)

        # Connect the signals and show the window
        self.builder.connect_signals(self)
        self.window.show()

    # ===============================================
    # Main Window Functions
    # ===============================================

    def on_btnAdd_clicked(self, widget):
        self.windowAddDistro.show()

    def on_btnRemove_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            qd = QuestionDialog(self.btnRemove.get_label(), _("Are you sure you want to remove the selected distribution from the list?\n" \
                                                              "(This will not remove the directory and its data)"), self.window)
            answer = qd.show()
            if answer:
                self.saveDistroFile(distroPath=path, addDistro=False)
        self.fillTreeViewDistros()

    def on_btnEdit_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            de = EditDistro(path)
            services = []
            if exists(join(path, 'root/etc/apache2/apache2.conf')):
                services.append("apache2")
            if exists(join(path, 'root/etc/mysql/debian.cnf')):
                services.append("mysql")
            if services:
                msg = "If you need to update packages that depend on these services,\n" \
                      "you will need to manually start them:\n"
                for service in services:
                    msg += "\nservice %s start" % service
                msg += "\n\nWhen done:\n"
                for service in services:
                    msg += "\nservice %s stop" % service
                self.showInfo(_("Services detected"), msg, self.window)
                functions.repaintGui()
            de.openTerminal()

    def on_btnUpgrade_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        upgraded = False
        for path in selected:
            upgraded = True
            rootPath = "%s/root" % path
            de = EditDistro(path)
            de.openTerminal("apt-get update")
            if exists(join(rootPath, 'etc/apache2/apache2.conf')):
                de.openTerminal("service apache2 start")
            if exists(join(rootPath, 'etc/mysql/debian.cnf')):
                de.openTerminal("service mysql start")
            de.openTerminal("apt-get -y --force-yes -o Dpkg::Options::=\"--force-confnew\" dist-upgrade")
            if exists(join(rootPath, 'etc/apache2/apache2.conf')):
                de.openTerminal("service apache2 stop")
            if exists(join(rootPath, 'etc/mysql/debian.cnf')):
                de.openTerminal("service mysql stop")

            # Cleanup old kernel and headers
            script = "rmoldkernel.sh"
            scriptSource = join(self.scriptDir, "files/{}".format(script))
            scriptTarget = join(rootPath, script)
            if exists(scriptSource):
                copy(scriptSource, scriptTarget)
                self.ec.run("chmod a+x %s" % scriptTarget)
                de.openTerminal("/bin/bash %s" % script)
                remove(scriptTarget)

            # Build EFI files
            if self.hostEfiArchitecture != "":
                print(">> Start building EFI files")
                self.build_efi_files()

            # Download offline packages
            print(">> Start downloading offline packages")
            self.download_offline_packages()

        if upgraded and exists("/usr/bin/aplay") and exists(self.doneWav):
            self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False)

    def on_btnLocalize_clicked(self, widget):
        # Set locale
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            rootPath = "%s/root" % path
            de = EditDistro(path)
            script = "setlocale.sh"
            scriptSource = join(self.scriptDir, "files/{}".format(script))
            scriptTarget = join(rootPath, script)
            if exists(scriptSource):
                copy(scriptSource, scriptTarget)
                self.ec.run("chmod a+x %s" % scriptTarget)
                de.openTerminal("/bin/bash %s" % script)
                remove(scriptTarget)

    def build_efi_files(self):

        # TODO - also 32-bit installs (haven't tested this)

        modules = "part_gpt part_msdos ntfs ntfscomp hfsplus fat ext2 normal chain boot configfile linux " \
                "multiboot iso9660 gfxmenu gfxterm loadenv efi_gop efi_uga loadbios fixvideo png " \
                "ext2 ntfscomp loopback search minicmd cat cpuid appleldr elf usb videotest " \
                "halt help ls reboot echo test normal sleep memdisk tar font video_fb video " \
                "gettext true  video_bochs video_cirrus multiboot2 acpi gfxterm_background gfxterm_menu"

        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            rootPath = "%s/root" % path
            bootPath = "{}/boot".format(path)
            arch = functions.getGuestEfiArchitecture(rootPath)

            grubEfiName = "bootx64"
            efiName = "x64"
            if arch != "x86_64":
                arch = "i386"
                grubEfiName = "bootia32"
                efiName = "ia32"

            try:
                if not exists("{}/efi/boot".format(bootPath)):
                    makedirs("{}/efi/boot".format(bootPath))

                self.ec.run("efi-image {}/~tmp {}-efi {}".format(bootPath, arch, efiName))
                if exists("{}/~tmp/efi.img".format(bootPath) and
                   exists("{}/~tmp/boot/grub/{}-efi".format(bootPath, arch))):
                    self.ec.run("rm -r {}/boot/grub/{}-efi".format(bootPath, arch))
                    self.ec.run("mv -vf {}/~tmp/boot/grub/{}-efi {}/boot/grub/".format(bootPath, arch, bootPath))
                    self.ec.run("mv -vf {}/~tmp/efi.img {}/boot/grub/".format(bootPath, bootPath))
                    self.ec.run("rm -r {}/~tmp".format(bootPath))

                self.ec.run("grub-mkimage -O {}-efi -d /usr/lib/grub/{}-efi "
                            "-o {}/efi/boot/{}.efi "
                            "-p \"/boot/grub\" {}".format(arch, arch, bootPath, grubEfiName, modules))

                print((">> Finished building EFI files"))

            except Exception as detail:
                self.showError("Error: build EFI files", detail, self.window)

    def download_offline_packages(self):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            rootPath = "%s/root" % path
            arch = functions.getGuestEfiArchitecture(rootPath)
            de = EditDistro(path)
            script = "offline.sh"
            scriptSource = join(self.scriptDir, "files/{}".format(script))
            scriptTarget = join(rootPath, script)
            offlineSource = join(rootPath, "offline")
            offlineTarget = join(rootPath, "../boot/offline")
            if exists(scriptSource):
                try:
                    copy(scriptSource, scriptTarget)
                    self.ec.run("chmod a+x %s" % scriptTarget)
                    # Run the script
                    de.openTerminal("/bin/bash {} {}".format(script, arch))
                    # Remove script
                    remove(scriptTarget)
                    # Move offline directory to boot directory
                    if exists(offlineSource):
                        print(("%s exists" % offlineSource))
                        if exists(offlineTarget):
                            print((">> Remove %s" % offlineTarget))
                            rmtree(offlineTarget)
                        print((">> Move %s to %s" % (offlineSource, offlineTarget)))
                        move(offlineSource, offlineTarget)
                    else:
                        print((">> Cannot find: %s" % offlineSource))
                except Exception as detail:
                    self.showError("Error: getting offline packages", detail, self.window)
            else:
                print((">> Cannot find: %s" % scriptSource))


    def on_btnBuildIso_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        msg = ""
        for path in selected:
            self.toggleGuiElements(True)
            self.showOutput("Start building ISO in: %s" % path)
            functions.repaintGui()

            # Start building the ISO in a thread
            t = BuildIso(path, self.queue)
            t.start()
            self.queue.join()

            # Thread is done
            # Get the data from the queue
            ret = self.queue.get()
            self.queue.task_done()

            if ret is not None:
                self.showOutput(ret)
                if "error" in ret.lower():
                    self.showError("Error", ret, self.window)
                else:
                    msg += "%s\n" % ret

        if msg != "":
            if exists("/usr/bin/aplay") and exists(self.doneWav):
                self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False)
            self.showInfo("", msg, self.window)
        self.toggleGuiElements(False)

    def on_chkSelectAll_toggled(self, widget):
        self.tvHandlerDistros.treeviewToggleAll(toggleColNrList=[0], toggleValue=widget.get_active())

    def on_tvDistros_row_activated(self, widget, path, column):
        self.tvHandlerDistros.treeviewToggleRows(toggleColNrList=[0])

    def on_btnHelp_clicked(self, widget):
        if functions.isPackageInstalled("firefox"):
            system("firefox file://%s &" % self.help)
        else:
            system("xdg-open file://%s &" % self.help)

    def on_btnOpenDir_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            system("xdg-open %s &" % path)

    def fillTreeViewDistros(self, selectDistros=[]):
        contentList = [[_("Select"), _("Distribution"), _("Working directory")]]
        distros = self.getDistros()
        for distro in distros:
            select = False
            for selectDistro in selectDistros:
                if distro[0] == selectDistro:
                    select = True
            contentList.append([select, distro[0], distro[1]])
        self.tvHandlerDistros.fillTreeview(contentList=contentList, columnTypesList=['bool', 'str', 'str'], firstItemIsColName=True)

    def getDistros(self):
        distros = []
        if exists(self.distroFile):
            with open(self.distroFile, 'r') as f:
                lines = f.readlines()
            for line in lines:
                line = line.strip().rstrip('/')
                print(line)
                if exists(line):
                    dg = DistroGeneral(line)
                    isoName = dg.description
                    distros.append([isoName, line])
            # Sort on iso name
            if distros:
                distros = sorted(distros, key=operator.itemgetter(0))
        return distros

    # ===============================================
    # Add ISO Window Functions
    # ===============================================

    def on_btnIso_clicked(self, widget):
        fleFilter = Gtk.FileFilter()
        fleFilter.set_name("ISO")
        fleFilter.add_mime_type("application/x-cd-image")
        fleFilter.add_pattern("*.iso")

        startDir = None
        if exists(dirname(self.txtIso.get_text())):
            startDir = dirname(self.txtIso.get_text())

        filePath = SelectFileDialog(title=_('Select SolydXK ISO'), start_directory=startDir, parent=self.window, gtkFileFilter=fleFilter).show()
        if filePath is not None:
            self.txtIso.set_text(filePath)

    def on_btnDir_clicked(self, widget):
        startDir = None
        if exists(self.txtDir.get_text()):
            startDir = self.txtDir.get_text()
        dirText = SelectDirectoryDialog(title=_('Select directory'), start_directory=startDir, parent=self.window).show()
        if dirText is not None:
            self.txtDir.set_text(dirText)

    def on_btnSave_clicked(self, widget):
        self.iso = ""
        if self.chkFromIso.get_active():
            self.iso = self.txtIso.get_text()
        self.dir = self.txtDir.get_text()

        title = _("Save existing working directory")
        if self.iso != "":
            title = _("Unpack ISO and save")

        if not exists(self.dir):
            makedirs(self.dir)

        if not exists(self.dir):
            self.showError(title, _("Could not create directory %(dir)s: exiting" % {"dir": self.dir}), self.window)
        else:
            self.windowAddDistro.hide()
            if self.iso != "":
                if not exists(self.iso):
                    self.showInfo(self.btnSave.get_label(), _("The path to the ISO file does not exist:\n{}".format(self.iso)), self.window)
                    return
                if listdir(self.dir):
                    qd = QuestionDialog(self.btnSave.get_label(),
                        _("The destination directory is not empty.\n"
                        "Are you sure you want to overwrite all data in {}?".format(self.dir)),
                        self.window)
                    answer = qd.show()
                    if not answer:
                        return

                self.showOutput("Start unpacking the ISO...")
                self.toggleGuiElements(True)
                t = IsoUnpack(self.mountDir, self.iso, self.dir, self.queue)
                t.start()
                self.queue.join()
                GObject.timeout_add(1000, self.checkThread, True)
            else:
                self.saveDistroFile(self.dir, True)
                self.fillTreeViewDistros()
                self.showOutput(_("Existing working directory added"))
                #self.toggleGuiElements(False)

    def on_btnCancel_clicked(self, widget):
        self.windowAddDistro.hide()

    def on_addDistroWindow_delete_event(self, widget, data=None):
        self.windowAddDistro.hide()
        return True

    def on_txtIso_changed(self, widget):
        path = self.txtIso.get_text()
        if exists(path):
            self.txtDir.set_sensitive(True)
            self.btnDir.set_sensitive(True)
            if exists(self.txtDir.get_text()):
                self.btnSave.set_sensitive(True)
        else:
            self.txtDir.set_sensitive(False)
            self.btnDir.set_sensitive(False)
            self.btnSave.set_sensitive(False)

    def on_txtDir_changed(self, widget):
        blnFromIso = self.chkFromIso.get_active()
        isoPath = self.txtIso.get_text()
        dirText = self.txtDir.get_text()
        self.btnSave.set_sensitive(False)
        if exists(dirText):
            if blnFromIso:
                if exists(isoPath):
                    self.btnSave.set_sensitive(True)
            else:
                self.btnSave.set_sensitive(True)

    def on_chkFromIso_toggled(self, widget):
        if widget.get_active():
            self.lblIso.set_visible(True)
            self.boxIso.set_visible(True)
            self.txtDir.set_sensitive(False)
            self.btnDir.set_sensitive(False)
            self.btnSave.set_sensitive(False)
            self.lblDir.set_text(_("Unpack ISO to directory"))
            self.btnSave.set_label(_("Unpack & Save"))
        else:
            self.txtIso.set_text("")
            self.lblIso.set_visible(False)
            self.boxIso.set_visible(False)
            self.txtDir.set_sensitive(True)
            self.btnDir.set_sensitive(True)
            self.btnSave.set_sensitive(True)
            self.lblDir.set_text(_("Work directory"))
            self.btnSave.set_label(_("Save"))

    # ===============================================
    # General functions
    # ===============================================

    def showInfo(self, title, message, parent=None):
        MessageDialogSafe(title, message, Gtk.MessageType.INFO, parent).show()

    def showError(self, title, message, parent=None):
        MessageDialogSafe(title, message, Gtk.MessageType.ERROR, parent).show()

    def showOutput(self, message):
        print(message)
        functions.pushMessage(self.statusbar, message)

    def checkThread(self, addDistro=None):
        #print 'Thread count = ' + str(threading.active_count())
        # As long there's a thread active, keep spinning
        if threading.active_count() > 1:
            return True

        # Thread is done
        # Get the data from the queuez
        ret = self.queue.get()
        self.queue.task_done()

        # Thread is done
        if addDistro is not None:
            self.saveDistroFile(self.dir, addDistro)
            self.fillTreeViewDistros(self.isoName)
        self.toggleGuiElements(False)
        if exists("/usr/bin/aplay") and exists(self.doneWav):
            self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False)
        if ret is not None:
            self.showOutput(ret)
            if "error" in ret.lower():
                self.showError("Error", ret, self.window)
            else:
                self.showInfo("", ret, self.window)
        return False

    def toggleGuiElements(self, startThread):
        if startThread:
            self.chkSelectAll.set_sensitive(False)
            self.tvDistros.set_sensitive(False)
            self.btnAdd.set_sensitive(False)
            self.btnBuildIso.set_sensitive(False)
            self.btnEdit.set_sensitive(False)
            self.btnRemove.set_sensitive(False)
            self.btnUpgrade.set_sensitive(False)
        else:
            self.chkSelectAll.set_sensitive(True)
            self.tvDistros.set_sensitive(True)
            self.btnAdd.set_sensitive(True)
            self.btnBuildIso.set_sensitive(True)
            self.btnEdit.set_sensitive(True)
            self.btnRemove.set_sensitive(True)
            self.btnUpgrade.set_sensitive(True)

    def saveDistroFile(self, distroPath, addDistro=True):
        newCont = []
        dg = DistroGeneral(distroPath)
        self.isoName = dg.description

        cfg = []
        if exists(self.distroFile):
            with open(self.distroFile, 'r') as f:
                cfg = f.readlines()
            for line in cfg:
                line = line.strip()
                if distroPath not in line and exists(line):
                    newCont.append(line)

        if addDistro:
            newCont.append(distroPath)

        with open(self.distroFile, 'w') as f:
            f.write('\n'.join(newCont))

        self.iso = ""
        self.dir = ""

    # Close the gui
    def on_constructorWindow_destroy(self, widget):
        # Close the app
        Gtk.main_quit()
Exemplo n.º 12
0
class DDM(object):

    def __init__(self, test=False):
        # Testing
        self.test = test
        # Set to true for testing Optimus
        self.test_optimus = False

        # Load window and widgets
        self.scriptName = basename(__file__)
        self.scriptDir = abspath(dirname(__file__))
        self.mediaDir = join(self.scriptDir, '../../share/ddm')
        self.builder = Gtk.Builder()
        self.builder.add_from_file(join(self.mediaDir, 'ddm.glade'))

        # Main window objects
        go = self.builder.get_object
        self.window = go("ddmWindow")
        self.tvDDM = go("tvDDM")
        self.btnSave = go("btnSave")
        self.btnHelp = go("btnHelp")
        self.btnQuit = go("btnQuit")
        self.pbDDM = go("pbDDM")
        self.chkBackports = go("chkBackports")

        self.window.set_title(_("Device Driver Manager"))
        self.btnSave.set_label(_("Install"))
        self.btnHelp.set_label(_("Help"))
        self.btnQuit.set_label(_("Quit"))
        self.chkBackports.set_label(_("Use Backports"))

        # Initiate variables
        self.queue = Queue(-1)
        self.threads = {}
        self.hardware = []
        self.loadedDrivers = []
        self.notSupported = []
        self.paeBooted = False
        self.htmlDir = join(self.mediaDir, "html")
        self.helpFile = join(self.get_language_dir(), "help.html")
        log = getoutput("cat /usr/bin/ddm | grep 'LOG=' | cut -d'=' -f 2")
        self.logFile = log[0]
        self.log = Logger(self.logFile, addLogTime=False, maxSizeKB=5120)
        self.tvDDMHandler = TreeViewHandler(self.tvDDM)
        self.tvDDMHandler.connect('checkbox-toggled', self.tv_checkbox_toggled)

        # Connect builder signals and show window
        self.builder.connect_signals(self)
        self.window.show_all()

        # Fill treeview
        self.fill_treeview_ddm()

        # Check backports
        if len(self.hardware) < 2 or not has_backports():
            self.chkBackports.hide()

        self.get_loaded_graphical_driver()
        self.get_loaded_wireless_driver()

    # ===============================================
    # Language specific functions
    # ===============================================

    def get_language_dir(self):
        # First test if full locale directory exists, e.g. html/pt_BR,
        # otherwise perhaps at least the language is there, e.g. html/pt
        # and if that doesn't work, try html/pt_PT
        lang = self.get_current_language()
        path = join(self.htmlDir, lang)
        if not isdir(path):
            base_lang = lang.split('_')[0].lower()
            path = join(self.htmlDir, base_lang)
            if not isdir(path):
                path = join(self.htmlDir, "{}_{}".format(base_lang, base_lang.upper()))
                if not isdir(path):
                    path = join(self.htmlDir, 'en')
        return path

    def get_current_language(self):
        lang = os.environ.get('LANG', 'US').split('.')[0]
        if lang == '':
            lang = 'en'
        return lang

    # ===============================================
    # Main window functions
    # ===============================================

    def on_btnSave_clicked(self, widget):
        # Save selected hardware
        arguments = []

        model = self.tvDDM.get_model()
        itr = model.get_iter_first()
        while itr is not None:
            action = 'no change'
            selected = model.get_value(itr, 0)
            device = model.get_value(itr, 2)
            manufacturerId = ''

            # Check currently selected state with initial state
            # This decides whether we should install or purge the drivers
            for hw in self.hardware:
                self.log.write("Device = {} in {}".format(device, hw[2]), 'on_btnSave_clicked')
                if device in hw[2]:
                    manufacturerId = hw[4]
                    if hw[0] and not selected:
                        action = 'purge'
                    elif not hw[0] and selected:
                        action = 'install'
                    break

            self.log.write("{}: {} ({})".format(action, device, manufacturerId), 'on_btnSave_clicked')

            # Install/purge selected driver
            option = ""
            if action == 'install':
                option = "-i"
            elif action == 'purge':
                option = "-p"

            if option:
                driver = ''
                # Run the manufacturer specific bash script
                if manufacturerId == '1002':
                    driver = 'ati'
                elif manufacturerId == '10de':
                    driver = 'nvidia '
                elif manufacturerId == '14e4':
                    driver = 'broadcom '
                elif 'pae' in manufacturerId:
                    driver = 'pae '
                if driver:
                    arguments.append("{} {}".format(option, driver))

            # Get the next in line
            itr = model.iter_next(itr)

        # Execute the command
        if arguments:
            if '-i' in arguments and not hasInternetConnection():
                title = _("No internet connection")
                msg = _("You need an internet connection to install the additional software.\n"
                        "Please, connect to the internet and try again.")
                WarningDialog(title, msg)
            else:
                # Warn for use of Backports
                if self.chkBackports.get_active():
                    answer = QuestionDialog(self.chkBackports.get_label(),
                            _("You have selected to install drivers from the backports repository whenever they are available.\n\n"
                              "Although you can run more up to date software using the backports repository,\n"
                              "you introduce a greater risk of breakage doing so.\n\n"
                              "Are you sure you want to continue?"))
                    if not answer:
                        self.chkBackports.set_active(False)
                        return True
                    arguments.append("-b")

                # Testing
                if self.test:
                    arguments.append("-t")

                command = "ddm {}".format(" ".join(arguments))
                self.log.write("Command to execute: {}".format(command), 'on_btnSave_clicked')
                self.exec_command(command)

    def on_btnQuit_clicked(self, widget):
        self.on_ddmWindow_destroy(widget)

    def on_btnHelp_clicked(self, widget):
        # Open the help file as the real user (not root)
        shell_exec("%s/open-as-user \"%s\"" % (self.scriptDir, self.helpFile))

    def get_supported_hardware(self):
        # Fill self.hardware
        self.hardware = []

        # First row are column names
        self.hardware.append([_("Install"), '', _("Device"), 'driver', 'manid', 'deviceid'])

        # Get hardware information
        self.get_ati()
        self.get_nvidia()
        self.get_broadcom()
        self.get_pae()

    # This method is fired by the TreeView.checkbox-toggled event
    def tv_checkbox_toggled(self, obj, path, colNr, toggleValue):
        path = int(path)
        model = self.tvDDM.get_model()
        itr = model.get_iter(path)
        description = model[itr][2].lower()

        if 'pae' in description and not toggleValue and self.paeBooted:
            title = _("Remove kernel")
            msg = _("You cannot remove a booted kernel.\nPlease, boot another kernel and try again.")
            self.log.write(msg, 'tv_checkbox_toggled')
            WarningDialog(title, msg)
            model[itr][0] = True

    def fill_treeview_ddm(self):
        # Fill a list with supported hardware
        self.get_supported_hardware()

        # columns: checkbox, image (logo), device, driver
        columnTypes = ['bool', 'GdkPixbuf.Pixbuf', 'str']

        # Keep some info from the user
        showHw = []
        for hw in self.hardware:
            showHw.append([hw[0], hw[1], hw[2]])

        # Fill treeview
        self.tvDDMHandler.fillTreeview(contentList=showHw, columnTypesList=columnTypes, firstItemIsColName=True, fontSize=12000)

        # Show message if nothing is found or hardware is not supported
        title = _("Hardware scan")
        if self.notSupported:
            if len(self.hardware) < 2:
                self.set_buttons_state(False)
            msg = _("There are no available drivers for your hardware:")
            msg = "{}\n\n{}".format(msg, '\n'.join(self.notSupported))
            self.log.write(msg, 'fill_treeview_ddm')
            WarningDialog(title, msg)
        elif len(self.hardware) < 2:
            self.set_buttons_state(False)
            msg = _("DDM did not find any supported hardware.")
            self.log.write(msg, 'fill_treeview_ddm')
            MessageDialog(title, msg)

    def exec_command(self, command):
        try:
            # Run the command in a separate thread
            self.set_buttons_state(False)
            name = 'cmd'
            t = ExecuteThreadedCommands([command], self.queue)
            self.threads[name] = t
            t.daemon = True
            t.start()
            self.queue.join()
            GObject.timeout_add(250, self.check_thread, name)

        except Exception as detail:
            ErrorDialog(self.btnSave.get_label(), detail)

    def set_buttons_state(self, enable):
        if not enable:
            # Disable buttons
            self.btnSave.set_sensitive(False)
        else:
            # Enable buttons and reset progress bar
            self.btnSave.set_sensitive(True)
            self.pbDDM.set_fraction(0)

    def check_thread(self, name):
        if self.threads[name].is_alive():
            self.pbDDM.pulse()
            if not self.queue.empty():
                ret = self.queue.get()
                self.log.write("Queue returns: {}".format(ret), 'check_thread')
                self.queue.task_done()
                self.show_message(ret)
            return True

        # Thread is done
        self.log.write(">> Thread is done", 'check_thread')
        if not self.queue.empty():
            ret = self.queue.get()
            self.queue.task_done()
            self.show_message(ret)
        del self.threads[name]

        self.set_buttons_state(True)

        return False

    # Close the gui
    def on_ddmWindow_destroy(self, widget):
        # Close the app
        Gtk.main_quit()

    # ===============================================
    # Hardware functions
    # ===============================================

    def get_ati(self):
        # Debian Wiki: https://wiki.debian.org/ATIProprietary
        # Supported devices 14.9 (Jessie): http://support.amd.com/en-us/kb-articles/Pages/AMDCatalyst14-9LINReleaseNotes.aspx

        manufacturerId = '1002'
        startSeries = 5000
        deviceArray = self.get_lspci_info(manufacturerId, 'VGA')

        if self.test:
            #deviceArray = [['Advanced Micro Devices [AMD] nee ATI Manhattan [Mobility Radeon HD 5400 Series]', manufacturerId, '68e0']]
            #deviceArray = [['Advanced Micro Devices, Inc. [AMD/ATI] RV710 [Radeon HD 4350/4550]', manufacturerId, '68e0']]
            #deviceArray = [['Advanced Micro Devices [AMD/ATI] RS880 [Radeon HD 4290]', manufacturerId, '68e0']]
            #deviceArray = [['Advanced Micro Devices, Inc. [AMD/ATI] Tonga PRO [Radeon R9 285]', manufacturerId, '6939']]
            deviceArray = [['Advanced Micro Devices, Inc. [AMD/ATI] Bonaire [FirePro W5100]', manufacturerId, '6649']]

        if deviceArray:
            self.log.write("Device(s): {}".format(deviceArray), 'get_ati')
            # Check if fglrx is loaded
            # If it is: checkbox is selected
            loadedDrv = self.get_loaded_graphical_driver()
            self.log.write("Loaded graphical driver: {}".format(loadedDrv), 'get_ati')

            # Get the manufacturer's logo
            logo = join(self.mediaDir, 'images/ati.png')

            # Fill the hardware array
            for device in deviceArray:
                self.log.write("ATI device found: {}".format(device[0]), 'get_ati')
                # Check for supported cards
                matchObj = re.search('radeon\s+[0-9a-z ]+|fire[a-z]+\s+[0-9a-z -]+', device[0], flags=re.IGNORECASE)
                if matchObj:
                    if " hd " in matchObj.group(0).lower():
                        # Check if ATI series is above 5000
                        matchObjSeries = re.search('[0-9]{4}', matchObj.group(0))
                        if matchObjSeries:
                            series = int(matchObjSeries.group(0))
                            # Don't show older ATI Radeon HD cards
                            if series < startSeries:
                                break
                    elif 'fire' in matchObj.group(0).lower():
                        title = _("ATI FirePro/Gl card found")
                        msg = _("Installing the proprietary driver for an ATI FirePro/Gl card may render your system unbootable.\n\n"
                                "Proceed at your own risk.")
                        self.log.write(msg, 'get_ati')
                        WarningDialog(title, msg)

                    self.log.write("ATI series: {}".format(matchObj.group(0)), 'get_ati')

                    # Check if the available driver is already loaded
                    selected = False
                    driver = 'fglrx'
                    if loadedDrv == driver:
                        selected = True

                    # Fill self.hardware
                    #shortDevice = self.shorten_long_string(device[0], 100)
                    self.hardware.append([selected, logo, device[0], driver, device[1], device[2]])
                else:
                    self.notSupported.append(device[0])

    def get_nvidia(self):
        manufacturerId = '10de'
        deviceArray = self.get_lspci_info(manufacturerId, 'VGA')

        if self.test:
            deviceArray = [['NVIDIA Corporation GT218 [GeForce G210M]', manufacturerId, '0a74']]
            if self.test_optimus:
                deviceArray = [['Intel Corporation Haswell-ULT Integrated Graphics Controller', '8086', '0a16'], \
                                ['NVIDIA Corporation GK107M [GeForce GT 750M]', manufacturerId, '0fe4']]

        if deviceArray:
            optimus = False
            devices = []

            self.log.write("Device(s): {}".format(deviceArray), 'get_nvidia')

            # Check if nvidia is loaded
            # If it is: checkbox is selected
            loadedDrv = self.get_loaded_graphical_driver()
            self.log.write("Loaded graphical driver: {}".format(loadedDrv), 'get_nvidia')

            # Get the manufacturer's logo
            logo = join(self.mediaDir, 'images/nvidia.png')

            # Fill the hardware array
            for device in deviceArray:
                if device[1] == '8086':
                    optimus = True
                else:
                    devices.append(device)

            for device in devices:
                self.log.write("Nvidia device found: {}".format(device[0]), 'get_nvidia')
                optimusString = ""
                if optimus:
                    optimusString = "(Optimus) "

                # Check if the available driver is already loaded
                selected = False
                if optimus:
                    if loadedDrv == 'nvidia' or loadedDrv == 'intel':
                        bbversion = getPackageVersion("bumblebee-nvidia")
                        self.log.write("bumblebee-nvidia version: {}".format(bbversion), 'get_nvidia')
                        if bbversion != '':
                            selected = True
                elif loadedDrv == 'nvidia':
                    selected = True

                driver = ""
                if optimus:
                    driver = "bumblebee-nvidia"
                else:
                    if self.test:
                        driver = 'nvidia-driver'
                    else:
                        nvidiaDetect = getoutput("nvidia-detect | grep nvidia- | tr -d ' '")
                        if nvidiaDetect:
                            driver = nvidiaDetect[0]

                self.log.write("Nvidia driver to use: {}".format(driver), 'get_nvidia')

                # Fill self.hardware
                if driver != "":
                    #shortDevice = "{0}{1}".format(optimusString, self.shorten_long_string(device[0], 100))
                    self.hardware.append([selected, logo, "{0}{1}".format(optimusString, device[0]), driver, device[1], device[2]])

    def get_broadcom_ids(self, driver_name):
        driver_name = driver_name.upper()
        ids = getoutput("cat /usr/bin/ddm | grep '{}=' | cut -d'=' -f 2".format(driver_name))
        if len(ids) > 0:
            return ids[0].split('|')
        return []

    def get_broadcom(self):
        ## Hardware list (device ids)
        ## http://linuxwireless.org/en/users/Drivers/b43
        deviceIds = {}
        deviceIds['b43'] = self.get_broadcom_ids('b43')
        deviceIds['b43legacy'] = self.get_broadcom_ids('b43legacy')
        deviceIds['wldebian'] = self.get_broadcom_ids('wldebian')
        deviceIds['brcmdebian'] = self.get_broadcom_ids('brcmdebian')
        deviceIds['unknown'] = self.get_broadcom_ids('unknown')

        self.log.write("Broadcom deviceIds = {}".format(deviceIds))

        manufacturerId = '14e4'

        deviceArray = self.get_lspci_info(manufacturerId)

        if self.test:
            deviceArray = [['Broadcom Corporation BCM43142 802.11a/b/g', manufacturerId, '4365']]

        if deviceArray:
            self.log.write("Device(s): {}".format(deviceArray), 'get_broadcom')
            # Check if broadcom is loaded
            # If it is: checkbox is selected
            loadedDrv = self.get_loaded_wireless_driver()
            self.log.write("Loaded wireless driver: {}".format(loadedDrv), 'get_broadcom')

            # Get the manufacturer's logo
            logo = join(self.mediaDir, 'images/broadcom.png')

            # Fill the hardware array
            for device in deviceArray:
                self.log.write("Broadcom device found: {}".format(device[0]), 'get_broadcom')
                driver = ''
                for key, did in list(deviceIds.items()):
                    #print(("{}:{} in {}:{}".format(device[0], device[2], key, did)))
                    if device[2] in did:
                        driver = key
                        break

                if driver != '':
                    if driver == 'unknown':
                        self.notSupported.append(device[0])
                        self.log.write("Broadcom device not supported: {}".format(device[0]), 'get_broadcom')
                    else:
                        self.log.write("Broadcom driver to use: {}".format(driver), 'get_broadcom')
                        # Check if the available driver is already loaded
                        selected = False
                        if loadedDrv == driver:
                            selected = True

                        # Fill self.hardware
                        #shortDevice = self.shorten_long_string(device[0], 100)
                        self.hardware.append([selected, logo, device[0], driver, device[1], device[2]])

    def get_pae(self):
        machine = getoutput('uname -m')[0]
        release = getoutput('uname -r')[0]

        if self.test:
            machine = 'i686'
            release = '3.16.0-4-586'

        self.log.write("PAE check: machine={} / release={}".format(machine, release), 'get_pae')

        if machine == 'i686':
            # Check if PAE is installed and running
            selected = False
            if 'pae' in release:
                self.paeBooted = True
                selected = True
            else:
                if getPackageVersion('linux-image-686-pae') != '':
                    selected = True

            # Get the logo
            logo = join(self.mediaDir, 'images/pae.png')

            # Fill self.hardware
            paeDescription = _("PAE capable system")
            self.hardware.append([selected, logo, paeDescription, '', 'pae', ''])

    def get_lspci_info(self, manufacturerId, filterString=''):
        deviceArray = []
        output = []

        # Check for Optimus
        if manufacturerId == '10de':
            output = getoutput("lspci -vnn | grep '\[030[02]\]'")

            if self.test_optimus:
                output = ['00:02.0 VGA compatible controller [0300]: Intel Corporation Haswell-ULT Integrated Graphics Controller [8086:0a16] (rev 09) (prog-if 00 [VGA controller])', \
                          '01:00.0 3D controller [0302]: NVIDIA Corporation GK107M [GeForce GT 750M] [10de:0fe4] (rev a1)']

        # Optimus will return 2 devices
        # If there are less than 2 devices, do regular check
        if len(output) < 2:
            if filterString != '':
                filterString = " | grep {}".format(filterString)
            output = getoutput("lspci -nn -d {}:{}".format(manufacturerId, filterString))

        if output:
            self.log.write("lspci output = {}".format(output), 'get_lspci_info')

        for line in output:
            matchObj = re.search(':\W(.*)\W\[(.+):(.+)\]', line)
            if matchObj:
                deviceArray.append([matchObj.group(1), matchObj.group(2), matchObj.group(3)])
        return deviceArray

    def shorten_long_string(self, longString, charLen, breakOnWord=True):
        tmpArr = []
        if breakOnWord:
            stringArr = longString.split(' ')
            nrChrs = 0
            for s in stringArr:
                nrChrs += len(s) + 1
                if nrChrs < charLen:
                    tmpArr.append(s)
                else:
                    break
        else:
            if len(longString) > charLen:
                tmpArr.append("{}...".format(longString[0:charLen]))
            else:
                tmpArr.append(longString)
        return ' '.join(tmpArr)

    # Return graphics module used by X.org
    # TODO: is lsmod an alternative?
    def get_loaded_graphical_driver(self):
        # sort on the most recent X.org log
        module = ''
        log = ''
        logDir = '/var/log/'
        logPath = None
        logs = glob(os.path.join(logDir, 'Xorg.*.log*'))
        logs.sort()

        for logPath in logs:
            # Search for "depth" in each line and check the used module
            # Sometimes these logs are saved as binary: open as read-only binary
            # When opening as ascii, read() will throw error: "UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80"
            with open(logPath, 'rb') as f:
                # replace utf-8 binary read errors (with ?)
                log = f.read().decode(encoding='utf-8', errors='replace')
                #print((log))

            matchObj = re.search('([a-zA-Z]*)\(\d+\):\s+depth.*framebuffer', log, flags=re.IGNORECASE)
            if matchObj:
                module = matchObj.group(1).lower()
                self.log.write("Log module={}".format(module))
                break

        return module

    # Return used wireless driver
    def get_loaded_wireless_driver(self):
        driver = ''
        logDir = '/var/log/'
        for logPath in glob(os.path.join(logDir, 'syslog*')):
            if driver == '' and not 'gz' in logPath:
                # Open the log file
                lines = []
                with open(logPath, 'rb') as f:
                    log = f.read().decode(encoding='utf-8', errors='replace')
                    lines = list(log.splitlines())

                for line in reversed(lines):
                    # First check for Network Manager entry
                    # Search for wlan0 in each line and get the listed driver
                    matchObj = re.search('\(wlan\d\):.*driver:\s*\'([a-zA-Z0-9\-]*)', line, flags=re.IGNORECASE)
                    if matchObj:
                        driver = matchObj.group(1)
                        self.log.write("Network Manager driver={}".format(driver))
                        break
                    else:
                        # Wicd
                        # Search for ieee in each line and get the listed driver
                        matchObj = re.search('ieee.*implement', line, flags=re.IGNORECASE)
                        if matchObj:
                            driver = matchObj.group(0)
                            self.log.write("Wicd driver={}".format(driver))
                            break

        return driver

    def show_message(self, cmdOutput):
        try:
            self.log.write("Command output: {}".format(cmdOutput), 'show_message')
            ret = int(cmdOutput)
            if ret > 1 and ret != 255:
                if ret == 1:
                    ErrorDialog(self.btnSave.get_label(), _("Run as root."))
                elif ret == 2:
                    ErrorDialog(self.btnSave.get_label(), _("Wrong arguments passed to ddm."))
                elif ret == 3:
                    ErrorDialog(self.btnSave.get_label(), _("There are no driver available."))
                elif ret == 4:
                    ErrorDialog(self.btnSave.get_label(), _("The driver cannot be found in repository."))
                elif ret == 5:
                    ErrorDialog(self.btnSave.get_label(), _("Download error.\nCheck your internet connection."))
                elif ret == 6:
                    ErrorDialog(self.btnSave.get_label(), _("DDM cannot purge the driver."))
                elif ret == 7:
                    ErrorDialog(self.btnSave.get_label(), _("This card is not supported."))
                else:
                    msg = _("There was an error during the installation.\n"
                    "Please, run 'sudo apt-get -f install' in a terminal.\n"
                    "Visit our forum for support: http://forums.solydxk.com")
                    ErrorDialog(self.btnSave.get_label(), msg)
            else:
                msg = _("The software has been successfully installed.")
                msg_restart = _("You will need to restart your system.")
                MessageDialog(self.btnSave.get_label(), "{}\n\n{}".format(msg, msg_restart))
        except:
            ErrorDialog(self.btnSave.get_label(), cmdOutput)
Exemplo n.º 13
0
class DDM(object):
    def __init__(self, test=False):
        # Testing
        self.test = test
        # Set to true for testing Optimus
        self.test_optimus = False

        # Load window and widgets
        self.scriptName = basename(__file__)
        self.scriptDir = abspath(dirname(__file__))
        self.mediaDir = join(self.scriptDir, '../../share/ddm')
        self.builder = Gtk.Builder()
        self.builder.add_from_file(join(self.mediaDir, 'ddm.glade'))

        # Main window objects
        go = self.builder.get_object
        self.window = go("ddmWindow")
        self.tvDDM = go("tvDDM")
        self.btnSave = go("btnSave")
        self.btnHelp = go("btnHelp")
        self.btnQuit = go("btnQuit")
        self.pbDDM = go("pbDDM")
        self.chkBackports = go("chkBackports")

        self.window.set_title(_("Device Driver Manager"))
        self.btnSave.set_label(_("Install"))
        self.btnHelp.set_label(_("Help"))
        self.btnQuit.set_label(_("Quit"))
        self.chkBackports.set_label(_("Use Backports"))

        # Initiate variables
        self.queue = Queue(-1)
        self.threads = {}
        self.hardware = []
        self.loadedDrivers = []
        self.notSupported = []
        self.paeBooted = False
        self.htmlDir = join(self.mediaDir, "html")
        self.helpFile = join(self.get_language_dir(), "help.html")
        log = getoutput("cat /usr/bin/ddm | grep 'LOG=' | cut -d'=' -f 2")
        self.logFile = log[0]
        self.log = Logger(self.logFile, addLogTime=False, maxSizeKB=5120)
        self.tvDDMHandler = TreeViewHandler(self.tvDDM)
        self.tvDDMHandler.connect('checkbox-toggled', self.tv_checkbox_toggled)

        # Connect builder signals and show window
        self.builder.connect_signals(self)
        self.window.show_all()

        # Fill treeview
        self.fill_treeview_ddm()

        # Check backports
        if len(self.hardware) < 2 or not has_backports():
            self.chkBackports.hide()

        self.get_loaded_graphical_driver()
        self.get_loaded_wireless_driver()

    # ===============================================
    # Language specific functions
    # ===============================================

    def get_language_dir(self):
        # First test if full locale directory exists, e.g. html/pt_BR,
        # otherwise perhaps at least the language is there, e.g. html/pt
        # and if that doesn't work, try html/pt_PT
        lang = self.get_current_language()
        path = join(self.htmlDir, lang)
        if not isdir(path):
            base_lang = lang.split('_')[0].lower()
            path = join(self.htmlDir, base_lang)
            if not isdir(path):
                path = join(self.htmlDir,
                            "{}_{}".format(base_lang, base_lang.upper()))
                if not isdir(path):
                    path = join(self.htmlDir, 'en')
        return path

    def get_current_language(self):
        lang = os.environ.get('LANG', 'US').split('.')[0]
        if lang == '':
            lang = 'en'
        return lang

    # ===============================================
    # Main window functions
    # ===============================================

    def on_btnSave_clicked(self, widget):
        # Save selected hardware
        arguments = []

        model = self.tvDDM.get_model()
        itr = model.get_iter_first()
        while itr is not None:
            action = 'no change'
            selected = model.get_value(itr, 0)
            device = model.get_value(itr, 2)
            manufacturerId = ''

            # Check currently selected state with initial state
            # This decides whether we should install or purge the drivers
            for hw in self.hardware:
                self.log.write("Device = {} in {}".format(device, hw[2]),
                               'on_btnSave_clicked')
                if device in hw[2]:
                    manufacturerId = hw[4]
                    if hw[0] and not selected:
                        action = 'purge'
                    elif not hw[0] and selected:
                        action = 'install'
                    break

            self.log.write(
                "{}: {} ({})".format(action, device, manufacturerId),
                'on_btnSave_clicked')

            # Install/purge selected driver
            option = ""
            if action == 'install':
                option = "-i"
            elif action == 'purge':
                option = "-p"

            if option:
                driver = ''
                # Run the manufacturer specific bash script
                if manufacturerId == '1002':
                    driver = 'ati'
                elif manufacturerId == '10de':
                    driver = 'nvidia '
                elif manufacturerId == '14e4':
                    driver = 'broadcom '
                elif 'pae' in manufacturerId:
                    driver = 'pae '
                if driver:
                    arguments.append("{} {}".format(option, driver))

            # Get the next in line
            itr = model.iter_next(itr)

        # Execute the command
        if arguments:
            if '-i' in arguments and not hasInternetConnection():
                title = _("No internet connection")
                msg = _(
                    "You need an internet connection to install the additional software.\n"
                    "Please, connect to the internet and try again.")
                WarningDialog(title, msg)
            else:
                # Warn for use of Backports
                if self.chkBackports.get_active():
                    answer = QuestionDialog(
                        self.chkBackports.get_label(),
                        _("You have selected to install drivers from the backports repository whenever they are available.\n\n"
                          "Although you can run more up to date software using the backports repository,\n"
                          "you introduce a greater risk of breakage doing so.\n\n"
                          "Are you sure you want to continue?"))
                    if not answer:
                        self.chkBackports.set_active(False)
                        return True
                    arguments.append("-b")

                # Testing
                if self.test:
                    arguments.append("-t")

                command = "ddm {}".format(" ".join(arguments))
                self.log.write("Command to execute: {}".format(command),
                               'on_btnSave_clicked')
                self.exec_command(command)

    def on_btnQuit_clicked(self, widget):
        self.on_ddmWindow_destroy(widget)

    def on_btnHelp_clicked(self, widget):
        # Open the help file as the real user (not root)
        shell_exec("%s/open-as-user \"%s\"" % (self.scriptDir, self.helpFile))

    def get_supported_hardware(self):
        # Fill self.hardware
        self.hardware = []

        # First row are column names
        self.hardware.append(
            [_("Install"), '',
             _("Device"), 'driver', 'manid', 'deviceid'])

        # Get hardware information
        self.get_ati()
        self.get_nvidia()
        self.get_broadcom()
        self.get_pae()

    # This method is fired by the TreeView.checkbox-toggled event
    def tv_checkbox_toggled(self, obj, path, colNr, toggleValue):
        path = int(path)
        model = self.tvDDM.get_model()
        itr = model.get_iter(path)
        description = model[itr][2].lower()

        if 'pae' in description and not toggleValue and self.paeBooted:
            title = _("Remove kernel")
            msg = _(
                "You cannot remove a booted kernel.\nPlease, boot another kernel and try again."
            )
            self.log.write(msg, 'tv_checkbox_toggled')
            WarningDialog(title, msg)
            model[itr][0] = True

    def fill_treeview_ddm(self):
        # Fill a list with supported hardware
        self.get_supported_hardware()

        # columns: checkbox, image (logo), device, driver
        columnTypes = ['bool', 'GdkPixbuf.Pixbuf', 'str']

        # Keep some info from the user
        showHw = []
        for hw in self.hardware:
            showHw.append([hw[0], hw[1], hw[2]])

        # Fill treeview
        self.tvDDMHandler.fillTreeview(contentList=showHw,
                                       columnTypesList=columnTypes,
                                       firstItemIsColName=True,
                                       fontSize=12000)

        # Show message if nothing is found or hardware is not supported
        title = _("Hardware scan")
        if self.notSupported:
            if len(self.hardware) < 2:
                self.set_buttons_state(False)
            msg = _("There are no available drivers for your hardware:")
            msg = "{}\n\n{}".format(msg, '\n'.join(self.notSupported))
            self.log.write(msg, 'fill_treeview_ddm')
            WarningDialog(title, msg)
        elif len(self.hardware) < 2:
            self.set_buttons_state(False)
            msg = _("DDM did not find any supported hardware.")
            self.log.write(msg, 'fill_treeview_ddm')
            MessageDialog(title, msg)

    def exec_command(self, command):
        try:
            # Run the command in a separate thread
            self.set_buttons_state(False)
            name = 'cmd'
            t = ExecuteThreadedCommands([command], self.queue)
            self.threads[name] = t
            t.daemon = True
            t.start()
            self.queue.join()
            GObject.timeout_add(250, self.check_thread, name)

        except Exception as detail:
            ErrorDialog(self.btnSave.get_label(), detail)

    def set_buttons_state(self, enable):
        if not enable:
            # Disable buttons
            self.btnSave.set_sensitive(False)
        else:
            # Enable buttons and reset progress bar
            self.btnSave.set_sensitive(True)
            self.pbDDM.set_fraction(0)

    def check_thread(self, name):
        if self.threads[name].is_alive():
            self.pbDDM.pulse()
            if not self.queue.empty():
                ret = self.queue.get()
                self.log.write("Queue returns: {}".format(ret), 'check_thread')
                self.queue.task_done()
                self.show_message(ret)
            return True

        # Thread is done
        self.log.write(">> Thread is done", 'check_thread')
        if not self.queue.empty():
            ret = self.queue.get()
            self.queue.task_done()
            self.show_message(ret)
        del self.threads[name]

        self.set_buttons_state(True)

        return False

    # Close the gui
    def on_ddmWindow_destroy(self, widget):
        # Close the app
        Gtk.main_quit()

    # ===============================================
    # Hardware functions
    # ===============================================

    def get_ati(self):
        # Debian Wiki: https://wiki.debian.org/ATIProprietary
        # Supported devices 14.9 (Jessie): http://support.amd.com/en-us/kb-articles/Pages/AMDCatalyst14-9LINReleaseNotes.aspx

        manufacturerId = '1002'
        startSeries = 5000
        deviceArray = self.get_lspci_info(manufacturerId, 'VGA')

        if self.test:
            #deviceArray = [['Advanced Micro Devices [AMD] nee ATI Manhattan [Mobility Radeon HD 5400 Series]', manufacturerId, '68e0']]
            #deviceArray = [['Advanced Micro Devices, Inc. [AMD/ATI] RV710 [Radeon HD 4350/4550]', manufacturerId, '68e0']]
            #deviceArray = [['Advanced Micro Devices [AMD/ATI] RS880 [Radeon HD 4290]', manufacturerId, '68e0']]
            #deviceArray = [['Advanced Micro Devices, Inc. [AMD/ATI] Tonga PRO [Radeon R9 285]', manufacturerId, '6939']]
            deviceArray = [[
                'Advanced Micro Devices, Inc. [AMD/ATI] Bonaire [FirePro W5100]',
                manufacturerId, '6649'
            ]]

        if deviceArray:
            self.log.write("Device(s): {}".format(deviceArray), 'get_ati')
            # Check if fglrx is loaded
            # If it is: checkbox is selected
            loadedDrv = self.get_loaded_graphical_driver()
            self.log.write("Loaded graphical driver: {}".format(loadedDrv),
                           'get_ati')

            # Get the manufacturer's logo
            logo = join(self.mediaDir, 'images/ati.png')

            # Fill the hardware array
            for device in deviceArray:
                self.log.write("ATI device found: {}".format(device[0]),
                               'get_ati')
                # Check for supported cards
                matchObj = re.search(
                    'radeon\s+[0-9a-z ]+|fire[a-z]+\s+[0-9a-z -]+',
                    device[0],
                    flags=re.IGNORECASE)
                if matchObj:
                    if " hd " in matchObj.group(0).lower():
                        # Check if ATI series is above 5000
                        matchObjSeries = re.search('[0-9]{4}',
                                                   matchObj.group(0))
                        if matchObjSeries:
                            series = int(matchObjSeries.group(0))
                            # Don't show older ATI Radeon HD cards
                            if series < startSeries:
                                break
                    elif 'fire' in matchObj.group(0).lower():
                        title = _("ATI FirePro/Gl card found")
                        msg = _(
                            "Installing the proprietary driver for an ATI FirePro/Gl card may render your system unbootable.\n\n"
                            "Proceed at your own risk.")
                        self.log.write(msg, 'get_ati')
                        WarningDialog(title, msg)

                    self.log.write("ATI series: {}".format(matchObj.group(0)),
                                   'get_ati')

                    # Check if the available driver is already loaded
                    selected = False
                    driver = 'fglrx'
                    if loadedDrv == driver:
                        selected = True

                    # Fill self.hardware
                    #shortDevice = self.shorten_long_string(device[0], 100)
                    self.hardware.append([
                        selected, logo, device[0], driver, device[1], device[2]
                    ])
                else:
                    self.notSupported.append(device[0])

    def get_nvidia(self):
        manufacturerId = '10de'
        deviceArray = self.get_lspci_info(manufacturerId, 'VGA')

        if self.test:
            deviceArray = [[
                'NVIDIA Corporation GT218 [GeForce G210M]', manufacturerId,
                '0a74'
            ]]
            if self.test_optimus:
                deviceArray = [['Intel Corporation Haswell-ULT Integrated Graphics Controller', '8086', '0a16'], \
                                ['NVIDIA Corporation GK107M [GeForce GT 750M]', manufacturerId, '0fe4']]

        if deviceArray:
            optimus = False
            devices = []

            self.log.write("Device(s): {}".format(deviceArray), 'get_nvidia')

            # Check if nvidia is loaded
            # If it is: checkbox is selected
            loadedDrv = self.get_loaded_graphical_driver()
            self.log.write("Loaded graphical driver: {}".format(loadedDrv),
                           'get_nvidia')

            # Get the manufacturer's logo
            logo = join(self.mediaDir, 'images/nvidia.png')

            # Fill the hardware array
            for device in deviceArray:
                if device[1] == '8086':
                    optimus = True
                else:
                    devices.append(device)

            for device in devices:
                self.log.write("Nvidia device found: {}".format(device[0]),
                               'get_nvidia')
                optimusString = ""
                if optimus:
                    optimusString = "(Optimus) "

                # Check if the available driver is already loaded
                selected = False
                if optimus:
                    if loadedDrv == 'nvidia' or loadedDrv == 'intel':
                        bbversion = getPackageVersion("bumblebee-nvidia")
                        self.log.write(
                            "bumblebee-nvidia version: {}".format(bbversion),
                            'get_nvidia')
                        if bbversion != '':
                            selected = True
                elif loadedDrv == 'nvidia':
                    selected = True

                driver = ""
                if optimus:
                    driver = "bumblebee-nvidia"
                else:
                    if self.test:
                        driver = 'nvidia-driver'
                    else:
                        nvidiaDetect = getoutput(
                            "nvidia-detect | grep nvidia- | tr -d ' '")
                        if nvidiaDetect:
                            driver = nvidiaDetect[0]

                self.log.write("Nvidia driver to use: {}".format(driver),
                               'get_nvidia')

                # Fill self.hardware
                if driver != "":
                    #shortDevice = "{0}{1}".format(optimusString, self.shorten_long_string(device[0], 100))
                    self.hardware.append([
                        selected, logo,
                        "{0}{1}".format(optimusString, device[0]), driver,
                        device[1], device[2]
                    ])

    def get_broadcom_ids(self, driver_name):
        driver_name = driver_name.upper()
        ids = getoutput(
            "cat /usr/bin/ddm | grep '{}=' | cut -d'=' -f 2".format(
                driver_name))
        if len(ids) > 0:
            return ids[0].split('|')
        return []

    def get_broadcom(self):
        ## Hardware list (device ids)
        ## http://linuxwireless.org/en/users/Drivers/b43
        deviceIds = {}
        deviceIds['b43'] = self.get_broadcom_ids('b43')
        deviceIds['b43legacy'] = self.get_broadcom_ids('b43legacy')
        deviceIds['wldebian'] = self.get_broadcom_ids('wldebian')
        deviceIds['brcmdebian'] = self.get_broadcom_ids('brcmdebian')
        deviceIds['unknown'] = self.get_broadcom_ids('unknown')

        self.log.write("Broadcom deviceIds = {}".format(deviceIds))

        manufacturerId = '14e4'

        deviceArray = self.get_lspci_info(manufacturerId)

        if self.test:
            deviceArray = [[
                'Broadcom Corporation BCM43142 802.11a/b/g', manufacturerId,
                '4365'
            ]]

        if deviceArray:
            self.log.write("Device(s): {}".format(deviceArray), 'get_broadcom')
            # Check if broadcom is loaded
            # If it is: checkbox is selected
            loadedDrv = self.get_loaded_wireless_driver()
            self.log.write("Loaded wireless driver: {}".format(loadedDrv),
                           'get_broadcom')

            # Get the manufacturer's logo
            logo = join(self.mediaDir, 'images/broadcom.png')

            # Fill the hardware array
            for device in deviceArray:
                self.log.write("Broadcom device found: {}".format(device[0]),
                               'get_broadcom')
                driver = ''
                for key, did in list(deviceIds.items()):
                    #print(("{}:{} in {}:{}".format(device[0], device[2], key, did)))
                    if device[2] in did:
                        driver = key
                        break

                if driver != '':
                    if driver == 'unknown':
                        self.notSupported.append(device[0])
                        self.log.write(
                            "Broadcom device not supported: {}".format(
                                device[0]), 'get_broadcom')
                    else:
                        self.log.write(
                            "Broadcom driver to use: {}".format(driver),
                            'get_broadcom')
                        # Check if the available driver is already loaded
                        selected = False
                        if loadedDrv == driver:
                            selected = True

                        # Fill self.hardware
                        #shortDevice = self.shorten_long_string(device[0], 100)
                        self.hardware.append([
                            selected, logo, device[0], driver, device[1],
                            device[2]
                        ])

    def get_pae(self):
        machine = getoutput('uname -m')[0]
        release = getoutput('uname -r')[0]

        if self.test:
            machine = 'i686'
            release = '3.16.0-4-586'

        self.log.write(
            "PAE check: machine={} / release={}".format(machine, release),
            'get_pae')

        if machine == 'i686':
            # Check if PAE is installed and running
            selected = False
            if 'pae' in release:
                self.paeBooted = True
                selected = True
            else:
                if getPackageVersion('linux-image-686-pae') != '':
                    selected = True

            # Get the logo
            logo = join(self.mediaDir, 'images/pae.png')

            # Fill self.hardware
            paeDescription = _("PAE capable system")
            self.hardware.append(
                [selected, logo, paeDescription, '', 'pae', ''])

    def get_lspci_info(self, manufacturerId, filterString=''):
        deviceArray = []
        output = []

        # Check for Optimus
        if manufacturerId == '10de':
            output = getoutput("lspci -vnn | grep '\[030[02]\]'")

            if self.test_optimus:
                output = ['00:02.0 VGA compatible controller [0300]: Intel Corporation Haswell-ULT Integrated Graphics Controller [8086:0a16] (rev 09) (prog-if 00 [VGA controller])', \
                          '01:00.0 3D controller [0302]: NVIDIA Corporation GK107M [GeForce GT 750M] [10de:0fe4] (rev a1)']

        # Optimus will return 2 devices
        # If there are less than 2 devices, do regular check
        if len(output) < 2:
            if filterString != '':
                filterString = " | grep {}".format(filterString)
            output = getoutput("lspci -nn -d {}:{}".format(
                manufacturerId, filterString))

        if output:
            self.log.write("lspci output = {}".format(output),
                           'get_lspci_info')

        for line in output:
            matchObj = re.search(':\W(.*)\W\[(.+):(.+)\]', line)
            if matchObj:
                deviceArray.append(
                    [matchObj.group(1),
                     matchObj.group(2),
                     matchObj.group(3)])
        return deviceArray

    def shorten_long_string(self, longString, charLen, breakOnWord=True):
        tmpArr = []
        if breakOnWord:
            stringArr = longString.split(' ')
            nrChrs = 0
            for s in stringArr:
                nrChrs += len(s) + 1
                if nrChrs < charLen:
                    tmpArr.append(s)
                else:
                    break
        else:
            if len(longString) > charLen:
                tmpArr.append("{}...".format(longString[0:charLen]))
            else:
                tmpArr.append(longString)
        return ' '.join(tmpArr)

    # Return graphics module used by X.org
    # TODO: is lsmod an alternative?
    def get_loaded_graphical_driver(self):
        # sort on the most recent X.org log
        module = ''
        log = ''
        logDir = '/var/log/'
        logPath = None
        logs = glob(os.path.join(logDir, 'Xorg.*.log*'))
        logs.sort()

        for logPath in logs:
            # Search for "depth" in each line and check the used module
            # Sometimes these logs are saved as binary: open as read-only binary
            # When opening as ascii, read() will throw error: "UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80"
            with open(logPath, 'rb') as f:
                # replace utf-8 binary read errors (with ?)
                log = f.read().decode(encoding='utf-8', errors='replace')
                #print((log))

            matchObj = re.search('([a-zA-Z]*)\(\d+\):\s+depth.*framebuffer',
                                 log,
                                 flags=re.IGNORECASE)
            if matchObj:
                module = matchObj.group(1).lower()
                self.log.write("Log module={}".format(module))
                break

        return module

    # Return used wireless driver
    def get_loaded_wireless_driver(self):
        driver = ''
        logDir = '/var/log/'
        for logPath in glob(os.path.join(logDir, 'syslog*')):
            if driver == '' and not 'gz' in logPath:
                # Open the log file
                lines = []
                with open(logPath, 'rb') as f:
                    log = f.read().decode(encoding='utf-8', errors='replace')
                    lines = list(log.splitlines())

                for line in reversed(lines):
                    # First check for Network Manager entry
                    # Search for wlan0 in each line and get the listed driver
                    matchObj = re.search(
                        '\(wlan\d\):.*driver:\s*\'([a-zA-Z0-9\-]*)',
                        line,
                        flags=re.IGNORECASE)
                    if matchObj:
                        driver = matchObj.group(1)
                        self.log.write(
                            "Network Manager driver={}".format(driver))
                        break
                    else:
                        # Wicd
                        # Search for ieee in each line and get the listed driver
                        matchObj = re.search('ieee.*implement',
                                             line,
                                             flags=re.IGNORECASE)
                        if matchObj:
                            driver = matchObj.group(0)
                            self.log.write("Wicd driver={}".format(driver))
                            break

        return driver

    def show_message(self, cmdOutput):
        try:
            self.log.write("Command output: {}".format(cmdOutput),
                           'show_message')
            ret = int(cmdOutput)
            if ret > 1 and ret != 255:
                if ret == 1:
                    ErrorDialog(self.btnSave.get_label(), _("Run as root."))
                elif ret == 2:
                    ErrorDialog(self.btnSave.get_label(),
                                _("Wrong arguments passed to ddm."))
                elif ret == 3:
                    ErrorDialog(self.btnSave.get_label(),
                                _("There are no driver available."))
                elif ret == 4:
                    ErrorDialog(self.btnSave.get_label(),
                                _("The driver cannot be found in repository."))
                elif ret == 5:
                    ErrorDialog(
                        self.btnSave.get_label(),
                        _("Download error.\nCheck your internet connection."))
                elif ret == 6:
                    ErrorDialog(self.btnSave.get_label(),
                                _("DDM cannot purge the driver."))
                elif ret == 7:
                    ErrorDialog(self.btnSave.get_label(),
                                _("This card is not supported."))
                else:
                    msg = _(
                        "There was an error during the installation.\n"
                        "Please, run 'sudo apt-get -f install' in a terminal.\n"
                        "Visit our forum for support: http://forums.solydxk.com"
                    )
                    ErrorDialog(self.btnSave.get_label(), msg)
            else:
                msg = _("The software has been successfully installed.")
                msg_restart = _("You will need to restart your system.")
                MessageDialog(self.btnSave.get_label(),
                              "{}\n\n{}".format(msg, msg_restart))
        except:
            ErrorDialog(self.btnSave.get_label(), cmdOutput)
class USBCreator(Gtk.Window):

    def __init__(self):
        super(USBCreator, self).__init__()

        # Where do we live?
        self.scriptDir = abspath(dirname(__file__)) + "/"

        # Now, get our configuration data
        self.settingsData = config.Config(join(self.scriptDir,
                "files/multi-usb-creator.conf"))

        # Get the path to our dialog glade file and then
        # create the object

        self.dialogBox = USBCreatorDialogBox(join(self.scriptDir, self.settingsData.getValue("localfiles", "icon-path")))

        self.builder = Gtk.Builder()
        self.builder.add_from_file(join(self.scriptDir,
                self.settingsData.getValue("localfiles", "glade-file")))

        # Main window objects
        go = self.builder.get_object
        self.USBCreatorWindow = go("USBCreatorWindow")
        self.btnClose = go("btnClose")
        self.btnExecute = go("btnExecute")
        self.btnHelp = go("btnHelp")
        self.btnListDownLoadISOs = go("btnListDownLoadISOs")
        self.btnRefresh = go("btnRefresh")
        self.btnSelectISOFile = go("btnSelectISOFile")
        self.cmbSelectedUSBDevice = go("cmbSelectedUSBDevice")
        self.cmbDeleteISO = go("cmbDeleteISO")
        self.cmbDownLoad = go("cmbDownLoad")
        self.lblAvailable = go("lblAvailable")
        self.lblRequired = go("lblRequired")
        self.lblUSBCreatorWindowTitle = go("lblUSBCreatorWindowTitle")
        self.rbAddISO = go("rbAddISO")
        self.rbCleanUSB = go("rbCleanUSB")
        self.rbDelete = go("rbDelete")
        self.rbDownLoad = go("rbDownLoad")
        self.txtAvailable = go("txtAvailable")
        self.txtISOFileName = go("txtISOFileName")
        self.txtRequired = go("txtRequired")
        self.pbCopy = go("pbCopy")
        self.lblIServerSOListWindowTitle = go("lblIServerSOListWindowTitle")

        # Cleaning the USB device is a function only available to
        # user running as (or logged in as) root. Of course, a note
        # to this effect is in the help!
        self.rbCleanUSB.set_sensitive(bool(os.getuid() == 0))

        # FileChooserDialog objects
        self.FileChooserDialog = go("FileChooserDialog")
        self.btnFileChooserDialogCancel = go("btnFileChooserDialogCancel")
        self.btnFileChooserDialogOpen = go("btnFileChooserDialogOpen")

        # The Server ISO List window
        self.ServerISOListWindow = go("ServerISOListWindow")
        self.lblIServerSOListWindowTitle = go("lblIServerSOListWindowTitle")
        self.btnServerISOListWindowClose = go("btnServerISOListWindowClose")
        self.tvServerISOData = go("tvServerISOData")

        # Help window objects
        self.HelpWindow = go("HelpWindow")
        self.lblHelpWindowTitle = go("lblHelpWindowTitle")
        self.lblHelpWindowHelpText = go("lblHelpWindowHelpText")

        # Create our handlers
        # First, our ComboBox handlers
        self.cmbHandlerSelectUSB = ComboBoxHandler(self.cmbSelectedUSBDevice, None)
        self.cmbHandlerDownLoad = ComboBoxHandler(self.cmbDownLoad, None)
        self.cmbHandlerDeleteISO = ComboBoxHandler(self.cmbDeleteISO, None)
        self.tvHandlerServerISOData = TreeViewHandler(self.tvServerISOData, None)
        self.cmbHandlerServerISO = ComboBoxHandler(self.cmbDownLoad, None)

        # Now, the data handlers
        self.usbDataHandler = USBDataListHandler()
        self.installedFilesHandler = InstalledFilesListHandler()
        self.serverISOHandler = ServerISOListHandler()

        # Might as well get the server ISO list while we're here
        self.serverISOHandler.getServerISOList(self.settingsData.getValue("url", "solydxk-iso-list-URL"), self.settingsData.getValue("localfiles", "local-iso-list"))
        # And populate the Server ISO TreeView
        self.tvHandlerServerISOData.fillTreeview(contentList=self.serverISOHandler.getTreeViewList(),
            columnTypesList=["str", "str", "str", "str", "str", "str"], firstItemIsColName=True)

        self.cmbHandlerDownLoad.fillComboBox(self.serverISOHandler.getComboBoxList())
        if self.serverISOHandler.rowCount > 0:
            self.cmbDownLoad.set_active(0)

        # Get the USB data because it all starts with the USB devices
        # and, then, populate the USB ComboBox
        #self.usbDataHandler.getUSBData()
        self.RefreshUSBSelector()

        # Translations
        self.USBCreatorWindow.set_title(_("SolydXK Multi-Boot USB Creator"))
        self.rbAddISO.set_label(_("Add ISO"))
        self.rbDownLoad.set_label(_("Down Load"))
        self.rbDelete.set_label(_("Delete"))
        self.rbCleanUSB.set_label(_("Clean USB"))
        self.lblAvailable.set_label(_("Space Available (GiB):"))
        self.lblRequired.set_label(_("Required:"))
        self.lblIServerSOListWindowTitle.set_label(_("ISOs Available for Download"))

        self.HelpWindow.set_title(_("Multi-Boot Help"))
        self.lblHelpWindowTitle.set_label(_("Multi-Boot Help"))
        self.HelpWindowPopulateText()

        # Finally, we can connect our signals, hide the FileChooserDialog
        # (just in case) # and show our main window.
        self.FileChooserDialog.hide()
        self.HelpWindow.hide()
        theHeight = self.USBCreatorWindow.get_size()[1]
        #theWidth = self.USBCreatorWindow.get_size()[0]
        self.USBCreatorWindow.resize(626, theHeight)
        self.pbCopy.set_visible(False)

        self.theQ = Queue(-1)

        self.builder.connect_signals(self)
        self.USBCreatorWindow.show()
        Gtk.main()

    ##########################################
    #
    # USBCreatorWindow Event Handlers
    #
    ##########################################
    def IsEnoughSpace(self):
        if self.requiredSpace > self.availableSpace:
            self.dialogBox.infoDialog(_("Insufficient Space Available"),
            _("The selected USB device has insuffiecient\nspace for the indicate operation."))
            return(False)
        else:
            return(True)

    def on_btnExecute_clicked(self, widget):
        if self.cmbSelectedUSBDevice.get_active() == -1:
            self.dialogBox.infoDialog(_("No USB Device"),
                (_("Please select a USB device to which to copy the .iso file")))
        else:
            if self.rbAddISO.get_active():
                if self.IsEnoughSpace():
                    self.DoISOAdd()

            elif self.rbDownLoad.get_active():
                if self.IsEnoughSpace():
                    self.DoISODownLoad()

            elif self.rbDelete.get_active():
                self.DoISODelete()

            elif self.rbCleanUSB.get_active():
                self.CleanTheUSBDevice()
            else:
                logging.debug(_("Problem with Radio Buttons"))

    def on_btnHelp_clicked(self, widget):
        self.HelpWindow.show()

    def on_btnListDownLoadISOs_clicked(self, widget):
        self.ServerISOListWindow.show()

    def on_btnRefresh_clicked(self, widget):
        self.usbDataHandler.getUSBData()
        self.RefreshUSBSelector()
        self.rbAddISO.set_active(True)

    def on_btnSelectISOFile_clicked(self, widget):
        self.FileChooserDialog.show()

    def on_cmbDeleteISO_changed(self, widget):
        self.rbDelete.set_active(True)
        #self.on_RadioButton_toggled(self.rbDelete)

    def on_cmbDownLoad_changed(self, widget):
        self.rbDownLoad.set_active(True)
        #self.on_RadioButton_toggled(self.rbDownLoad)

    def on_cmbSelectedUSBDevice_changed(self, widget):
        self.DoNewUSBSelected()
        self.rbAddISO.set_active(True)
        #self.on_RadioButton_toggled(self.rbAddISO)

    def on_RadioButton_toggled(self, widget):
        if widget.get_active():
            if self.rbAddISO.get_active():
                # Get the filename from the text entry control (if there is one).
                # Check to see if the text is the path to a file. If it is,
                # then get the size of the file and show it to the user.
                theFileName = self.txtISOFileName.get_text()
                if os.path.isfile(theFileName):
                    self.requiredSpace = int(os.path.getsize(theFileName))
                    self.txtRequired.set_text(self.Convert2Gigs(self.requiredSpace))

            elif self.rbDownLoad.get_active():
                # Get the index of the ComboBox, use that as a pointer to the data
                if self.cmbDownLoad.get_active() != -1:
                    self.requiredSpace = int(self.serverISOHandler.getFileSize(self.cmbDownLoad.get_active()))
                    self.txtRequired.set_text(self.Convert2Gigs(self.requiredSpace))

            elif self.rbDelete.get_active():
                pass
            elif self.rbCleanUSB.get_active():
                pass
            else:
                logging.debug(_("Unknown RadioButton selected"))

    def on_USBCreatorWindow_destroy(self, widget):
        Gtk.main_quit()

    ##########################################
    #
    # USBCreatorWindow utility functions
    #
    ##########################################

    # Clean the USB device indicated in the cmbSelectedUSBDevice
    def CleanTheUSBDeviceCheck(self):
        if self.theThread.is_alive():
            retVal = True
            self.pbCopy.pulse()
            if not self.theQ.empty():
                lst = self.theQ.get(False)
                if lst:
                    if isinstance(lst[0], str):
                        self.dialogBox.errorDialog(_("Error Encountered"),
                            _("MultiBootISOCreator:CleanTheUSBDeviceCheck - Error: {}").format(lst[0]))
                self.theQ.task_done()
        else:
            retVal = False
            try:
                if not self.theQ.empty():
                    lst = self.theQ.get(False)
                    if lst:
                        if isinstance(lst[0], str):
                            self.dialogBox.errorDialog(_("Error Encountered"),
                                _("MultiBootISOCreator:CleanTheUSBDeviceCheck - Error: {}").format(lst[0]))
                        else:
                            if bool(lst[0]):
                                self.dialogBox.infoDialog(_("Cleaning Is Complete"),
                                    _("The USB Device has been re-initialized.\nIt is safe to remove it and re-insert it for\nISO file copy/download operations."))
                            else:
                                self.dialogBox.errorDialog(_("Error Encountered"),
                                    _("MultiBootISOCreator:CleanTheUSBDeviceCheck\n - Error encountered during cleaning operation."))
            except Exception as detail:
                logging.debug(_("Error Encountered"),
                        _("MultiBootISOCreator:CleanTheUSBDeviceCheck - Error: {}").format(detail))
                self.dialogBox.errorDialog("MultiBootISOCreator:CleanTheUSBDeviceCheck - Error: {}".format(detail))
            finally:
                # Save our current index
                localActiveIndex = self.cmbSelectedUSBDevice.get_active()

                # Relaod all the USB related data
                self.RefreshUSBSelector()

                # Point our ComboBox back where it was
                if localActiveIndex <= self.usbDataHandler.rowCount:
                    self.cmbSelectedUSBDevice.set_active(localActiveIndex)
                elif self.usbDataHandler.rowCount > 0:
                    self.cmbSelectedUSBDevice.set_active(0)
                else:
                    self.cmbSelectedUSBDevice.set_active(-1)

                self.DoNewUSBSelected()

                self.rbAddISO.set_active(True)
                self.on_RadioButton_toggled(self.rbAddISO)

                self.SetButtonsSensitive(True)

        return(retVal)

    def CleanTheUSBDevice(self):
        if self.usbDataHandler.rowCount <= 0:
            self.dialogBox.infoDialog(_("Ummm ... No USB device"),
                _("Did you forget to mount a USB device with which we can play?"))
        else:
            response = self.dialogBox.warningDialog(_("Please confirm"),
                _("Please confirm you wish to re-initialize\nthe device\n\n{}").format(self.usbDataHandler.getDevice(self.cmbSelectedUSBDevice.get_active())))
            if response != Gtk.ResponseType.OK:
                self.dialogBox.infoDialog(_("Operation Cancelled"),
                    _("Operation to re-initialize the\n" +
                    "USB device has been cancelled."))
            else:
                theCommandList = []
                theCommandList.clear()

                # The device in the data comes back with the partition number on it.
                theDevice = self.usbDataHandler.getDevice(self.cmbSelectedUSBDevice.get_active())[:-1]

                # This is where we are going to mount the device to install grub. We need to
                # ensure that whatever mount point we choose is not already in use. One way
                # to do that is to create it and then blow it away when we are done.
                while True:
                    rootMountPoint = "/media/{}".format((uuid.uuid1()).hex)
                    if not os.path.exists(rootMountPoint):
                        os.makedirs(rootMountPoint)
                        break

                # The device is mounted at /media/<user>/<usb label>. We want to unmount that puppy so
                # we can do our thang to it.
                theCommandList.append("umount {}".format(self.usbDataHandler.getMountPoint(self.cmbSelectedUSBDevice.get_active())))
                theCommandList.append("dd if=/dev/zero of={} bs=4M count=25".format(theDevice))
                theCommandList.append("parted -a optimal -s {} mklabel msdos".format(theDevice))
                theCommandList.append("parted -a optimal -s {} mkpart primary fat32 0% 100% set 1 boot on".format(theDevice))
                theCommandList.append("mkdosfs -F32 -n 'SOLYDXK' -I {}1".format(theDevice))

                # OK, now mount it where we can find it.
                theCommandList.append("mount {0}1 {1}".format(theDevice, rootMountPoint))
                theCommandList.append("tar zxf {0}/files/solydxk-usb.tar.gz  -C {1}".format(self.scriptDir, rootMountPoint))
                theCommandList.append("grub-install --root-directory={0} --no-floppy --recheck --force {1}".format(rootMountPoint, theDevice))
                theCommandList.append("umount {}1".format(theDevice))
                theCommandList.append("rmdir {0}".format(rootMountPoint))

                self.theThread = ExecuteCommandList(theCommandList, self.theQ)
                self.theThread.daemon = True
                self.theThread.start()
                self.theQ.join()
                self.pbCopy.set_text(_("Cleaning Device"))
                self.pbCopy.set_show_text(True)
                self.SetButtonsSensitive(False)
                GLib.timeout_add(100, self.CleanTheUSBDeviceCheck)

    def Convert2Gigs(self, theSize):
        return("{:.2f}".format((float(theSize) / float(2 ** 30))))

    def CopyCheck(self, destFileName):
        if self.theThread.is_alive():
            retVal = True
            self.pbCopy.pulse()
            if not self.theQ.empty():
                lst = self.theQ.get(False)
                if lst:
                    if isinstance(lst[0], str):
                        self.dialogBox.errorDialog(_("Error Encountered"),
                            _("MultiBootISOCreator:CopyCheck - Error: {}").format(lst[0]))
                        removeFile = True
                self.theQ.task_done()
        else:
            removeFile = False
            retVal = False
            try:
                if not self.theQ.empty():
                    lst = self.theQ.get(False)
                    if lst:
                        if isinstance(lst[0], str):
                            self.dialogBox.errorDialog(_("Error Encountered"),
                                _("MultiBootISOCreator:CopyCheck - Error: {}").format(lst[0]))
                            removeFile = True
                        else:
                            if bool(lst[0]):
                                self.txtISOFileName.set_text("")
                                self.dialogBox.infoDialog(_("Copy Is Complete"),
                                    _("The USB Device has been updated.\n" +
                                    "It is safe to unmount and remove it."))
                            else:
                                self.dialogBox.errorDialog(_("Error Encountered"),
                                    _("MultiBootISOCreator:CopyCheck - Error encountered during copy."))
                                removeFile = True
                    self.theQ.task_done()

            except Exception as detail:
                logging.debug(_("MultiBootISOCreator:CopyCheck - Error: {}").format(detail))
                self.dialogBox.errorDialog(_("Error Encountered"),
                    _("MultiBootISOCreator:CopyCheck - Error: {}").format(detail))
                removeFile = True

            finally:
                if removeFile:
                    if os.path.isfile(destFileName):
                        os.remove(destFileName)
                del self.theThread

                # Save our current index
                localActiveIndex = self.cmbSelectedUSBDevice.get_active()

                # Relaod all the USB related data
                self.RefreshUSBSelector()

                # Point our ComboBox back where it was
                if localActiveIndex <= self.usbDataHandler.rowCount:
                    self.cmbSelectedUSBDevice.set_active(localActiveIndex)
                elif self.usbDataHandler.rowCount > 0:
                    self.cmbSelectedUSBDevice.set_active(0)
                else:
                    self.cmbSelectedUSBDevice.set_active(-1)

                self.DoNewUSBSelected()
                self.UpdateUSBGrubConfig()

                self.rbAddISO.set_active(True)
                self.on_RadioButton_toggled(self.rbAddISO)

                self.SetButtonsSensitive(True)

        return(retVal)

    def CopyFile(self, theFileName):
        commandList = []
        commandList.clear()
        commandList.append("cp {0} {1}".format(self.txtISOFileName.get_text(), theFileName))
        self.theThread = ExecuteCommandList(commandList, self.theQ)
        self.theThread.daemon = True
        self.theThread.start()
        self.theQ.join()
        self.pbCopy.set_text(_("Copying File"))
        self.pbCopy.set_show_text(True)
        self.SetButtonsSensitive(False)
        GLib.timeout_add(100, self.CopyCheck, theFileName)

    def DownLoadCheck(self, destFileName):
        if self.theThread.is_alive():
            retVal = True
            self.pbCopy.pulse()
            if not self.theQ.empty():
                lst = self.theQ.get(False)
                if lst:
                    self.pbCopy.set_text(lst[0])
                    self.pbCopy.set_show_text(True)
                self.theQ.task_done()
        else:
            retVal = False
            removeFile = False
            try:
                if not self.theQ.empty():
                    lst = self.theQ.get(False)
                    if lst:
                        if isinstance(lst[0], str):
                            # We have an error message
                            self.dialogBox.errorDialog(_("Error Encountered"),
                                _("Error encountered during download: {}").format(lst[0]))
                            removeFile = True
                        else:
                            # Take care of the results of the MD5Sum calculation
                            if lst[0]:
                                # All is right in the world
                                self.dialogBox.infoDialog(_("Download Is Complete"),
                                    _("The USB Device has been updated.\nIt is safe to unmount and remove it."))
                            else:
                                # Bad MD5Sum
                                self.dialogBox.errorDialog(_("MD5Sum is not a match"),
                                _("Download complete but appears to be\n" +
                                "corrupted. The computed MD5Sum does\n" +
                                "not match the value from the server.\n\n" +
                                "The file has been removed from the USG device."))
                                removeFile = True

            except Exception as detail:
                logging.debug(_("MultiBootISOCreator:DownLoadCheck - exception: {}").format(detail))
                self.dialogBox(_("Error in DownLoad."), _("MultiBootISOCreator:DownLoadCheck - exception: {}").format(detail))
                removeFile = True

            finally:
                self.theQ.task_done()
                del self.theThread

                if removeFile:
                    if os.path.isfile(destFileName):
                        os.remove(destFileName)

                # No matter whether we were successful or not, we need to clean
                # up our mess and set the controls back to their rightful state.
                # Save our current index
                localActiveIndex = self.cmbSelectedUSBDevice.get_active()

                # Relaod all the USB related data
                self.RefreshUSBSelector()

                # Point our ComboBox back where it was
                if localActiveIndex <= self.usbDataHandler.rowCount:
                    self.cmbSelectedUSBDevice.set_active(localActiveIndex)
                elif self.usbDataHandler.rowCount > 0:
                    self.cmbSelectedUSBDevice.set_active(0)
                else:
                    self.cmbSelectedUSBDevice.set_active(-1)

                self.DoNewUSBSelected()
                self.UpdateUSBGrubConfig()

                self.rbDownLoad.set_active(True)
                self.on_RadioButton_toggled(self.rbDownLoad)

                self.SetButtonsSensitive(True)

        return(retVal)

    def DownLoadFile(self):
        sourceURL = self.serverISOHandler.getDownLoadURL(self.cmbDownLoad.get_active())
        theMD5 = self.serverISOHandler.getMD5Sum(self.cmbDownLoad.get_active())
        destFileName = "{0}/{1}.iso".format(self.usbDataHandler.getMountPoint(self.cmbSelectedUSBDevice.get_active()),
            self.serverISOHandler.getSpinID(self.cmbDownLoad.get_active()))
        self.theThread = DownLoadISO(sourceURL, theMD5, destFileName, self.theQ)
        self.theThread.daemon = True
        self.theThread.start()
        self.theQ.join()

        self.pbCopy.set_show_text(True)
        self.SetButtonsSensitive(False)
        GLib.timeout_add(100, self.DownLoadCheck, destFileName)

    def DoISOAdd(self):
        if self.txtISOFileName.get_text() != "":
            theMessage = _("Please confirm you wish to copy the file\n\n\"{0}\"\n\nto the selected USB device.").format(self.txtISOFileName.get_text())
            response = self.dialogBox.warningDialog("Please Confirm ISO Copy", theMessage)

            if response == Gtk.ResponseType.OK:
                thePattern = re.compile(self.settingsData.getValue("regex-patterns", "solydxk-iso-file-pattern"))
                theBaseName = basename(self.txtISOFileName.get_text())
                if not thePattern.match(theBaseName):
                    self.dialogBox.infoDialog(_("Not a Known SolydXK ISO"),
                        _("The filename supplied does not match that\n" +
                        "of a known SolydXK ISO file.\n\n" +
                        "Please check your information and try again."))
                else:
                    theBaseName = theBaseName[:8] + ".iso"
                    theFileName = "{0}/{1}".format(self.usbDataHandler.getMountPoint(self.cmbSelectedUSBDevice.get_active()), theBaseName)
                    if os.path.isfile(theFileName):
                        response = self.dialogBox.questionDialog(_("Over-Write File?"),
                            _("The selected file already exists on the USB device.\n\nOver-write this file?"))
                    else:
                        response = Gtk.ResponseType.YES

                    if response == Gtk.ResponseType.YES:
                        self.CopyFile(theFileName)
        else:
            self.dialogBox.infoDialog(_("No file selected"),
            _("Please select a file to be copied to the selected USB Device"))

    def DoISODownLoad(self):
        if self.cmbDownLoad.get_active() == -1:
            self.dialogBox.infoDialog(_("No file selected"),
                _("Please select a file to be downloaded to the USB device"))
        else:
            self.DownLoadFile()

    def DoISODelete(self):
        localActiveIndex = self.cmbDeleteISO.get_active()
        if localActiveIndex == -1:
            self.dialogBox.infoDialog(_("No file selected"),
                _("Please select a file to be deleted from the USB device."))
        else:
            theFileName = "{0}/{1}".format(self.usbDataHandler.getMountPoint(self.cmbSelectedUSBDevice.get_active()),
                self.installedFilesHandler.getFileName(localActiveIndex))
            response = self.dialogBox.questionDialog(_("Please confirm file deletion"),
                _("Please confirm you wish to delete the file\n\n{}\n\nfrom the selected USB device.").format(theFileName))
            if response == Gtk.ResponseType.YES:
                if os.path.isfile(theFileName):
                    os.remove(theFileName)

                    # Rebuild all our USB data
                    localActiveIndex = self.cmbSelectedUSBDevice.get_active()
                    self.RefreshUSBSelector()
                    self.cmbSelectedUSBDevice.set_active(localActiveIndex)
                    self.UpdateUSBGrubConfig()
                    self.dialogBox.infoDialog(_("Operation complete"), _("The selected file has been deleted."))
                else:
                    self.dialogBox.errorDialog(_("File not found."),
                        _("The indicated file was not found on the USB device."))

    def DoNewUSBSelected(self):
        if(len(self.usbDataHandler.getList())) > 0:
            self.installedFilesHandler.buildInstalledFilesList(self.usbDataHandler.getMountPoint(self.cmbSelectedUSBDevice.get_active()))
            self.cmbHandlerDeleteISO.fillComboBox(self.installedFilesHandler.getFilesListComboBoxData())
            if self.installedFilesHandler.rowCount > 0:
                self.cmbDeleteISO.set_active(0)

            self.availableSpace = int(self.usbDataHandler.getAvailableSpace(self.cmbSelectedUSBDevice.get_active()))
            self.txtAvailable.set_text(self.Convert2Gigs(self.availableSpace))
            self.requiredSpace = 0
            self.txtRequired.set_text("")
        else:
            self.txtAvailable.set_text("")
            self.availableSpace = int(0)

    # Refresh the cmbSelectedUSBDevice and associated data
    def RefreshUSBSelector(self):
        self.usbDataHandler.getUSBData()
        if len(self.usbDataHandler.getList()) > 0:
            self.cmbHandlerSelectUSB.fillComboBox(self.usbDataHandler.getUSBComboBoxData())
            self.cmbSelectedUSBDevice.set_active(0)
            self.DoNewUSBSelected()
        else:
            self.cmbHandlerSelectUSB.clearComboBox()
            self.DoNewUSBSelected()

    def SetButtonsSensitive(self, newState):
        self.pbCopy.set_visible(not newState)
        self.btnClose.set_sensitive(newState)
        self.btnExecute.set_sensitive(newState)
        self.btnHelp.set_sensitive(newState)
        self.btnListDownLoadISOs.set_sensitive(newState)
        self.btnRefresh.set_sensitive(newState)
        self.btnSelectISOFile.set_sensitive(newState)

    def UpdateUSBGrubConfig(self):
        if self.installedFilesHandler.rowCount <= 0:
            self.dialogBox.infoDialog(_("No USB Device found."), _("It appears you forgot to mount a USB device!"))
        else:
            self.SetButtonsSensitive(False)

            theGrubFile = "{0}/boot/grub/grub.cfg".format(self.usbDataHandler.getMountPoint(self.cmbSelectedUSBDevice.get_active()))
            if os.path.isfile(theGrubFile):
                os.remove(theGrubFile)

            with open(theGrubFile, "w") as outFile:
                outFile.write("# Set the paths to the iso files\n")
                for i in range(self.installedFilesHandler.rowCount):
                    theFileName = self.installedFilesHandler.getFileName(i)
                    outFile.write("set {0}=\"/{1}\"\n".format(theFileName[:-4], theFileName))

                outFile.write("\n# Seconds to wait until starting the default menu entry\n")
                outFile.write("set timeout=10\n\n")
                outFile.write("# Default menu entry (0 = first)\nset default=0\n\n")
                outFile.write("loadfont /boot/grub/fonts/unicode.pf2\n")
                outFile.write("set gfxmode=auto\n")
                outFile.write("#set gfxmode=1024x768\n")
                outFile.write("insmod efi_gop\n")
                outFile.write("insmod efi_uga\n")
                outFile.write("insmod gfxterm\n")
                outFile.write("set gfxpayload=keep\n")
                outFile.write("terminal_output gfxterm\n\n")

                outFile.write("insmod png\n")
                outFile.write("background_image -m stretch /boot/grub/grubbg.png\n")
                outFile.write("set menu_color_normal=white/black\n")
                outFile.write("set menu_color_highlight=dark-gray/white\n\n")
                outFile.write("# Create the menu entries here:\n")

                # This block of code is for the way SolydXK names the various spins, as of 23AUG2014.
                # At some time in the future, Debian will promote Jessie to stable and SolydXK
                # will be changing the names of the spins. At that time, this block of code should
                # be removed from the application and the next (commented) block should be uncommented
                # and tested.
                for i in range(self.installedFilesHandler.rowCount):
                    theFileName = self.installedFilesHandler.getFileName(i)[:-4]

                    if theFileName[-2:] == "be":
                        theEdition = _("Business Edition 64-bit")
                    elif theFileName[-2:] == "bo":
                        theEdition = _("Back Office 64-bit")
                    else:
                        theEdition = _("Home Edition ") + theFileName[-2:] + "-bit"

                    if theFileName[-3:-2] == "k":
                        theDeskTop = "KDE"
                    else:
                        theDeskTop = "Xfce"

                    outFile.write("menuentry \"Solyd{0} {1} (with {2} desktop)\" {3}\n".format(theFileName[-3:-2], theEdition, theDeskTop, "{"))
                    outFile.write("bootoptions=\"findiso=${0} boot=live config username=solydxk hostname=solydxk quite splash\"\n".format(theFileName))
                    outFile.write("search --set -f ${0}\n".format(theFileName))
                    outFile.write("loopback loop ${0}\n".format(theFileName))
                    outFile.write("linux (loop)/live/vmlinuz $bootoptions\n")
                    outFile.write("initrd (loop)/live/initrd.img\n}\n")

                # This is the block of code that should be uncommented when Jessie is promoted to stable.
                # This block assumes that the first two columns of iso-data.csv file will be formatted as:
                # solydk32	SolydK 32-bit
                # solydk64	SolydK 64-bit
                # solydx32	SolydX 32-bit
                # solydx64	SolydX 64-bit
                # solydkbo	SolydK BO 64-bit
                # solydkee32	SolydK EE 32-bit
                # solydkee64	SolydK EE 64-bit
                # solydxee32	SolydX EE 32-bit
                # solydxee64	SolydX EE 64-bit

                #for i in range(self.installedFilesHandler.rowCount):
                    #theFileName = self.installedFilesHandler.getFileName(i)[:-4]

                    #if theFileName[-2:] == "be":
                        #theEdition = _(" Back Office Edition")
                        #theArch = "64"
                    #else:
                        #theArch = theFileName[-2:]
                        #if theFileName[-4:-2] == "ee":
                            #theEdition = _(" Enthusiast's Edition")
                        #else:
                            #theEdition = ""

                    #if theFileName[-3:-2] == "k":
                        #theDeskTop = "KDE"
                    #else:
                        #theDeskTop = "Xfce"

                    #outFile.write("menuentry \"Solyd{} {}-bit (with {} desktop\" {}".format(theEdition, theArch, theDeskTop, "{"))
                    #outFile.write("bootoptions=\"findiso=${0} boot=live config username=solydxk hostname=solydxk quite splash\"\n".format(theFileName))
                    #outFile.write("search --set -f ${0}\n".format(theFileName))
                    #outFile.write("loopback loop ${0}\n".format(theFileName))
                    #outFile.write("linux (loop)/live/vmlinuz $bootoptions\n")
                    #outFile.write("initrd (loop)/live/initrd.img\n}\n")

            self.dialogBox.infoDialog(_("GRUB re-write complete"),
                _("Please remove the USB device.\n\n" +
                "Please re-insert the USB device\n" +
                "If you wish to install ISO files.\n"))
            self.SetButtonsSensitive(True)

    ##########################################
    #
    # FileChooserDialog Event Handlers
    #
    ##########################################
    def on_FileChooserDialog_cancel(self, widget):
        self.txtISOFileName.set_text("")
        self.FileChooserDialog.hide()
        self.rbAddISO.set_active(True)
        self.on_RadioButton_toggled(self.rbAddISO)

    def on_FileChooserDialog_open(self, widget):
        self.txtISOFileName.set_text(self.FileChooserDialog.get_filename())
        self.FileChooserDialog.hide()
        self.rbAddISO.set_active(True)
        self.on_RadioButton_toggled(self.rbAddISO)

    def on_btnHelpWindowOK_clicked(self, widget):
        self.HelpWindow.hide()

    def on_HelpWindow_destroy(self, widget):
        self.HelpWindow.hide()

    def HelpWindowPopulateText(self):
        self.lblHelpWindowHelpText.set_label(
_("""-  Select USB - Using the ComboBox, select the USB device to be used as the Multi-Boot device.

-  Refresh - Clicking this button will cause the list of  USB devices to be refreshed. This can be useful if one starts the application before inserting the intended  target USB device.

-  Add ISO - This option allows for the copying of an already downloaded SolydXK .iso file to the target USB device.

-  File - This button opens a FileChooserDialog to be used to select the source .iso file. The path to the selected .iso file will be written to the TextEntry control.

-  Down Load - This option allows for downloading a SolydXK .iso file from the distro servers. The downloaded file will be written directly to the selected USB device.

-  Information - Opens a window showing a list of all the .iso files available for download, with associated metadata (size, m35sum, etc).

-  Delete - Use this option to remove an .iso file from the target USB device. Can be useful if there are old files there and you need more space for a new file ... or to over-write an old file with an updated version.

-  Clean USB - It sometimes happens that a USB device will need to be completely and thoroughly erased before the created USB will successfully boot. This option (available only run application is run as root) allows the user to completely re-initialize the USB device. All data will be deleted, a new partition table, a new data partition created, all required files written, and grub installed.

-  USB Space - This line indicates the amount of spaces available on the target USB device and the amount of space required to write the selected .iso file to the target USB device.

-  Help - This message

-  Execute - Perform the user selected operation.

-  Close - Exit the program."""))

    ##########################################
    #
    # ServerISOListWindow Event Handlers
    #
    ##########################################
    def on_btnServerISOListWindowClose_clicked(self, widget):
        self.ServerISOListWindow.hide()
Exemplo n.º 15
0
class Constructor(object):
    def __init__(self):
        self.scriptDir = abspath(dirname(__file__))
        self.shareDir = join(self.scriptDir,
                             '../../../share/trail/constructor')
        self.userAppDir = join(get_user_home_dir(), ".constructor")
        self.distroFile = join(self.userAppDir, "distros.list")

        # Create the user's application directory if it doesn't exist
        if not isdir(self.userAppDir):
            user_name = getUserLoginName()
            makedirs(self.userAppDir)
            old_distro_file = join(self.scriptDir, "distros.list")
            if exists(old_distro_file):
                move(old_distro_file, self.distroFile)
            system("chown -R %s:%s %s" %
                   (user_name, user_name, self.userAppDir))

        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(join(self.shareDir, 'constructor.glade'))

        # Main window objects
        go = self.builder.get_object
        self.window = go('constructorWindow')
        self.tvDistros = go('tvDistros')
        self.lblOutput = go('lblOutput')
        self.statusbar = go('statusbar')
        self.btnAdd = go('btnAdd')
        self.chkSelectAll = go('chkSelectAll')
        self.btnRemove = go('btnRemove')
        self.btnEdit = go('btnEdit')
        self.btnUpgrade = go('btnUpgrade')
        self.btnLocalize = go('btnLocalize')
        self.btnBuildIso = go('btnBuildIso')

        # Add iso window objects
        self.windowAddDistro = go('addDistroWindow')
        self.txtIso = go('txtIso')
        self.txtDir = go('txtDir')
        self.btnDir = go('btnDir')
        self.btnSave = go('btnSave')
        self.lblIso = go('lblIso')
        self.boxIso = go('boxIso')
        self.lblDir = go('lblDir')
        self.chkFromIso = go('chkFromIso')

        # Main window translations
        self.window.set_title(_("Constructor"))
        self.chkSelectAll.set_label(_("Select all"))
        self.btnAdd.set_label("_{}".format(_("Add")))
        self.btnRemove.set_label("_{}".format(_("Remove")))
        self.btnEdit.set_label("_{}".format(_("Edit")))
        self.btnUpgrade.set_label("_{}".format(_("Upgrade")))
        self.btnLocalize.set_label("_{}".format(_("Localize")))
        self.btnBuildIso.set_label("_{}".format(_("Build")))

        # Add iso window translations
        self.lblIso.set_text(_("ISO"))
        go('btnCancel').set_label("_{}".format(_("Cancel")))

        # Init
        self.ec = ExecCmd()
        self.ec.run("modprobe loop", False)
        self.queue = Queue()
        self.mountDir = "/mnt/constructor"
        self.distroAdded = False
        self.iso = None
        self.dir = None
        self.isoName = None
        self.chkFromIso.set_active(True)
        self.toggleGuiElements(False)

        # Treeviews
        self.tvHandlerDistros = TreeViewHandler(self.tvDistros)
        self.fillTreeViewDistros()

        # Version information
        ver = _("Version")
        self.version = "%s: %s" % (ver, getPackageVersion('constructor'))
        self.showOutput(self.version)

        # Connect the signals and show the window
        self.builder.connect_signals(self)
        self.window.show()

    # ===============================================
    # Main Window Functions
    # ===============================================

    def on_btnAdd_clicked(self, widget):
        self.windowAddDistro.show()

    def on_btnRemove_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0,
                                                          valueColNr=2)
        for path in selected:
            qd = QuestionDialog(self.btnRemove.get_label(), _("Are you sure you want to remove the selected distribution from the list?\n" \
                                                              "(This will not remove the directory and its data)"), self.window)
            answer = qd.show()
            if answer:
                self.saveDistroFile(distroPath=path, addDistro=False)
        self.fillTreeViewDistros()

    def on_btnEdit_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0,
                                                          valueColNr=2)
        for path in selected:
            de = EditDistro(path)
            services = []
            if exists(join(path, 'root/etc/apache2/apache2.conf')):
                services.append("apache2")
            if exists(join(path, 'root/etc/mysql/debian.cnf')):
                services.append("mysql")
            if services:
                msg = "If you need to update packages that depend on these services,\n" \
                      "you will need to manually start them:\n"
                for service in services:
                    msg += "\nservice %s start" % service
                msg += "\n\nWhen done:\n"
                for service in services:
                    msg += "\nservice %s stop" % service
                self.showInfo(_("Services detected"), msg, self.window)
                repaintGui()
            de.openTerminal()

    def on_btnUpgrade_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0,
                                                          valueColNr=2)
        upgraded = False
        for path in selected:
            upgraded = True
            rootPath = "%s/root" % path
            force = get_apt_force(rootPath)
            de = EditDistro(path)
            de.openTerminal("apt-get update")
            if exists(join(rootPath, 'etc/apache2/apache2.conf')):
                de.openTerminal("service apache2 start")
            if exists(join(rootPath, 'etc/mysql/debian.cnf')):
                de.openTerminal("service mysql start")
            de.openTerminal(
                "apt-get -y %s -o Dpkg::Options::=\"--force-confnew\" dist-upgrade"
                % force)
            if exists(join(rootPath, 'etc/apache2/apache2.conf')):
                de.openTerminal("service apache2 stop")
            if exists(join(rootPath, 'etc/mysql/debian.cnf')):
                de.openTerminal("service mysql stop")

            # Cleanup old kernel and headers
            script = "rmoldkernel.sh"
            scriptSource = join(self.scriptDir, "files/{}".format(script))
            scriptTarget = join(rootPath, script)
            if exists(scriptSource):
                copy(scriptSource, scriptTarget)
                self.ec.run("chmod a+x '%s'" % scriptTarget)
                de.openTerminal("/bin/bash %s" % script)
                silent_remove(scriptTarget)

            # Download offline packages
            print(">> Start downloading offline packages")
            self.download_offline_packages()

        if upgraded and exists("/usr/bin/aplay") and exists(self.doneWav):
            self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False)

    def on_btnLocalize_clicked(self, widget):
        # Set locale
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0,
                                                          valueColNr=2)
        for path in selected:
            rootPath = "%s/root" % path
            de = EditDistro(path)
            script = "setlocale.sh"
            scriptSource = join(self.scriptDir, "files/{}".format(script))
            scriptTarget = join(rootPath, script)
            if exists(scriptSource):
                copy(scriptSource, scriptTarget)
                self.ec.run("chmod a+x '%s'" % scriptTarget)
                de.openTerminal("/bin/bash %s" % script)
                silent_remove(scriptTarget)

    def download_offline_packages(self):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0,
                                                          valueColNr=2)
        for path in selected:
            rootPath = "%s/root" % path
            arch = getGuestEfiArchitecture(rootPath)
            de = EditDistro(path)
            script = "offline.sh"
            scriptSource = join(self.scriptDir, "files/{}".format(script))
            scriptTarget = join(rootPath, script)
            offlineSource = join(rootPath, "offline")
            offlineTarget = join(rootPath, "../boot/offline")
            if exists(scriptSource):
                try:
                    copy(scriptSource, scriptTarget)
                    self.ec.run("chmod a+x '%s'" % scriptTarget)
                    # Run the script
                    de.openTerminal("/bin/bash {} {}".format(script, arch))
                    # Remove script
                    silent_remove(scriptTarget)
                    # Move offline directory to boot directory
                    if exists(offlineSource):
                        print(("%s exists" % offlineSource))
                        if exists(offlineTarget):
                            print((">> Remove %s" % offlineTarget))
                            silent_remove(offlineTarget)
                        print((">> Move %s to %s" %
                               (offlineSource, offlineTarget)))
                        move(offlineSource, offlineTarget)
                    else:
                        print((">> Cannot find: %s" % offlineSource))
                except Exception as detail:
                    self.showError("Error: getting offline packages", detail,
                                   self.window)
            else:
                print((">> Cannot find: %s" % scriptSource))

    def on_btnBuildIso_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0,
                                                          valueColNr=2)
        msg = ""
        for path in selected:
            self.toggleGuiElements(True)
            self.showOutput("Start building ISO in: %s" % path)
            repaintGui()

            # Start building the ISO in a thread
            t = BuildIso(path, self.queue)
            t.start()
            self.queue.join()

            # Thread is done
            # Get the data from the queue
            ret = self.queue.get()
            self.queue.task_done()

            if ret is not None:
                self.showOutput(ret)
                if "error" in ret.lower():
                    self.showError("Error", ret, self.window)
                else:
                    msg += "%s\n" % ret

        if msg != "":
            if exists("/usr/bin/aplay") and exists(self.doneWav):
                self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False)
            self.showInfo("", msg, self.window)
        self.toggleGuiElements(False)

    def on_chkSelectAll_toggled(self, widget):
        self.tvHandlerDistros.treeviewToggleAll(
            toggleColNrList=[0], toggleValue=widget.get_active())

    def on_tvDistros_row_activated(self, widget, path, column):
        self.tvHandlerDistros.treeviewToggleRows(toggleColNrList=[0])

    def on_btnOpenDir_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0,
                                                          valueColNr=2)
        for path in selected:
            system("open-as-user %s root" % path)

    def fillTreeViewDistros(self, selectDistros=[]):
        contentList = [[
            _("Select"),
            _("Distribution"),
            _("Working directory")
        ]]
        distros = self.getDistros()
        for distro in distros:
            select = False
            for selectDistro in selectDistros:
                if distro[0] == selectDistro:
                    select = True
            contentList.append([select, distro[0], distro[1]])
        self.tvHandlerDistros.fillTreeview(
            contentList=contentList,
            columnTypesList=['bool', 'str', 'str'],
            firstItemIsColName=True)

    def getDistros(self):
        distros = []
        if exists(self.distroFile):
            with open(self.distroFile, 'r') as f:
                lines = f.readlines()
            for line in lines:
                line = line.strip().rstrip('/')
                print(line)
                if exists(line):
                    dg = DistroGeneral(line)
                    isoName = dg.description
                    distros.append([isoName, line])
            # Sort on iso name
            if distros:
                distros = sorted(distros, key=operator.itemgetter(0))
        return distros

    # ===============================================
    # Add ISO Window Functions
    # ===============================================

    def on_btnIso_clicked(self, widget):
        fleFilter = Gtk.FileFilter()
        fleFilter.set_name("ISO")
        fleFilter.add_mime_type("application/x-cd-image")
        fleFilter.add_pattern("*.iso")

        startDir = None
        if exists(dirname(self.txtIso.get_text())):
            startDir = dirname(self.txtIso.get_text())

        filePath = SelectFileDialog(title=_('Select ISO'),
                                    start_directory=startDir,
                                    parent=self.window,
                                    gtkFileFilter=fleFilter).show()
        if filePath is not None:
            self.txtIso.set_text(filePath)

    def on_btnDir_clicked(self, widget):
        startDir = None
        if exists(self.txtDir.get_text()):
            startDir = self.txtDir.get_text()
        dirText = SelectDirectoryDialog(title=_('Select directory'),
                                        start_directory=startDir,
                                        parent=self.window).show()
        if dirText is not None:
            self.txtDir.set_text(dirText)

    def on_btnSave_clicked(self, widget):
        self.iso = ""
        if self.chkFromIso.get_active():
            self.iso = self.txtIso.get_text()
        self.dir = self.txtDir.get_text()

        title = _("Save existing working directory")
        if self.iso != "":
            title = _("Unpack ISO and save")

        if not exists(self.dir):
            makedirs(self.dir)

        if not exists(self.dir):
            self.showError(
                title,
                _("Could not create directory %(dir)s: exiting" %
                  {"dir": self.dir}), self.window)
        else:
            self.windowAddDistro.hide()
            if self.iso != "":
                if not exists(self.iso):
                    self.showInfo(
                        self.btnSave.get_label(),
                        _("The path to the ISO file does not exist:\n{}".
                          format(self.iso)), self.window)
                    return
                if listdir(self.dir):
                    qd = QuestionDialog(
                        self.btnSave.get_label(),
                        _("The destination directory is not empty.\n"
                          "Are you sure you want to overwrite all data in {}?".
                          format(self.dir)), self.window)
                    answer = qd.show()
                    if not answer:
                        return

                self.showOutput("Start unpacking the ISO...")
                self.toggleGuiElements(True)
                t = IsoUnpack(self.mountDir, self.iso, self.dir, self.queue)
                t.start()
                self.queue.join()
                GObject.timeout_add(1000, self.checkThread, True)
            else:
                self.saveDistroFile(self.dir, True)
                self.fillTreeViewDistros()
                self.showOutput(_("Existing working directory added"))
                #self.toggleGuiElements(False)

    def on_btnCancel_clicked(self, widget):
        self.windowAddDistro.hide()

    def on_addDistroWindow_delete_event(self, widget, data=None):
        self.windowAddDistro.hide()
        return True

    def on_txtIso_changed(self, widget):
        path = self.txtIso.get_text()
        if exists(path):
            self.txtDir.set_sensitive(True)
            self.btnDir.set_sensitive(True)
            if exists(self.txtDir.get_text()):
                self.btnSave.set_sensitive(True)
        else:
            self.txtDir.set_sensitive(False)
            self.btnDir.set_sensitive(False)
            self.btnSave.set_sensitive(False)

    def on_txtDir_changed(self, widget):
        blnFromIso = self.chkFromIso.get_active()
        isoPath = self.txtIso.get_text()
        dirText = self.txtDir.get_text()
        self.btnSave.set_sensitive(False)
        if exists(dirText):
            if blnFromIso:
                if exists(isoPath):
                    self.btnSave.set_sensitive(True)
            else:
                self.btnSave.set_sensitive(True)

    def on_chkFromIso_toggled(self, widget):
        if widget.get_active():
            self.lblIso.set_visible(True)
            self.boxIso.set_visible(True)
            self.txtDir.set_sensitive(False)
            self.btnDir.set_sensitive(False)
            self.btnSave.set_sensitive(False)
            self.lblDir.set_text(_("Unpack ISO to directory"))
            self.btnSave.set_label(_("Unpack & Save"))
        else:
            self.txtIso.set_text("")
            self.lblIso.set_visible(False)
            self.boxIso.set_visible(False)
            self.txtDir.set_sensitive(True)
            self.btnDir.set_sensitive(True)
            self.btnSave.set_sensitive(True)
            self.lblDir.set_text(_("Work directory"))
            self.btnSave.set_label(_("Save"))

    # ===============================================
    # General functions
    # ===============================================

    def showInfo(self, title, message, parent=None):
        MessageDialogSafe(title, message, Gtk.MessageType.INFO, parent).show()

    def showError(self, title, message, parent=None):
        MessageDialogSafe(title, message, Gtk.MessageType.ERROR, parent).show()

    def showOutput(self, message):
        print(message)
        pushMessage(self.statusbar, message)

    def checkThread(self, addDistro=None):
        #print 'Thread count = ' + str(threading.active_count())
        # As long there's a thread active, keep spinning
        if threading.active_count() > 1:
            return True

        # Thread is done
        # Get the data from the queuez
        ret = self.queue.get()
        self.queue.task_done()

        # Thread is done
        if addDistro is not None:
            self.saveDistroFile(self.dir, addDistro)
            self.fillTreeViewDistros(self.isoName)
        self.toggleGuiElements(False)
        if exists("/usr/bin/aplay") and exists(self.doneWav):
            self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False)
        if ret is not None:
            self.showOutput(ret)
            if "error" in ret.lower():
                self.showError("Error", ret, self.window)
            else:
                self.showInfo("", ret, self.window)
        return False

    def toggleGuiElements(self, startThread):
        if startThread:
            self.chkSelectAll.set_sensitive(False)
            self.tvDistros.set_sensitive(False)
            self.btnAdd.set_sensitive(False)
            self.btnBuildIso.set_sensitive(False)
            self.btnEdit.set_sensitive(False)
            self.btnRemove.set_sensitive(False)
            self.btnUpgrade.set_sensitive(False)
            self.btnLocalize.set_sensitive(False)
            self.btnDir.set_sensitive(False)
        else:
            self.chkSelectAll.set_sensitive(True)
            self.tvDistros.set_sensitive(True)
            self.btnAdd.set_sensitive(True)
            self.btnBuildIso.set_sensitive(True)
            self.btnEdit.set_sensitive(True)
            self.btnRemove.set_sensitive(True)
            self.btnUpgrade.set_sensitive(True)
            self.btnLocalize.set_sensitive(True)
            self.btnDir.set_sensitive(True)

    def saveDistroFile(self, distroPath, addDistro=True):
        newCont = []
        dg = DistroGeneral(distroPath)
        self.isoName = dg.description

        cfg = []
        if exists(self.distroFile):
            with open(self.distroFile, 'r') as f:
                cfg = f.readlines()
            for line in cfg:
                line = line.strip()
                if distroPath not in line and exists(line):
                    newCont.append(line)

        if addDistro:
            newCont.append(distroPath)

        with open(self.distroFile, 'w') as f:
            f.write('\n'.join(newCont))

        self.iso = ""
        self.dir = ""

    # Close the gui
    def on_constructorWindow_destroy(self, widget):
        # Close the app
        Gtk.main_quit()
Exemplo n.º 16
0
class LightDMManager:

    def __init__(self):
        self.scriptDir = os.path.dirname(os.path.realpath(__file__))

        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(join(self.scriptDir, '../../share/lightdm-manager/lightdm-manager.glade'))
        # Main window objects
        go = self.builder.get_object
        self.window = go('ldmWindow')
        self.swUsers = go('swUsers')
        self.tvUsers = go('tvUsers')
        self.btnSave = go('btnSave')
        self.imgBackground = go('imgBackground')
        self.btnUsers = go('btnUsers')
        self.btnAppearance = go('btnAppearance')
        self.chkHideUsers = go('chkHideUsers')
        self.ebFace = go('ebFace')
        self.imgFace = go('imgFace')
        self.nbLightDM = go('nbLightDM')
        self.cmbThemes = go('cmbThemes')

        # Read from config file
        self.cfg = Config('lightdm-manager.conf')
        self.lightdmConf = self.cfg.getValue('CONFIG', 'lightdmConf')
        self.desktopbaseDir = self.cfg.getValue('CONFIG', 'desktopbaseDir')
        gktGreeterConf = self.cfg.getValue('CONFIG', 'gtkGreeterConf')
        kdeGreeterConf = self.cfg.getValue('CONFIG', 'kdeGreeterConf')
        if exists(gktGreeterConf):
            self.greeterConf = gktGreeterConf
        else:
            self.greeterConf = kdeGreeterConf

        # Translations
        title = _("LightDM Manager")
        self.window.set_title(title)
        self.btnUsers.set_label("_{}".format(_("Users")))
        self.btnAppearance.set_label("_{}".format(_("Appearance")))
        go('lblBackground').set_label(_("Background"))
        go('lblTheme').set_label(_("Theme"))
        go('lblLightDmMenu').set_label(_("Menu"))
        self.chkHideUsers.set_label(_("Hide users"))
        go('lblUsersFace').set_label(_("User icon"))
        go('lblUsersAutologin').set_label(_("Auto-login"))

        # Get current background image
        self.cfgGreeter = Config(self.greeterConf)
        try:
            self.curBgPath = self.cfgGreeter.getValue('greeter', 'background')
            self.curTheme = self.cfgGreeter.getValue('greeter', 'theme-name')
        except:
            self.curBgPath = None
            self.curTheme = None

        # Get current auto-login user
        self.cfgLightdm = Config(self.lightdmConf)
        try:
            self.curAutoUser = self.cfgLightdm.getValue('SeatDefaults', 'autologin-user').strip()
            self.curHideUsers = False
            ghu = self.cfgLightdm.getValue('SeatDefaults', 'greeter-hide-users').strip()
            if 'true' in ghu:
                self.curHideUsers = True
        except:
            self.curAutoUser = None
            self.curHideUsers = False

        # Init
        self.usr = User()
        self.newbgImg = self.curBgPath
        self.newAutoUser = self.curAutoUser
        self.newTheme = self.curTheme
        self.themes = []
        self.selectedMenuItem = None
        self.debug = False
        self.logPath = ''
        self.prevPath = None
        self.tempFace = "/tmp/face"
        self.newFaces = []
        self.loggedUser = functions.getUserLoginName()
        self.curUser = self.loggedUser
        self.selectImg = join(self.scriptDir, '../../share/lightdm-manager/select.png')

        # Handle arguments
        try:
            opts, args = getopt.getopt(sys.argv[1:], 'dl:', ['debug', 'log='])
        except getopt.GetoptError:
            print(("Arguments cannot be parsed: %s" % str(sys.argv[1:])))
            sys.exit(1)

        for opt, arg in opts:
            if opt in ('-d', '--debug'):
                self.debug = True
            elif opt in ('-l', '--log'):
                self.logPath = arg

        # Initialize logging
        if self.debug:
            if not self.logPath:
                self.logPath = 'lightdm-manager.log'
        self.log = Logger(self.logPath, 'debug', True, None, self.window)

        # Backup config files because ConfigParser does not preserve commented lines
        if not exists("%s.org" % self.greeterConf):
            copy(self.greeterConf, "%s.org" % self.greeterConf)
            self.log.write("%(conf1)s copied to %(conf2)s.org" % { "conf1": self.greeterConf, "conf2": self.greeterConf }, 'LightDMManager.main', 'debug')
        if not exists("%s.org" % self.lightdmConf):
            copy(self.lightdmConf, "%s.org" % self.lightdmConf)
            self.log.write("%(conf1)s copied to %(conf2)s.org" % { "conf1": self.lightdmConf, "conf2": self.lightdmConf }, 'LightDMManager.main', 'debug')

        # Initiate the treeview handler and connect the custom toggle event with usersCheckBoxToggled
        self.tvHandler = TreeViewHandler(self.tvUsers, self.log)
        self.tvHandler.connect('checkbox-toggled', self.usersCheckBoxToggled)

        # Get users
        self.users = self.usr.getUsers()
        self.fillUsers()
        self.tvHandler.selectValue(self.curUser, 1)
        self.setBackground(self.curBgPath)
        self.cmbHandlerThemes = ComboBoxHandler(self.cmbThemes)
        self.listThemes()
        self.chkHideUsers.set_active(self.curHideUsers)

        # Show users menu
        self.on_btnUsers_clicked(None)
        self.on_tvUsers_cursor_changed(None)

        self.version = functions.getPackageVersion('lightdm-manager')

        # Connect the signals and show the window
        self.builder.connect_signals(self)
        self.window.show()

    # ===============================================
    # Menu section functions
    # ===============================================

    def on_btnUsers_clicked(self, widget, event=None):
        if self.selectedMenuItem != menuItems[0]:
            self.selectedMenuItem = menuItems[0]
            self.nbLightDM.set_current_page(0)

    def on_btnAppearance_clicked(self, widget, event=None):
        if self.selectedMenuItem != menuItems[1]:
            self.selectedMenuItem = menuItems[1]
            self.nbLightDM.set_current_page(1)

    # ===============================================
    # Functions
    # ===============================================

    def on_ebFace_enter_notify_event(self, widget, event):
        self.window.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))

    def on_ebFace_leave_notify_event(self, widget, event):
        self.window.get_window().set_cursor(None)

    def on_ebFace_button_release_event(self, widget, event):
        home = self.usr.getUserHomeDir(self.curUser)
        primaryGroup = self.usr.getUserPrimaryGroupName(self.curUser)
        imagePath = SelectImageDialog(_('Select user image'), home, self.window).show()
        if imagePath is not None:
            tempUserImg = "%(tempFace)s.%(curUser)s" % {"tempFace": self.tempFace, "curUser": self.curUser}
            self.newFaces.append([tempUserImg, join(home, ".face"), self.curUser, primaryGroup])
            print((">>> self.newFaces = %s" % self.newFaces))
            ih = ImageHandler(imagePath)
            ih.makeFaceImage(tempUserImg)
            if exists(tempUserImg):
                self.imgFace.set_from_pixbuf(ih.pixbuf)
            else:
                # This should never happen
                self.imgFace.set_from_file(self.selectImg)

    def on_tvUsers_cursor_changed(self, widget):
        self.curUser = self.tvHandler.getSelectedValue(1)
        showFace = None
        if self.newFaces:
            for face in self.newFaces:
                if face[2] == self.curUser and exists(face[0]):
                    showFace = GdkPixbuf.Pixbuf.new_from_file(face[0])
        if showFace is None:
            showFace = self.usr.getUserFacePixbuf(self.curUser)
        if showFace is None:
            # Still no user icon found: show select image
            self.imgFace.set_from_file(self.selectImg)
        else:
            self.imgFace.set_from_pixbuf(showFace)

    def on_btnSave_clicked(self, widget):
        saved = False
        saveHideUsers = False
        saveAutoUser = False
        saveFaces = False
        saveBackground = False
        saveTheme = False
        if self.chkHideUsers.get_active() != self.curHideUsers:
            saveHideUsers = True
        if self.curAutoUser != self.newAutoUser:
            saveAutoUser = True
        if self.newFaces:
            saveFaces = True
        if self.curBgPath != self.newbgImg:
            saveBackground = True
        self.newTheme = self.cmbHandlerThemes.getValue()
        if self.curTheme != self.newTheme:
            saveTheme = True

        if saveHideUsers or saveAutoUser or saveFaces or saveBackground or saveTheme:
            qd = QuestionDialog(_("LightDM settings"), _("Settings have changed\n\nDo you want to save the new settings?"), self.window)
            answer = qd.show()
            if answer:
                if saveAutoUser:
                    if self.newAutoUser is not None:
                        # Save the auto-login user
                        self.cfgLightdm.setValue('SeatDefaults', 'autologin-user', self.newAutoUser)
                        self.cfgLightdm.setValue('SeatDefaults', 'autologin-user-timeout', '0')
                        self.curAutoUser = self.newAutoUser
                        self.log.write("New auto-login user: %(usr)s" % { "usr": self.curAutoUser }, 'LightDMManager.saveSettings', 'debug')
                    else:
                        self.cfgLightdm.removeOption('SeatDefaults', 'autologin-user')
                        self.cfgLightdm.removeOption('SeatDefaults', 'autologin-user-timeout')
                        self.curAutoUser = None
                        self.log.write("Auto-login disabled", 'LightDMManager.saveSettings', 'debug')
                if saveHideUsers:
                    hideUsers = str(self.chkHideUsers.get_active()).lower()
                    self.cfgLightdm.setValue('SeatDefaults', 'greeter-hide-users', hideUsers)
                    self.log.write("Hide users saved: %(users)s" % {"users": hideUsers}, 'LightDMManager.saveSettings', 'debug')
                if saveFaces:
                    for face in self.newFaces:
                        if exists(face[0]):
                            copy(face[0], face[1])
                            if exists(face[1]):
                                os.system("chown %(owner)s:%(group)s %(path)s" % {"owner": face[2], "group": face[3], "path": face[1]})
                    self.log.write("User icons saved", 'LightDMManager.saveSettings', 'debug')
                if saveTheme:
                    self.cfgGreeter.setValue('greeter', 'theme-name', self.newTheme)
                    self.curTheme = self.newTheme
                    self.log.write("Theme saved: %(theme)s" % { "theme": self.curTheme }, 'LightDMManager.saveSettings', 'debug')
                if saveBackground:
                    if os.path.exists(self.newbgImg):
                        self.cfgGreeter.setValue('greeter', 'background', self.newbgImg)
                        self.curBgPath = self.newbgImg
                        self.log.write("Background saved: %(background)s" % { "background": self.curBgPath }, 'LightDMManager.saveSettings', 'debug')
                saved = True
            else:
                if os.path.exists(self.curBgPath):
                    self.setBackground(self.curBgPath)
                    self.log.write("Current background: %(background)s" % { "background": self.curBgPath }, 'LightDMManager.saveSettings', 'debug')
                else:
                    self.imgBackground.set_from_file(join(self.scriptDir, '../../share/lightdm-manager/select.png'))
                    self.log.write("No background set", 'LightDMManager.saveSettings', 'debug')
                self.fillUsers()

        if saved:
            self.curHideUsers = self.chkHideUsers.get_active()
            self.curAutoUser = self.newAutoUser
            self.newFaces = []
            self.curBgPath = self.newbgImg
            self.curTheme = self.newTheme
            MessageDialogSafe(_("Saved"), _("LightDM settings saved successfully."), Gtk.MessageType.INFO, self.window).show()


    def on_ebBackground_button_release_event(self, widget, event):
        self.newbgImg = SelectImageDialog(_("Choose background image"), self.desktopbaseDir, self.window).show()
        if exists(self.newbgImg) and self.newbgImg != self.curBgPath:
            self.setBackground(self.newbgImg)
            self.log.write(_("New background: %(bg)s") % { "bg": self.newbgImg }, 'LightDMManager.chooseFile', 'info')

    def on_ebBackground_enter_notify_event(self, widget, event):
        self.window.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))

    def on_ebBackground_leave_notify_event(self, widget, event):
        self.window.get_window().set_cursor(None)

    # This method is fired by the TreeView.checkbox-toggled event
    def usersCheckBoxToggled(self, obj, path, colNr, toggleValue):
        path = int(path)
        model = self.tvUsers.get_model()
        itr = model.get_iter(path)
        user = model[itr][1]

        if self.prevPath != path or toggleValue:
            # Only one toggle box can be selected (or none)
            self.tvHandler.treeviewToggleAll([0], False, 1, user)
            # Save current path
            self.prevPath = path
            # Save selected user
            self.newAutoUser = user
            self.log.write(_("Auto-login user selected: %(usr)s") % { "usr": user }, 'LightDMManager.usersCheckBoxToggled', 'info')
        elif self.prevPath == path and not toggleValue:
            self.newAutoUser = None

    def listThemes(self):
        themeDir = '/usr/share/themes'
        themeDirLocal = '~/.local/share/themes'
        dirs = functions.locate('gtk-*', themeDir, True) + functions.locate('gtk-*', themeDirLocal, True)
        for path in dirs:
            dirList = path.split('/')
            for d in dirList:
                if 'gtk-' in d:
                    break
                themeName = d
            if themeName not in self.themes:
                self.themes.append(themeName)
        if self.themes:
            self.cmbHandlerThemes.fillComboBox(self.themes)
            if self.curTheme in self.themes:
                self.cmbHandlerThemes.selectValue(self.curTheme)

    def fillUsers(self):
        selUsr = False
        contentList = []
        i = 0
        for usr in self.users:
            if usr == self.curAutoUser:
                selUsr = True
                self.prevPath = i
            else:
                selUsr = False
            contentList.append([selUsr, usr])
            i += 1

        # Fill treeview with users
        #fillTreeview(contentList, columnTypesList, columnHideList=[-1], setCursor=0, setCursorWeight=400, firstItemIsColName=False, appendToExisting=False, appendToTop=False)
        columnTypesList = ['bool', 'str']
        self.tvHandler.fillTreeview(contentList, columnTypesList)

    def setBackground(self, path):
        # Set Background
        if path is not None:
            if exists(path):
                ih = ImageHandler(path)
                ih.resizeImage(height=200)
                self.imgBackground.set_from_pixbuf(ih.pixbuf)
            else:
                self.imgBackground.set_from_file(self.selectImg)
        else:
            self.imgBackground.set_from_file(self.selectImg)

    # ===============================================
    # General functions
    # ===============================================

    def on_ldmWindow_destroy(self, widget, data=None):
        # Close the app
        self.on_btnSave_clicked(None)
        for tmp in self.newFaces:
            os.remove(tmp[0])
        Gtk.main_quit()
Exemplo n.º 17
0
class UserManager(object):

    def __init__(self):
        self.scriptDir = abspath(dirname(__file__))

        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(join(self.scriptDir, '../../share/usermanager/usermanager.glade'))
        # Main window objects
        go = self.builder.get_object
        self.window = go('usermanagerWindow')
        self.tvUsersMain = go('tvUsersMain')
        self.tvUserGroupsMain = go('tvUserGroupsMain')
        self.chkShowSystemUsers = go('chkShowSystemUsers')
        self.btnUserEdit = go('btnUserEdit')
        self.btnUserRemove = go('btnUserRemove')
        # User window objects
        self.windowUser = go('usermanagerUserWindow')
        self.nbUser = go('nbUser')
        self.txtLoginName = go('txtLoginName')
        self.txtRealName = go('txtRealName')
        self.txtUserID = go('txtUserID')
        self.txtHomeDirectory = go('txtHomeDirectory')
        self.cmbShells = go('cmbShells')
        self.cmbPrimaryGroup = go('cmbPrimaryGroup')
        self.cmbPrimaryGroupEntry = go('cmbPrimaryGroupEntry')
        self.tvUserGroups = go('tvUserGroups')
        self.ebFace = go('ebFace')
        self.imgFace = go('imgFace')
        self.lblUserID = go('lblUserID')
        self.txtPassword = go('txtPassword')
        self.txtLastChanged = go('txtLastChanged')
        self.cmbValidUntilMonth = go('cmbValidUntilMonth')
        self.spbValidUntilDay = go('spbValidUntilDay')
        self.spbValidUntilYear = go('spbValidUntilYear')
        self.radEnabled = go('radEnabled')
        self.radDisabled = go('radDisabled')
        self.radValidUntilAlways = go('radValidUntilAlways')
        self.radValidUntilDate = go('radValidUntilDate')
        self.spbRequirePasswordAfter = go('spbRequirePasswordAfter')
        self.spbWarnBeforePasswordExpires = go('spbWarnBeforePasswordExpires')
        self.spbDisableAccountWhenPasswordExpires = go('spbDisableAccountWhenPasswordExpires')
        self.spbEnforceMinimumPasswordAge = go('spbEnforceMinimumPasswordAge')
        self.chkRequirePasswordAfter = go('chkRequirePasswordAfter')
        self.chkEnforceMinimumPasswordAge = go('chkEnforceMinimumPasswordAge')
        self.tvGroupsMain = go('tvGroupsMain')
        # Group window objects
        self.windowGroup = go('usermanagerGroupWindow')
        self.txtGroupName = go('txtGroupName')
        self.txtGroupID = go('txtGroupID')
        self.tvAccounts = go('tvAccounts')
        self.tvSelectedAccounts = go('tvSelectedAccounts')
        self.tvGroupAccounts = go('tvGroupAccounts')

        # Main window translations
        self.window.set_title(_("User manager"))
        self.chkShowSystemUsers.set_label(_("Show system users"))
        self.btnUserEdit.set_label(_("Edit"))
        self.btnUserRemove.set_label(_("Remove"))
        go('btnUserAdd').set_label(_("Add"))
        go('lblUsersTab').set_text(_("Users"))
        go('lblGroupsTab').set_text(_("Groups"))
        go('lblUsersMain').set_text(go('lblUsersTab').get_text())
        go('lblUserGroupsMain').set_text(_("User groups"))
        go('lblGroupsMain').set_text(go('lblGroupsTab').get_text())
        go('lblGroupAccountsMain').set_text(_("Group accounts"))
        # User window translations
        self.windowUser.set_title(_("User settings"))
        go('lblUserDetails').set_text(_("Details"))
        go('lblStatus').set_text(_("Status"))
        go('lblLoginName').set_text(_("Login name"))
        go('lblRealName').set_text(_("Real name"))
        go('lblUserID').set_text(_("User ID"))
        go('lblPrimaryGroup').set_text(_("Primary group"))
        go('lblHomeDirectory').set_text(_("Home directory"))
        go('lblShell').set_text(_("Shell"))
        self.radEnabled.set_label(_("Enabled"))
        self.radDisabled.set_label(_("Disabled"))
        #go('lblPrivilegesAndGroups').set_text(_("Privileges and groups"))
        go('lblPrivilegesAndGroups').set_text(go('lblGroupsTab').get_text())
        go('lblPrivileges').set_text(_("Privileges"))
        go('lblUserGroups').set_text(go('lblGroupsTab').get_text())
        go('lblPasswordSecurity').set_text(_("Password"))
        go('lblPassword').set_text(go('lblPasswordSecurity').get_text())
        go('lblLastChanged').set_text(_("Last changed"))
        go('lblValidUntil').set_text(_("Valid until"))
        self.radValidUntilAlways.set_label(_("Always"))
        go('lblPasswordAging').set_text(_("Password aging"))
        self.chkRequirePasswordAfter.set_label(_("Require password after"))
        go('lblWarnBeforePasswordExpires').set_text(_("Warn before password expires after"))
        go('lblDisableAccountWhenPasswordExpires').set_text(_("Disable account when password expires after"))
        self.chkEnforceMinimumPasswordAge.set_label(_("Enforce minimum password age"))
        go('lblDays1').set_text(_("days"))
        go('lblDays2').set_text(go('lblDays1').get_text())
        go('lblDays3').set_text(go('lblDays1').get_text())
        go('lblDays4').set_text(go('lblDays1').get_text())
        go('btnSaveUser').set_label(_("Save"))
        go('btnCancelUser').set_label(_("Cancel"))
        # Group window translations
        self.windowGroup.set_title(_("Group settings"))
        go('lblGroupName').set_text(_("Group name"))
        go('lblGroupID').set_text(_("Group ID"))
        go('lblAccounts').set_text(_("Available accounts"))
        go('lblSelectedAccounts').set_text(_("Selected accounts"))
        go('btnOkGroup').set_label(go('btnSaveUser').get_label())
        go('btnCancelGroup').set_label(go('btnCancelUser').get_label())
        go('btnAddAccount').set_label(go('btnUserAdd').get_label())
        go('btnRemoveAccount').set_label(self.btnUserRemove.get_label())

        # Init
        self.ec = ExecCmd()
        self.usr = User()
        self.usersInfo = {}
        self.user = {}
        self.users = []
        self.accounts = []
        self.shells = self.usr.getShells()
        self.groups = None
        self.loggedinUser = self.usr.getLoggedinUser()
        self.loggedinUserPrimaryGroup = self.usr.getUserPrimaryGroupName(self.loggedinUser)
        self.selectedUser = None
        self.selectedGroup = None
        self.selectedGroupAccounts = None
        self.userFace = None
        self.radEnabled.set_active(True)
        self.radValidUntilAlways.set_active(True)
        self.setValidUntil(False)
        self.setRequirePasswordAfter(False)
        self.setEnforceMinimumPasswordAge(False)
        self.txtUserID.set_editable(False)
        self.txtUserID.set_can_focus(False)
        self.txtLastChanged.set_editable(False)
        self.txtLastChanged.set_can_focus(False)
        self.txtGroupID.set_editable(False)
        self.txtGroupID.set_can_focus(False)
        self.passwordChanged = False
        self.homeDirChanged = False
        self.tempFace = "/tmp/face.png"

        self.filterText(self.txtLoginName)
        self.filterText(self.txtGroupName)
        self.filterText(self.cmbPrimaryGroupEntry)

        # Treeviews
        self.tvHandlerUsersMain = TreeViewHandler(self.tvUsersMain)
        self.tvHandlerUserGroupsMain = TreeViewHandler(self.tvUserGroupsMain)
        self.tvHandlerUserGroups = TreeViewHandler(self.tvUserGroups)
        self.tvHandlerAccounts = TreeViewHandler(self.tvAccounts)
        self.tvHandlerSelectedAccounts = TreeViewHandler(self.tvSelectedAccounts)
        self.tvHandlerGroupsMain = TreeViewHandler(self.tvGroupsMain)
        self.tvHandlerGroupAccounts = TreeViewHandler(self.tvGroupAccounts)
        # Comboboxes
        self.cmbHandlerShells = ComboBoxHandler(self.cmbShells)
        self.cmbHandlerPrimaryGroup = ComboBoxHandler(self.cmbPrimaryGroup)
        self.cmbHandlerValidUntilMonth = ComboBoxHandler(self.cmbValidUntilMonth)

        # Get data
        self.refreshData()
        self.cmbHandlerShells.fillComboBox(self.shells)
        self.cmbHandlerValidUntilMonth.fillComboBox(functions.getMonthsList())
        year = datetime.now().year
        adj = Gtk.Adjustment(value=year, lower=year, upper=year + 10, step_incr=1, page_incr=0, page_size=0)
        self.spbValidUntilYear.set_adjustment(adj)

        # Connect the signals and show the window
        self.builder.connect_signals(self)
        self.window.show()


    # ===============================================
    # User functions
    # ===============================================

    def on_tvUsersMain_cursor_changed(self, widget, event=None):
        self.showUserData()

    def on_tvUsersMain_row_activated(self, widget, path, column):
        self.on_btnUserEdit_clicked(None)

    def on_btnUserAdd_clicked(self, widget):
        self.imgFace.set_from_pixbuf(self.usr.getUserFacePixbuf())
        self.tvHandlerUserGroups.fillTreeview(contentList=self.getUserGroupsComplete(), columnTypesList=['bool', 'str'])
        self.cmbHandlerShells.selectValue('/bin/bash')
        self.cmbHandlerPrimaryGroup.fillComboBox(self.groups)
        self.txtLoginName.set_editable(True)
        self.txtLoginName.set_can_focus(True)
        self.txtLoginName.set_text("new_user")
        self.txtUserID.set_text(str(self.usr.getNewUserID()))
        self.updateNewLogin()
        self.txtPassword.set_text("")
        self.radValidUntilAlways.set_active(True)
        self.windowUser.show()

    def on_btnUserEdit_clicked(self, widget):
        if self.selectedUser is not None:
            if not self.userFace is None:
                self.imgFace.set_from_pixbuf(self.userFace)
                self.imgFace.show()
                self.nbUser.get_nth_page(2).show()

            else:
                self.imgFace.hide()
                # Hide passwords tab
                self.nbUser.get_nth_page(2).hide()

            self.txtLoginName.set_editable(False)
            self.txtLoginName.set_can_focus(False)
            self.txtLoginName.set_text(self.user['user'].pw_name)
            if ",,," in self.user['user'].pw_gecos:
                self.txtRealName.set_text(self.user['user'].pw_name)
            else:
                self.txtRealName.set_text(self.user['user'].pw_gecos)
            self.txtUserID.set_text(str(self.user['user'].pw_uid))
            self.txtHomeDirectory.set_text(self.user['user'].pw_dir)
            self.tvHandlerUserGroups.fillTreeview(contentList=self.getUserGroupsComplete(self.user['groups']), columnTypesList=['bool', 'str'])
            self.cmbHandlerShells.selectValue(self.user['user'].pw_shell)
            self.cmbHandlerPrimaryGroup.fillComboBox(self.groups, self.user['prgrp'])
            self.txtPassword.set_text("")
            self.txtLastChanged.set_text(self.usr.intToDate(nr_days=self.user['pwd'].sp_lstchg, format_string='%d %B %Y', start_date=datetime(1970, 1, 1)))

            daysMin = self.user['pwd'].sp_min
            print((">>> daysMin = %d" % daysMin))
            if daysMin > 0:
                self.radValidUntilDate.set_active(True)
                dt = self.usr.intToDate(nr_days=daysMin)
                self.spbValidUntilDay.set_value(dt.day)
                self.cmbValidUntilMonth.set_active(dt.month - 1)
                self.spbValidUntilYear.set_value(dt.year)
            else:
                self.radValidUntilAlways.set_active(True)

            daysMax = self.user['pwd'].sp_max
            daysWarn = self.user['pwd'].sp_warn
            daysInact = self.user['pwd'].sp_inact
            print((">>> daysMax = %d" % daysMax))
            print((">>> daysWarn = %d" % daysWarn))
            print((">>> daysInact = %d" % daysInact))
            if daysMax < 1000 and daysWarn >= 0 and daysInact >= 0:
                self.chkRequirePasswordAfter.set_active(True)
                self.spbRequirePasswordAfter.set_value(daysMax)
                self.spbWarnBeforePasswordExpires.set_value(daysWarn)
                self.spbDisableAccountWhenPasswordExpires.set_value(daysInact)
            else:
                self.chkRequirePasswordAfter.set_active(False)

            daysExpire = self.user['pwd'].sp_expire
            print((">>> daysExpire = %d" % daysExpire))
            if daysExpire >= 0:
                self.chkEnforceMinimumPasswordAge.set_active(True)
                self.spbEnforceMinimumPasswordAge.set_value(daysExpire)
            else:
                self.chkEnforceMinimumPasswordAge.set_active(False)

            self.windowUser.show()

    def on_btnUserRemove_clicked(self, widget):
        # Remove user
        if self.selectedUser is not None:
            title = _("Remove user")
            if self.selectedUser != self.loggedinUser:
                qd = QuestionDialog(title, _("Are you sure you want to remove the following user:\n\n'%(user)s'") % { "user": self.selectedUser }, self.window)
                answer = qd.show()
                if answer:
                    # TODO
                    ret = self.usr.deleteUser(self.selectedUser)
                    if ret == "":
                        self.showInfo(title, _("User successfully removed: %(user)s") % {"user": self.selectedUser}, self.window)
                        self.refreshData()
                    else:
                        retMsg = "\n\n%s" % ret
                        self.showError(title, _("Could not remove user: %(user)s %(retmsg)s") % {"user": self.selectedUser, "retmsg": retMsg}, self.window)
            else:
                self.showError(title, _("You cannot remove the currently logged in user"), self.window)

    def on_chkShowSystemUsers_toggled(self, widget):
        self.refreshData()

    def on_btnBrowse_clicked(self, widget):
        directory = SelectDirectoryDialog(_('Select user directory'), self.txtHomeDirectory.get_text(), self.windowUser).show()
        if directory is not None:
            self.user['user'].pw_dir = directory
            self.txtHomeDirectory.set_text(self.user['user'].pw_dir)

    def on_radEnabled_toggled(self, widget):
        if widget.get_active():
            print(">>> Enable user")

    def on_radDisabled_toggled(self, widget):
        if widget.get_active():
            print(">>> Disable user")

    def on_radValidUntilAlways_toggled(self, widget):
        if widget.get_active():
            self.setValidUntil(False)

    def on_radValidUntilDate_toggled(self, widget):
        if widget.get_active():
            self.setValidUntil(True)

    def setValidUntil(self, boolean=True):
        self.spbValidUntilDay.set_sensitive(boolean)
        self.cmbValidUntilMonth.set_sensitive(boolean)
        self.spbValidUntilYear.set_sensitive(boolean)
        if not boolean:
            self.spbValidUntilDay.set_value(0)
            self.cmbValidUntilMonth.set_active(-1)
            self.spbValidUntilYear.set_value(0)

    def on_chkRequirePasswordAfter_toggled(self, widget):
        if widget.get_active():
            self.setRequirePasswordAfter(True)
        else:
            self.setRequirePasswordAfter(False)

    def setRequirePasswordAfter(self, boolean=True):
        self.spbRequirePasswordAfter.set_sensitive(boolean)
        self.spbWarnBeforePasswordExpires.set_sensitive(boolean)
        self.spbDisableAccountWhenPasswordExpires.set_sensitive(boolean)
        if not boolean:
            self.spbRequirePasswordAfter.set_value(0)
            self.spbWarnBeforePasswordExpires.set_value(0)
            self.spbDisableAccountWhenPasswordExpires.set_value(0)

    def on_chkEnforceMinimumPasswordAge_toggled(self, widget):
        if widget.get_active():
            self.setEnforceMinimumPasswordAge(True)
        else:
            self.setEnforceMinimumPasswordAge(False)

    def setEnforceMinimumPasswordAge(self, boolean=True):
        self.spbEnforceMinimumPasswordAge.set_sensitive(boolean)
        if not boolean:
            self.spbEnforceMinimumPasswordAge.set_value(0)

    def on_txtLoginName_changed(self, widget):
        self.updateNewLogin()

    def on_btnSaveUser_clicked(self, widget):
        errMsgs = []
        changed = False
        name = self.txtLoginName.get_text().strip()
        if name != "":
            userExists = self.usr.doesUserExist(name)

            realName = self.txtRealName.get_text().strip()
            if realName != "":
                if self.user['user'].pw_gecos == realName:
                    realName = ""
                else:
                    print(">>> realName changed")
                    changed = True

            prGroup = self.cmbHandlerPrimaryGroup.getValue()
            if prGroup != "":
                if self.user['prgrp'] == prGroup:
                    prGroup = ""
                else:
                    print(">>> prGroup changed")
                    changed = True
            elif not userExists:
                errMsgs.append(_("You need to provide a primary group for a new user"))

            home = self.txtHomeDirectory.get_text().strip()
            facePath = ""
            if home != "":
                if exists(self.tempFace):
                    facePath = join(home, ".face")
                if self.user['user'].pw_dir == home:
                    home = ""
                else:
                    print(">>> home changed")
                    changed = True

            groups = self.tvHandlerUserGroups.getToggledValues()
            if functions.areListsEqual(groups, self.user['groups']):
                groups = []
            else:
                print(">>> groups changed")
                changed = True

            password = self.txtPassword.get_text().strip()
            if password != "":
                encPwd = self.usr.encryptPassword(password)
                if self.user['pwd'].sp_pwd ==  encPwd:
                    password = ""
                else:
                    print(">>> password changed")
                    changed = True
            elif not userExists:
                errMsgs.append(_("You need to provide a password for a new user"))

        else:
            errMsgs.append(_("You need to provide a name for a new user"))

        shell = self.cmbHandlerShells.getValue()

        # TODO
        #if self.radValidUntilDate.get_active():
            #vuDay = self.spbValidUntilDay.get_value_as_int()
            #vuMonth = self.cmbValidUntilMonth.get_active() + 1
            #vuYear = self.spbValidUntilYear.get_value_as_int()
            #(datetime(vuYear, vuMonth, vuDay, 0, 0, 0) - datetime.now()).days

        #manageUser(self, user, primary_group="", group_list=[], shell="", home_dir="", full_name="", password="", expire_date="", inactive_days=""):
        title = _("Save user settings")
        if errMsgs:
            msg = "\n".join(errMsgs)
            self.showError(title, msg, self.windowUser)
        elif changed or facePath != "":
            err = 0
            if changed:
                err = self.usr.manageUser(user=name, full_name=realName, primary_group=prGroup, home_dir=home, group_list=groups, shell=shell, password=password)
            if err == 0:
                if exists(self.tempFace):
                    move(self.tempFace, facePath)
                self.showInfo(title, _("User saved: %(user)s") % {"user": name}, self.windowUser)
                self.refreshData()
            else:
                # TODO
                pass

            if exists(self.tempFace):
                os.remove(self.tempFace)
            self.windowUser.hide()
        else:
            self.showInfo(title, _("Nothing was changed on user: %(user)s") % {"user": name}, self.windowUser)
            self.windowUser.hide()

        if exists(self.tempFace):
            os.remove(self.tempFace)

    def on_cmbValidUntilMonth_changed(self, widget):
        monthDays = functions.getDaysInMonth(widget.get_active() + 1)
        adj = Gtk.Adjustment(value=1, lower=1, upper=monthDays, step_incr=1, page_incr=0, page_size=0)
        self.spbValidUntilDay.set_adjustment(adj)
        self.spbValidUntilDay.set_value(1)

    def updateNewLogin(self):
        txt = self.txtLoginName.get_text()
        self.txtRealName.set_text(txt)
        self.txtHomeDirectory.set_text("/home/%s" % txt)
        self.cmbHandlerPrimaryGroup.setValue(txt)

    def getUsers(self):
        self.users = []
        self.usersInfo = self.usr.getAllUsersInfoDict(self.chkShowSystemUsers.get_active())
        for ui in self.usersInfo:
            self.users.append([ui['face'], ui['user'].pw_name])

    def showUserData(self):
        # Show user groups in tvUserGroupsMain
        self.selectedUser = self.tvHandlerUsersMain.getSelectedValue(1)
        print((">>> selectedUser = %s" % self.selectedUser))
        for ui in self.usersInfo:
            if ui['user'].pw_name == self.selectedUser:
                self.user = ui
                break
        self.tvHandlerUserGroupsMain.fillTreeview(contentList=self.user['groups'], columnTypesList=['str'])
        self.userFace = self.usr.getUserFacePixbuf(self.user['user'].pw_name, None, 32)

    def getUserGroupsComplete(self, userGroups=None):
        ugc = []
        for group in self.groups:
            if group != self.user['prgrp']:
                isUg = False
                if userGroups is not None:
                    for ug in userGroups:
                        if ug == group:
                            isUg = True
                            break
                ugc.append([isUg, group])
        return ugc

    def on_btnCancelUser_clicked(self, widget):
        # Close add share window without saving
        if exists(self.tempFace):
            os.remove(self.tempFace)
        self.windowUser.hide()

    def on_ebFace_button_release_event(self, widget, data=None):
        imagePath = SelectImageDialog(_('Select user image'), self.user['user'].pw_dir, self.windowUser).show()
        if imagePath is not None:
            ih = ImageHandler(imagePath)
            ih.makeFaceImage(self.tempFace)
            if exists(self.tempFace):
                self.imgFace.set_from_pixbuf(ih.pixbuf)

    def on_usermanagerUserWindow_delete_event(self, widget, data=None):
        if exists(self.tempFace):
            os.remove(self.tempFace)
        self.windowUser.hide()
        return True

    def fillTreeViewUsers(self):
        self.tvHandlerUsersMain.fillTreeview(contentList=self.users, columnTypesList=['GdkPixbuf.Pixbuf', 'str'], fixedImgHeight=48)

    def on_ebFace_enter_notify_event(self, widget, data=None):
        self.windowUser.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))

    def on_ebFace_leave_notify_event(self, widget, data=None):
        self.windowUser.get_window().set_cursor(None)


    # ===============================================
    # Group functions
    # ===============================================

    def on_tvGroupsMain_cursor_changed(self, widget, event=None):
        self.selectedGroup = self.tvHandlerGroupsMain.getSelectedValue()
        self.selectedGroupAccounts = self.usr.getGroupAccounts(self.selectedGroup)
        print((">>> selectedGroup 1 =  %s" % str(self.selectedGroup)))
        print((">>> selectedGroupAccounts 1 =  %s" % str(self.selectedGroupAccounts)))
        self.tvHandlerGroupAccounts.fillTreeview(self.selectedGroupAccounts, ['str'])

    def on_tvGroupsMain_row_activated(self, widget, path, column):
        self.on_btnGroupEdit_clicked(None)

    def on_btnGroupAdd_clicked(self, widget):
        self.selectedGroup = None
        self.selectedGroupAccounts = None
        print((">>> selectedGroup 2 =  %s" % str(self.selectedGroup)))
        print((">>> selectedGroupAccounts 2 =  %s" % str(self.selectedGroupAccounts)))
        self.txtGroupName.set_editable(True)
        self.txtGroupName.set_can_focus(True)
        self.txtGroupName.set_text("")
        self.txtGroupID.set_text(str(self.usr.getNewGroupID()))
        self.fillTreeViewAccounts(self.users)
        self.windowGroup.show()

    def on_btnGroupEdit_clicked(self, widget):
        if self.selectedGroup is not None:
            availableAccounts = []
            groupAccounts = []
            for acc in self.users:
                if acc[1] in self.selectedGroupAccounts:
                    groupAccounts.append(acc)
                else:
                    availableAccounts.append(acc)

            self.fillTreeViewAccounts(availableAccounts, groupAccounts)
            self.txtGroupName.set_text(self.selectedGroup)
            self.txtGroupName.set_editable(False)
            self.txtGroupName.set_can_focus(False)
            self.txtGroupID.set_text(str(self.usr.getGroupID(self.selectedGroup)))
            self.tvAccounts.grab_focus()
            self.windowGroup.show()

    def on_btnGroupRemove_clicked(self, widget):
        # Remove group
        if self.selectedGroup is not None:
            title = _("Remove group")
            if self.selectedGroup != self.loggedinUserPrimaryGroup:
                qd = QuestionDialog(title, _("Are you sure you want to remove the following group:\n\n'%(group)s'") % { "group": self.selectedGroup }, self.windowGroup)
                answer = qd.show()
                if answer:
                    ret = self.usr.deleteGroup(self.selectedGroup)
                    if ret == "":
                        self.showInfo(title, _("Group successfully removed: %(group)s") % {"group": self.selectedGroup}, self.windowGroup)
                        self.refreshData()
                    else:
                        retMsg = "\n\n%s" % ret
                        self.showError(title, _("Could not remove group: %(group)s %(retmsg)s") % {"group": self.selectedGroup, "retmsg": retMsg}, self.windowGroup)
            else:
                self.showError(title, _("You cannot remove the currently logged in user's primary group"), self.windowGroup)

    def on_btnAddAccount_clicked(self, widget):
        rows = self.tvHandlerAccounts.getSelectedRows()
        for row in rows:
            self.tvHandlerAccounts.delRow(row[0])
            self.tvHandlerSelectedAccounts.addRow(row[1])

    def on_btnRemoveAccount_clicked(self, widget):
        # TODO: check if you want to remove an account from its own primary group
        rows = self.tvHandlerSelectedAccounts.getSelectedRows()
        for row in rows:
            acc = row[1][1]
            pg = self.usr.getUserPrimaryGroupName(acc)
            if pg != self.selectedGroup:
                self.tvHandlerSelectedAccounts.delRow(row[0])
                self.tvHandlerAccounts.addRow(row[1])
            else:
                self.showError(_("Remove account"), _("You cannot remove an account from its own primary group"), self.windowGroup)

    def on_btnOkGroup_clicked(self, widget):
        group = self.txtGroupName.get_text()
        if group != "":
            newSelAccs = self.tvHandlerSelectedAccounts.getColumnValues(1)

            if self.selectedGroupAccounts is not None:
                # New accounts in group
                for a in newSelAccs:
                    if a not in self.selectedGroupAccounts:
                        # Add group to account
                        self.usr.addGroupToAccount(a, group)

                # Removed accounts in group
                for a in self.selectedGroupAccounts:
                    if a not in newSelAccs:
                        # Remove group from account
                        self.usr.removeGroupFromAccount(a, group)
            else:
                # Create new group
                self.usr.createGroup(group)
                # Add group to each selected account
                for a in newSelAccs:
                    self.usr.addGroupToAccount(a, group)

            # Close the user window
            self.windowGroup.hide()
            self.refreshData()

    def on_btnCancelGroup_clicked(self, widget):
        self.windowGroup.hide()

    def on_usermanagerGroupWindow_delete_event(self, widget, data=None):
        self.windowGroup.hide()
        return True

    def on_tvAccounts_row_activated(self, widget, path, column):
        self.on_btnAddAccount_clicked(None)

    def on_tvSelectedAccounts_row_activated(self, widget, path, column):
        self.on_btnRemoveAccount_clicked(None)

    def fillTreeViewAccounts(self, availableAccounts, selectedAccounts=None):
        if (availableAccounts is None or not availableAccounts) and selectedAccounts:
            # Dirty hack to prepare the available accounts treeview
            self.tvHandlerAccounts.fillTreeview(contentList=selectedAccounts, columnTypesList=['GdkPixbuf.Pixbuf', 'str'], fixedImgHeight=24)
            self.tvHandlerAccounts.clearTreeView()
        else:
            self.tvHandlerAccounts.fillTreeview(contentList=availableAccounts, columnTypesList=['GdkPixbuf.Pixbuf', 'str'], fixedImgHeight=24)

        if (selectedAccounts is None or not selectedAccounts) and availableAccounts:
            # Dirty hack to prepare the selected accounts treeview
            self.tvHandlerSelectedAccounts.fillTreeview(contentList=availableAccounts, columnTypesList=['GdkPixbuf.Pixbuf', 'str'], fixedImgHeight=24)
            self.tvHandlerSelectedAccounts.clearTreeView()
        else:
            self.tvHandlerSelectedAccounts.fillTreeview(contentList=selectedAccounts, columnTypesList=['GdkPixbuf.Pixbuf', 'str'], fixedImgHeight=24)

    def fillTreeViewGroups(self):
        self.tvHandlerGroupsMain.fillTreeview(contentList=self.groups, columnTypesList=['str'])
        self.on_tvGroupsMain_cursor_changed(None)

    # ===============================================
    # General functions
    # ===============================================

    def refreshData(self):
        self.getUsers()
        self.groups = self.usr.getGroups()
        self.fillTreeViewUsers()
        self.fillTreeViewGroups()
        self.showUserData()

    def showInfo(self, title, message, parent):
        MessageDialogSave(title, message, Gtk.MessageType.INFO, parent).show()

    def showError(self, title, message, parent):
        MessageDialogSave(title, message, Gtk.MessageType.ERROR, parent).show()

    def filterText(self, widget):
        def filter(entry, *args):
            text = entry.get_text().strip().lower()
            entry.set_text(''.join([i for i in text if i in '0123456789abcdefghijklmnopqrstuvwxyz-._']))
        widget.connect('changed', filter)

    # Close the gui
    def on_usermanagerWindow_destroy(self, widget):
        # Close the app
        Gtk.main_quit()
Exemplo n.º 18
0
class UpdateManagerPref(object):
    def __init__(self):
        # Check if script is running
        self.scriptName = basename(__file__)
        self.umglobal = UmGlobal()

        # Handle arguments
        parser = argparse.ArgumentParser(
            description='SolydXK Update Manager Preferences')
        parser.add_argument('-r', '--reload', action="store_true", help='')
        args, extra = parser.parse_known_args()

        print(("args = {}".format(args)))
        if args.reload:
            pids = self.umglobal.getProcessPids("updatemanagerpref.py")
            if len(pids) > 1:
                print(("updatemanagerpref.py already running - kill pid {}".
                       format(pids[0])))
                os.system("kill {}".format(pids[0]))

        # Initiate logging
        self.logFile = join('/var/log', self.umglobal.settings['log'])
        self.log = Logger(self.logFile)

        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(
            join(self.umglobal.shareDir, 'updatemanagerpref.glade'))

        # Preferences window objects
        go = self.builder.get_object
        self.window = go("windowPref")
        self.nbPref = go('nbPref')
        self.btnSaveMirrors = go('btnSaveMirrors')
        self.btnCheckMirrorsSpeed = go("btnCheckMirrorsSpeed")
        self.lblMirrors = go('lblMirrors')
        self.tvMirrors = go("tvMirrors")
        self.btnRemoveBlackList = go("btnRemoveBlacklist")
        self.btnAddBlackList = go("btnAddBlacklist")
        self.tvBlacklist = go("tvBlacklist")
        self.tvAvailable = go("tvAvailable")
        self.lblGeneral = go("lblGeneral")
        self.btnSaveGeneral = go("btnSaveGeneral")
        self.chkHideMaintenance = go("chkHideMaintenance")
        self.chkAutostart = go("chkAutostart")

        # GUI translations
        self.window.set_title(_("Update Manager Preferences"))
        self.btnSaveMirrors.set_label(_("Save mirrors"))
        self.btnCheckMirrorsSpeed.set_label(_("Check mirrors speed"))
        self.btnRemoveBlackList.set_label(_("Remove"))
        self.btnAddBlackList.set_label(_("Blacklist"))
        self.lblMirrors.set_label(_("Repository mirrors"))
        self.lblGeneral.set_label(_("General"))
        go("lblHideMaintenance").set_label(_("Hide maintenance"))
        go("lblBlacklist").set_label(_("Blacklisted packages"))
        go("lblMirrorsText").set_label(_("Select the fastest repository"))
        go("lblBlacklistText").set_label(_("Blacklisted packages"))
        go("lblAvailableText").set_label(_("Available packages"))
        go("lblGlobalSettings").set_label(_("Global settings"))

        # Initiate the treeview handler and connect the custom toggle event with on_tvMirrors_toggle
        self.tvMirrorsHandler = TreeViewHandler(self.tvMirrors)
        self.tvMirrorsHandler.connect('checkbox-toggled',
                                      self.on_tvMirrors_toggle)

        self.tvBlacklistHandler = TreeViewHandler(self.tvBlacklist)
        self.tvAvailableHandler = TreeViewHandler(self.tvAvailable)

        # Initialize
        self.ec = ExecCmd(loggerObject=self.log)
        self.queue = Queue()
        self.excludeMirrors = ['security', 'community']
        self.activeMirrors = self.umglobal.getMirrorData(
            excludeMirrors=self.excludeMirrors)
        self.deadMirrors = self.umglobal.getMirrorData(getDeadMirrors=True)
        self.mirrors = self.getMirrors()
        self.threads = {}
        self.blacklist = []
        self.available = []

        self.fillGeneralSettings()
        self.fillTreeViewMirrors()
        self.fillTreeViewBlackList()
        self.fillTreeViewAvailable()

        # Connect the signals and show the window
        self.builder.connect_signals(self)
        self.window.show()

    # ===============================================
    # Main window functions
    # ===============================================

    def on_btnSaveGeneral_clicked(self, widget):
        self.saveGeneralSettings()

    def on_btnCheckMirrorsSpeed_clicked(self, widget):
        self.checkMirrorsSpeed()

    def on_btnSaveMirrors_clicked(self, widget):
        self.saveMirrors()

    def on_btnCancel_clicked(self, widget):
        self.window.destroy()

    def on_btnRemoveBlacklist_clicked(self, widget):
        self.removeBlacklist()

    def on_btnAddBlacklist_clicked(self, widget):
        self.addBlacklist()

    # ===============================================
    # Blacklist functions
    # ===============================================

    def fillGeneralSettings(self):
        for tab in self.umglobal.settings["hide-tabs"]:
            if tab == "maintenance":
                self.chkHideMaintenance.set_active(True)
        self.chkAutostart.set_active(self.umglobal.settings["autostart"])

    def fillTreeViewBlackList(self):
        self.blacklist = []
        cmd = "env LANG=C dpkg --get-selections | grep hold$ | awk '{print $1}'"
        lst = self.ec.run(cmd, False)
        for pck in lst:
            self.blacklist.append([False, pck.strip()])
        # Fill treeview
        columnTypesList = ['bool', 'str']
        self.tvBlacklistHandler.fillTreeview(self.blacklist, columnTypesList,
                                             0, 400, False)

    def fillTreeViewAvailable(self):
        self.available = []
        cmd = "env LANG=C dpkg --get-selections | grep install$ | awk '{print $1}'"
        lst = self.ec.run(cmd, False)
        for pck in lst:
            self.available.append([False, pck.strip()])
        # Fill treeview
        columnTypesList = ['bool', 'str']
        self.tvAvailableHandler.fillTreeview(self.available, columnTypesList,
                                             0, 400, False)

    def addBlacklist(self):
        packages = self.tvAvailableHandler.getToggledValues()
        for pck in packages:
            self.log.write("Blacklist package: %s" % pck,
                           "UMPref.addBlacklist", "debug")
            cmd = "echo '%s hold' | dpkg --set-selections" % pck
            system(cmd)
        self.fillTreeViewBlackList()
        self.fillTreeViewAvailable()

    def removeBlacklist(self):
        packages = self.tvBlacklistHandler.getToggledValues()
        for pck in packages:
            self.log.write("Remove package from blacklist: %s" % pck,
                           "UMPref.removeBlacklist", "debug")
            cmd = "echo '%s install' | dpkg --set-selections" % pck
            system(cmd)
        self.fillTreeViewBlackList()
        self.fillTreeViewAvailable()

    # ===============================================
    # Mirror functions
    # ===============================================

    def fillTreeViewMirrors(self):
        # Fill mirror list
        if len(self.mirrors) > 1:
            # Fill treeview
            columnTypesList = ['bool', 'str', 'str', 'str', 'str']
            self.tvMirrorsHandler.fillTreeview(self.mirrors, columnTypesList,
                                               0, 400, True)

            # TODO - We have no mirrors: hide the tab until we do
            #self.nbPref.get_nth_page(1).set_visible(False)
        else:
            self.nbPref.get_nth_page(1).set_visible(False)

    def saveMirrors(self):
        # Safe mirror settings
        replaceRepos = []
        # Get user selected mirrors
        model = self.tvMirrors.get_model()
        itr = model.get_iter_first()
        while itr is not None:
            sel = model.get_value(itr, 0)
            if sel:
                repo = model.get_value(itr, 2)
                url = model.get_value(itr, 3)
                not_changed = ''
                # Get currently selected data
                for mirror in self.mirrors:
                    if mirror[0] and mirror[2] == repo:
                        if mirror[3] != url:
                            # Currently selected mirror
                            replaceRepos.append([mirror[3], url])
                        else:
                            not_changed = url
                        break
                if url not in replaceRepos and url not in not_changed:
                    # Append the repositoriy to the sources file
                    replaceRepos.append(['', url])
            itr = model.iter_next(itr)

        if not replaceRepos:
            # Check for dead mirrors
            model = self.tvMirrors.get_model()
            itr = model.get_iter_first()
            while itr is not None:
                sel = model.get_value(itr, 0)
                if sel:
                    repo = model.get_value(itr, 2)
                    url = model.get_value(itr, 3)
                    # Get currently selected data
                    for mirror in self.deadMirrors:
                        if mirror[1] == repo and mirror[2] != url:
                            # Currently selected mirror
                            replaceRepos.append([mirror[2], url])
                            break
                itr = model.iter_next(itr)

        if replaceRepos:
            self.btnSaveMirrors.set_sensitive(False)
            self.btnCheckMirrorsSpeed.set_sensitive(False)

            m = Mirror()
            ret = m.save(replaceRepos, self.excludeMirrors)
            if ret == '':
                self.ec.run(cmd="apt-get update",
                            outputTreeView=self.tvMirrors)
                self.umglobal.getLocalInfo()
                self.mirrors = self.getMirrors()
                self.fillTreeViewMirrors()
            else:
                self.log.write(ret, "UMPref.saveMirrors", "exception")

            self.btnSaveMirrors.set_sensitive(True)
            self.btnCheckMirrorsSpeed.set_sensitive(True)
        else:
            msg = _("There are no repositories to save.")
            MessageDialog(self.lblMirrors.get_label(), msg)

    def getMirrors(self):
        mirrors = [[
            _("Current"),
            _("Country"),
            _("Repository"),
            _("URL"),
            _("Speed")
        ]]
        for mirror in self.activeMirrors:
            if mirror:
                self.log.write("Mirror data: %s" % ' '.join(mirror),
                               "UMPref.getMirrors", "debug")
                blnCurrent = self.isUrlInSources(mirror[2])
                mirrors.append(
                    [blnCurrent, mirror[0], mirror[1], mirror[2], ''])
        return mirrors

    def isUrlInSources(self, url):
        url = "://%s" % url
        blnRet = False

        for repo in self.umglobal.repos:
            if url in repo:
                blnRet = True
                for excl in self.excludeMirrors:
                    if excl in repo:
                        blnRet = False
                        break
                break
        return blnRet

    def checkMirrorsSpeed(self):
        name = 'mirrorspeed'
        self.btnCheckMirrorsSpeed.set_sensitive(False)
        self.btnSaveMirrors.set_sensitive(False)
        t = MirrorGetSpeed(self.mirrors, self.queue, self.umglobal)
        self.threads[name] = t
        t.daemon = True
        t.start()
        self.queue.join()
        GObject.timeout_add(5, self.checkThread, name)

    def checkThread(self, name):
        if self.threads[name].is_alive():
            lst = self.queue.get()
            if lst:
                self.writeSpeed(lst[0], lst[1])
            self.queue.task_done()
            return True

        # Thread is done
        if not self.queue.empty():
            lst = self.queue.get()
            if lst:
                self.writeSpeed(lst[0], lst[1])
            self.queue.task_done()
        del self.threads[name]
        self.btnCheckMirrorsSpeed.set_sensitive(True)
        self.btnSaveMirrors.set_sensitive(True)
        return False

    def writeSpeed(self, url, speed):
        model = self.tvMirrors.get_model()
        itr = model.get_iter_first()
        while itr is not None:
            repo = model.get_value(itr, 3)
            if repo == url:
                self.log.write("Mirror speed for %s = %s" % (url, speed),
                               "UMPref.writeSpeed", "debug")
                model.set_value(itr, 4, speed)
                path = model.get_path(itr)
                self.tvMirrors.scroll_to_cell(path)
            itr = model.iter_next(itr)
        self.tvMirrors.set_model(model)
        # Repaint GUI, or the update won't show
        while Gtk.events_pending():
            Gtk.main_iteration()

    def on_tvMirrors_toggle(self, obj, path, colNr, toggleValue):
        path = int(path)
        model = self.tvMirrors.get_model()
        selectedIter = model.get_iter(path)
        selectedRepo = model.get_value(selectedIter, 2)

        rowCnt = 0
        itr = model.get_iter_first()
        while itr is not None:
            if rowCnt != path:
                repo = model.get_value(itr, 2)
                if repo == selectedRepo:
                    model[itr][0] = False
            itr = model.iter_next(itr)
            rowCnt += 1

    # ===============================================
    # General functions
    # ===============================================

    def saveGeneralSettings(self):
        lst = []
        for tab in self.umglobal.settings["hide-tabs"]:
            if tab != "maintenance":
                lst.append(tab)
        if self.chkHideMaintenance.get_active():
            lst.append("maintenance")
        if lst:
            self.umglobal.saveSettings('misc', 'hide-tabs', ",".join(lst))
        else:
            self.umglobal.saveSettings('misc', 'hide-tabs', "")

        # Automatically start updatemanager on boot
        autostart = self.chkAutostart.get_active()
        self.umglobal.settings["autostart"] = autostart
        if autostart:
            if exists(self.umglobal.autostartSourceFile) and \
               exists(self.umglobal.autostartDir):
                copy(self.umglobal.autostartSourceFile,
                     self.umglobal.autostartDir)
        elif exists(self.umglobal.autostartDestFile):
            remove(self.umglobal.autostartDestFile)

        msg = _("The new settings will take effect after UM restart.")
        MessageDialog(self.lblGeneral.get_label(), msg)

    # Close the gui
    def on_windowPref_destroy(self, widget):
        Gtk.main_quit()
Exemplo n.º 19
0
class UpdateManagerPref(object):

    def __init__(self):
        # Check if script is running
        self.scriptName = basename(__file__)
        self.umglobal = UmGlobal()

        # Handle arguments
        parser = argparse.ArgumentParser(description='SolydXK Update Manager Preferences')
        parser.add_argument('-r','--reload', action="store_true", help='')
        args, extra = parser.parse_known_args()

        print(("args = {}".format(args)))
        if args.reload:
            pids = self.umglobal.getProcessPids("updatemanagerpref.py")
            if len(pids) > 1:
                print(("updatemanagerpref.py already running - kill pid {}".format(pids[0])))
                os.system("kill {}".format(pids[0]))

        # Initiate logging
        self.logFile = join('/var/log', self.umglobal.settings['log'])
        self.log = Logger(self.logFile)

        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(join(self.umglobal.shareDir, 'updatemanagerpref.glade'))

        # Preferences window objects
        go = self.builder.get_object
        self.window = go("windowPref")
        self.nbPref = go('nbPref')
        self.btnSaveMirrors = go('btnSaveMirrors')
        self.btnCheckMirrorsSpeed = go("btnCheckMirrorsSpeed")
        self.lblMirrors = go('lblMirrors')
        self.tvMirrors = go("tvMirrors")
        self.btnRemoveBlackList = go("btnRemoveBlacklist")
        self.btnAddBlackList = go("btnAddBlacklist")
        self.tvBlacklist = go("tvBlacklist")
        self.tvAvailable = go("tvAvailable")
        self.lblGeneral = go("lblGeneral")
        self.btnSaveGeneral = go("btnSaveGeneral")
        self.chkHideMaintenance = go("chkHideMaintenance")
        self.chkAutostart = go("chkAutostart")

        # GUI translations
        self.window.set_title(_("Update Manager Preferences"))
        self.btnSaveMirrors.set_label(_("Save mirrors"))
        self.btnCheckMirrorsSpeed.set_label(_("Check mirrors speed"))
        self.btnRemoveBlackList.set_label(_("Remove"))
        self.btnAddBlackList.set_label(_("Blacklist"))
        self.lblMirrors.set_label(_("Repository mirrors"))
        self.lblGeneral.set_label(_("General"))
        go("lblHideMaintenance").set_label(_("Hide maintenance"))
        go("lblBlacklist").set_label(_("Blacklisted packages"))
        go("lblMirrorsText").set_label(_("Select the fastest repository"))
        go("lblBlacklistText").set_label(_("Blacklisted packages"))
        go("lblAvailableText").set_label(_("Available packages"))
        go("lblGlobalSettings").set_label(_("Global settings"))

        # Initiate the treeview handler and connect the custom toggle event with on_tvMirrors_toggle
        self.tvMirrorsHandler = TreeViewHandler(self.tvMirrors)
        self.tvMirrorsHandler.connect('checkbox-toggled', self.on_tvMirrors_toggle)

        self.tvBlacklistHandler = TreeViewHandler(self.tvBlacklist)
        self.tvAvailableHandler = TreeViewHandler(self.tvAvailable)

        # Initialize
        self.ec = ExecCmd(loggerObject=self.log)
        self.queue = Queue()
        self.excludeMirrors = ['security', 'community']
        self.activeMirrors = self.umglobal.getMirrorData(excludeMirrors=self.excludeMirrors)
        self.deadMirrors = self.umglobal.getMirrorData(getDeadMirrors=True)
        self.mirrors = self.getMirrors()
        self.threads = {}
        self.blacklist = []
        self.available = []

        self.fillGeneralSettings()
        self.fillTreeViewMirrors()
        self.fillTreeViewBlackList()
        self.fillTreeViewAvailable()

        # Connect the signals and show the window
        self.builder.connect_signals(self)
        self.window.show()

    # ===============================================
    # Main window functions
    # ===============================================

    def on_btnSaveGeneral_clicked(self, widget):
        self.saveGeneralSettings()

    def on_btnCheckMirrorsSpeed_clicked(self, widget):
        self.checkMirrorsSpeed()

    def on_btnSaveMirrors_clicked(self, widget):
        self.saveMirrors()

    def on_btnCancel_clicked(self, widget):
        self.window.destroy()

    def on_btnRemoveBlacklist_clicked(self, widget):
        self.removeBlacklist()

    def on_btnAddBlacklist_clicked(self, widget):
        self.addBlacklist()

    # ===============================================
    # Blacklist functions
    # ===============================================

    def fillGeneralSettings(self):
        for tab in self.umglobal.settings["hide-tabs"]:
            if tab == "maintenance":
                self.chkHideMaintenance.set_active(True)
        self.chkAutostart.set_active(self.umglobal.settings["autostart"])

    def fillTreeViewBlackList(self):
        self.blacklist = []
        cmd = "env LANG=C dpkg --get-selections | grep hold$ | awk '{print $1}'"
        lst = self.ec.run(cmd, False)
        for pck in lst:
            self.blacklist.append([False, pck.strip()])
        # Fill treeview
        columnTypesList = ['bool', 'str']
        self.tvBlacklistHandler.fillTreeview(self.blacklist, columnTypesList, 0, 400, False)

    def fillTreeViewAvailable(self):
        self.available = []
        cmd = "env LANG=C dpkg --get-selections | grep install$ | awk '{print $1}'"
        lst = self.ec.run(cmd, False)
        for pck in lst:
            self.available.append([False, pck.strip()])
        # Fill treeview
        columnTypesList = ['bool', 'str']
        self.tvAvailableHandler.fillTreeview(self.available, columnTypesList, 0, 400, False)

    def addBlacklist(self):
        packages = self.tvAvailableHandler.getToggledValues()
        for pck in packages:
            self.log.write("Blacklist package: %s" % pck, "UMPref.addBlacklist", "debug")
            cmd = "echo '%s hold' | dpkg --set-selections" % pck
            system(cmd)
        self.fillTreeViewBlackList()
        self.fillTreeViewAvailable()

    def removeBlacklist(self):
        packages = self.tvBlacklistHandler.getToggledValues()
        for pck in packages:
            self.log.write("Remove package from blacklist: %s" % pck, "UMPref.removeBlacklist", "debug")
            cmd = "echo '%s install' | dpkg --set-selections" % pck
            system(cmd)
        self.fillTreeViewBlackList()
        self.fillTreeViewAvailable()

    # ===============================================
    # Mirror functions
    # ===============================================

    def fillTreeViewMirrors(self):
        # Fill mirror list
        if len(self.mirrors) > 1:
            # Fill treeview
            columnTypesList = ['bool', 'str', 'str', 'str', 'str']
            self.tvMirrorsHandler.fillTreeview(self.mirrors, columnTypesList, 0, 400, True)

            # TODO - We have no mirrors: hide the tab until we do
            #self.nbPref.get_nth_page(1).set_visible(False)
        else:
            self.nbPref.get_nth_page(1).set_visible(False)

    def saveMirrors(self):
        # Safe mirror settings
        replaceRepos = []
        # Get user selected mirrors
        model = self.tvMirrors.get_model()
        itr = model.get_iter_first()
        while itr is not None:
            sel = model.get_value(itr, 0)
            if sel:
                repo = model.get_value(itr, 2)
                url = model.get_value(itr, 3)
                not_changed = ''
                # Get currently selected data
                for mirror in self.mirrors:
                    if mirror[0] and mirror[2] == repo:
                        if mirror[3] != url:
                            # Currently selected mirror
                            replaceRepos.append([mirror[3], url])
                        else:
                            not_changed = url
                        break
                if url not in replaceRepos and url not in not_changed:
                    # Append the repositoriy to the sources file
                    replaceRepos.append(['', url])
            itr = model.iter_next(itr)

        if not replaceRepos:
            # Check for dead mirrors
            model = self.tvMirrors.get_model()
            itr = model.get_iter_first()
            while itr is not None:
                sel = model.get_value(itr, 0)
                if sel:
                    repo = model.get_value(itr, 2)
                    url = model.get_value(itr, 3)
                    # Get currently selected data
                    for mirror in self.deadMirrors:
                        if mirror[1] == repo and mirror[2] != url:
                            # Currently selected mirror
                            replaceRepos.append([mirror[2], url])
                            break
                itr = model.iter_next(itr)

        if replaceRepos:
            self.btnSaveMirrors.set_sensitive(False)
            self.btnCheckMirrorsSpeed.set_sensitive(False)

            m = Mirror()
            ret = m.save(replaceRepos, self.excludeMirrors)
            if ret == '':
                self.ec.run(cmd="apt-get update", outputTreeView=self.tvMirrors)
                self.umglobal.getLocalInfo()
                self.mirrors = self.getMirrors()
                self.fillTreeViewMirrors()
            else:
                self.log.write(ret, "UMPref.saveMirrors", "exception")

            self.btnSaveMirrors.set_sensitive(True)
            self.btnCheckMirrorsSpeed.set_sensitive(True)
        else:
            msg = _("There are no repositories to save.")
            MessageDialog(self.lblMirrors.get_label(), msg)

    def getMirrors(self):
        mirrors = [[_("Current"), _("Country"), _("Repository"), _("URL"), _("Speed")]]
        for mirror in  self.activeMirrors:
            if mirror:
                self.log.write("Mirror data: %s" % ' '.join(mirror), "UMPref.getMirrors", "debug")
                blnCurrent = self.isUrlInSources(mirror[2])
                mirrors.append([blnCurrent, mirror[0], mirror[1], mirror[2], ''])
        return mirrors

    def isUrlInSources(self, url):
        url = "://%s" % url
        blnRet = False

        for repo in self.umglobal.repos:
            if url in repo:
                blnRet = True
                for excl in self.excludeMirrors:
                    if excl in repo:
                        blnRet = False
                        break
                break
        return blnRet

    def checkMirrorsSpeed(self):
        name = 'mirrorspeed'
        self.btnCheckMirrorsSpeed.set_sensitive(False)
        self.btnSaveMirrors.set_sensitive(False)
        t = MirrorGetSpeed(self.mirrors, self.queue, self.umglobal)
        self.threads[name] = t
        t.daemon = True
        t.start()
        self.queue.join()
        GObject.timeout_add(5, self.checkThread, name)

    def checkThread(self, name):
        if self.threads[name].is_alive():
            lst = self.queue.get()
            if lst:
                self.writeSpeed(lst[0], lst[1])
            self.queue.task_done()
            return True

        # Thread is done
        if not self.queue.empty():
            lst = self.queue.get()
            if lst:
                self.writeSpeed(lst[0], lst[1])
            self.queue.task_done()
        del self.threads[name]
        self.btnCheckMirrorsSpeed.set_sensitive(True)
        self.btnSaveMirrors.set_sensitive(True)
        return False

    def writeSpeed(self, url, speed):
        model = self.tvMirrors.get_model()
        itr = model.get_iter_first()
        while itr is not None:
            repo = model.get_value(itr, 3)
            if repo == url:
                self.log.write("Mirror speed for %s = %s" % (url, speed), "UMPref.writeSpeed", "debug")
                model.set_value(itr, 4, speed)
                path = model.get_path(itr)
                self.tvMirrors.scroll_to_cell(path)
            itr = model.iter_next(itr)
        self.tvMirrors.set_model(model)
        # Repaint GUI, or the update won't show
        while Gtk.events_pending():
            Gtk.main_iteration()

    def on_tvMirrors_toggle(self, obj, path, colNr, toggleValue):
        path = int(path)
        model = self.tvMirrors.get_model()
        selectedIter = model.get_iter(path)
        selectedRepo = model.get_value(selectedIter, 2)

        rowCnt = 0
        itr = model.get_iter_first()
        while itr is not None:
            if rowCnt != path:
                repo = model.get_value(itr, 2)
                if repo == selectedRepo:
                    model[itr][0] = False
            itr = model.iter_next(itr)
            rowCnt += 1

    # ===============================================
    # General functions
    # ===============================================

    def saveGeneralSettings(self):
        lst = []
        for tab in self.umglobal.settings["hide-tabs"]:
            if tab != "maintenance":
                lst.append(tab)
        if self.chkHideMaintenance.get_active():
            lst.append("maintenance")
        if lst:
            self.umglobal.saveSettings('misc', 'hide-tabs', ",".join(lst))
        else:
            self.umglobal.saveSettings('misc', 'hide-tabs', "")

        # Automatically start updatemanager on boot
        autostart = self.chkAutostart.get_active()
        self.umglobal.settings["autostart"] = autostart
        if autostart:
            if exists(self.umglobal.autostartSourceFile) and \
               exists(self.umglobal.autostartDir):
                copy(self.umglobal.autostartSourceFile, self.umglobal.autostartDir)
        elif exists(self.umglobal.autostartDestFile):
            remove(self.umglobal.autostartDestFile)

        msg = _("The new settings will take effect after UM restart.")
        MessageDialog(self.lblGeneral.get_label(), msg)

    # Close the gui
    def on_windowPref_destroy(self, widget):
        Gtk.main_quit()
Exemplo n.º 20
0
class USBCreator(object):

    def __init__(self):

        # Load window and widgets
        self.scriptName = basename(__file__)
        self.scriptDir = abspath(dirname(__file__))
        self.mediaDir = join(self.scriptDir, '../../share/usb-creator')
        self.builder = Gtk.Builder()
        self.builder.add_from_file(join(self.mediaDir, 'usb-creator.glade'))

        # Main window objects
        go = self.builder.get_object
        self.window = go("usb-creator")
        self.lblDevice = go("lblDevice")
        self.lblIso = go("lblIso")
        self.lblAvailable = go("lblAvailable")
        self.lblRequired = go("lblRequired")
        self.cmbDevice = go("cmbDevice")
        self.cmbDeviceHandler = ComboBoxHandler(self.cmbDevice)
        self.txtIso = go("txtIso")
        self.btnRefresh = go("btnRefresh")
        self.btnUnmount = go("btnUnmount")
        self.btnBrowseIso = go("btnBrowseIso")
        self.btnClear = go("btnClear")
        self.chkFormatDevice = go("chkFormatDevice")
        self.chkRepairDevice = go("chkRepairDevice")
        self.btnExecute = go("btnExecute")
        self.lblUsb = go("lblUsb")
        self.tvUsbIsos = go("tvUsbIsos")
        self.btnDelete = go("btnDelete")
        self.pbUsbCreator = go("pbUsbCreator")
        self.statusbar = go("statusbar")

        # Translations
        self.window.set_title(_("USB Creator"))
        self.lblDevice.set_label(_("Device"))
        self.lblUsb.set_label(_("USB"))
        self.available_text = _("Available")
        self.required_text = _("Required")
        self.chkFormatDevice.set_label(_("Format device"))
        self.chkFormatDevice.set_tooltip_text(_("Warning: all data will be lost"))
        self.chkRepairDevice.set_label(_("Repair device"))
        self.chkRepairDevice.set_tooltip_text(_("Tries to repair an unbootable USB"))
        self.btnExecute.set_label("_{}".format(_("Execute")))
        self.lblIso.set_label(_("ISO"))
        self.btnDelete.set_label("_{}".format(_("Delete")))
        self.btnRefresh.set_tooltip_text(_("Refresh device list"))
        self.btnUnmount.set_tooltip_text(_("Unmount device"))
        self.btnBrowseIso.set_tooltip_text(_("Browse for ISO file"))
        self.btnClear.set_tooltip_text(_("Clear the ISO field"))

        # Log lines to show: check string, percent done (0=pulse, appends last word in log line), show line (translatable)
        self.log_lines = []
        self.log_lines.append(["partitioning usb", 5, _("Partitioning USB...")])
        self.log_lines.append(["searching for bad blocks", 0, _("Searching for bad block")])
        self.log_lines.append(["installing", 15, _("Installing Grub...")])
        self.log_lines.append(["rsync", 25, _("Start copying ISO...")])
        self.log_lines.append(["left to copy", 0, _("kB left to copy:")])
        self.log_lines.append(["check hash", 85, _("Check hash of ISO...")])

        # Initiate variables
        self.devices = []
        self.device = {}
        self.device['path'] = ''
        self.device['size'] = 0
        self.device['has_partition'] = False
        self.device['mount'] = ''
        self.device['available'] = 0
        self.device["new_iso"] = ''
        self.device["new_iso_required"] = 0
        self.logos = self.get_logos()
        self.queue = Queue(-1)
        self.threads = {}
        self.htmlDir = join(self.mediaDir, "html")
        self.helpFile = join(self.get_language_dir(), "help.html")
        log = getoutput("cat /usr/bin/usb-creator | grep 'LOG=' | cut -d'=' -f 2")
        self.log_file = log[0]
        self.log = Logger(self.log_file, addLogTime=False, maxSizeKB=5120)
        self.tvUsbIsosHandler = TreeViewHandler(self.tvUsbIsos)

        self.lblAvailable.set_label('')
        self.lblRequired.set_label('')

        # Connect builder signals and show window
        self.builder.connect_signals(self)
        self.window.show_all()

        # Get attached devices
        self.on_btnRefresh_clicked()

        # Init log
        init_log = ">>> Start USB Creator: {} <<<".format(datetime.now())
        self.log.write(init_log)

        # Version information
        self.version_text = _("Version")
        self.pck_version = getPackageVersion('usb-creator')
        self.set_statusbar_message("{}: {}".format(self.version_text, self.pck_version))

    # ===============================================
    # Main window functions
    # ===============================================

    def on_btnExecute_clicked(self, widget):
        if exists(self.device["path"]):
            arguments = []
            arguments.append("-d {}".format(self.device["path"]))
            clear = self.chkFormatDevice.get_active()
            repair = self.chkRepairDevice.get_active()
            iso = self.device["new_iso"]
            iso_path = self.txtIso.get_text().strip()

            # ISO path does not exist
            if iso != iso_path:
                msg = _("Cannot add ISO from path: {}.\n"
                        "Please, remove the ISO path or browse for an existing ISO.")
                WarningDialog(self.btnExecute.get_label(), msg.format(iso_path))
                return True

            # Check if there is enough space
            available = self.device["available"]
            if self.chkFormatDevice.get_active():
                available = self.device["size"]
            if available - self.device["new_iso_required"] < 0:
                msg = _("There is not enough space available on the pen drive.\n"
                        "Please, remove unneeded files before continuing.")
                WarningDialog(self.btnExecute.get_label(), msg)
                return True

            if clear:
                arguments.append("-f")
                arguments.append("-b")
            if repair:
                arguments.append("-r")
                arguments.append("-b")
                arguments.append("-g")
                # This should use the history file to get the original hash
                arguments.append("-s")
            if exists(iso):
                arguments.append("-i \"{}\"".format(iso))
                arguments.append("-s")

            cmd = "usb-creator {}".format(" ".join(arguments))
            self.log.write("Execute command: {}".format(cmd))
            self.exec_command(cmd)

    def on_btnDelete_clicked(self, widget):
        selected_isos = self.tvUsbIsosHandler.getToggledValues(toggleColNr=0, valueColNr=2)
        if selected_isos:
            msg =  _("Are you sure you want to remove the selected ISO from the device?")
            answer = QuestionDialog(self.btnDelete.get_label(), msg)
            if answer:
                for iso in selected_isos:
                    iso_path = join(self.device["mount"], iso)
                    if exists(iso_path):
                        os.remove(iso_path)
                        self.log.write("Remove ISO: {}".format(iso_path))
                shell_exec("usb-creator -d {} -g".format(self.device["path"]))
                self.on_cmbDevice_changed()
                self.fill_treeview_usbcreator(self.device["mount"])

    def on_btnBrowseIso_clicked(self, widget):
        file_filter = Gtk.FileFilter()
        file_filter.set_name("ISO")
        file_filter.add_mime_type("application/x-cd-image")
        file_filter.add_pattern("*.iso")

        start_dir = dirname(self.txtIso.get_text().strip())
        if not exists(start_dir):
            start_dir = expanduser("~")

        iso = SelectFileDialog(title=_('Select ISO'), start_directory=start_dir, gtkFileFilter=file_filter).show()
        if iso is not None:
            self.log.write("Add ISO: {}".format(iso))
            self.txtIso.set_text(iso)

    def on_btnClear_clicked(self, widget):
        self.txtIso.set_text('')

    def on_txtIso_changed(self, widget=None):
        iso_path = self.txtIso.get_text().strip()
        if exists(iso_path):
            if isdir(iso_path):
                isos = glob(join(iso_path, '*.iso'))
                if isos:
                    required = 0
                    for iso in isos:
                        # Check if these ISOs overwrite current USB ISOs
                        check_usb_iso_size = 0
                        if not self.chkFormatDevice.get_active():
                            check_usb_iso = join(self.device["mount"], basename(iso))
                            if exists(check_usb_iso):
                                check_usb_iso_size = self.get_iso_size(check_usb_iso)
                        required += (self.get_iso_size(iso) - check_usb_iso_size)
                    if required < 0:
                        required = 0
                    self.lblRequired.set_label("{}: {} MB".format(self.required_text, int(required / 1024)))
                    # Save the info
                    self.device["new_iso"] = iso_path
                    self.device["new_iso_required"] = required
                    self.log.write("New ISO directory: {}, {}".format(iso_path, required))
                else:
                    self.device["new_iso"] = ''
                    self.device["new_iso_required"] = 0
                    self.log.write("New ISO directory does not contain ISOs: {}".format(iso_path))
            else:
                # Check if this ISO overwrites current USB ISO
                check_usb_iso_size = 0
                if not self.chkFormatDevice.get_active():
                    check_usb_iso = join(self.device["mount"], basename(iso_path))
                    if exists(check_usb_iso):
                        check_usb_iso_size = self.get_iso_size(check_usb_iso)
                required = (self.get_iso_size(iso_path) - check_usb_iso_size)
                self.lblRequired.set_label("{}: {} MB".format(self.required_text, int(required / 1024)))
                # Save the info
                self.device["new_iso"] = iso_path
                self.device["new_iso_required"] = required
                self.log.write("New ISO: {}, {}".format(iso_path, required))
        else:
            self.device["new_iso"] = ''
            self.device["new_iso_required"] = 0
            self.lblRequired.set_text('')

    def on_btnRefresh_clicked(self, widget=None):
        self.devices = self.get_devices()
        self.cmbDeviceHandler.fillComboBox(self.devices, 0)

    def on_btnUnmount_clicked(self, widget):
        unmount_text = _("Unmount")
        device = self.device["path"]
        self.unmount_device(device)
        self.on_btnRefresh_clicked()
        if device in self.devices:
            msg = _("Could not unmount the device.\n"
                    "Please unmount the device manually.")
        else:
            msg = _("You can now safely remove the device.")
        MessageDialog(unmount_text, msg)

    def on_cmbDevice_changed(self, widget=None):
        device = self.cmbDeviceHandler.getValue()
        if device is not None:
            mount = ''
            size = 0
            available = 0

            # Get the size of the USB
            usb_size = getoutput("env LANG=C udisks --show-info {} | grep size".format(device))
            if usb_size:
                # udisks returns bytes, while df returns kbs
                size = int(int(usb_size[0].split(":")[1].strip()) / 1024)

            # Assume that the USB is empty (will check later)
            available = size

            # Get free size on USB
            has_partition = self.device_has_partition(device)
            if has_partition:
                mount = self.get_device_mount(device)
                # This function can be called from on_chkFormatDevice_toggled
                if widget != self.chkFormatDevice:
                    self.chkFormatDevice.set_sensitive(True)
                    self.chkFormatDevice.set_active(False)
                    free_size = getoutput("df --output=avail {}1 | awk 'NR==2'".format(device))
                    if free_size:
                        available = int(free_size[0])
            else:
                self.chkFormatDevice.set_active(True)
                self.chkFormatDevice.set_sensitive(False)

            self.chkRepairDevice.set_active(False)
            self.fill_treeview_usbcreator(mount)
            self.lblAvailable.set_label("{}: {} MB".format(self.available_text, int(available / 1024)))

            # Save the info
            self.device['path'] = device
            self.device['size'] = size
            self.device['has_partition'] = has_partition
            self.device['mount'] = mount
            self.device['available'] = available
            self.log.write("Selected device info: {}".format(self.device))

            # Update info
            iso_path = self.txtIso.get_text().strip()
            if iso_path != "" and exists(iso_path):
                self.on_txtIso_changed()
        else:
            self.fill_treeview_usbcreator()
            self.lblAvailable.set_label('')
            self.lblRequired.set_label('')
            self.txtIso.set_text('')
            self.device['path'] = ''
            self.device['size'] = 0
            self.device['has_partition'] = False
            self.device['mount'] = ''
            self.device['available'] = 0
            self.device["new_iso"] = ''
            self.device["new_iso_required"] = 0

    def on_chkFormatDevice_toggled(self, widget):
        # Recalculate available space and requied space
        self.on_cmbDevice_changed(widget)

    def on_btnHelp_clicked(self, widget):
        # Open the help file as the real user (not root)
        shell_exec("%s/open-as-user \"%s\"" % (self.scriptDir, self.helpFile))

    def fill_treeview_usbcreator(self, mount=''):
        isos_list = []
        # columns: checkbox, image (logo), device, driver
        column_types = ['bool', 'GdkPixbuf.Pixbuf', 'str', 'str']

        if exists(mount):
            isos = glob(join(mount, '*.iso'))
            for iso in isos:
                iso_name = basename(iso)
                iso_name_lower = iso_name.lower()
                iso_size = "{} MB".format(int(self.get_iso_size(iso) / 1024))
                iso_logo = ""
                for key, logo in list(self.logos.items()):
                    if key != "iso":
                        if key in iso_name_lower:
                            if len(logo) > len(iso_logo):
                                iso_logo = logo
                if iso_logo == "":
                    iso_logo = self.logos["iso"]
                self.log.write("ISO on {}: {}, {}, {}".format(mount, iso_name, iso_size, iso_logo))
                isos_list.append([False, iso_logo, iso_name, iso_size])

        # Fill treeview
        self.tvUsbIsosHandler.fillTreeview(contentList=isos_list, columnTypesList=column_types)

    def exec_command(self, command):
        try:
            # Run the command in a separate thread
            self.set_buttons_state(False)
            name = 'cmd'
            t = ExecuteThreadedCommands([command], self.queue)
            self.threads[name] = t
            t.daemon = True
            t.start()
            self.queue.join()
            GObject.timeout_add(1000, self.check_thread, name)

        except Exception as detail:
            ErrorDialog(self.btnExecute.get_label(), detail)

    def check_thread(self, name):
        if self.threads[name].is_alive():
            self.set_progress()
            if not self.queue.empty():
                ret = self.queue.get()
                self.log.write("Queue returns: {}".format(ret), 'check_thread')
                self.queue.task_done()
                self.show_message(ret)
            return True

        # Thread is done
        self.log.write(">> Thread is done", 'check_thread')
        if not self.queue.empty():
            ret = self.queue.get()
            self.queue.task_done()
            self.show_message(ret)
        del self.threads[name]
        self.set_buttons_state(True)
        self.on_cmbDevice_changed()
        self.fill_treeview_usbcreator(self.device["mount"])
        self.set_statusbar_message("{}: {}".format(self.version_text, self.pck_version))
        return False

    def set_buttons_state(self, enable):
        if not enable:
            # Disable buttons
            self.btnExecute.set_sensitive(False)
            self.btnDelete.set_sensitive(False)
            self.btnBrowseIso.set_sensitive(False)
            self.btnRefresh.set_sensitive(False)
            self.btnUnmount.set_sensitive(False)
            self.btnClear.set_sensitive(False)
            self.chkFormatDevice.set_sensitive(False)
            self.chkRepairDevice.set_sensitive(False)
            self.cmbDevice.set_sensitive(False)
            self.txtIso.set_sensitive(False)
        else:
            # Enable buttons and reset progress bar
            self.btnExecute.set_sensitive(True)
            self.btnDelete.set_sensitive(True)
            self.btnBrowseIso.set_sensitive(True)
            self.btnRefresh.set_sensitive(True)
            self.btnUnmount.set_sensitive(True)
            self.btnClear.set_sensitive(True)
            self.chkFormatDevice.set_sensitive(True)
            self.chkRepairDevice.set_sensitive(True)
            self.cmbDevice.set_sensitive(True)
            self.txtIso.set_sensitive(True)
            self.pbUsbCreator.set_fraction(0)

    def get_logos(self):
        logos_dict = {}
        logos_path = join(self.mediaDir, 'logos')
        logos = glob(join(logos_path, '*.png'))
        for logo in logos:
            key = splitext(basename(logo))[0]
            logos_dict[key] = logo
        return logos_dict

    def set_progress(self):
        if exists(self.log_file):
            msg = ''
            last_line = getoutput("tail -50 {} | grep -v DEBUG | grep -v ==".format(self.log_file))
            for line in reversed(last_line):
                # Check for session start line: that is the last line to check
                if ">>>>>" in line and "<<<<<" in line:
                    break
                for chk_line in self.log_lines:
                    if chk_line[0] in line.lower():
                        #print((line))
                        word = ''
                        if chk_line[1] == 0:
                            self.pbUsbCreator.pulse()
                            words = line.split(' ')
                            for word in reversed(words):
                                if word.strip() != '':
                                    break
                        else:
                            self.pbUsbCreator.set_fraction(float(chk_line[1] / 100))
                        msg = "{} {}".format(chk_line[2], word)
                        break
                if msg != '':
                    break
            self.set_statusbar_message(msg)

    def set_statusbar_message(self, message):
        if message is not None:
            context = self.statusbar.get_context_id('message')
            self.statusbar.push(context, message)

    def get_devices(self):
        devices = []
        my_devices = getoutput("udisks --enumerate-device-files | egrep '/dev/sd[a-z]$'")
        for device in my_devices:
            info = getoutput("env LANG=C udisks --show-info {}".format(device))
            detachable = False
            has_partition = False
            for line in info:
                if "detachable" in line and "1" in line:
                    detachable = True
                elif "partition" in line:
                    has_partition = True
                if detachable and has_partition:
                    devices.append(device)
                    break
        devices.sort()
        return devices

    def device_has_partition(self, device):
        part_count = getoutput("udisks --show-info {} | grep count | grep -v block".format(device))
        if part_count:
            if "1" in part_count[0]:
                return True
        return False

    def get_device_mount(self, device):
        shell_exec("udisks --mount {}1".format(device))
        mount = getoutput("grep %s1 /etc/mtab | awk '{print $2}' | sed 's/\\040/ /g'" % device)
        if mount:
            return mount[0]
        return ''

    def get_iso_size(self, iso):
        iso_size = getoutput("du -Lk \"%s\" | awk '{print $1}'" % iso)
        if iso_size:
            return int(iso_size[0])
        return 0

    def unmount_device(self, device):
        shell_exec("udisks --unmount {}1".format(device))
        shell_exec("udisks --detach {}".format(device))

    # Close the gui
    def on_usbcreator_destroy(self, widget):
        # Unmount devices
        for device in self.devices:
            if self.get_device_mount(device) != "":
                self.unmount_device(device)
        # Close the app
        Gtk.main_quit()

    def show_message(self, cmdOutput):
        try:
            self.log.write("Command output: {}".format(cmdOutput), 'show_message')
            ret = int(cmdOutput)
            if ret > 1 and ret != 255:
                if ret == 1:
                    ErrorDialog(self.btnExecute.get_label(), _("Run this application with root permission."))
                elif ret == 2:
                    ErrorDialog(self.btnExecute.get_label(), _("Wrong arguments were passed to usb-creator."))
                elif ret == 3:
                    ErrorDialog(self.btnExecute.get_label(), _("The device was not found or no device was given."))
                elif ret == 4:
                    ErrorDialog(self.btnExecute.get_label(), _("Given ISO path was not found."))
                elif ret == 5:
                    ErrorDialog(self.btnExecute.get_label(), _("Device is in use by another application."))
                elif ret == 6:
                    ErrorDialog(self.btnExecute.get_label(), _("Unable to mount the device."))
                elif ret == 7:
                    ErrorDialog(self.btnExecute.get_label(), _("Hash mismatch."))
                elif ret == 8:
                    ErrorDialog(self.btnExecute.get_label(), _("The device has no fat32 partition."))
                elif ret == 9:
                    ErrorDialog(self.btnExecute.get_label(), _("The device has no bootloader installed."))
                elif ret == 10:
                    ErrorDialog(self.btnExecute.get_label(), _("There is not enough space available on the device."))
                elif ret == 11:
                    ErrorDialog(self.btnExecute.get_label(), _("Unable to guess distribution from ISO name.\n"
                                                               "Make sure you have the distribution name in the ISO name."))
                else:
                    msg = _("An unknown error accured.\n"
                            "Please, visit our forum for support: http://forums.solydxk.com")
                    ErrorDialog(self.window.get_title(), msg)
            else:
                msg = _("The USB was successfully written.")
                MessageDialog(self.window.get_title(), msg)
        except:
            ErrorDialog(self.btnExecute.get_label(), cmdOutput)

    # ===============================================
    # Language specific functions
    # ===============================================

    def get_language_dir(self):
        # First test if full locale directory exists, e.g. html/pt_BR,
        # otherwise perhaps at least the language is there, e.g. html/pt
        # and if that doesn't work, try html/pt_PT
        lang = self.get_current_language()
        path = join(self.htmlDir, lang)
        if not isdir(path):
            base_lang = lang.split('_')[0].lower()
            path = join(self.htmlDir, base_lang)
            if not isdir(path):
                path = join(self.htmlDir, "{}_{}".format(base_lang, base_lang.upper()))
                if not isdir(path):
                    path = join(self.htmlDir, 'en')
        return path

    def get_current_language(self):
        lang = os.environ.get('LANG', 'US').split('.')[0]
        if lang == '':
            lang = 'en'
        return lang
Exemplo n.º 21
0
class Constructor(object):

    def __init__(self):
        self.scriptDir = abspath(dirname(__file__))
        self.shareDir = join(self.scriptDir, '../../../share/solydxk/constructor')
        self.userAppDir = join(get_user_home_dir(), ".constructor")
        self.distroFile = join(self.userAppDir, "distros.list")

        # Create the user's application directory if it doesn't exist
        if not isdir(self.userAppDir):
            user_name = getUserLoginName()
            makedirs(self.userAppDir)
            old_distro_file = join(self.scriptDir, "distros.list")
            if exists(old_distro_file):
                move(old_distro_file, self.distroFile)
            system("chown -R %s:%s %s" % (user_name, user_name, self.userAppDir))

        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(join(self.shareDir, 'constructor.glade'))

        # Main window objects
        go = self.builder.get_object
        self.window = go('constructorWindow')
        self.tvDistros = go('tvDistros')
        self.lblOutput = go('lblOutput')
        self.statusbar = go('statusbar')
        self.btnAdd = go('btnAdd')
        self.chkSelectAll = go('chkSelectAll')
        self.btnRemove = go('btnRemove')
        self.btnEdit = go('btnEdit')
        self.btnUpgrade = go('btnUpgrade')
        self.btnLocalize = go('btnLocalize')
        self.btnBuildIso = go('btnBuildIso')

        # Add iso window objects
        self.windowAddDistro = go('addDistroWindow')
        self.txtIso = go('txtIso')
        self.txtDir = go('txtDir')
        self.btnDir = go('btnDir')
        self.btnSave = go('btnSave')
        self.btnHelp = go('btnHelp')
        self.lblIso = go('lblIso')
        self.boxIso = go('boxIso')
        self.lblDir = go('lblDir')
        self.chkFromIso = go('chkFromIso')

        # Main window translations
        self.window.set_title(_("SolydXK Constructor"))
        self.chkSelectAll.set_label(_("Select all"))
        self.btnAdd.set_label("_{}".format(_("Add")))
        self.btnRemove.set_label("_{}".format(_("Remove")))
        self.btnEdit.set_label("_{}".format(_("Edit")))
        self.btnUpgrade.set_label("_{}".format(_("Upgrade")))
        self.btnLocalize.set_label("_{}".format(_("Localize")))
        self.btnBuildIso.set_label("_{}".format(_("Build")))
        self.btnHelp.set_label("_{}".format(_("Help")))

        # Add iso window translations
        self.windowAddDistro.set_title(_("Add Distribution"))
        self.lblIso.set_text(_("ISO"))
        go('lblFromIso').set_label("Create from ISO")
        go('btnCancel').set_label("_{}".format(_("Cancel")))
  
        # Init
        self.ec = ExecCmd()
        self.ec.run("modprobe loop", False)
        self.queue = Queue()
        self.mountDir = "/mnt/constructor"
        self.distroAdded = False
        self.iso = None
        self.dir = None
        self.isoName = None
        self.doneWav = join(self.shareDir, 'done.wav')
        self.htmlDir = join(self.shareDir, "html")
        self.help = join(self.get_language_dir(), "help.html")
        self.chkFromIso.set_active(True)
        self.toggleGuiElements(False)

        # Treeviews
        self.tvHandlerDistros = TreeViewHandler(self.tvDistros)
        self.fillTreeViewDistros()

        # Version information
        ver = _("Version")
        self.version = "%s: %s" % (ver, getPackageVersion('solydxk-constructor'))
        self.showOutput(self.version)

        # Connect the signals and show the window
        self.builder.connect_signals(self)
        self.window.show()

    # ===============================================
    # Main Window Functions
    # ===============================================

    def on_btnAdd_clicked(self, widget):
        self.windowAddDistro.show()

    def on_btnRemove_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            qd = QuestionDialog(self.btnRemove.get_label(), _("Are you sure you want to remove the selected distribution from the list?\n" \
                                                              "(This will not remove the directory and its data)"), self.window)
            answer = qd.show()
            if answer:
                self.saveDistroFile(distroPath=path, addDistro=False)
        self.fillTreeViewDistros()

    def on_btnEdit_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            de = EditDistro(path)
            services = []
            if exists(join(path, 'root/etc/apache2/apache2.conf')):
                services.append("apache2")
            if exists(join(path, 'root/etc/mysql/debian.cnf')):
                services.append("mysql")
            if services:
                msg = "If you need to update packages that depend on these services,\n" \
                      "you will need to manually start them:\n"
                for service in services:
                    msg += "\nservice %s start" % service
                msg += "\n\nWhen done:\n"
                for service in services:
                    msg += "\nservice %s stop" % service
                self.showInfo(_("Services detected"), msg, self.window)
                repaintGui()
            de.openTerminal()

    def on_btnUpgrade_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        upgraded = False
        for path in selected:
            upgraded = True
            rootPath = "%s/root" % path
            force = get_apt_force(rootPath)
            de = EditDistro(path)
            de.openTerminal("apt-get update")
            if exists(join(rootPath, 'etc/apache2/apache2.conf')):
                de.openTerminal("service apache2 start")
            if exists(join(rootPath, 'etc/mysql/debian.cnf')):
                de.openTerminal("service mysql start")
            de.openTerminal("apt-get -y %s -o Dpkg::Options::=\"--force-confnew\" dist-upgrade" % force)
            if exists(join(rootPath, 'etc/apache2/apache2.conf')):
                de.openTerminal("service apache2 stop")
            if exists(join(rootPath, 'etc/mysql/debian.cnf')):
                de.openTerminal("service mysql stop")

            # Cleanup old kernel and headers
            script = "rmoldkernel.sh"
            scriptSource = join(self.scriptDir, "files/{}".format(script))
            scriptTarget = join(rootPath, script)
            if exists(scriptSource):
                copy(scriptSource, scriptTarget)
                self.ec.run("chmod a+x '%s'" % scriptTarget)
                de.openTerminal("/bin/bash %s" % script)
                silent_remove(scriptTarget)

            # Download offline packages
            print(">> Start downloading offline packages")
            self.download_offline_packages(path)

        if upgraded and exists("/usr/bin/aplay") and exists(self.doneWav):
            self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False)

    def on_btnLocalize_clicked(self, widget):
        # Set locale
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            rootPath = "%s/root" % path
            de = EditDistro(path)
            script = "setlocale.sh"
            scriptSource = join(self.scriptDir, "files/{}".format(script))
            scriptTarget = join(rootPath, script)
            if exists(scriptSource):
                copy(scriptSource, scriptTarget)
                self.ec.run("chmod a+x '%s'" % scriptTarget)
                de.openTerminal("/bin/bash %s" % script)
                silent_remove(scriptTarget)
            
            # Copy Grub locale files to ISO boot directory and configure grub.cfg
            grub_path = "%s/boot/boot/grub/" % path
            grubcfg_path = "%s/grub.cfg" % grub_path
            locale_path = "%s/root/boot/grub/locale" % path
            default_path = "%s/root/etc/default" % path
            locale = self.ec.run("grep -oP '(?<=LANG=).*?(?=\.)' %s/locale" % default_path)[0]
            if exists(locale_path) and \
               exists(grubcfg_path) and \
               locale:
                self.ec.run("cp -rf %s %s" % (locale_path, grub_path))
                self.ec.run("sed -i 's/set lang=.*/set lang=%s/' %s" % (locale, grubcfg_path))

    def download_offline_packages(self, path):
        rootPath = "%s/root" % path
        arch = getGuestEfiArchitecture(rootPath)
        de = EditDistro(path)
        script = "offline.sh"
        scriptSource = join(self.scriptDir, "files/{}".format(script))
        scriptTarget = join(rootPath, script)
        offlineSource = join(rootPath, "offline")
        offlineTarget = join(rootPath, "../boot/offline")
        if exists(scriptSource):
            try:
                copy(scriptSource, scriptTarget)
                self.ec.run("chmod a+x '%s'" % scriptTarget)
                # Run the script
                de.openTerminal("/bin/bash {} {}".format(script, arch))
                # Remove script
                silent_remove(scriptTarget)
                # Move offline directory to boot directory
                if exists(offlineSource):
                    print(("%s exists" % offlineSource))
                    if exists(offlineTarget):
                        print((">> Remove %s" % offlineTarget))
                        silent_remove(offlineTarget)
                    print((">> Move %s to %s" % (offlineSource, offlineTarget)))
                    move(offlineSource, offlineTarget)
                else:
                    print((">> Cannot find: %s" % offlineSource))
            except Exception as detail:
                self.showError("Error: getting offline packages", detail, self.window)
        else:
            print((">> Cannot find: %s" % scriptSource))


    def on_btnBuildIso_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        msg = ""
        for path in selected:
            self.toggleGuiElements(True)
            self.showOutput("Start building ISO in: %s" % path)
            repaintGui()

            # Start building the ISO in a thread
            t = BuildIso(path, self.queue)
            t.start()
            self.queue.join()

            # Thread is done
            # Get the data from the queue
            ret = self.queue.get()
            self.queue.task_done()

            if ret is not None:
                self.showOutput(ret)
                if "error" in ret.lower():
                    self.showError("Error", ret, self.window)
                else:
                    msg += "%s\n" % ret

        if msg != "":
            if exists("/usr/bin/aplay") and exists(self.doneWav):
                self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False)
            self.showInfo("", msg, self.window)
        self.toggleGuiElements(False)

    def on_chkSelectAll_toggled(self, widget):
        self.tvHandlerDistros.treeviewToggleAll(toggleColNrList=[0], toggleValue=widget.get_active())

    def on_tvDistros_row_activated(self, widget, path, column):
        self.tvHandlerDistros.treeviewToggleRows(toggleColNrList=[0])

    def on_btnHelp_clicked(self, widget):
        system("open-as-user %s" % self.help)

    def on_btnOpenDir_clicked(self, widget):
        selected = self.tvHandlerDistros.getToggledValues(toggleColNr=0, valueColNr=2)
        for path in selected:
            system("open-as-user %s" % path)

    def fillTreeViewDistros(self, selectDistros=[]):
        contentList = [[_("Select"), _("Distribution"), _("Working directory")]]
        distros = self.getDistros()
        for distro in distros:
            select = False
            for selectDistro in selectDistros:
                if distro[0] == selectDistro:
                    select = True
            contentList.append([select, distro[0], distro[1]])
        self.tvHandlerDistros.fillTreeview(contentList=contentList, columnTypesList=['bool', 'str', 'str'], firstItemIsColName=True)

    def getDistros(self):
        distros = []
        if exists(self.distroFile):
            with open(self.distroFile, 'r') as f:
                lines = f.readlines()
            for line in lines:
                line = line.strip().rstrip('/')
                print(line)
                if exists(line):
                    dg = DistroGeneral(line)
                    isoName = dg.description
                    distros.append([isoName, line])
            # Sort on iso name
            if distros:
                distros = sorted(distros, key=operator.itemgetter(0))
        return distros

    # ===============================================
    # Add ISO Window Functions
    # ===============================================

    def on_btnIso_clicked(self, widget):
        fleFilter = Gtk.FileFilter()
        fleFilter.set_name("ISO")
        fleFilter.add_mime_type("application/x-cd-image")
        fleFilter.add_pattern("*.iso")

        startDir = None
        if exists(dirname(self.txtIso.get_text())):
            startDir = dirname(self.txtIso.get_text())

        filePath = SelectFileDialog(title=_('Select SolydXK ISO'), start_directory=startDir, parent=self.window, gtkFileFilter=fleFilter).show()
        if filePath is not None:
            self.txtIso.set_text(filePath)

    def on_btnDir_clicked(self, widget):
        startDir = None
        if exists(self.txtDir.get_text()):
            startDir = self.txtDir.get_text()
        dirText = SelectDirectoryDialog(title=_('Select directory'), start_directory=startDir, parent=self.window).show()
        if dirText is not None:
            self.txtDir.set_text(dirText)

    def on_btnSave_clicked(self, widget):
        self.iso = ""
        if self.chkFromIso.get_active():
            self.iso = self.txtIso.get_text()
        self.dir = self.txtDir.get_text()

        title = _("Save existing working directory")
        if self.iso != "":
            title = _("Unpack ISO and save")

        if not exists(self.dir):
            makedirs(self.dir)

        if not exists(self.dir):
            self.showError(title, _("Could not create directory %(dir)s: exiting" % {"dir": self.dir}), self.window)
        else:
            self.windowAddDistro.hide()
            if self.iso != "":
                if not exists(self.iso):
                    self.showInfo(self.btnSave.get_label(), _("The path to the ISO file does not exist:\n{}".format(self.iso)), self.window)
                    return
                if listdir(self.dir):
                    qd = QuestionDialog(self.btnSave.get_label(),
                        _("The destination directory is not empty.\n"
                        "Are you sure you want to overwrite all data in {}?".format(self.dir)),
                        self.window)
                    answer = qd.show()
                    if not answer:
                        return

                self.showOutput("Start unpacking the ISO...")
                self.toggleGuiElements(True)
                t = IsoUnpack(self.mountDir, self.iso, self.dir, self.queue)
                t.start()
                self.queue.join()
                GObject.timeout_add(1000, self.checkThread, True)
            else:
                self.saveDistroFile(self.dir, True)
                self.fillTreeViewDistros()
                self.showOutput(_("Existing working directory added"))
                #self.toggleGuiElements(False)

    def on_btnCancel_clicked(self, widget):
        self.windowAddDistro.hide()

    def on_addDistroWindow_delete_event(self, widget, data=None):
        self.windowAddDistro.hide()
        return True

    def on_txtIso_changed(self, widget):
        path = self.txtIso.get_text()
        if exists(path):
            self.txtDir.set_sensitive(True)
            self.btnDir.set_sensitive(True)
            if exists(self.txtDir.get_text()):
                self.btnSave.set_sensitive(True)
        else:
            self.txtDir.set_sensitive(False)
            self.btnDir.set_sensitive(False)
            self.btnSave.set_sensitive(False)

    def on_txtDir_changed(self, widget):
        blnFromIso = self.chkFromIso.get_active()
        isoPath = self.txtIso.get_text()
        dirText = self.txtDir.get_text()
        self.btnSave.set_sensitive(False)
        if exists(dirText):
            if blnFromIso:
                if exists(isoPath):
                    self.btnSave.set_sensitive(True)
            else:
                self.btnSave.set_sensitive(True)

    def on_chkFromIso_toggled(self, widget):
        if widget.get_active():
            self.lblIso.set_visible(True)
            self.boxIso.set_visible(True)
            self.txtDir.set_sensitive(False)
            self.btnDir.set_sensitive(False)
            self.btnSave.set_sensitive(False)
            self.lblDir.set_text(_("Unpack ISO to directory"))
            self.btnSave.set_label(_("Unpack & Save"))
        else:
            self.txtIso.set_text("")
            self.lblIso.set_visible(False)
            self.boxIso.set_visible(False)
            self.txtDir.set_sensitive(True)
            self.btnDir.set_sensitive(True)
            self.btnSave.set_sensitive(True)
            self.lblDir.set_text(_("Work directory"))
            self.btnSave.set_label(_("Save"))

    # ===============================================
    # General functions
    # ===============================================

    def showInfo(self, title, message, parent=None):
        MessageDialogSafe(title, message, Gtk.MessageType.INFO, parent).show()

    def showError(self, title, message, parent=None):
        MessageDialogSafe(title, message, Gtk.MessageType.ERROR, parent).show()

    def showOutput(self, message):
        print(message)
        pushMessage(self.statusbar, message)

    def checkThread(self, addDistro=None):
        #print 'Thread count = ' + str(threading.active_count())
        # As long there's a thread active, keep spinning
        if threading.active_count() > 1:
            return True

        # Thread is done
        # Get the data from the queuez
        ret = self.queue.get()
        self.queue.task_done()

        # Thread is done
        if addDistro is not None:
            self.saveDistroFile(self.dir, addDistro)
            self.fillTreeViewDistros(self.isoName)
        self.toggleGuiElements(False)
        if exists("/usr/bin/aplay") and exists(self.doneWav):
            self.ec.run("/usr/bin/aplay '%s'" % self.doneWav, False)
        if ret is not None:
            self.showOutput(ret)
            if "error" in ret.lower():
                self.showError("Error", ret, self.window)
            else:
                self.showInfo("", ret, self.window)
        return False

    def toggleGuiElements(self, startThread):
        if startThread:
            self.chkSelectAll.set_sensitive(False)
            self.tvDistros.set_sensitive(False)
            self.btnAdd.set_sensitive(False)
            self.btnBuildIso.set_sensitive(False)
            self.btnEdit.set_sensitive(False)
            self.btnRemove.set_sensitive(False)
            self.btnUpgrade.set_sensitive(False)
            self.btnLocalize.set_sensitive(False)
            self.btnDir.set_sensitive(False)
            self.btnHelp.set_sensitive(False)
        else:
            self.chkSelectAll.set_sensitive(True)
            self.tvDistros.set_sensitive(True)
            self.btnAdd.set_sensitive(True)
            self.btnBuildIso.set_sensitive(True)
            self.btnEdit.set_sensitive(True)
            self.btnRemove.set_sensitive(True)
            self.btnUpgrade.set_sensitive(True)
            self.btnLocalize.set_sensitive(True)
            self.btnDir.set_sensitive(True)
            self.btnHelp.set_sensitive(True)

    def saveDistroFile(self, distroPath, addDistro=True):
        newCont = []
        dg = DistroGeneral(distroPath)
        self.isoName = dg.description

        cfg = []
        if exists(self.distroFile):
            with open(self.distroFile, 'r') as f:
                cfg = f.readlines()
            for line in cfg:
                line = line.strip()
                if distroPath != line and exists(line):
                    newCont.append(line)

        if addDistro:
            newCont.append(distroPath)

        with open(self.distroFile, 'w') as f:
            f.write('\n'.join(newCont))

        self.iso = ""
        self.dir = ""

    # Close the gui
    def on_constructorWindow_destroy(self, widget):
        # Close the app
        Gtk.main_quit()
        
    # ===============================================
    # Language specific functions
    # ===============================================

    def get_language_dir(self):
        # First test if full locale directory exists, e.g. html/pt_BR,
        # otherwise perhaps at least the language is there, e.g. html/pt
        # and if that doesn't work, try html/pt_PT
        lang = self.get_current_language()
        path = join(self.htmlDir, lang)
        if not isdir(path):
            base_lang = lang.split('_')[0].lower()
            path = join(self.htmlDir, base_lang)
            if not isdir(path):
                path = join(self.htmlDir, "{}_{}".format(base_lang, base_lang.upper()))
                if not isdir(path):
                    path = join(self.htmlDir, 'en')
        return path

    def get_current_language(self):
        lang = environ.get('LANG', 'US').split('.')[0]
        if lang == '':
            lang = 'en'
        return lang
Exemplo n.º 22
0
class UpdateManager(object):

    def __init__(self):
        # Check if script is running
        self.scriptName = basename(__file__)
        self.umglobal = UmGlobal()
        self.user = self.umglobal.getLoginName()

        # Initiate logging
        self.logFile = join('/var/log', self.umglobal.settings['log'])
        print(("UM log = %s" % self.logFile))
        self.log = Logger(self.logFile, maxSizeKB=5120)

        # Remove scripts
        self.deleteScripts(self.umglobal.localUpdVersion)

        # Initialize
        self.ec = ExecCmd(loggerObject=self.log)
        self.apt = UmApt(self.umglobal)
        self.kernelVersion = self.umglobal.getKernelVersion()
        self.upgradables = []
        self.upgradableUM = []
        self.window = None

        # Handle arguments
        parser = argparse.ArgumentParser(description='SolydXK Update Manager')
        parser.add_argument('-q','--quick', action="store_true", help='Quick upgrade')
        parser.add_argument('-r','--reload', action="store_true", help='')
        args, extra = parser.parse_known_args()

        self.quickUpdate = False
        if args.quick and not self.umglobal.newUpd:
            self.quickUpdate = True
        if args.reload:
            pids = self.umglobal.getProcessPids("updatemanager.py")
            if len(pids) > 1:
                print(("updatemanager.py already running - kill pid {}".format(pids[0])))
                os.system("kill {}".format(pids[0]))

        # Set some global translations
        self.aptErrorText = _("Apt error")
        self.upgradablePackagesText = _("The following packages will be upgraded:")
        self.newPackagesText = _("The following NEW packages will be installed:")
        self.removedPackagesText = _("The following packages will be REMOVED:")
        self.heldbackPackagesText = _("The following packages have been kept back:")
        self.downgradePackagesText = _("The following packages are going to be downgraded:")

        # Cleanup first
        for fle in glob(join(self.umglobal.filesDir, '.um*')):
            remove(fle)

        # Load window and widgets
        self.builder = Gtk.Builder()
        self.builder.add_from_file(join(self.umglobal.shareDir, 'updatemanager.glade'))
        go = self.builder.get_object

        # Quick update
        if self.quickUpdate:
            # Refresh data
            self.refresh()
            self.umglobal.collectData()
            self.apt.createPackagesInfoList()
            self.apt.createPackageLists()
            self.fillTreeView()

            # Run upgrade
            nid = self.run_upgrade()

            if nid != "":
                self.on_command_done(None, 0, nid)

            sys.exit(2)

        # Make sure the files directory is set correctly
        self.checkFilesDir()

        # Main window objects
        self.window = go("windowMain")
        #self.window.set_icon_from_file(join(self.umglobal.iconsDir, self.umglobal.settings["icon-connected"]))
        self.tvPck = go("tvPck")
        self.swTerminal = go("swTerminal")
        self.statusbar = go("statusbar")
        self.btnInstall = go("btnInstall")
        self.btnRefresh = go("btnRefresh")
        self.btnPackages = go("btnPackages")
        self.btnOutput = go("btnOutput")
        self.btnInfo = go("btnInfo")
        self.btnPreferences = go("btnPreferences")
        self.nbMain = go("nbMain")
        self.swInfo = go("swInfo")
        self.btnMaintenance = go("btnMaintenance")
        self.lblMaintenance = go("lblMaintenance")
        self.tvMaintenance = go("tvMaintenance")
        self.btnMaintenanceExecute = go("btnMaintenanceExecute")
        self.chkMaintenanceSelectAll = go("chkMaintenanceSelectAll")
        self.radUnneeded = go("radUnneeded")
        self.radCleanCache = go("radCleanCache")
        self.radDowngradable = go("radDowngradable")
        self.radNotavailable = go("radNotavailable")
        self.radOldKernel = go("radOldKernel")
        self.lblMaintenanceHelp = go("lblMaintenanceHelp")

        # Translations
        self.window.set_title(_("SolydXK Update Manager"))
        self.btnInstall.set_label(_("Install"))
        self.btnRefresh.set_label(_("Refresh"))
        self.btnOutput.set_label(_("Output"))
        self.btnInfo.set_label(_("Information"))
        self.btnPreferences.set_label(_("Preferences"))
        self.btnMaintenance.set_label(_("Maintenance"))
        self.btnPackages.set_label(_("Packages"))
        self.uptodateText = self.umglobal.connectedText
        self.lblMaintenance.set_label(self.btnMaintenance.get_label())
        self.btnMaintenanceExecute.set_label(_("Execute"))
        self.chkMaintenanceSelectAll.set_label(_("Select all"))
        self.radCleanCache.set_label(_("Clean up the apt cache"))
        self.radUnneeded.set_label(_("Remove unneeded packages"))
        self.radNotavailable.set_label(_("Remove packages not available\nin the repositories"))
        self.radOldKernel.set_label(_("Remove old kernels"))
        self.radDowngradable.set_label(_("Downgrade packages with\nonly lower versions available"))
        self.lblMaintenanceHelp.set_label(_("Make sure you create\n"
                                             "a system image before you\n"
                                             "continue (e.g. Clonezilla)."))

        # VTE Terminal
        self.terminal = VirtualTerminal(userInputAllowed=self.umglobal.settings["allow-terminal-user-input"])
        self.swTerminal.add(self.terminal)
        self.terminal.set_vexpand(True)
        self.terminal.set_hexpand(True)
        self.terminal.connect('command-done', self.on_command_done)
        self.terminal.connect('line-added', self.on_line_added)
        palletList = ['#4A4A4A', '#BD1919', '#118011', '#CE6800', '#1919BC', '#8D138D', '#139494', '#A7A7A7']
        self.terminal.setTerminalColors("#000000", "#FFFFFF", palletList)
        self.swTerminal.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse("#FFFFFF"))

        # Disable all buttons
        self.btnInfo.set_sensitive(False)
        self.btnPreferences.set_sensitive(False)
        self.btnOutput.set_sensitive(False)
        self.btnRefresh.set_sensitive(False)
        self.btnInstall.set_sensitive(False)
        self.btnPackages.set_sensitive(False)
        self.btnMaintenance.set_sensitive(False)

        # Hide tabs if needed
        for tab in self.umglobal.settings["hide-tabs"]:
            if tab == "packages":
                self.nbMain.get_nth_page(0).set_visible(False)
                self.btnPackages.set_visible(False)
            elif tab == "output":
                self.nbMain.get_nth_page(1).set_visible(False)
                self.btnOutput.set_visible(False)
            elif tab == "info":
                self.nbMain.get_nth_page(2).set_visible(False)
                self.btnInfo.set_visible(False)
            elif tab == "maintenance":
                self.nbMain.get_nth_page(3).set_visible(False)
                self.btnMaintenance.set_visible(False)

        # Connect the signals and show the window
        self.builder.connect_signals(self)
        self.window.show()

        # Force the window to show
        while Gtk.events_pending():
            Gtk.main_iteration()

        # Just show something that we're busy
        msg = _("Gathering information...")
        self.terminal.executeCommand('echo "%s"' % msg, 'init')
        self.showOutput()

        # Treeview handlers
        self.tvHandler = TreeViewHandler(self.tvPck)
        self.tvMaintenanceHandler = TreeViewHandler(self.tvMaintenance)

        # Version information
        ver = _("Version")
        pckVer = self.apt.getPackageVersion('updatemanager')
        versionInfo = "%(ver)s: %(pckVer)s" % { "ver": ver, "pckVer": pckVer }
        if self.umglobal.localUpdVersion != "2000.01.01":
            versionInfo = "%(ver)s: %(pckVer)s" % { "ver": ver, "pckVer": pckVer }
        self.pushMessage(versionInfo)

        # Log basic information
        self.log.write("==============================================", "UM.init", "debug")
        self.log.write("UM version = %s" % versionInfo, "UM.init", "debug")
        self.log.write("==============================================", "UM.init", "debug")
        mirrorsList = join(self.umglobal.filesDir, basename(self.umglobal.settings["mirrors-list"]))
        if exists(mirrorsList):
            self.log.write("Mirrors list", "UM.init", "debug")
            with open(mirrorsList, 'r') as f:
                for line in f.readlines():
                    self.log.write(line, "UM.init", "debug")
            self.log.write("==============================================", "UM.init", "debug")

        # Refresh apt cache
        self.refresh()

        # Initialize maintenance screen
        self.fillTreeViewMaintenance()

    # ===============================================
    # Main window functions
    # ===============================================

    def on_btnInstall_clicked(self, widget):
        self.run_upgrade()

    def run_upgrade(self):
        nid = ""
        aptHasErrors = self.apt.aptHasErrors()
        if aptHasErrors is not None:
            MessageDialog(self.aptErrorText, aptHasErrors)
        elif self.upgradables:

            if self.apt.upgradablePackages:
                self.log.write("=================== upgradable pacages ====================", "UM.run_upgrade", "debug")
                self.log.write(self.createLogString(self.apt.upgradablePackages), "UM.run_upgrade", "debug")
            if self.apt.removedPackages:
                self.log.write("==================== removed packages =====================", "UM.run_upgrade", "debug")
                self.log.write(self.createLogString(self.apt.removedPackages), "UM.run_upgrade", "debug")
            if self.apt.newPackages:
                self.log.write("======================= new packages =======================", "UM.run_upgrade", "debug")
                self.log.write(self.createLogString(self.apt.newPackages), "UM.run_upgrade", "debug")
            if self.apt.heldbackPackages:
                self.log.write("=================== kept back packages =====================", "UM.run_upgrade", "debug")
                self.log.write(self.createLogString(self.apt.heldbackPackages), "UM.run_upgrade", "debug")

            if not self.quickUpdate:
                self.showOutput()
            contMsg = _("Continue installation?")
            if self.upgradableUM:
                cmd = "%s install updatemanager" % self.umglobal.settings['apt-get-string']
                cmd += "; %s install %s" % (self.umglobal.settings['apt-get-string'], " ".join(self.apt.getPackageDependencies('updatemanager')))
                nid = 'uminstallum'
                self.prepForCommand(nid)
                if self.quickUpdate:
                    self.ec.run(cmd)
                else:
                    self.terminal.executeCommand(cmd, nid)
                self.log.write("Execute command: %s (%s)" % (cmd, nid), "UM.on_btnInstall_clicked", "debug")
            else:
                msg = self.getDistUpgradeInfo()
                answer = True
                if msg != "":
                    answer = self.showConfirmationDlg(contMsg, msg)
                if answer:
                    cmd = "%s dist-upgrade" % self.umglobal.settings['apt-get-string']
                    #if self.umglobal.newUpd:
                    pre = join(self.umglobal.filesDir, self.umglobal.settings['pre-upd'].replace("[VERSION]", self.umglobal.serverUpdVersion))
                    post = join(self.umglobal.filesDir, self.umglobal.settings['post-upd'].replace("[VERSION]", self.umglobal.serverUpdVersion))
                    if exists(pre):
                        cmd = "/bin/bash %(pre)s; %(cmd)s" % { "pre": pre, "cmd": cmd }
                    if exists(post):
                        cmd = "%(cmd)s; /bin/bash %(post)s" % { "cmd": cmd, "post": post }
                    nid = 'umupd'
                    self.prepForCommand(nid)
                    if self.quickUpdate:
                        self.ec.run(cmd)
                    else:
                        self.terminal.executeCommand(cmd, nid)
                    self.log.write("Execute command: %s (%s)" % (cmd, nid), "UM.on_btnInstall_clicked", "debug")

        else:
            if not self.quickUpdate:
                MessageDialog(self.btnInstall.get_label(), self.uptodateText)

        return nid

    def on_btnRefresh_clicked(self, widget):
        self.refresh()

    def on_btnPackages_clicked(self, widget):
        self.showPackages()

    def on_btnOutput_clicked(self, widget):
        self.showOutput()

    def on_btnInfo_clicked(self, widget):
        self.showInfo()

    def on_btnPreferences_clicked(self, widget):
        # Run preferences in its own thread
        pref_thread = threading.Thread(target=self.openPreferences)
        pref_thread.setDaemon(True)
        pref_thread.start()

    def openPreferences(self):
        os.system("updatemanager -p")

    def on_btnMaintenance_clicked(self, widget):
        self.showMaintenance()

    def on_radCleanCache_toggled(self, widget):
        if widget.get_active():
            self.fillTreeViewMaintenance()

    def on_radUnneeded_toggled(self, widget):
        if widget.get_active():
            message = "%s\n\n%s" % (_("You might need to run this several times."), self.lblMaintenanceHelp.get_label().replace("\n", " "))
            WarningDialog(self.btnMaintenance.get_label(), message)
            self.fillTreeViewMaintenance()

    def on_radNotavailable_toggled(self, widget):
        if widget.get_active():
            message = "%s\n\n%s" % (_("Removing not available packages may break your system!"), self.lblMaintenanceHelp.get_label().replace("\n", " "))
            WarningDialog(self.btnMaintenance.get_label(), message)
            self.fillTreeViewMaintenance()

    def on_radOldKernel_toggled(self, widget):
        if widget.get_active():
            message = "%s\n\n%s" % (_("Once removed you will not be able to boot these kernels!"), self.lblMaintenanceHelp.get_label().replace("\n", " "))
            WarningDialog(self.btnMaintenance.get_label(), message)
            self.fillTreeViewMaintenance()

    def on_radDowngradable_toggled(self, widget):
        if widget.get_active():
            message = "%s\n\n%s" % (_("Downgrading packages may break your system!"), self.lblMaintenanceHelp.get_label().replace("\n", " "))
            WarningDialog(self.btnMaintenance.get_label(), message)
            self.fillTreeViewMaintenance()

    def on_btnMaintenanceExecute_clicked(self, widget):
        self.executeMaintenance()

    def on_chkMaintenanceSelectAll_toggled(self, widget):
        self.tvMaintenanceHandler.treeviewToggleAll(toggleColNrList=[0], toggleValue=widget.get_active())

    # ===============================================
    # Maintenance functions
    # ===============================================

    def enableMaintenance(self, enable=True):
        self.btnMaintenanceExecute.set_sensitive(enable)
        self.radUnneeded.set_sensitive(enable)
        self.radCleanCache.set_sensitive(enable)
        self.radDowngradable.set_sensitive(enable)
        self.radOldKernel.set_sensitive(enable)
        self.radNotavailable.set_sensitive(enable)
        self.chkMaintenanceSelectAll.set_sensitive(enable)
        if enable:
            self.chkMaintenanceSelectAll.set_active(False)

    def fillTreeViewMaintenance(self):
        blnCleanCache = self.radCleanCache.get_active()
        blnUnneeded = self.radUnneeded.get_active()
        blnDowngradable = self.radDowngradable.get_active()
        blnNotavailable = self.radNotavailable.get_active()
        blnOldKernels = self.radOldKernel.get_active()

        self.enableMaintenance(False)

        columnTypesList = ['bool', 'str', 'str', 'str']
        packages = [["", _("Package"), _("Installed"), _("Available")]]
        # Clear the treeview first
        self.tvMaintenanceHandler.fillTreeview(packages, columnTypesList, 0, 400, True)

        msg = ""
        if blnCleanCache:
            msg = _("Hit the Execute button to clean the cache.")
            columnTypesList = ['str']
            packages.append([msg])
        elif blnUnneeded:
            msg = self.radUnneeded.get_label()
            self.apt.createPackageLists("apt-get autoremove")
            for pck in self.apt.removedPackages:
                packages.append([False, pck[0], pck[1], pck[2]])
            self.apt.createPackageLists()
            # Add orphaned files
            self.apt.fillOrphanedPackages()
            for pck in self.apt.orphanedPackages:
                packages.append([False, pck[0], pck[1], pck[2]])
        elif blnNotavailable:
            msg = self.radNotavailable.get_label()
            self.apt.fillNotAvailablePackages()
            for pck in self.apt.notavailablePackages:
                if pck[0][0:6] != "linux-":
                    packages.append([False, pck[0], pck[1], ""])
        elif blnOldKernels:
            msg = self.radOldKernel.get_label()
            self.apt.fillKernelPackages()
            for pck in self.apt.kernelPackages:
                if "headers-486" not in pck[0] \
                    and "headers-586" not in pck[0] \
                    and "headers-686" not in pck[0] \
                    and "headers-amd64" not in pck[0] \
                    and "image-486" not in pck[0] \
                    and "image-586" not in pck[0] \
                    and "image-686" not in pck[0] \
                    and "image-amd64" not in pck[0]:

                    checkVersion = self.kernelVersion
                    if "kbuild" in pck[0]:
                        indMinus = self.kernelVersion.index("-")
                        indZero = self.kernelVersion.index("0")
                        if indZero > 0 and indZero < indMinus:
                            ind = indZero - 1
                        else:
                            ind = indMinus
                        if ind > 0:
                            checkVersion = self.kernelVersion[0:ind]
                    if checkVersion not in pck[0]:
                        packages.append([False, pck[0], pck[1], ""])
        elif blnDowngradable:
            msg = self.radDowngradable.get_label()
            self.apt.fillDowngradablePackages()
            for pck in self.apt.downgradablePackages:
                packages.append([False, pck[0], pck[1], pck[2]])

        if len(packages) > 1:
            self.tvMaintenanceHandler.fillTreeview(packages, columnTypesList, 0, 400, True)
        else:
            if not blnCleanCache:
                msg = _("\"%s\"\n did not return any results.") % msg
                MessageDialog(self.btnMaintenance.get_label(), msg)

        self.enableMaintenance(True)

    def executeMaintenance(self):
        blnCleanCache = self.radCleanCache.get_active()
        blnDowngradable = self.radDowngradable.get_active()
        blnNotNeeded = self.radUnneeded.get_active()
        downgradeString = ""
        deleteString = ""
        updateGrub = False
        cmd = ""

        self.enableMaintenance(False)

        if blnCleanCache:
            safe = False
            msg = _("Do you want to completely clean the apt cache?\n\n"
                    "When No, only unavailable installation packages are removed.")
            answer = QuestionDialog(self.radCleanCache.get_label(), msg)
            if answer:
                safe = True
            self.apt.cleanCache(safe)
            msg = _("Apt cache has been cleaned.")
            MessageDialog(self.radCleanCache.get_label(), msg)
        else:
            # Get user selected packages
            model = self.tvMaintenance.get_model()
            itr = model.get_iter_first()
            while itr is not None:
                sel = model.get_value(itr, 0)
                if sel:
                    pck = model.get_value(itr, 1)
                    avVer = model.get_value(itr, 3)
                    if blnDowngradable:
                        downgradeString += " %(pck)s=%(avVer)s" % {"pck": pck, "avVer": avVer}
                    else:
                        deleteString += " %s" % pck
                        if "linux-image" in pck:
                            updateGrub = True
                itr = model.iter_next(itr)

            if downgradeString != "":
                cmd = "%s install %s" % (self.umglobal.settings['apt-get-string'], downgradeString)
            elif deleteString != "":
                cmd = "%s purge %s" % (self.umglobal.settings['apt-get-string'], deleteString)

        if cmd != "":
            self.apt.createPackageLists(cmd)
            msg = self.getDistUpgradeInfo()
            answer = True
            if msg != "":
                contMsg = _("Execute maintenance changes?")
                answer = self.showConfirmationDlg(contMsg, msg)
            if answer:
                if updateGrub:
                    cmd += "; update-grub"
                if blnNotNeeded:
                    cmd += "; %s purge $(COLUMNS=132 dpkg -l | grep ^rc | awk '{ print $2 }')" % self.umglobal.settings['apt-get-string']
                self.showOutput()
                nid = 'ummaintenance'
                self.prepForCommand(nid)
                self.terminal.executeCommand(cmd, nid)
                self.log.write("Execute command: %s (%s)" % (cmd, nid), "UM.executeMaintenance", "debug")

        self.enableMaintenance(True)

    # ===============================================
    # General functions
    # ===============================================

    def prepForCommand(self, nid):
        os.system("touch %s" % self.umglobal.umfiles[nid])
        if not self.quickUpdate:
            self.btnRefresh.set_sensitive(False)
            self.btnInstall.set_sensitive(False)
            self.btnMaintenance.set_sensitive(False)

    def on_line_added(self, terminal, line):
        if line.strip()[0:2].upper() == "E:":
            self.log.write(line, "UM.on_line_added", "error")
        else:
            self.log.write(line, "UM.on_line_added", "info")

    def on_command_done(self, terminal, pid, nid):
        if nid != "init":
            self.log.write("Command finished (pid=%s, nid=%s)" % (pid, nid), "UM.on_command_done", "info")
            if nid == "uminstallum":
                # Reload UM
                self.log.write("Updating UM: kill process of updatemanagerpref.py", "UM.on_command_done", "debug")
                self.umglobal.killScriptProcess("updatemanagerpref.py")
                # Reload tray as user
                self.log.write("Updating UM: kill process of updatemanagertray.py", "UM.on_command_done", "debug")
                self.umglobal.killScriptProcess("updatemanagertray.py")
                cmd = "sudo -u {} updatemanager -t -r".format(self.user)
                os.system(cmd)
                self.log.write("UM updated: reload tray as user {}".format(self.user), "UM.on_command_done", "debug")
                # Reload UM window
                cmd = join(self.umglobal.scriptDir, "updatemanager.py")
                if self.quickUpdate:
                    cmd = join(self.umglobal.scriptDir, "updatemanager.py -q")
                self.umglobal.reloadWindow(cmd, self.user)
                self.log.write("UM updated: reload {0} as user {1}".format(cmd, self.user), "UM.on_command_done", "debug")
            elif nid == "umrefresh":
                # Build installed packages info list
                self.apt.createPackagesInfoList()
                # Run post update when needed
                self.postUpdate()
            elif nid == "ummaintenance":
                self.enableMaintenance(True)
                self.fillTreeViewMaintenance()
                self.btnInstall.set_sensitive(True)
                self.btnRefresh.set_sensitive(True)
                self.btnPackages.set_sensitive(True)
                self.btnMaintenance.set_sensitive(True)
                self.showMaintenance()
            elif nid == 'umupd':
                # Save update version in hist file
                self.umglobal.saveHistVersion("upd", self.umglobal.serverUpdVersion)
                self.log.write("Save history upd=%s" % self.umglobal.serverUpdVersion, "UM.on_command_done", "debug")
                self.deleteScripts()

            # Refresh data after install or update
            self.umglobal.collectData()
            self.apt.createPackageLists()
            self.fillTreeView()

            # Enable the buttons and load the info page
            self.log.write("Re-initiate window after terminal command: %s" % str(not self.quickUpdate), "UM.on_command_done", "debug")
            if not self.quickUpdate:
                self.btnInstall.set_sensitive(True)
                self.btnRefresh.set_sensitive(True)
                self.btnPackages.set_sensitive(True)
                self.btnMaintenance.set_sensitive(True)
                self.btnInfo.set_sensitive(True)
                self.loadInfo()

                if self.umglobal.newUpd:
                    self.showInfo()
                else:
                    # This throws a lock file error in Plasma5
                    #aptHasErrors = self.apt.aptHasErrors()
                    #if aptHasErrors is not None:
                        #MessageDialog(self.aptErrorText, aptHasErrors)
                    #el
                    if self.upgradables:
                        self.showPackages()
                    else:
                        self.showInfo()
                        MessageDialog(self.btnInfo.get_label(), self.uptodateText)
                self.log.write("Re-initiate window complete", "UM.on_command_done", "debug")

            # Cleanup name file(s)
            for fle in glob(join(self.umglobal.filesDir, '.um*')):
                remove(fle)

    def createLogString(self, packagesList):
        lst = []
        for data in packagesList:
            lst.append(data[0])
        return ' '.join(lst)

    def refresh(self):
        # Refresh server info
        print((self.umglobal.hasInternet))
        self.umglobal.getServerInfo()
        print((self.umglobal.hasInternet))

        # Check of programs locking apt
        prog = self.apt.getAptCacheLockedProgram(self.umglobal.settings["apt-packages"])
        if prog is not None:
            msg = _("Another program is locking the apt cache\n\n"
                    "Please, close the program before refreshing:\n"
                    "* %s" % prog)
            MessageDialog(self.btnRefresh.get_label(), msg)
            self.log.write("%s is locking the apt cache" % prog, "UM.refresh", "warning")
        elif self.umglobal.hasInternet:
            if not self.quickUpdate:
                # Update the apt cache
                self.btnPreferences.set_sensitive(True)
                self.btnOutput.set_sensitive(True)
                self.btnRefresh.set_sensitive(False)
                self.btnInstall.set_sensitive(False)
                self.btnMaintenance.set_sensitive(False)
                self.btnPackages.set_sensitive(True)
                self.showOutput()
            cmd = "dpkg --configure -a; %s -f install; apt-get update" % self.umglobal.settings['apt-get-string']
            nid = 'umrefresh'
            self.prepForCommand(nid)
            if self.quickUpdate:
                self.ec.run(cmd)
            else:
                self.terminal.executeCommand(cmd, nid)
            self.apt.initAptShowVersions()
            self.log.write("Execute command: %s (%s)" % (cmd, nid), "UM.refresh", "debug")
        else:
            if not self.quickUpdate:
                # No internet connection
                self.btnInstall.set_sensitive(True)
                self.btnRefresh.set_sensitive(True)
                self.btnPackages.set_sensitive(True)
                self.btnMaintenance.set_sensitive(True)
                self.btnInfo.set_sensitive(True)
                self.btnOutput.set_sensitive(True)
                self.btnPreferences.set_sensitive(True)
                self.loadInfo()
                self.showInfo()

    def postUpdate(self):
        # Check for changed version information
        if self.umglobal.newUpd and self.umglobal.serverUpdVersion is not None:
            self.getScripts([self.umglobal.settings['pre-upd'].replace("[VERSION]", self.umglobal.serverUpdVersion),
                            self.umglobal.settings['post-upd'].replace("[VERSION]", self.umglobal.serverUpdVersion)])

    def fillTreeView(self):
        # First check if this application is upgradable
        self.upgradableUM = self.getUpgradablePackages(packageNames=["updatemanager"])
        if self.upgradableUM:
            self.upgradables = self.upgradableUM
        else:
            # Get a list of packages that can be upgraded
            self.upgradableUM = []
            self.upgradables = self.getUpgradablePackages()
            #if not self.upgradables:
                ## Check for black listed packages
                #cmd = "dpkg --get-selections | grep hold$ | awk '{print $1}'"
                #lst = self.ec.run(cmd, False)
                #for pck in lst:
                    #self.upgradables.append([pck.strip(), _("blacklisted"), ""])

        if not self.quickUpdate:
            contentList = [[_("Package"), _("Current version"), _("New version")]] + self.upgradables
            self.tvHandler.fillTreeview(contentList=contentList, columnTypesList=['str', 'str', 'str'], firstItemIsColName=True)

    def getUpgradablePackages(self, packageNames=[]):
        upckList = []
        if packageNames:
            upckList = []
            for packageName in packageNames:
                for upck in self.apt.upgradablePackages:
                    if upck[0] == packageName:
                        upckList.append(upck)
                        break
        else:
            upckList = self.apt.upgradablePackages
        return upckList

    def getDistUpgradeInfo(self, upgradablesOnly=False):
        info = ""
        if upgradablesOnly:
            if self.apt.upgradablePackages:
                info = "<strong>%s</strong><br>" % self.apt.upgradablePackagesText
                for pck in self.apt.upgradablePackages:
                    info += "%s " % pck[0]
        else:
            if self.apt.removedPackages:
                info = "<strong>%s</strong><br>" % self.removedPackagesText
                for pck in self.apt.removedPackages:
                    info += "%s " % pck[0]
            if self.apt.newPackages:
                if info != "":
                    info += "<p>&nbsp;</p>"
                info += "<strong>%s</strong><br>" % self.newPackagesText
                for pck in self.apt.newPackages:
                    info += "%s " % pck[0]
            if self.apt.heldbackPackages:
                if info != "":
                    info += "<p>&nbsp;</p>"
                info += "<strong>%s</strong><br>" % self.heldbackPackagesText
                for pck in self.apt.heldbackPackages:
                    info += "%s " % pck[0]
            if self.apt.downgradablePackages:
                if info != "":
                    info += "<p>&nbsp;</p>"
                info += "<strong>%s</strong><br>" % self.downgradePackagesText
                for pck in self.apt.downgradablePackages:
                    info += "%s " % pck[0]
        return info

    def showPackages(self):
        self.nbMain.set_current_page(0)

    def showOutput(self):
        self.nbMain.set_current_page(1)
        self.terminal.grab_focus()

    def showInfo(self):
        self.nbMain.set_current_page(2)

    def showMaintenance(self):
        self.nbMain.set_current_page(3)

    def showConfirmationDlg(self, title, message):
        head = "<html><head><style>body { font-family: Arial, Helvetica, Verdana, Sans-serif; font-size: 12px; color: #555555; background: #ffffff; }</style></head><body>"
        end = "</body></html>"
        html = "%s%s%s" % (head, message, end)
        sw = Gtk.ScrolledWindow()
        sw.add(SimpleBrowser(html))
        return CustomQuestionDialog(title, sw, 550, 300, self.window).show()

    # Get pre-install script and post-install script from the server
    def getScripts(self, files):
        for fle in files:
            flePath = join(self.umglobal.filesDir, fle)
            if not exists(flePath):
                # Get the new scripts if they exist
                url = "%s/%s/%s" % (self.umglobal.settings['solydxk'], self.umglobal.settings["umfilesdir"], fle)
                try:
                    txt = urlopen(url).read().decode('utf-8')
                    if txt != '':
                        # Save to a file and make executable
                        self.log.write("Save script = %s" % flePath, "UM.getScripts", "debug")
                        with open(flePath, 'w') as f:
                            f.write(txt)
                        chmod(flePath, 0o755)
                except:
                    pass

    def loadInfo(self):
        languageDir = self.get_language_dir()
        url = join("file://%s" % languageDir, self.umglobal.settings['up-to-date'])
        self.btnInfo.set_icon_name("help-about")
        if self.upgradables:
            url = join("file://%s" % languageDir, self.umglobal.settings['updates'])
            if self.umglobal.newUpd:
                url = "%s/%s/%s" % (self.umglobal.settings['solydxk'], self.umglobal.settings["umfilesdir"], self.umglobal.settings['upd-info'])
        elif self.umglobal.serverUpdVersion is None:
            url = join("file://%s" % languageDir, self.umglobal.settings['not-found'])

        self.log.write("Load info url: %s" % url, "UM.loadInfo", "debug")

        children = self.swInfo.get_children()
        if children:
            children[0].openUrl(url)
        else:
            self.swInfo.add(SimpleBrowser(url))

    def get_language_dir(self):
        # First test if full locale directory exists, e.g. html/pt_BR,
        # otherwise perhaps at least the language is there, e.g. html/pt
        # and if that doesn't work, try html/pt_PT
        lang = self.get_current_language()
        path = join(self.umglobal.htmlDir, lang)
        if not isdir(path):
            base_lang = lang.split('_')[0].lower()
            path = join(self.umglobal.htmlDir, base_lang)
            if not isdir(path):
                path = join(self.umglobal.htmlDir, "{}_{}".format(base_lang, base_lang.upper()))
                if not isdir(path):
                    path = join(self.umglobal.htmlDir, 'en')
        return path

    def get_current_language(self):
        lang = os.environ.get('LANG', 'US').split('.')[0]
        if lang == '':
            lang = 'en'
        return lang

    def pushMessage(self, message):
        if message is not None:
            context = self.statusbar.get_context_id('message')
            self.statusbar.push(context, message)

    def checkFilesDir(self):
        if not exists(self.umglobal.filesDir):
            makedirs(self.umglobal.filesDir)
        oldFiles = glob(join(self.umglobal.scriptDir, 'pre-*')) + \
                   glob(join(self.umglobal.scriptDir, 'post-*')) + \
                   [join(self.umglobal.scriptDir, 'updatemanager.hist')] + \
                   [join(self.umglobal.scriptDir, 'mirrors.list')]
        for fle in oldFiles:
            if exists(fle):
                fleName = basename(fle)
                if not exists(join(self.umglobal.filesDir, fleName)):
                    move(fle, self.umglobal.filesDir)
                else:
                    remove(fle)
        chmod(self.umglobal.filesDir, 0o777)

    def deleteScripts(self, UpdVersion=None):
        UpdVersion = "*"
        if UpdVersion is not None:
            UpdVersion = "*%s" % UpdVersion

        oldFiles = glob(join(self.umglobal.filesDir, "pre-%s" % UpdVersion)) + glob(join(self.umglobal.filesDir, "post-%s" % UpdVersion))
        for fle in oldFiles:
            remove(fle)
            self.log.write("Cleanup file: %s" % fle, "UM.deleteScripts", "debug")

    # Close the gui
    def on_windowMain_destroy(self, widget):
        # Close the app
        Gtk.main_quit()
Exemplo n.º 23
0
class USBCreator(object):
    def __init__(self):

        # Load window and widgets
        self.scriptName = basename(__file__)
        self.scriptDir = abspath(dirname(__file__))
        self.mediaDir = join(self.scriptDir, '../../share/usb-creator')
        self.builder = Gtk.Builder()
        self.builder.add_from_file(join(self.mediaDir, 'usb-creator.glade'))

        # Main window objects
        go = self.builder.get_object
        self.window = go("usb-creator")
        self.lblDevice = go("lblDevice")
        self.lblIso = go("lblIso")
        self.lblAvailable = go("lblAvailable")
        self.lblRequired = go("lblRequired")
        self.cmbDevice = go("cmbDevice")
        self.cmbDeviceHandler = ComboBoxHandler(self.cmbDevice)
        self.txtIso = go("txtIso")
        self.btnRefresh = go("btnRefresh")
        self.btnUnmount = go("btnUnmount")
        self.btnBrowseIso = go("btnBrowseIso")
        self.btnClear = go("btnClear")
        self.chkFormatDevice = go("chkFormatDevice")
        self.chkRepairDevice = go("chkRepairDevice")
        self.btnExecute = go("btnExecute")
        self.lblUsb = go("lblUsb")
        self.tvUsbIsos = go("tvUsbIsos")
        self.btnDelete = go("btnDelete")
        self.pbUsbCreator = go("pbUsbCreator")
        self.statusbar = go("statusbar")

        # Translations
        self.window.set_title(_("USB Creator"))
        self.lblDevice.set_label(_("Device"))
        self.lblUsb.set_label(_("USB"))
        self.available_text = _("Available")
        self.required_text = _("Required")
        self.chkFormatDevice.set_label(_("Format device"))
        self.chkFormatDevice.set_tooltip_text(
            _("Warning: all data will be lost"))
        self.chkRepairDevice.set_label(_("Repair device"))
        self.chkRepairDevice.set_tooltip_text(
            _("Tries to repair an unbootable USB"))
        self.btnExecute.set_label("_{}".format(_("Execute")))
        self.lblIso.set_label(_("ISO"))
        self.btnDelete.set_label("_{}".format(_("Delete")))
        self.btnRefresh.set_tooltip_text(_("Refresh device list"))
        self.btnUnmount.set_tooltip_text(_("Unmount device"))
        self.btnBrowseIso.set_tooltip_text(_("Browse for ISO file"))
        self.btnClear.set_tooltip_text(_("Clear the ISO field"))

        # Log lines to show: check string, percent done (0=pulse, appends last word in log line), show line (translatable)
        self.log_lines = []
        self.log_lines.append(
            ["partitioning usb", 5,
             _("Partitioning USB...")])
        self.log_lines.append(
            ["searching for bad blocks", 0,
             _("Searching for bad block")])
        self.log_lines.append(["installing", 15, _("Installing Grub...")])
        self.log_lines.append(["rsync", 25, _("Start copying ISO...")])
        self.log_lines.append(["left to copy", 0, _("kB left to copy:")])
        self.log_lines.append(["check hash", 85, _("Check hash of ISO...")])

        # Initiate variables
        self.device = {}
        self.device['path'] = ''
        self.device['mount'] = ''
        self.device['size'] = 0
        self.device['available'] = 0
        self.device["new_iso"] = ''
        self.device["new_iso_required"] = 0
        self.logos = self.get_logos()
        self.queue = Queue(-1)
        self.threads = {}
        self.htmlDir = join(self.mediaDir, "html")
        self.helpFile = join(self.get_language_dir(), "help.html")
        log = getoutput(
            "cat /usr/bin/usb-creator | grep 'LOG=' | cut -d'=' -f 2")
        self.log_file = log[0]
        self.log = Logger(self.log_file, addLogTime=False, maxSizeKB=5120)
        self.tvUsbIsosHandler = TreeViewHandler(self.tvUsbIsos)
        self.udisks2 = Udisks2()

        self.lblAvailable.set_label('')
        self.lblRequired.set_label('')

        # Connect builder signals and show window
        self.builder.connect_signals(self)
        self.window.show_all()

        # Get attached devices
        self.on_btnRefresh_clicked()

        # Init log
        init_log = ">>> Start USB Creator: {} <<<".format(datetime.now())
        self.log.write(init_log)

        # Version information
        self.version_text = _("Version")
        self.pck_version = getPackageVersion('usb-creator')
        self.set_statusbar_message("{}: {}".format(self.version_text,
                                                   self.pck_version))

    # ===============================================
    # Main window functions
    # ===============================================

    def on_btnExecute_clicked(self, widget):
        if exists(self.device["path"]):
            arguments = []
            arguments.append("-d {}".format(self.device["path"]))
            clear = self.chkFormatDevice.get_active()
            repair = self.chkRepairDevice.get_active()
            iso = self.device["new_iso"]
            iso_path = self.txtIso.get_text().strip()

            # ISO path does not exist
            if iso != iso_path:
                msg = _(
                    "Cannot add ISO from path: {}.\n"
                    "Please, remove the ISO path or browse for an existing ISO."
                )
                WarningDialog(self.btnExecute.get_label(),
                              msg.format(iso_path))
                return True

            # Check if there is enough space
            available = self.device["available"]
            if self.chkFormatDevice.get_active():
                available = self.device["size"]
            if (available) - self.device["new_iso_required"] < 0:
                msg = _(
                    "There is not enough space available on the pen drive.\n"
                    "Please, remove unneeded files before continuing.")
                WarningDialog(self.btnExecute.get_label(), msg)
                return True

            if clear:
                arguments.append("-f")
                arguments.append("-b")
            if repair:
                arguments.append("-r")
                arguments.append("-b")
                arguments.append("-g")
                # This should use the history file to get the original hash
                arguments.append("-s")
            if exists(iso):
                arguments.append("-i \"{}\"".format(iso))
                arguments.append("-s")

            cmd = "usb-creator {}".format(" ".join(arguments))
            self.log.write("Execute command: {}".format(cmd))
            self.exec_command(cmd)

    def on_btnDelete_clicked(self, widget):
        selected_isos = self.tvUsbIsosHandler.getToggledValues(toggleColNr=0,
                                                               valueColNr=2)
        if selected_isos:
            msg = _(
                "Are you sure you want to remove the selected ISO from the device?"
            )
            answer = QuestionDialog(self.btnDelete.get_label(), msg)
            if answer:
                for iso in selected_isos:
                    iso_path = join(self.device["mount"], iso)
                    if exists(iso_path):
                        os.remove(iso_path)
                        self.log.write("Remove ISO: {}".format(iso_path))
                shell_exec("usb-creator -d {} -g".format(self.device["path"]))
                self.on_btnRefresh_clicked()
                self.fill_treeview_usbcreator(self.device["mount"])

    def on_btnBrowseIso_clicked(self, widget):
        file_filter = Gtk.FileFilter()
        file_filter.set_name("ISO")
        file_filter.add_mime_type("application/x-cd-image")
        file_filter.add_pattern("*.iso")

        start_dir = dirname(self.txtIso.get_text().strip())
        if not exists(start_dir):
            start_dir = expanduser("~")

        iso = SelectFileDialog(title=_('Select ISO'),
                               start_directory=start_dir,
                               gtkFileFilter=file_filter).show()
        if iso is not None:
            self.log.write("Add ISO: {}".format(iso))
            self.txtIso.set_text(iso)

    def on_btnClear_clicked(self, widget):
        self.txtIso.set_text('')

    def on_txtIso_changed(self, widget=None):
        iso_path = self.txtIso.get_text().strip()
        if exists(iso_path):
            if isdir(iso_path):
                isos = glob(join(iso_path, '*.iso'))
                if isos:
                    required = 0
                    for iso in isos:
                        # Check if these ISOs overwrite current USB ISOs
                        check_usb_iso_size = 0
                        if not self.chkFormatDevice.get_active():
                            check_usb_iso = join(self.device["mount"],
                                                 basename(iso))
                            if exists(check_usb_iso):
                                check_usb_iso_size = self.get_iso_size(
                                    check_usb_iso)
                        required += (self.get_iso_size(iso) -
                                     check_usb_iso_size)
                    if required < 0:
                        required = 0
                    self.lblRequired.set_label("{}: {} MB".format(
                        self.required_text, int(required / 1024)))
                    # Save the info
                    self.device["new_iso"] = iso_path
                    self.device["new_iso_required"] = required
                    self.log.write("New ISO directory: {}, {}".format(
                        iso_path, required))
                else:
                    self.device["new_iso"] = ''
                    self.device["new_iso_required"] = 0
                    self.log.write(
                        "New ISO directory does not contain ISOs: {}".format(
                            iso_path))
            else:
                # Check if this ISO overwrites current USB ISO
                check_usb_iso_size = 0
                if not self.chkFormatDevice.get_active():
                    check_usb_iso = join(self.device["mount"],
                                         basename(iso_path))
                    if exists(check_usb_iso):
                        check_usb_iso_size = self.get_iso_size(check_usb_iso)
                required = (self.get_iso_size(iso_path) - check_usb_iso_size)
                self.lblRequired.set_label("{}: {} MB".format(
                    self.required_text, int(required / 1024)))
                # Save the info
                self.device["new_iso"] = iso_path
                self.device["new_iso_required"] = required
                self.log.write("New ISO: {}, {}".format(iso_path, required))
        else:
            self.device["new_iso"] = ''
            self.device["new_iso_required"] = 0
            self.lblRequired.set_text('')

    def on_btnRefresh_clicked(self, widget=None):
        self.udisks2.fill_devices()
        drives = self.udisks2.get_drives()
        self.cmbDeviceHandler.fillComboBox(drives, 0)

    def on_btnUnmount_clicked(self, widget):
        unmount_text = _("Unmount")
        device = self.device["path"]
        try:
            self.udisks2.unmount_drive(device)
            self.on_btnRefresh_clicked()
            msg = _("You can now safely remove the device.")
        except Exception as e:
            msg = _("Could not unmount the device.\n"
                    "Please unmount the device manually.")
            self.log.write("ERROR: %s" % str(e))
        MessageDialog(unmount_text, msg)

    def on_cmbDevice_changed(self, widget=None):
        drive_path = self.cmbDeviceHandler.getValue()
        device_paths = []
        if drive_path is not None:
            drive = self.udisks2.devices[drive_path]
            device_paths = self.udisks2.get_drive_device_paths(drive_path)
            device = ''
            if device_paths:
                device = device_paths[0]
            mount = ''
            size = 0
            available = 0

            # Get free size on USB
            size = drive['total_size']
            available = drive['free_size']
            if self.chkFormatDevice.get_active():
                available = size

            if device != '':
                mount = drive[device]['mount_point']
                if not exists(mount):
                    # Mount if not already mounted
                    try:
                        mount = self.udisks2.mount_device(device)
                    except Exception as e:
                        self.show_message(6)
                        self.log.write("ERROR: %s" % str(e))
                if exists(mount) and not self.chkFormatDevice.get_active():
                    size = drive[device]['total_size']
                    available = drive[device]['free_size']
                # This function can be called from on_chkFormatDevice_toggled
                if widget != self.chkFormatDevice:
                    self.chkFormatDevice.set_sensitive(True)
                    self.chkFormatDevice.set_active(False)
            else:
                # No partition: always format the device
                self.chkFormatDevice.set_active(True)
                self.chkFormatDevice.set_sensitive(False)

            self.chkRepairDevice.set_active(False)
            self.fill_treeview_usbcreator(mount)
            self.lblAvailable.set_label("{}: {} MB".format(
                self.available_text, int(available / 1024)))

            # Save the info
            self.device['path'] = drive_path
            self.device['mount'] = mount
            self.device['size'] = size
            self.device['available'] = available
            self.log.write("Selected device info: {}".format(self.device))

            # Update info
            iso_path = self.txtIso.get_text().strip()
            if iso_path != "" and exists(iso_path):
                self.on_txtIso_changed()
        else:
            self.fill_treeview_usbcreator()
            self.lblAvailable.set_label('')
            self.lblRequired.set_label('')
            self.txtIso.set_text('')
            self.device['path'] = ''
            self.device['mount'] = ''
            self.device['size'] = 0
            self.device['available'] = 0
            self.device["new_iso"] = ''
            self.device["new_iso_required"] = 0

    def on_chkFormatDevice_toggled(self, widget):
        # Recalculate available space and requied space
        self.on_cmbDevice_changed(widget)

    def on_btnHelp_clicked(self, widget):
        # Open the help file as the real user (not root)
        shell_exec("%s/open-as-user \"%s\"" % (self.scriptDir, self.helpFile))

    def fill_treeview_usbcreator(self, mount=''):
        isos_list = []
        # columns: checkbox, image (logo), device, driver
        column_types = ['bool', 'GdkPixbuf.Pixbuf', 'str', 'str']

        if exists(mount):
            isos = glob(join(mount, '*.iso'))
            for iso in isos:
                iso_name = basename(iso)
                iso_name_lower = iso_name.lower()
                iso_size = "{} MB".format(int(self.get_iso_size(iso) / 1024))
                iso_logo = ""
                for key, logo in list(self.logos.items()):
                    if key != "iso":
                        if key in iso_name_lower:
                            if len(logo) > len(iso_logo):
                                iso_logo = logo
                if iso_logo == "":
                    iso_logo = self.logos["iso"]
                self.log.write("ISO on {}: {}, {}, {}".format(
                    mount, iso_name, iso_size, iso_logo))
                isos_list.append([False, iso_logo, iso_name, iso_size])

        # Fill treeview
        self.tvUsbIsosHandler.fillTreeview(contentList=isos_list,
                                           columnTypesList=column_types)

    def exec_command(self, command):
        try:
            # Run the command in a separate thread
            self.set_buttons_state(False)
            name = 'cmd'
            t = ExecuteThreadedCommands([command], self.queue)
            self.threads[name] = t
            t.daemon = True
            t.start()
            self.queue.join()
            GObject.timeout_add(1000, self.check_thread, name)

        except Exception as detail:
            ErrorDialog(self.btnExecute.get_label(), detail)

    def check_thread(self, name):
        if self.threads[name].is_alive():
            self.set_progress()
            if not self.queue.empty():
                ret = self.queue.get()
                self.log.write("Queue returns: {}".format(ret), 'check_thread')
                self.queue.task_done()
                self.show_message(ret)
            return True

        # Thread is done
        self.log.write(">> Thread is done", 'check_thread')
        ret = 0
        if not self.queue.empty():
            ret = self.queue.get()
            self.queue.task_done()
        del self.threads[name]
        self.set_buttons_state(True)
        self.on_btnRefresh_clicked()
        self.fill_treeview_usbcreator(self.device["mount"])
        self.set_statusbar_message("{}: {}".format(self.version_text,
                                                   self.pck_version))
        self.show_message(ret)
        return False

    def set_buttons_state(self, enable):
        if not enable:
            # Disable buttons
            self.btnExecute.set_sensitive(False)
            self.btnDelete.set_sensitive(False)
            self.btnBrowseIso.set_sensitive(False)
            self.btnRefresh.set_sensitive(False)
            self.btnUnmount.set_sensitive(False)
            self.btnClear.set_sensitive(False)
            self.chkFormatDevice.set_sensitive(False)
            self.chkRepairDevice.set_sensitive(False)
            self.cmbDevice.set_sensitive(False)
            self.txtIso.set_sensitive(False)
        else:
            # Enable buttons and reset progress bar
            self.btnExecute.set_sensitive(True)
            self.btnDelete.set_sensitive(True)
            self.btnBrowseIso.set_sensitive(True)
            self.btnRefresh.set_sensitive(True)
            self.btnUnmount.set_sensitive(True)
            self.btnClear.set_sensitive(True)
            self.chkFormatDevice.set_sensitive(True)
            self.chkRepairDevice.set_sensitive(True)
            self.cmbDevice.set_sensitive(True)
            self.txtIso.set_sensitive(True)
            self.pbUsbCreator.set_fraction(0)

    def get_logos(self):
        logos_dict = {}
        logos_path = join(self.mediaDir, 'logos')
        logos = glob(join(logos_path, '*.png'))
        for logo in logos:
            key = splitext(basename(logo))[0]
            logos_dict[key] = logo
        return logos_dict

    def set_progress(self):
        if exists(self.log_file):
            msg = ''
            last_line = getoutput(
                "tail -50 {} | grep -v DEBUG | grep -v ==".format(
                    self.log_file))
            for line in reversed(last_line):
                # Check for session start line: that is the last line to check
                if ">>>>>" in line and "<<<<<" in line:
                    break
                for chk_line in self.log_lines:
                    if chk_line[0] in line.lower():
                        #print((line))
                        word = ''
                        if chk_line[1] == 0:
                            self.pbUsbCreator.pulse()
                            words = line.split(' ')
                            for word in reversed(words):
                                if word.strip() != '':
                                    break
                        else:
                            self.pbUsbCreator.set_fraction(
                                float(chk_line[1] / 100))
                        msg = "{} {}".format(chk_line[2], word)
                        break
                if msg != '':
                    break
            self.set_statusbar_message(msg)

    def set_statusbar_message(self, message):
        if message is not None:
            context = self.statusbar.get_context_id('message')
            self.statusbar.push(context, message)

    def get_iso_size(self, iso):
        # Returns kilobytes
        iso_info = os.stat(iso)
        if iso_info:
            return int(iso_info.st_size / 1024)
        return 0

    # Close the gui
    def on_usbcreator_destroy(self, widget):
        # Unmount devices of drive and power-off drive
        try:
            if exists(self.device['path']) and \
               exists(self.device['mount']):
                self.udisks2.unmount_drive(self.device['path'])
                self.udisks2.poweroff_drive(self.device['path'])
        except Exception as e:
            self.log.write("ERROR: %s" % str(e))
        # Close the app
        Gtk.main_quit()

    def show_message(self, cmdOutput):
        try:
            self.log.write("Command output: {}".format(cmdOutput),
                           'show_message')
            ret = int(cmdOutput)
            if ret > 1 and ret != 255:
                if ret == 1:
                    ErrorDialog(
                        self.btnExecute.get_label(),
                        _("Run this application with root permission."))
                elif ret == 2:
                    ErrorDialog(
                        self.btnExecute.get_label(),
                        _("Wrong arguments were passed to usb-creator."))
                elif ret == 3:
                    ErrorDialog(
                        self.btnExecute.get_label(),
                        _("The device was not found or no device was given."))
                elif ret == 4:
                    ErrorDialog(self.btnExecute.get_label(),
                                _("Given ISO path was not found."))
                elif ret == 5:
                    ErrorDialog(self.btnExecute.get_label(),
                                _("Device is in use by another application."))
                elif ret == 6:
                    ErrorDialog(self.btnExecute.get_label(),
                                _("Unable to mount the device."))
                elif ret == 7:
                    ErrorDialog(self.btnExecute.get_label(),
                                _("Hash mismatch."))
                elif ret == 8:
                    ErrorDialog(self.btnExecute.get_label(),
                                _("The device has no fat32 partition."))
                elif ret == 9:
                    ErrorDialog(self.btnExecute.get_label(),
                                _("The device has no bootloader installed."))
                elif ret == 10:
                    ErrorDialog(
                        self.btnExecute.get_label(),
                        _("There is not enough space available on the device.")
                    )
                elif ret == 11:
                    ErrorDialog(
                        self.btnExecute.get_label(),
                        _("Unable to guess distribution from ISO name.\n"
                          "Make sure you have the distribution name in the ISO name."
                          ))
                else:
                    msg = _(
                        "An unknown error accured.\n"
                        "Please, visit our forum for support: http://forums.solydxk.com"
                    )
                    ErrorDialog(self.window.get_title(), msg)
            else:
                msg = _("The USB was successfully written.")
                MessageDialog(self.window.get_title(), msg)
        except:
            ErrorDialog(self.btnExecute.get_label(), cmdOutput)

    # ===============================================
    # Language specific functions
    # ===============================================

    def get_language_dir(self):
        # First test if full locale directory exists, e.g. html/pt_BR,
        # otherwise perhaps at least the language is there, e.g. html/pt
        # and if that doesn't work, try html/pt_PT
        lang = self.get_current_language()
        path = join(self.htmlDir, lang)
        if not isdir(path):
            base_lang = lang.split('_')[0].lower()
            path = join(self.htmlDir, base_lang)
            if not isdir(path):
                path = join(self.htmlDir,
                            "{}_{}".format(base_lang, base_lang.upper()))
                if not isdir(path):
                    path = join(self.htmlDir, 'en')
        return path

    def get_current_language(self):
        lang = os.environ.get('LANG', 'US').split('.')[0]
        if lang == '':
            lang = 'en'
        return lang