Exemplo n.º 1
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.º 2
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
Exemplo n.º 3
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.º 4
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.º 5
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.º 6
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.º 7
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.º 8
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()