def on_lock_button_clicked(self, button): logger.debug("in on_lock_button_clicked") loop = self.backing_volume.udisks_object.get_loop() if loop: # Ensure that the loop device is removed after locking the volume loop.call_set_autoclear_sync( True, GLib.Variant('a{sv}', {}), # options None) # cancellable try: self.unmount() self.backing_volume.lock() except GLib.Error as e: # Show a more helpful message for the known error cases if e.domain == "udisks-error-quark" and e.code == UDisks.Error.DEVICE_BUSY: body = _( "One or more applications are keeping the volume busy.") # Show a general error message and print the detailed, technical # message in unknown error cases else: # Translators: Don't translate {volume_name} or {error_message}, # they are placeholder and will be replaced. body = _( "Couldn't lock volume {volume_name}:\n{error_message}". format(volume_name=self.name, error_message=e.message)) self.manager.show_warning(_("Locking the volume failed"), body) return
def mount_cb(gio_volume: Gio.Volume, result: Gio.AsyncResult): logger.debug("in mount_cb") self.hide_spinner() try: gio_volume.mount_finish(result) except GLib.Error as e: if e.code == Gio.IOErrorEnum.FAILED_HANDLED: logger.warning("Couldn't unlock volume: %s:", e.message) return logger.exception(e) if "No key available with this passphrase" in e.message or \ "No device header detected with this passphrase" in e.message: title = _("Wrong passphrase or parameters") else: title = _("Error unlocking volume") # Translators: Don't translate {volume_name} or {error_message}, # they are placeholder and will be replaced. body = _("Couldn't unlock volume {volume_name}:\n{error_message}".format(volume_name=self.name, error_message=e.message)) self.manager.show_warning(title, body) return finally: self.manager.mount_op_lock.release() if open_after_unlock: # The GVolume now changed from the loop device to the dm device, so # by also updating the udisks object we change this volume from the # crypto backing loop device to the unlocked device-mapper device, # which we can then open self.udisks_object = self._find_udisks_object() self.open()
def on_add_file_container_button_clicked(self, button, data=None): path = self.choose_container_path() if path in self.container_list.backing_file_paths: self.show_warning( title=_("Container already added"), body=_("The file container %s should already be listed.") % path) return if path: self.unlock_file_container(path)
def __init__(self, application: Gtk.Application): self.udisks_client = UDisks.Client.new_sync() self.udisks_manager = self.udisks_client.get_manager() self.gio_volume_monitor = Gio.VolumeMonitor.get() self.gio_volume_monitor.connect("volume-changed", self.on_volume_changed) self.gio_volume_monitor.connect("volume-added", self.on_volume_added) self.gio_volume_monitor.connect("volume-removed", self.on_volume_removed) self.udev_client = GUdev.Client() self.mount_op_lock = Lock() self.builder = Gtk.Builder.new_from_file(MAIN_UI_FILE) self.builder.set_translation_domain(TRANSLATION_DOMAIN) self.builder.connect_signals(self) self.window = self.builder.get_object( "window") # type: Gtk.ApplicationWindow self.window.set_application(application) self.window.set_title(_("Unlock VeraCrypt Volumes")) self.container_list = ContainerList() self.device_list = DeviceList() containers_frame = self.builder.get_object("containers_frame") containers_frame.add(self.container_list.list_box) devices_frame = self.builder.get_object("devices_frame") devices_frame.add(self.device_list.list_box) self.add_tcrypt_volumes() logger.debug("showing window") self.window.show_all() self.window.present()
def name(self) -> str: """Short description for display to the user. The block device label or partition label, if any, plus the size""" block_label = self.udisks_object.get_block().props.id_label partition = self.udisks_object.get_partition() if block_label: # Translators: Don't translate {volume_label} or {volume_size}, # they are placeholders and will be replaced. return _("{volume_label} ({volume_size})").format(volume_label=block_label, volume_size=self.size_for_display) elif partition and partition.props.name: # Translators: Don't translate {partition_name} or {partition_size}, # they are placeholders and will be replaced. return _("{partition_name} ({partition_size})").format(partition_name=partition.props.name, partition_size=self.size_for_display) else: # Translators: Don't translate {volume_size}, it's a placeholder # and will be replaced. return _("{volume_size} Volume").format(volume_size=self.size_for_display)
def description(self) -> str: """Longer description for display to the user.""" if self.udisks_object.get_block().props.read_only: # Translators: Don't translate {volume_name}, it's a placeholder and # will be replaced. It needs to be present in the translated string. desc = _("{volume_name} (Read-Only)").format(volume_name=self.name) else: desc = self.name if self.partition_table_object and self.partition_table_object.get_loop( ): # This is a partition of a loop device, so lets include the backing file name # Translators: Don't translate {partition_name} and {container_path}, they # are placeholders and will be replaced. They need to be present # in the translated string. return _("{partition_name} in {container_path}").format( partition_name=desc, container_path=self.backing_file_name) elif self.is_file_container: # This is file container, lets include the file name # Translators: Don't translate {volume_name} and {path_to_file_container}, # they are placeholders and will be replaced. You should only have to translate # this string if it makes sense to reverse the order of the placeholders. return _("{volume_name} – {path_to_file_container}").format( volume_name=desc, path_to_file_container=self.backing_file_name) elif self.is_partition and self.drive_object: # This is a partition on a drive, lets include the drive name # Translators: Don't translate {partition_name} and {drive_name}, they # are placeholders and will be replaced. They need to be present # in the translated string. return _("{partition_name} on {drive_name}").format( partition_name=desc, drive_name=self.drive_name) elif self.drive_name: # This is probably an unpartitioned drive, so lets include the drive name # Translators: Don't translate {volume_name} and {drive_name}, # they are placeholders and will be replaced. You should only have to translate # this string if it makes sense to reverse the order of the placeholders. return _("{volume_name} – {drive_name}").format( volume_name=desc, drive_name=self.drive_name) else: return desc
def attach_file_container(self, path: str) -> Union[Volume, None]: logger.debug("attaching file %s. backing_file_paths: %s", path, self.container_list.backing_file_paths) warning = dict() try: fd = os.open(path, os.O_RDWR) except PermissionError as e: # Try opening read-only try: fd = os.open(path, os.O_RDONLY) warning["title"] = _("Container opened read-only") # Translators: Don't translate {path}, it's a placeholder and will be replaced. warning["body"] = _( "The file container {path} could not be opened with write access. " "It was opened read-only instead. You will not be able to modify the " "content of the container.\n" "{error_message}").format(path=path, error_message=str(e)) except PermissionError as e: self.show_warning(title=_("Error opening file"), body=str(e)) return None fd_list = Gio.UnixFDList() fd_list.append(fd) udisks_path, __ = self.udisks_manager.call_loop_setup_sync( GLib.Variant('h', 0), # fd index GLib.Variant('a{sv}', {}), # options fd_list, # the fd list None) # cancellable logger.debug("Created loop device %s", udisks_path) volume = self._wait_for_loop_setup(path) if volume: if warning: self.show_warning(title=warning["title"], body=warning["body"]) return volume elif not self._udisks_object_is_tcrypt(udisks_path): # Remove the loop device self.udisks_client.get_object(udisks_path).get_loop().call_delete( GLib.Variant('a{sv}', {}), # options None, # cancellable None, # callback None) # user data self.show_warning( title=_("Not a VeraCrypt container"), body=_( "The file %s does not seem to be a VeraCrypt container.") % path) else: self.show_warning( title=_("Failed to add container"), body= _("Could not add file container %s: Timeout while waiting for loop setup." "Please try using the <i>Disks</i> application instead.") % path)
class ContainerList(VolumeList): """Manages attached file containers""" placeholder_label = _("No file containers added") @property def backing_file_paths(self) -> List[str]: return [volume.backing_file_name for volume in self.volumes] def find_by_backing_file(self, path: str) -> Union[Volume, None]: for volume in self.volumes: if volume.backing_file_name == path: return volume raise VolumeNotFoundError()
def choose_container_path(self): dialog = Gtk.FileChooserDialog( _("Choose File Container"), self.window, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT)) result = dialog.run() if result != Gtk.ResponseType.ACCEPT: dialog.destroy() return path = dialog.get_filename() dialog.destroy() return path
class DeviceList(VolumeList): """Manages physically connected drives and partitions""" placeholder_label = _("No VeraCrypt devices detected")