././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1713981306.3669415 udiskie-2.5.3/0000755000175100001770000000000014612243572012571 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/CHANGES.rst0000644000175100001770000003666314612243552014407 0ustar00runnerdockerCHANGELOG --------- 2.5.3 ----- Date 24.04.2024 - allow matching against a list of values in config rules (``device_config``) [thanks @dajt1725] - fix ``--terminal`` command line option (was not doing anything) [@dajt1725] - fix AttributeError when *python-keyring-keyutils* is installed instead of *keyutils* [thanks @bubbleguuum] 2.5.2 ~~~~~ Date: 27.01.2024 - drop external dependency on distutils (#278) - rename ``--notify-command`` to ``--event-hook`` to prevent misunderstandings (#279) 2.5.1 ~~~~~ Date: 23.11.2023 - fix default install location for bash completion scripts (#275) - fix zsh completion for device names with spaces (#253) 2.5.0 ~~~~~ Date: 03.10.2023 - add bash completions for CLI options (thanks @oersen) - updates for translations - added German translation - add bright outlines for wayland icons in dark themes - ignore snap loop devices by default - maintenance on CI workflows - fix KeyError when properties are invalidated multiple times (#272) 2.4.2 ~~~~~ Date: 01.03.2022 - add support for AyatanaAppIndicator3 - update Turkish translation - fix some documentation typos and errors 2.4.1 ~~~~~ Date: 02.02.2022 - setup loop devices in read-write mode if sufficient permissions are available 2.4.0 ~~~~~ Date: 30.11.2021 - add icon name that is specifically designed for tray panel - add Turkish translations (thanks @ersen0!) - try to detect installed translation files also in non-system installations - make it possible to override localedir manually using the ``TEXTDOMAINDIR`` environment variable - add guide on how to contribute translations and improve the Makefile 2.3.3 ~~~~~ Date: 05.03.2021 - fix: buttons in "device-added" notifications always mounting the last partition instead of the selected one 2.3.2 ~~~~~ Date: 27.01.2021 - fix incorrect use of wayland settings (appindicator+workarounds) within X session, if a wayland session is or was active under the same user (even if that was in a different login session) - fix incorrect display of image menu items under X due to caching 2.3.1 ~~~~~ Date: 27.01.2021 - add workaround for missing menu checkboxes on sway/wayland - add workaround for menu not showing up with proper size on sway/wayland - distribute icons within udiskie package (allows displaying icons in local/venv installations) - fix ignored coroutine return values, fixes e.g. program exit code 2.3.0 ~~~~~ Date: 22.01.2021 - don't swallow STDOUT for notify-commands - default to appindicator if tray is requested under wayland - don't auto-disable tray when running in "pure" wayland session - set window role on the password dialog 2.2.0 ~~~~~ Date: 07.05.2020 - add Italian translation - remove unneeded ``xdg`` from optional dependencies - fix a typo in Spanish translation and update russian translation files 2.1.1 ~~~~~ Date: 17.04.2020 - handle XDG_CONFIG_HOME variable without external pyxdg dependency - silence warning when using AppIndicator - make ``--appindicator`` sufficient to show icon (previously additionally required ``--tray``) - improve wording in password dialog "Remember" -> "Cache" - make some of the logging more concise - fix recursive adding/removing of some child devices: - fix incorrect root device detection for devices without Drive property (e.g. children of loop devices) - fix ``--detach`` option when applied to partitions of loop devices 2.1.0 ~~~~~ Date: 02.02.2020 - fix some typos (thanks @torstehu, #197) - change how device rules are evaluated: lookup undecided rules on parent device (fixes issue with filters not applying to subdevices of a matched device, see #198) - change builtin rules to not show loop devices with ``HintIgnore``, see #181 - change how is_external attribute is compute: use the value from udisks directly (fixes issue with is_external property not behaving as expected, see #185) - add 'skip' keyword for rules to skip evaluation of further rules on this device, and continue directly on the parent 2.0.4 ~~~~~ Date: 21.01.2020 - fix user commands that output non-utf8 data 2.0.3 ~~~~~ Date: 20.01.2020 - fix exception when using non-device parameters with DeviceCommand (e.g. in --notify-command) 2.0.2 ~~~~~ Date: 30.12.2019 - hotfix for automounting being broken since 2.0.0 2.0.1 ~~~~~ Date: 28.12.2019 - use ``importlib.resources`` directly on py3.7 and above, rather than requiring ``importlib_resources`` as additional dependency 2.0.0 ~~~~~ Date: 26.12.2019 - require python >= 3.5 - drop python2 support - drop udisks1 support - drop command line options corresponding to udisks version selection (-1, -2) - use py35's ``async def`` functions -- improving stack traces upon exception - internal refactoring and simplifications - add "show password" checkbox in password dialog 1.7.7 ~~~~~ Date: 17.02.2019 - keep password dialog always on top - fix stdin-based password prompts 1.7.6 ~~~~~ Date: 17.02.2019 - add russian translations (thanks @mr-GreyWolf) - fixed deprecation warnings in setup.py (thanks @sealj553) 1.7.5 ~~~~~ Date: 24.05.2018 - fix "NameError: 'Async' is not defined" when starting without tray icon 1.7.4 ~~~~~ Date: 17.05.2018 - fix attribute error when using options in udiskie-mount (#159) - fix tray in appindicator mode (#156) - possibly fix non-deterministic bugs (due to garbage collection) by keeping global reference to all active asyncs 1.7.3 ~~~~~ Date: 13.12.2017 - temporary workaround for udisks2.7 requiring ``filesystem-mount-system`` when trying to mount a LUKS cleartext device diretcly after unlocking 1.7.2 ~~~~~ Date: 18.10.2017 - officially deprecate udisks1 - officially deprecate python2 (want python >= 3.5) - fix startup crash on py2 - fix exception when inserting LUKS device if ``--password-prompt`` or udisks1 is used - fix minor problem with zsh autocompletion 1.7.1 ~~~~~ Date: 02.10.2017 - add an "open keyfile" button to the password dialog - add warning if mounting device without ntfs-3g (#143) - fix problem with LVM devices 1.7.0 ~~~~~ Date: 26.03.2017 - add joined ``device_config`` list in the config file - deprecate ``mount_options`` and ``ignore_device`` in favor of ``device_config`` - can configure ``automount`` per device using the new ``device_config`` [#107] - can configure keyfiles (requires udisks 2.6.4) [#66] - remove mailing list 1.6.2 ~~~~~ Date: 06.03.2017 - Show losetup/quit actions only in ex-menu - Show note in menu if no devices are found 1.6.1 ~~~~~ Date: 24.02.2017 - add format strings for the undocumented ``udiskie-info`` utility - speed up autocompletion times, for ``udiskie-mount`` by about a factor three, for ``udiskie-umount`` by about a factor 10 1.6.0 ~~~~~ Date: 22.02.2017 - fix crash on startup if config file is empty - add ``--notify-command`` to notify external programs (@jgraef) [#127] - can enable/disable automounting via special right-click menu [#98] - do not explicitly specify filesystem when mounting [#131] 1.5.1 ~~~~~ Date: 03.06.2016 - fix unicode issue that occurs on python2 when stdout is redirected (in particular for zsh autocompletion) 1.5.0 ~~~~~ Date: 03.06.2016 - make systray menu flat (use ``udiskie --tray --menu smart`` to request the old menu) [#119] - extend support for loop devices (requires UDisks2) [#101] - support ubuntu/unity AppIndicator backend for status icon [#59] - add basic utility to obtain info on block devices [#122] - add zsh completions [#26] - improve UI menu labels for devices - fix error when force-ejecting device [#121] - respect configured ignore-rules in ``udiskie-umount`` - fix error message for empty task lists [#123] 1.4.12 ~~~~~~ Date: 15.05.2016 - log INFO events to STDOUT (#112) - fix exception in notifications when action is not available. This concerns the retry button in the ``job_failed`` notification, as well as the browse action in the ``device_mounted`` notification (#117) - don't show 'browse' action in tray menu if unavailable 1.4.11 ~~~~~~ Date: 13.05.2016 - protect password dialog against garbage collection (which makes the invoking coroutine hang up and not unlock the device) - fix add_all/remove_all operations: only consider leaf/root devices within the handleable devices hierarchy: - avoid considering the same device twice (#114) - makes sure every handleable device is considered at all in remove_all 1.4.10 ~~~~~~ Date: 11.05.2016 - signal failing mount/unmount operations with non-zero exit codes (#110) - suppress notifications for unhandled devices - add rules for docker devices marking them unhandled to avoid excessive notifications (#113) - allow mounting/unmounting using UUID (#90) - prevent warning when starting without X session (#102) - can now match against wildcards in config rules (#49) 1.4.9 ~~~~~ Date: 02.04.2016 - add is_loop and loop_file properties for devices - fix recursive mounting of crypto devices (udiskie-mount) - prevent empty submenus from showing 1.4.8 ~~~~~ Date: 09.02.2016 - fix problem with setupscript if utf8 is not the default encoding - fix crash when starting without X - basic support for loop devices (must be enabled explicitly at this time) - fix handling of 2 more error cases 1.4.7 ~~~~~ Date: 04.01.2016 - fix typo that prevents the yaml config file from being used - fix problem with glib/gio gir API on slackware (olders versions?) - fix bug when changing device state (e.g. when formatting existing device or burning ISO file to device) - improve handling of race conditions with udisks1 backend - fix notifications for devices without labels 1.4.6 ~~~~~ Date: 28.12.2015 - cleanup recent bugfixes - close some gates for more py2/unicode related bugs 1.4.5 ~~~~~ Date: 24.12.2015 - fix another bug with unicode data on command line (py2) - slightly improve stack traces in async code - further decrease verbosity while removing devices 1.4.4 ~~~~~ Date: 24.12.2015 - fix too narrow dependency enforcement - make udiskie slightly less verbose in default mode 1.4.3 ~~~~~ Date: 24.12.2015 - fix bug with unicode data on python2 - fix bug due to event ordering in udisks1 - fix bug due to inavailability of device data at specific time 1.4.2 ~~~~~ Date: 22.12.2015 - fix regression in get_password_tty 1.4.1 ~~~~~ Date: 19.12.2015 - fix problem in SmartTray due to recent transition to async 1.4.0 ~~~~~ Date: 19.12.2015 - go async (with self-made async module for now, until gbulb becomes ready) - specify GTK/Notify versions to be imported (hence fix warnings and a problem for the tray icon resulting from accidentally importing GTK2) - add optional password caching 1.3.2 ~~~~~ - revert "respect the automount flag for devices" - make dependency on Gtk optional 1.3.1 ~~~~~ - use icon hints from udev settings in notifications - respect the automount flag for devices - don't fail if libnotify is not available 1.3.0 ~~~~~ - add actions to "Device added" notification - allow to configure which actions should be added to notifications 1.2.1 ~~~~~ - fix unicode issue in setup script - update license/copyright notices 1.2.0 ~~~~~ - use UDisks2 by default - add --password-prompt command line argument and config file entry 1.1.3 ~~~~~ - fix password prompt for GTK2 (tray is still broken for GTK2) - fix minor documentation issues 1.1.2 ~~~~~ - add key ``device_id`` for matching devices rather than only file systems - improve documentation regarding dependencies 1.1.1 ~~~~~ - fix careless error in man page 1.1.0 ~~~~~ - implemented internationalization - added spanish translation - allow to choose icons from a configurable list 1.0.4 ~~~~~ - compatibility with older version of pygobject (e.g. in Slackware 14.1) 1.0.3 ~~~~~ - handle exception if no notification service is installed 1.0.2 ~~~~~ - fix crash when calling udiskie mount/unmount utilites without udisks1 installed 1.0.1 ~~~~~ - fix crash when calling udiskie without having udisks1 installed (regression) 1.0.0 ~~~~~ - port to PyGObject, removing dependencies on pygtk, zenity, dbus-python, python-notify - use a PyGObject based password dialog - remove --password-prompt parameter - rename command line parameters - add negations for all command line parameters 0.8.0 ~~~~~ - remove the '--filters' parameter for good - change config format to YAML - change default config path to $XDG_CONFIG_HOME/udiskie/config.yml - separate ignore filters from mount option filters - allow to match multiple attributes against a device (AND-wise) - allow to overwrite udiskies default handleability settings - raise exception if --config file doesn't exist - add --options parameter for udiskie-mount - simplify local installations 0.7.0 ~~~~~ There are some backward incompatible changes, hence the version break: - command line parameter '-f'/'--filters' renamed to '-C'/'--config' - add sections in config file to disable individual mount notifications and set defaults for some program options (udisks version, prompt, etc) - refactor ``udiskie.cli``, ``udiskie.config`` and ``udiskie.tray`` - revert 'make udiskie a namespace package' - add 'Browse folder' action to tray menu - add 'Browse folder' action button to mount notifications - add '--no-automounter' command line option to disable automounting - add '--auto-tray' command line option to use a tray icon that automatically disappears when no actions are available - show notifications when devices dis-/appear (can be disabled via config file) - show 'id_label' in tray menu, if available (instead of mount path or device path) - add 'Job failed' notifications - add 'Retry' button to failed notifications - remove automatic retries to unlock LUKS partitions - pass only device name to external password prompt - add '--quiet' command line option - ignore devices ignored by udev rules 0.6.4 ~~~~~ - fix logging in setup.py - more verbose log messages (with time) when having -v on - fix mounting devices that are added as 'external' and later changed to 'internal' [udisks1] (applies to LUKS devices that are opened by an udev rule for example) 0.6.3 (bug fix) ~~~~~~~~~~~~~~~ - fix exception in Mounter.detach_device if unable to detach - fix force-detach for UDisks2 backend - automatically use UDisks2 if UDisks1 is not available - mount unlocked devices only once, removes error message on UDisks2 - mention __ignore__ in man page 0.6.2 (aesthetic) ~~~~~~~~~~~~~~~~~ - add custom icons for the context menu of the system tray widget 0.6.1 (bug fix) ~~~~~~~~~~~~~~~ - fix udisks2 external device detection bug: all devices were considered external when using ``Sniffer`` (as done in the udiskie-mount and udiskie-umount tools) 0.6.0 (udisks2 support, bug fix) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - cache device states to avoid some race conditions - show filesystem label in mount/unmount notifications - retry to unlock LUKS devices when wrong password was entered twice - show 'eject' only if media is available (udisks1 ejects only in this case) - (un-) mount/lock notifications shown even when operations failed - refactor internal API - experimental support for udisks2 0.5.3 (feature, bug fix) ~~~~~~~~~~~~~~~~~~~~~~~~ - add '__ignore__' config file option to prevent handling specific devices - delay notifications until termination of long operations 0.5.2 (tray icon) ~~~~~~~~~~~~~~~~~ - add tray icon (pygtk based) - eject / detach drives from command line 0.5.1 (mainly internal changes) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - use setuptools entry points to create the executables - make udiskie a namespace package 0.5.0 (LUKS support) ~~~~~~~~~~~~~~~~~~~~ - support for LUKS devices (using zenity for password prompt) - major refactoring - use setuptools as installer ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/COPYING0000644000175100001770000000214014612243552013617 0ustar00runnerdockerCopyright (c) 2010-2012 Byron Clark (c) 2013-2023 Thomas Gläßle Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/MANIFEST.in0000644000175100001770000000037614612243552014333 0ustar00runnerdockerinclude doc/*.txt include doc/asciidoc.conf include doc/Makefile graft completions recursive-include lang *.pot *.po recursive-include udiskie *.ui recursive-include udiskie/icons *.svg include CONTRIBUTORS COPYING LICENSE include README.rst CHANGES.rst ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1713981306.3669415 udiskie-2.5.3/PKG-INFO0000644000175100001770000005126214612243572013674 0ustar00runnerdockerMetadata-Version: 2.1 Name: udiskie Version: 2.5.3 Summary: Removable disk automounter for udisks Home-page: https://github.com/coldfix/udiskie Author: Byron Clark Author-email: byron@theclarkfamily.name Maintainer: Thomas Gläßle Maintainer-email: t_glaessle@gmx.de License: MIT Project-URL: Bug Tracker, https://github.com/coldfix/udiskie/issues Project-URL: Source Code, https://github.com/coldfix/udiskie Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Environment :: X11 Applications :: GTK Classifier: Intended Audience :: Developers Classifier: Intended Audience :: End Users/Desktop Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: License :: OSI Approved :: MIT License Classifier: Topic :: Desktop Environment Classifier: Topic :: Software Development Classifier: Topic :: System :: Filesystems Classifier: Topic :: System :: Hardware Classifier: Topic :: Utilities Requires-Python: >=3.5 Description-Content-Type: text/x-rst Provides-Extra: password-cache License-File: COPYING ======= udiskie ======= |Version| |License| |Translations| *udiskie* is a udisks2_ front-end that allows to manage removable media such as CDs or flash drives from userspace. |Screenshot| Its features include: - automount removable media - notifications - tray icon - command line tools for manual un-/mounting - LUKS encrypted devices - unlocking with keyfiles (requires udisks 2.6.4) - loop devices (mounting iso archives) - password caching (requires python keyutils 0.3) All features can be individually enabled or disabled. **NOTE:** support for python2 and udisks1 have been removed. If you need a version of udiskie that supports python2, please check out the ``1.7.X`` releases or the ``maint-1.7`` branch. .. _udisks2: https://www.freedesktop.org/wiki/Software/udisks - `Documentation`_ - Usage_ - Installation_ - `Debug Info`_ - Troubleshooting_ - FAQ_ - `Man page`_ - `Source Code`_ - `Latest Release`_ - `Issue Tracker`_ .. _Documentation: https://github.com/coldfix/udiskie/wiki .. _Usage: https://github.com/coldfix/udiskie/wiki/Usage .. _Installation: https://github.com/coldfix/udiskie/wiki/Installation .. _Debug Info: https://github.com/coldfix/udiskie/wiki/Debug-Info .. _Troubleshooting: https://github.com/coldfix/udiskie/wiki/Troubleshooting .. _FAQ: https://github.com/coldfix/udiskie/wiki/FAQ .. _Man Page: https://raw.githubusercontent.com/coldfix/udiskie/master/doc/udiskie.8.txt .. _Source Code: https://github.com/coldfix/udiskie .. _Latest Release: https://pypi.python.org/pypi/udiskie/ .. _Issue Tracker: https://github.com/coldfix/udiskie/issues .. _Roadmap: https://github.com/coldfix/udiskie/blob/master/HACKING.rst#roadmap .. Badges: .. |Version| image:: https://img.shields.io/pypi/v/udiskie.svg :target: https://pypi.python.org/pypi/udiskie :alt: Version .. |License| image:: https://img.shields.io/pypi/l/udiskie.svg :target: https://github.com/coldfix/udiskie/blob/master/COPYING :alt: License: MIT .. |Translations| image:: http://weblate.coldfix.de/widgets/udiskie/-/udiskie/svg-badge.svg :target: http://weblate.coldfix.de/engage/udiskie/ :alt: Translations .. |Screenshot| image:: https://raw.githubusercontent.com/coldfix/udiskie/master/screenshot.png :target: https://raw.githubusercontent.com/coldfix/udiskie/master/screenshot.png :alt: Screenshot Hacking ------- *udiskie* is developed on github_. Feel free to contribute patches as pull requests here. If you don't have nor want a github account, you can send me the relevant files via email. Further resources: - `UDisks1 API`_ - `UDisks2 API`_ - `PyGObject APIs`_ .. _github: https://github.com/coldfix/udiskie .. _PEP8: http://www.python.org/dev/peps/pep-0008/ .. _`unit tests`: http://docs.python.org/2/library/unittest.html .. _`UDisks1 API`: http://udisks.freedesktop.org/docs/1.0.5/ .. _`UDisks2 API`: http://udisks.freedesktop.org/docs/latest/ .. _`PyGObject APIs`: http://lazka.github.io/pgi-docs/index.html Translations ------------ Translations by users are always welcome. The corresponding files are in the `lang`_ subfolder. In order to create a new translation, find out the locale name for your language, e.g. ``es_ES`` for Spanish, and create a translation file in the ``lang`` folder as follows:: cd lang make es_ES.po or simply copy the `udiskie.pot`_ to a ``.po`` file with the name of the target locale and start editing. It's also best to fill in your name and email address. The translations may become outdated as udiskie changes. If you notice an outdated translation, please edit the corresponding ``.po`` file in submit a patch, even for very small changes. In order to test udiskie with your locally edited translation files, type (still from the ``lang`` folder):: export TEXTDOMAINDIR=$PWD/../build/locale export LANG=es_ES.UTF-8 make mo udiskie .. _lang: https://github.com/coldfix/udiskie/tree/master/lang .. _udiskie.pot: https://raw.githubusercontent.com/coldfix/udiskie/master/lang/udiskie.pot CHANGELOG --------- 2.5.3 ----- Date 24.04.2024 - allow matching against a list of values in config rules (``device_config``) [thanks @dajt1725] - fix ``--terminal`` command line option (was not doing anything) [@dajt1725] - fix AttributeError when *python-keyring-keyutils* is installed instead of *keyutils* [thanks @bubbleguuum] 2.5.2 ~~~~~ Date: 27.01.2024 - drop external dependency on distutils (#278) - rename ``--notify-command`` to ``--event-hook`` to prevent misunderstandings (#279) 2.5.1 ~~~~~ Date: 23.11.2023 - fix default install location for bash completion scripts (#275) - fix zsh completion for device names with spaces (#253) 2.5.0 ~~~~~ Date: 03.10.2023 - add bash completions for CLI options (thanks @oersen) - updates for translations - added German translation - add bright outlines for wayland icons in dark themes - ignore snap loop devices by default - maintenance on CI workflows - fix KeyError when properties are invalidated multiple times (#272) 2.4.2 ~~~~~ Date: 01.03.2022 - add support for AyatanaAppIndicator3 - update Turkish translation - fix some documentation typos and errors 2.4.1 ~~~~~ Date: 02.02.2022 - setup loop devices in read-write mode if sufficient permissions are available 2.4.0 ~~~~~ Date: 30.11.2021 - add icon name that is specifically designed for tray panel - add Turkish translations (thanks @ersen0!) - try to detect installed translation files also in non-system installations - make it possible to override localedir manually using the ``TEXTDOMAINDIR`` environment variable - add guide on how to contribute translations and improve the Makefile 2.3.3 ~~~~~ Date: 05.03.2021 - fix: buttons in "device-added" notifications always mounting the last partition instead of the selected one 2.3.2 ~~~~~ Date: 27.01.2021 - fix incorrect use of wayland settings (appindicator+workarounds) within X session, if a wayland session is or was active under the same user (even if that was in a different login session) - fix incorrect display of image menu items under X due to caching 2.3.1 ~~~~~ Date: 27.01.2021 - add workaround for missing menu checkboxes on sway/wayland - add workaround for menu not showing up with proper size on sway/wayland - distribute icons within udiskie package (allows displaying icons in local/venv installations) - fix ignored coroutine return values, fixes e.g. program exit code 2.3.0 ~~~~~ Date: 22.01.2021 - don't swallow STDOUT for notify-commands - default to appindicator if tray is requested under wayland - don't auto-disable tray when running in "pure" wayland session - set window role on the password dialog 2.2.0 ~~~~~ Date: 07.05.2020 - add Italian translation - remove unneeded ``xdg`` from optional dependencies - fix a typo in Spanish translation and update russian translation files 2.1.1 ~~~~~ Date: 17.04.2020 - handle XDG_CONFIG_HOME variable without external pyxdg dependency - silence warning when using AppIndicator - make ``--appindicator`` sufficient to show icon (previously additionally required ``--tray``) - improve wording in password dialog "Remember" -> "Cache" - make some of the logging more concise - fix recursive adding/removing of some child devices: - fix incorrect root device detection for devices without Drive property (e.g. children of loop devices) - fix ``--detach`` option when applied to partitions of loop devices 2.1.0 ~~~~~ Date: 02.02.2020 - fix some typos (thanks @torstehu, #197) - change how device rules are evaluated: lookup undecided rules on parent device (fixes issue with filters not applying to subdevices of a matched device, see #198) - change builtin rules to not show loop devices with ``HintIgnore``, see #181 - change how is_external attribute is compute: use the value from udisks directly (fixes issue with is_external property not behaving as expected, see #185) - add 'skip' keyword for rules to skip evaluation of further rules on this device, and continue directly on the parent 2.0.4 ~~~~~ Date: 21.01.2020 - fix user commands that output non-utf8 data 2.0.3 ~~~~~ Date: 20.01.2020 - fix exception when using non-device parameters with DeviceCommand (e.g. in --notify-command) 2.0.2 ~~~~~ Date: 30.12.2019 - hotfix for automounting being broken since 2.0.0 2.0.1 ~~~~~ Date: 28.12.2019 - use ``importlib.resources`` directly on py3.7 and above, rather than requiring ``importlib_resources`` as additional dependency 2.0.0 ~~~~~ Date: 26.12.2019 - require python >= 3.5 - drop python2 support - drop udisks1 support - drop command line options corresponding to udisks version selection (-1, -2) - use py35's ``async def`` functions -- improving stack traces upon exception - internal refactoring and simplifications - add "show password" checkbox in password dialog 1.7.7 ~~~~~ Date: 17.02.2019 - keep password dialog always on top - fix stdin-based password prompts 1.7.6 ~~~~~ Date: 17.02.2019 - add russian translations (thanks @mr-GreyWolf) - fixed deprecation warnings in setup.py (thanks @sealj553) 1.7.5 ~~~~~ Date: 24.05.2018 - fix "NameError: 'Async' is not defined" when starting without tray icon 1.7.4 ~~~~~ Date: 17.05.2018 - fix attribute error when using options in udiskie-mount (#159) - fix tray in appindicator mode (#156) - possibly fix non-deterministic bugs (due to garbage collection) by keeping global reference to all active asyncs 1.7.3 ~~~~~ Date: 13.12.2017 - temporary workaround for udisks2.7 requiring ``filesystem-mount-system`` when trying to mount a LUKS cleartext device diretcly after unlocking 1.7.2 ~~~~~ Date: 18.10.2017 - officially deprecate udisks1 - officially deprecate python2 (want python >= 3.5) - fix startup crash on py2 - fix exception when inserting LUKS device if ``--password-prompt`` or udisks1 is used - fix minor problem with zsh autocompletion 1.7.1 ~~~~~ Date: 02.10.2017 - add an "open keyfile" button to the password dialog - add warning if mounting device without ntfs-3g (#143) - fix problem with LVM devices 1.7.0 ~~~~~ Date: 26.03.2017 - add joined ``device_config`` list in the config file - deprecate ``mount_options`` and ``ignore_device`` in favor of ``device_config`` - can configure ``automount`` per device using the new ``device_config`` [#107] - can configure keyfiles (requires udisks 2.6.4) [#66] - remove mailing list 1.6.2 ~~~~~ Date: 06.03.2017 - Show losetup/quit actions only in ex-menu - Show note in menu if no devices are found 1.6.1 ~~~~~ Date: 24.02.2017 - add format strings for the undocumented ``udiskie-info`` utility - speed up autocompletion times, for ``udiskie-mount`` by about a factor three, for ``udiskie-umount`` by about a factor 10 1.6.0 ~~~~~ Date: 22.02.2017 - fix crash on startup if config file is empty - add ``--notify-command`` to notify external programs (@jgraef) [#127] - can enable/disable automounting via special right-click menu [#98] - do not explicitly specify filesystem when mounting [#131] 1.5.1 ~~~~~ Date: 03.06.2016 - fix unicode issue that occurs on python2 when stdout is redirected (in particular for zsh autocompletion) 1.5.0 ~~~~~ Date: 03.06.2016 - make systray menu flat (use ``udiskie --tray --menu smart`` to request the old menu) [#119] - extend support for loop devices (requires UDisks2) [#101] - support ubuntu/unity AppIndicator backend for status icon [#59] - add basic utility to obtain info on block devices [#122] - add zsh completions [#26] - improve UI menu labels for devices - fix error when force-ejecting device [#121] - respect configured ignore-rules in ``udiskie-umount`` - fix error message for empty task lists [#123] 1.4.12 ~~~~~~ Date: 15.05.2016 - log INFO events to STDOUT (#112) - fix exception in notifications when action is not available. This concerns the retry button in the ``job_failed`` notification, as well as the browse action in the ``device_mounted`` notification (#117) - don't show 'browse' action in tray menu if unavailable 1.4.11 ~~~~~~ Date: 13.05.2016 - protect password dialog against garbage collection (which makes the invoking coroutine hang up and not unlock the device) - fix add_all/remove_all operations: only consider leaf/root devices within the handleable devices hierarchy: - avoid considering the same device twice (#114) - makes sure every handleable device is considered at all in remove_all 1.4.10 ~~~~~~ Date: 11.05.2016 - signal failing mount/unmount operations with non-zero exit codes (#110) - suppress notifications for unhandled devices - add rules for docker devices marking them unhandled to avoid excessive notifications (#113) - allow mounting/unmounting using UUID (#90) - prevent warning when starting without X session (#102) - can now match against wildcards in config rules (#49) 1.4.9 ~~~~~ Date: 02.04.2016 - add is_loop and loop_file properties for devices - fix recursive mounting of crypto devices (udiskie-mount) - prevent empty submenus from showing 1.4.8 ~~~~~ Date: 09.02.2016 - fix problem with setupscript if utf8 is not the default encoding - fix crash when starting without X - basic support for loop devices (must be enabled explicitly at this time) - fix handling of 2 more error cases 1.4.7 ~~~~~ Date: 04.01.2016 - fix typo that prevents the yaml config file from being used - fix problem with glib/gio gir API on slackware (olders versions?) - fix bug when changing device state (e.g. when formatting existing device or burning ISO file to device) - improve handling of race conditions with udisks1 backend - fix notifications for devices without labels 1.4.6 ~~~~~ Date: 28.12.2015 - cleanup recent bugfixes - close some gates for more py2/unicode related bugs 1.4.5 ~~~~~ Date: 24.12.2015 - fix another bug with unicode data on command line (py2) - slightly improve stack traces in async code - further decrease verbosity while removing devices 1.4.4 ~~~~~ Date: 24.12.2015 - fix too narrow dependency enforcement - make udiskie slightly less verbose in default mode 1.4.3 ~~~~~ Date: 24.12.2015 - fix bug with unicode data on python2 - fix bug due to event ordering in udisks1 - fix bug due to inavailability of device data at specific time 1.4.2 ~~~~~ Date: 22.12.2015 - fix regression in get_password_tty 1.4.1 ~~~~~ Date: 19.12.2015 - fix problem in SmartTray due to recent transition to async 1.4.0 ~~~~~ Date: 19.12.2015 - go async (with self-made async module for now, until gbulb becomes ready) - specify GTK/Notify versions to be imported (hence fix warnings and a problem for the tray icon resulting from accidentally importing GTK2) - add optional password caching 1.3.2 ~~~~~ - revert "respect the automount flag for devices" - make dependency on Gtk optional 1.3.1 ~~~~~ - use icon hints from udev settings in notifications - respect the automount flag for devices - don't fail if libnotify is not available 1.3.0 ~~~~~ - add actions to "Device added" notification - allow to configure which actions should be added to notifications 1.2.1 ~~~~~ - fix unicode issue in setup script - update license/copyright notices 1.2.0 ~~~~~ - use UDisks2 by default - add --password-prompt command line argument and config file entry 1.1.3 ~~~~~ - fix password prompt for GTK2 (tray is still broken for GTK2) - fix minor documentation issues 1.1.2 ~~~~~ - add key ``device_id`` for matching devices rather than only file systems - improve documentation regarding dependencies 1.1.1 ~~~~~ - fix careless error in man page 1.1.0 ~~~~~ - implemented internationalization - added spanish translation - allow to choose icons from a configurable list 1.0.4 ~~~~~ - compatibility with older version of pygobject (e.g. in Slackware 14.1) 1.0.3 ~~~~~ - handle exception if no notification service is installed 1.0.2 ~~~~~ - fix crash when calling udiskie mount/unmount utilites without udisks1 installed 1.0.1 ~~~~~ - fix crash when calling udiskie without having udisks1 installed (regression) 1.0.0 ~~~~~ - port to PyGObject, removing dependencies on pygtk, zenity, dbus-python, python-notify - use a PyGObject based password dialog - remove --password-prompt parameter - rename command line parameters - add negations for all command line parameters 0.8.0 ~~~~~ - remove the '--filters' parameter for good - change config format to YAML - change default config path to $XDG_CONFIG_HOME/udiskie/config.yml - separate ignore filters from mount option filters - allow to match multiple attributes against a device (AND-wise) - allow to overwrite udiskies default handleability settings - raise exception if --config file doesn't exist - add --options parameter for udiskie-mount - simplify local installations 0.7.0 ~~~~~ There are some backward incompatible changes, hence the version break: - command line parameter '-f'/'--filters' renamed to '-C'/'--config' - add sections in config file to disable individual mount notifications and set defaults for some program options (udisks version, prompt, etc) - refactor ``udiskie.cli``, ``udiskie.config`` and ``udiskie.tray`` - revert 'make udiskie a namespace package' - add 'Browse folder' action to tray menu - add 'Browse folder' action button to mount notifications - add '--no-automounter' command line option to disable automounting - add '--auto-tray' command line option to use a tray icon that automatically disappears when no actions are available - show notifications when devices dis-/appear (can be disabled via config file) - show 'id_label' in tray menu, if available (instead of mount path or device path) - add 'Job failed' notifications - add 'Retry' button to failed notifications - remove automatic retries to unlock LUKS partitions - pass only device name to external password prompt - add '--quiet' command line option - ignore devices ignored by udev rules 0.6.4 ~~~~~ - fix logging in setup.py - more verbose log messages (with time) when having -v on - fix mounting devices that are added as 'external' and later changed to 'internal' [udisks1] (applies to LUKS devices that are opened by an udev rule for example) 0.6.3 (bug fix) ~~~~~~~~~~~~~~~ - fix exception in Mounter.detach_device if unable to detach - fix force-detach for UDisks2 backend - automatically use UDisks2 if UDisks1 is not available - mount unlocked devices only once, removes error message on UDisks2 - mention __ignore__ in man page 0.6.2 (aesthetic) ~~~~~~~~~~~~~~~~~ - add custom icons for the context menu of the system tray widget 0.6.1 (bug fix) ~~~~~~~~~~~~~~~ - fix udisks2 external device detection bug: all devices were considered external when using ``Sniffer`` (as done in the udiskie-mount and udiskie-umount tools) 0.6.0 (udisks2 support, bug fix) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - cache device states to avoid some race conditions - show filesystem label in mount/unmount notifications - retry to unlock LUKS devices when wrong password was entered twice - show 'eject' only if media is available (udisks1 ejects only in this case) - (un-) mount/lock notifications shown even when operations failed - refactor internal API - experimental support for udisks2 0.5.3 (feature, bug fix) ~~~~~~~~~~~~~~~~~~~~~~~~ - add '__ignore__' config file option to prevent handling specific devices - delay notifications until termination of long operations 0.5.2 (tray icon) ~~~~~~~~~~~~~~~~~ - add tray icon (pygtk based) - eject / detach drives from command line 0.5.1 (mainly internal changes) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - use setuptools entry points to create the executables - make udiskie a namespace package 0.5.0 (LUKS support) ~~~~~~~~~~~~~~~~~~~~ - support for LUKS devices (using zenity for password prompt) - major refactoring - use setuptools as installer ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/README.rst0000644000175100001770000000473514612243552014267 0ustar00runnerdocker======= udiskie ======= |Version| |License| |Translations| *udiskie* is a udisks2_ front-end that allows to manage removable media such as CDs or flash drives from userspace. |Screenshot| Its features include: - automount removable media - notifications - tray icon - command line tools for manual un-/mounting - LUKS encrypted devices - unlocking with keyfiles (requires udisks 2.6.4) - loop devices (mounting iso archives) - password caching (requires python keyutils 0.3) All features can be individually enabled or disabled. **NOTE:** support for python2 and udisks1 have been removed. If you need a version of udiskie that supports python2, please check out the ``1.7.X`` releases or the ``maint-1.7`` branch. .. _udisks2: https://www.freedesktop.org/wiki/Software/udisks - `Documentation`_ - Usage_ - Installation_ - `Debug Info`_ - Troubleshooting_ - FAQ_ - `Man page`_ - `Source Code`_ - `Latest Release`_ - `Issue Tracker`_ .. _Documentation: https://github.com/coldfix/udiskie/wiki .. _Usage: https://github.com/coldfix/udiskie/wiki/Usage .. _Installation: https://github.com/coldfix/udiskie/wiki/Installation .. _Debug Info: https://github.com/coldfix/udiskie/wiki/Debug-Info .. _Troubleshooting: https://github.com/coldfix/udiskie/wiki/Troubleshooting .. _FAQ: https://github.com/coldfix/udiskie/wiki/FAQ .. _Man Page: https://raw.githubusercontent.com/coldfix/udiskie/master/doc/udiskie.8.txt .. _Source Code: https://github.com/coldfix/udiskie .. _Latest Release: https://pypi.python.org/pypi/udiskie/ .. _Issue Tracker: https://github.com/coldfix/udiskie/issues .. _Roadmap: https://github.com/coldfix/udiskie/blob/master/HACKING.rst#roadmap .. Badges: .. |Version| image:: https://img.shields.io/pypi/v/udiskie.svg :target: https://pypi.python.org/pypi/udiskie :alt: Version .. |License| image:: https://img.shields.io/pypi/l/udiskie.svg :target: https://github.com/coldfix/udiskie/blob/master/COPYING :alt: License: MIT .. |Translations| image:: http://weblate.coldfix.de/widgets/udiskie/-/udiskie/svg-badge.svg :target: http://weblate.coldfix.de/engage/udiskie/ :alt: Translations .. |Screenshot| image:: https://raw.githubusercontent.com/coldfix/udiskie/master/screenshot.png :target: https://raw.githubusercontent.com/coldfix/udiskie/master/screenshot.png :alt: Screenshot ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1713981306.3629415 udiskie-2.5.3/completions/0000755000175100001770000000000014612243572015125 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1713981306.3629415 udiskie-2.5.3/completions/bash/0000755000175100001770000000000014612243572016042 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/completions/bash/udiskie0000644000175100001770000000254614612243552017427 0ustar00runnerdocker# udiskie completion _udiskie() { local cur prev opts _get_comp_words_by_ref cur prev opts=( '-h' '--help' '-V' '--version' '-v' '--verbose' '-q' '--quiet' '-c' '--config' '-C' '--no-config' '-a' '--automount' '-A' '--no-automount' '-n' '--notify' '-N' '--no-notify' '-t' '--tray' '-s' '--smart-tray' '-T' '--no-tray' '-m' '--menu' '-p' '--password-prompt' '-P' '--no-password-prompt' '-f' '--file-manager' '-F' '--no-file-manager' '--terminal' '--no-terminal' '--appindicator' '--no-appindicator' '--password-cache' '--no-password-cache' '--event-hook' '--no-event-hook' '--menu-checkbox-workaround' '--no-menu-checkbox-workaround' '--menu-update-workaround' '--no-menu-update-workaround' ) case "$prev" in -c|--config) COMPREPLY=($(compgen -f -- "$cur")) return ;; -f|--file-manager|--event-hook|-p|--password-prompt|--terminal) COMPREPLY=($(compgen -c -- "$cur")) return ;; -m|--menu) COMPREPLY=($(compgen -W "flat nested" -- "$cur")) return ;; --password-cache) return ;; esac COMPREPLY=($(compgen -W "${opts[*]}" -- "$cur")) } && complete -F _udiskie udiskie # ex:et ts=2 sw=2 ft=sh ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/completions/bash/udiskie-info0000644000175100001770000000121714612243552020352 0ustar00runnerdocker# udiskie-info completion _udiskie_info() { local cur prev devs opts _get_comp_words_by_ref cur prev devs=( $(udiskie-info -a) ) opts=( '-h' '--help' '-V' '--version' '-v' '--verbose' '-q' '--quiet' '-c' '--config' '-C' '--no-config' '-a' '--all' '-o' '--output' '-f' '--filter' ) case $prev in -c|--config) COMPREPLY=($(compgen -f -- "$cur")) return ;; -o|--output|-f|--filter) return ;; esac COMPREPLY=($(compgen -W "${devs[*]} ${opts[*]}" -- "$cur")) } && complete -F _udiskie_info udiskie-info # ex:et ts=2 sw=2 ft=sh ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/completions/bash/udiskie-mount0000644000175100001770000000151114612243552020556 0ustar00runnerdocker# udiskie-mount completion _udiskie_mount() { local cur prev devs opts _get_comp_words_by_ref cur prev devs=( $(udiskie-info -a) ) opts=( '-h' '--help' '-V' '--version' '-v' '--verbose' '-q' '--quiet' '-c' '--config' '-C' '--no-config' '-a' '--all' '-r' '--recursive' '-R' '--no-recursive' '-o' '--options' '-p' '--password-prompt' '-P' '--no-password-prompt' ) case $prev in -c|--config) COMPREPLY=($(compgen -f -- "$cur")) return ;; -o|--options) return ;; -p|--password-prompt) COMPREPLY=($(compgen -c -- "$cur")) return ;; esac COMPREPLY=($(compgen -W "${devs[*]} ${opts[*]}" -- "$cur")) } && complete -F _udiskie_mount udiskie-mount # ex:et ts=2 sw=2 ft=sh ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/completions/bash/udiskie-umount0000644000175100001770000000137014612243552020746 0ustar00runnerdocker# udiskie-umount completion _udiskie_umount() { local cur prev devs opts _get_comp_words_by_ref cur prev devs=( $(udiskie-info -a) ) opts=( '-h' '--help' '-V' '--version' '-v' '--verbose' '-q' '--quiet' '-c' '--config' '-C' '--no-config' '-a' '--all' '-d' '--detach' '-D' '--no-detach' '-e' '--eject' '-E' '--no-eject' '-f' '--force' '-F' '--no-force' '-l' '--lock' '-L' '--no-lock' ) case $prev in -c|--config) COMPREPLY=($(compgen -f -- "$cur")) return ;; esac COMPREPLY=($(compgen -W "${devs[*]} ${opts[*]}" -- "$cur")) } && complete -F _udiskie_umount udiskie-umount # ex:et ts=2 sw=2 ft=sh ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1713981306.3629415 udiskie-2.5.3/completions/zsh/0000755000175100001770000000000014612243572015731 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/completions/zsh/_udiskie0000644000175100001770000000360514612243552017452 0ustar00runnerdocker#compdef udiskie # vim: ft=zsh sts=2 sw=2 ts=2 function _udiskie { local context curcontext="$curcontext" line state ret=1 local args tmp typeset -A opt_args args=( '(- *)'{-h,--help}"[show help]" '(- *)'{-V,--version}"[show version]" '(-q)'{-v,--verbose}"[more output]" '(-v)'{-q,--quiet}"[less output]" '(-C)'{-c,--config}"[set config file]:file:_files" '(-c)'{-C,--no-config}"[don't use config file]" '(-A)'{-a,--automount}"[automount new devices]" '(-a)'{-A,--no-automount}"[disable automounting]" '(-N)'{-n,--notify}"[show popup notifications]" '(-n)'{-N,--no-notify}"[disable notifications]" '(--no-appindicator)'--appindicator"[use appindicator for status icon]" '(--appindicator)'--no-appindicator"[don't use appindicator]" '(-T -s)'{-t,--tray}"[show tray icon]" '(-T -t)'{-s,--smart-tray}"[auto hide tray icon]" '(-t -s)'{-T,--no-tray}"[disable tray icon]" {-m,--menu}"[set behaviour for tray menu]:traymenu:(flat nested)" '(--no-password-cache)'--password-cache"[set timeout for passwords of encrypted devices to N minutes]:minutes" '(--password-cache)'--no-password-cache"[don't cache passwords for encrypted devices]" '(-P)'{-p,--password-prompt}"[Command for password retrieval]:passwordialog:->pprompt" '(-p)'{-P,--no-password-prompt}"[Disable unlocking]" '(-F)'{-f,--file-manager}"[set program for browsing directories]:filemanager:_path_commands" '(-f)'{-F,--no-file-manager}"[disable browsing]" '(--no-event-hook)'--event-hook"[execute this command on events]:minutes" '(--event-hook)'--no-event-hook"[don't execute event handler]" ) _arguments -C -s "$args[@]" && ret=0 case $state in pprompt) _alternative \ 'builtins:builtin prompt:(builtin:tty builtin:gui)' \ 'commands:command name:_path_commands' \ && ret=0 ;; esac return ret } _udiskie "$@" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/completions/zsh/_udiskie-canonical_paths0000644000175100001770000001015214612243552022571 0ustar00runnerdocker#autoload # NOTE: # This file is a copy of the upstream _canonical_paths file that was modified # to fix a problem with device names that contain spaces (see #253). # The fix was taken from a discussion on the zsh-workers mailing list, see: # https://www.zsh.org/mla/workers/2022/msg01377.html # The original file is usually installed at: # /usr/share/zsh/functions/Completion/Unix/_canonical_paths # This completion function completes all paths given to it, and also tries to # offer completions which point to the same file as one of the paths given # (relative path when an absolute path is given, and vice versa; when ..'s are # present in the word to be completed, and some paths got from symlinks). # Usage: _udiskie-canonical_paths [-A var] [-N] [-MJV12onfX] tag desc [paths...] # -A, if specified, takes the paths from the array variable specified. Paths # can also be specified on the command line as shown above. -N, if specified, # prevents canonicalizing the paths given before using them for completion, in # case they are already so. `tag' and `desc' arguments are well, obvious :) In # addition, the options -M, -J, -V, -1, -2, -o, -n, -F, -x, -X are passed to # compadd. _udiskie-canonical_paths_add_paths () { # origpref = original prefix # expref = expanded prefix # curpref = current prefix # canpref = canonical prefix # rltrim = suffix to trim and readd local origpref=$1 expref rltrim curpref canpref subdir [[ $2 != add ]] && matches=() expref=${~origpref} 2>/dev/null [[ $origpref == (|*/). ]] && rltrim=. curpref=${${expref%$rltrim}:-./} canpref=$curpref:P [[ $curpref == */ && $canpref == *[^/] ]] && canpref+=/ canpref+=$rltrim [[ $expref == *[^/] && $canpref == */ ]] && origpref+=/ # Append to $matches the subset of $files that matches $canpref. if [[ $canpref == $origpref ]]; then # This codepath honours any -M matchspec parameters. () { local -a tmp_buffer compadd -A tmp_buffer "$__gopts[@]" -a files matches+=( "${(@)tmp_buffer/$canpref/$origpref}" ) } else # ### Ideally, this codepath would do what the 'if' above does, # ### but telling compadd to pretend the "word on the command line" # ### is ${"the word on the command line"/$origpref/$canpref}. # ### The following approximates that. matches+=(${(q)${(M)files:#$canpref*}/$canpref/$origpref}) fi for subdir in $expref?*(@); do _udiskie-canonical_paths_add_paths ${subdir/$expref/$origpref} add done } _udiskie-canonical_paths() { # The following parameters are used by callee functions: # __gopts # matches # files # (possibly others) local __index typeset -a __gopts __opts zparseopts -D -a __gopts M+: J+: V+: o+: 1 2 n F: x+: X+: A:=__opts N=__opts : ${1:=canonical-paths} ${2:=path} __index=$__opts[(I)-A] (( $__index )) && set -- $@ ${(P)__opts[__index+1]} local expl ret=1 tag=$1 desc=$2 shift 2 if ! zmodload -F zsh/stat b:zstat 2>/dev/null; then _wanted "$tag" expl "$desc" compadd $__gopts $@ && ret=0 return ret fi typeset REPLY typeset -a matches files if (( $__opts[(I)-N] )); then files=($@) else files+=($@:P) fi local base=$PREFIX typeset -i blimit _udiskie-canonical_paths_add_paths $base if [[ -z $base ]]; then _udiskie-canonical_paths_add_paths / add elif [[ $base == ..(/.(|.))#(|/) ]]; then # This style controls how many parent directory links (..) to chase searching # for possible completions. The default is 8. Note that this chasing is # triggered only when the user enters at least a .. and the path completed # contains only . or .. components. A value of 0 turns off .. link chasing # altogether. zstyle -s ":completion:${curcontext}:$tag" \ canonical-paths-back-limit blimit || blimit=8 if [[ $base != */ ]]; then [[ $base != *.. ]] && base+=. base+=/ fi until [[ $base.. -ef $base || blimit -le 0 ]]; do base+=../ _udiskie-canonical_paths_add_paths $base add blimit+=-1 done fi _wanted "$tag" expl "$desc" compadd $__gopts -Q -U -a matches && ret=0 return ret } _udiskie-canonical_paths "$@" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/completions/zsh/_udiskie-mount0000644000175100001770000000251514612243552020611 0ustar00runnerdocker#compdef udiskie-mount # vim: ft=zsh sts=2 sw=2 ts=2 function _udiskie-mount { local context curcontext="$curcontext" line state ret=1 local args tmp typeset -A opt_args args=( '(- *)'{-h,--help}"[show help]" '(- *)'{-V,--version}"[show version]" '(-q)'{-v,--verbose}"[more output]" '(-v)'{-q,--quiet}"[less output]" '(-C)'{-c,--config}"[set config file]:file:_files" '(-c)'{-C,--no-config}"[don't use config file]" '(*)'{-a,--all}"[unmount all devices]" '(-R)'{-r,--recursive}"[recursively add devices]" '(-r)'{-R,--no-recursive}"[disable recursive mounting]" {-o,--options}"[set filesystem options]:file system option" '(-P)'{-p,--password-prompt}"[Command for password retrieval]:passwordialog:->pprompt" '(-p)'{-P,--no-password-prompt}"[Disable unlocking]" '*:dev or dir:->udevordir' ) _arguments -C -s "$args[@]" && ret=0 case "$state" in pprompt) _alternative \ 'builtins:builtin prompt:(builtin:tty builtin:gui)' \ 'commands:command name:_path_commands' \ && ret=0 ;; udevordir) local dev_tmp mp_tmp dev_tmp=( $(udiskie-info -a) ) _alternative \ 'device-paths: device path:_udiskie-canonical_paths -A dev_tmp -N device-paths device\ path' \ && ret=0 ;; esac return ret } _udiskie-mount "$@" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/completions/zsh/_udiskie-umount0000644000175100001770000000373314612243552021001 0ustar00runnerdocker#compdef udiskie-umount # vim: ft=zsh sts=2 sw=2 ts=2 function _udiskie-umount { local context curcontext="$curcontext" line state ret=1 typeset -A opt_args args=( '(- *)'{-h,--help}"[show help]" '(- *)'{-V,--version}"[show version]" '(-q)'{-v,--verbose}"[more output]" '(-v)'{-q,--quiet}"[less output]" '(-C)'{-c,--config}"[set config file]:file:_files" '(-c)'{-C,--no-config}"[don't use config file]" '(*)'{-a,--all}"[unmount all devices]" '(-D)'{-d,--detach}"[detach device]" '(-d)'{-D,--no-detach}"[don't detach device]" '(-E)'{-e,--eject}"[eject device]" '(-e)'{-E,--no-eject}"[don't eject device]" '(-F)'{-f,--force}"[recursive unmounting]" '(-f)'{-F,--no-force}"[no recursive unmountinng]" '(-L)'{-l,--lock}"[lock device after unmounting]" '(-l)'{-L,--no-lock}"[don't lock device]" '*:dev or dir:->udevordir' ) _arguments -C -s "$args[@]" && ret=0 case "$state" in udevordir) local dev_tmp mp_tmp loop_tmp dev_detail # "${(@f)X}" means to use lines as separators dev_detail=( "${(@f)$(udiskie-info -a -o '{device_presentation}<:1:>{mount_path}<:2:>{is_filesystem}<:3:>{is_mounted}<:4:>{is_loop}<:5:>{loop_file}')}" ) # select: 'device_presentation' dev_tmp=( ${dev_detail%%<:1:>*} ) # filter: 'is_filesystem' and 'is_mounted' mp_tmp=( ${(M)dev_detail:#*<:2:>True<:3:>True<:4:>*} ) # select: 'mount_path' mp_tmp=( ${mp_tmp##*<:1:>} ) mp_tmp=( ${mp_tmp%%<:2:>*} ) # filter: 'is_loop' loop_tmp=( ${(M)dev_detail:#*<:3:>True<:5:>*} ) # select: 'mount_path' loop_tmp=( ${loop_tmp##*<:5:>} ) _alternative \ 'directories:mount point:_udiskie-canonical_paths -A mp_tmp -N directories mount\ point' \ 'device-paths: device path:_udiskie-canonical_paths -A dev_tmp -N device-paths device\ path' \ 'loop-files: loop file:_udiskie-canonical_paths -A loop_tmp -N loop-files loop\ file' \ && ret=0 ;; esac return ret } _udiskie-umount "$@" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1713981306.3629415 udiskie-2.5.3/doc/0000755000175100001770000000000014612243572013336 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/doc/Makefile0000644000175100001770000000022214612243552014770 0ustar00runnerdockerudiskie.8: udiskie.8.txt asciidoc.conf a2x --asciidoc-opts="-f asciidoc.conf" -f manpage -L udiskie.8.txt .PHONY: clean clean: rm -f udiskie.8 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/doc/asciidoc.conf0000644000175100001770000000316514612243552015766 0ustar00runnerdocker## linkman: macro # Inspired by/borrowed from the GIT source tree at Documentation/asciidoc.conf # # Usage: linkman:command[manpage-section] # # Note, {0} is the manpage section, while {target} is the command. # # Show man link as: (
); if section is defined, else just show # the command. [macros] (?su)[\\]?(?Plinkman):(?P\S*?)\[(?P.*?)\]= [attributes] asterisk=* plus=+ caret=^ startsb=[ endsb=] tilde=~ ifdef::backend-docbook[] [linkman-inlinemacro] {0%{target}} {0#} {0#{target}{0}} {0#} endif::backend-docbook[] ifdef::backend-docbook[] ifndef::docbook-xsl-172[] # "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this. # v1.72 breaks with this because it replaces dots not in roff requests. [listingblock] {title} | {title#} endif::docbook-xsl-172[] endif::backend-docbook[] ifdef::doctype-manpage[] ifdef::backend-docbook[] [header] template::[header-declarations] {pacman_date} {mantitle} {manvolnum} udiskie udiskie {manname} {manpurpose} endif::backend-docbook[] endif::doctype-manpage[] ifdef::backend-xhtml11[] [linkman-inlinemacro] {target}{0?({0})} endif::backend-xhtml11[] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/doc/udiskie.8.txt0000644000175100001770000002737714612243552015720 0ustar00runnerdocker///// vim:set ts=4 sw=4 syntax=asciidoc noet: ///// udiskie(8) ========== Name ---- udiskie - automounter for removable media Synopsis -------- 'udiskie' [OPTIONS] 'udiskie-mount' [OPTIONS] (-a | DEVICE...) 'udiskie-umount' [OPTIONS] (-a | PATH...) Description ----------- *udiskie* is an udisks2 front-end written in python. Its main purpose is automatically mounting removable media, such as CDs or flash drives. It has optional mount notifications, a GTK tray icon and user level CLIs for manual mount and unmount operations. The media will be mounted in a new directory under '/media' or '/run/media/USER/', using the device name if possible. Common options -------------- *-h, \--help*:: Show help message and exit. *-V, \--version*:: Show help message and exit. *-v, \--verbose*:: Verbose output. *-q, \--quiet*:: Quiet output. *-c FILE, \--config=FILE*:: Specify config file. *-C, \--no-config*:: Don't use any config file. Shared Mount and Daemon options ------------------------------- *-p COMMAND, \--password-prompt=COMMAND*:: Password retrieval command. The string is formatted with the device attributes as keyword arguments, e.g.: -p "zenity --entry --hide-text --text 'Enter password for {device_presentation}:'" *-P, \--no-password-prompt*:: Disable unlocking of LUKS devices. Daemon options -------------- *-a, \--automount*:: Enable automounting new devices (default). *-A, \--no-automount*:: Disable automounting new devices. *-n, \--notify*:: Enable pop-up notifications (default). *-N, \--no-notify*:: Disable pop-up notifications. *-t, \--tray*:: Show tray icon. *-s, \--smart-tray*:: Show tray icon that automatically hides when there is no action available. *-T, \--no-tray*:: Disable tray icon (default). *-f PROGRAM, \--file-manager=PROGRAM*:: Set program to open mounted directories. Default is \'+xdg-open+'. Pass an empty string to disable this feature. *-F, \--no-file-manager*:: Disable browsing. *--terminal=PROGRAM*:: Set terminal command line to open mounted directories. Default is none! Pass an empty string to disable this feature. *--no-terminal*:: Disable terminal action. *--appindicator*:: Use AppIndicator3 for the status icon. Use this on Ubuntu/Unity if no icon is shown. *--no-appindicator*:: Use Gtk.StatusIcon for the status icon (default). *--password-cache MINUTES*:: Cache passwords for LUKS partitions and set the timeout. *--no-password-cache*:: Never cache passwords (default). *--event-hook COMMAND*:: Command to execute on device events. Command string be formatted using the event name and the list of device attributes (see below), e.g.: --event-hook "zenity --info --text '{event}: {device_presentation}'" *--no-event-hook*:: No notify command (default). Mount options ------------- *-a, \--all*:: Mount all handled devices. *-r, \--recursive*:: Recursively mount cleartext partitions after unlocking a LUKS device. This will happen by default when running the udiskie daemon. *-R, \--no-recursive*:: Disable recursive mounting (default). *-o OPTIONS, \--options=OPTIONS*:: Set mount options. Unmount options --------------- *-a, \--all*:: Unmount all handled devices. *-d, \--detach*:: Detach drive by e.g. powering down its physical port. *-D, \--no-detach*:: Don't detach drive (default). *-e, \--eject*:: Eject media from the drive, e.g CDROM. *-E, \--no-eject*:: Don't eject media (default). *-f, \--force*:: Force removal (recursive unmounting). *-F, \--no-force*:: Don't force removal (default). *-l, \--lock*:: Lock device after unmounting (default). *-L, \--no-lock*:: Don't lock device. Example Usage[[EU]] ------------------- Start *udiskie* in '~/.xinitrc': udiskie & Unmount media and power down USB device: udiskie-umount --detach /media/Sticky Mount all media: udiskie-mount -a Mount '/dev/sdb1': udiskie-mount /dev/sdb1 Configuration ------------- The file '.config/udiskie/config.yml' can be used to configure defaults for command line parameters and customize further settings. The actual path may differ depending on '$XDG_CONFIG_HOME'. The file format is YAML, see https://en.wikipedia.org/wiki/YAML. If you don't want to install PyYAML, it is possible to use an equivalent JSON file with the name 'config.json' instead. ---------------------------------------------------------------------- # This is an example (nonsense) configuration file for udiskie. program_options: # Configure defaults for command line options tray: auto # [bool] Enable the tray icon. "auto" # means auto-hide the tray icon when # there are no handled devices. menu: flat # ["flat" | "nested"] Set the # systray menu behaviour. automount: false # [bool] Enable automatic mounting. notify: true # [bool] Enable notifications. password_cache: 30 # [int] Password cache in minutes. Caching is # disabled by default. It can be disabled # explicitly by setting it to false file_manager: xdg-open # [string] Set program to open directories. It will be invoked # with the folder path as its last command line argument. terminal: 'termite -d' # [string] Set terminal command line to open directories. It will be # invoked with the folder path as its last command line argument. password_prompt: ["gnome-keyring-query", "get", "{id_uuid}"] # [string|list] Set command to retrieve passwords. If specified # as a list it defines the ARGV array for the program call. If # specified as a string, it will be expanded in a shell-like # manner. Each string will be formatted using `str.format`. For a # list of device attributes, see below. The two special string values # "builtin:gui" and "builtin:tty" signify to use udiskie's # builtin password prompt. event_hook: "zenity --info --text '{event}: {device_presentation}'" # [string|list] Set command to be executed on any device event. # This is specified like `password_prompt`. device_config: # List of device option rules. Each item can match any combination of device # attributes. Additionally, it defines the resulting action (see below). # Any rule can contain multiple filters (AND) and multiple actions. # Only the first matching rule that defines a given action is used. # The rules defined here are simply prepended to the builtin device rules, # so that it is possible to completely overwrite the defaults by specifying # a catch-all rule (i.e. a rule without device attributes). # Filter rules can be passed a list of values, in which case the rule is matched # if the attribute matches any of the values in the list. - device_file: /dev/dm-5 # [filter] ignore: false # [action] never ignore this device - id_uuid: # [filter] match by device UUID - 9d53-13ba # This rule matches on either of these uids - 8675-309a options: [noexec, nodev] # [action] mount options can be given as list ignore: false # [action] never ignore this device (even if fs=FAT) automount: false # [action] do not automount this device - id_type: vfat # [filter] match file system type ignore: true # [action] ignore all FAT devices - id_type: ntfs # [filter] (optional) skip: true # [action] skip all further (even builtin) rules # for all matched devices, and resolve action result # on parent device - ignore: True # never mount/unmount or even show this in the GUI automount: False # show but do not automount this device options: [] # additional options to be passed when mounting mount_options: # [deprecated] do not use ignore_device: # [deprecated] do not use notifications: # Customize which notifications are shown for how long. Possible # values are: # positive number timeout in seconds # false disable # -1 use the libnotify default timeout timeout: 1.5 # set the default for all notifications # Specify only if you want to overwrite the default: device_mounted: 5 # mount notification device_unmounted: false # unmount notification device_added: false # device has appeared device_removed: false # device has disappeared device_unlocked: -1 # encrypted device was unlocked device_locked: -1 # encrypted device was locked job_failed: -1 # mount/unlock/.. has failed quickmenu_actions: [mount, unmount, unlock, terminal, detach, delete] # List of actions to be shown in the quickmenu or the special value 'all'. # The quickmenu is shown on left-click if using flat menu type. notification_actions: # Define which actions should be shown on notifications. Note that there # are currently only a limited set of actions available for each # notification. Events that are not explicitly specified show the default # set of actions. Specify an empty list if you don't want to see any # notification for the specified event: device_mounted: [browse] device_added: [mount] icon_names: # Customize the icon set used by the tray widget. Each entry # specifies a list of icon names. The first installed icon from # that list will be used. media: [drive-removable-media, media-optical] browse: [document-open, folder-open] terminal: [terminal, terminator, xfce-terminal] mount: [udiskie-mount] unmount: [udiskie-unmount] unlock: [udiskie-unlock] lock: [udiskie-lock] eject: [udiskie-eject, media-eject] detach: [udiskie-detach] delete: [udiskie-eject] quit: [application-exit] ---------------------------------------------------------------------- All keys are optional. Reasonable defaults are used if you leave them unspecified. Device attributes ----------------- Some of the config entries make use of Device attributes. The following list of attributes is currently available, but there is no guarantee that they will remain available: Attribute Hint/Example is_drive is_block is_partition_table is_partition is_filesystem is_luks is_loop is_toplevel is_detachable is_ejectable has_media device_file block device path, e.g. "/dev/sdb1" device_presentation display string, e.g. "/dev/sdb1" device_size block device size device_id unique, persistent device identifier id_usage E.g. "filesystem" or "crypto" is_crypto is_ignored id_type E.g. "ext4" or "crypto_LUKS" id_label device label id_uuid device UUID is_luks_cleartext is_external udisks flag HintSystem=false is_systeminternal udisks flag HintSystem=true is_mounted mount_paths list of mount paths mount_path any mount path is_unlocked in_use device or any of its children mounted should_automount ui_label loop_file file backing the loop device setup_by_uid user that setup the loop device autoclear automatically delete loop device after use symlinks drive_model drive_vendor drive_label ui_device_label ui_device_presentation ui_id_label ui_id_uuid See Also -------- linkman:udisks[1] https://www.freedesktop.org/wiki/Software/udisks/ Contact ------- You can use the github issues to report any issues you encounter, ask general questions or suggest new features. If you don't have or like github, you can contact me by email: https://github.com/coldfix/udiskie/issues thomas@coldfix.de ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1713981306.3629415 udiskie-2.5.3/lang/0000755000175100001770000000000014612243572013512 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/lang/de.po0000644000175100001770000004376214612243552014454 0ustar00runnerdocker# SOME DESCRIPTIVE TITLE. # Copyright (C) 2022 Thomas Gläßle # This file is distributed under the same license as the udiskie package. # FIRST AUTHOR , 2022. # msgid "" msgstr "" "Project-Id-Version: udiskie 2.4.2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-04-16 21:10+0000\n" "PO-Revision-Date: 2022-04-18 00:28+0000\n" "Last-Translator: Thomas G. \n" "Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.11.2\n" #: ../udiskie/cli.py:46 #, python-brace-format msgid "These options are mutually exclusive: {0}" msgstr "Diese Optionen schließen sich aus: {0}" #: ../udiskie/cli.py:119 msgid "" "\n" " Note, that the options in the individual groups are mutually exclusive.\n" "\n" " The config file can be a JSON or preferably a YAML file. For an\n" " example, see the MAN page (or doc/udiskie.8.txt in the repository).\n" " " msgstr "" "\n" " Optionen innerhalb der Untergruppen schließen sich aus.\n" "\n" " Die Konfigurationsdatei kann JSON oder vorzugsweise YAML sein.\n" " Ein Beispiel findet sich auf der Manpage (oder in doc/udiskie.8.txt im " "Repository).\n" " " #: ../udiskie/cli.py:139 #, python-format msgid "%(message)s" msgstr "%(message)s" #: ../udiskie/cli.py:141 #, python-format msgid "%(levelname)s [%(asctime)s] %(name)s: %(message)s" msgstr "%(levelname)s [%(asctime)s] %(name)s: %(message)s" #: ../udiskie/cli.py:386 msgid "" "Typelib for 'libnotify' is not available. Possible causes include:\n" "\t- libnotify is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- libnotify was built with introspection disabled\n" "\n" "Starting udiskie without notifications." msgstr "" "Typbibliothek für 'libnotify' ist nicht verfügbar. Mögliche Gründe können " "sein:\n" "\t- libnotify ist nicht installiert\n" "\t- die Typbibliothek wird durch ein separates Paket bereitgestellt\n" "\t- libnotify wurde ohne Introspektion kompiliert\n" "\n" "udiskie wird ohne Benachrichtigungen gestartet." #: ../udiskie/cli.py:400 msgid "" "Not run within X or Wayland session.\n" "Starting udiskie without tray icon.\n" msgstr "" "Es wurde keine X oder Wayland Sitzung erkannt.\n" "udiskie wird daher ohne Trayicon gestartet.\n" #: ../udiskie/cli.py:407 msgid "" "Typelib for 'Gtk 3.0' is not available. Possible causes include:\n" "\t- GTK3 is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- GTK3 was built with introspection disabled\n" "Starting udiskie without tray icon.\n" msgstr "" "Typbibliothek für 'Gtk 3.0' ist nicht verfügbar. Mögliche Gründen können " "sein:\n" "\t- GTK3 ist nicht installiert\n" "\t- die Typbibliothek wird durch ein separates Paket bereitgestellt\n" "\t- GTK3 wurde ohne Introspektion kompiliert\n" "udiskie wird ohne Trayicon gestartet.\n" #: ../udiskie/cli.py:417 msgid "" "Typelib for 'AppIndicator3 0.1' is not available. Possible causes include:\n" "\t- libappindicator is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- it was built with introspection disabled\n" "Starting udiskie without appindicator icon.\n" msgstr "" "Typbibliothek für 'AppIndicator3 0.1' ist nicht verfügbar. Mögliche Gründe " "können sein:\n" "\t- libappindicator ist nicht installiert\n" "\t- die Typbibliothek wird durch ein separates Packet bereitgestellt\n" "\t- libappindicator wurde ohne Introspektion kompiliert\n" "udiskie wird ohne Benachrichtigungssymbol gestartet.\n" #: ../udiskie/cli.py:436 msgid "" "The 'notify_command' option was renamed to 'event_hook'. The old name still " "works, but may be removed in a future version. Please change your command " "line and config to use the new name." msgstr "" #: ../udiskie/cli.py:443 msgid "Ignoring 'notify_command' in favor of 'event_hook'." msgstr "" #: ../udiskie/config.py:129 msgid "Unknown matching attribute: {!r}" msgstr "Unbekannte Vergleichseigenschaft: {!r}" #: ../udiskie/config.py:131 #, python-brace-format msgid "new rule: {0}" msgstr "neue Regel: {0}" #: ../udiskie/config.py:134 #, python-brace-format msgid "{0} -> {1}" msgstr "{0} -> {1}" #: ../udiskie/config.py:153 #, python-brace-format msgid "{0} matched {1}" msgstr "{0} passt auf {1}" #: ../udiskie/config.py:230 #, python-brace-format msgid "Failed to read config file: {0}" msgstr "Fehler beim Lesen der Konfigurationsdatei: {0}" #: ../udiskie/config.py:233 msgid "Failed to read {0!r}: {1}" msgstr "Fehler beim Lesen von {0!r}: {1}" #: ../udiskie/depend.py:59 msgid "" "Missing runtime dependency GTK 3. Falling back to GTK 2 for password prompt" msgstr "" "Fehlende Laufzeitabhängigkeit an GTK3. Als Ausweichlösung wird nun GTK2 für " "die Passworteingabe verwendet" #: ../udiskie/depend.py:65 msgid "X server not connected!" msgstr "X Server ist nicht verbunden!" #: ../udiskie/mount.py:29 #, python-brace-format msgid "failed to {0} {1}: {2}" msgstr "Fehler bei {0} {1}: {2}" #: ../udiskie/mount.py:117 #, python-brace-format msgid "not browsing {0}: not mounted" msgstr "Anzeigen von {0} abgebrochen: Laufwerk nicht eingelegt" #: ../udiskie/mount.py:120 #, python-brace-format msgid "not browsing {0}: no program" msgstr "Anzeigen von {0} abgebrochen: kein Anzeigeprogramm konfiguriert" #: ../udiskie/mount.py:122 ../udiskie/mount.py:142 #, python-brace-format msgid "opening {0} on {0.mount_paths[0]}" msgstr "{0} wird im Pfad {0.mount_paths[0]} geöffnet" #: ../udiskie/mount.py:124 ../udiskie/mount.py:144 #, python-brace-format msgid "opened {0} on {0.mount_paths[0]}" msgstr "{0} wurde im Pfad {0.mount_paths[0]} geöffnet" #: ../udiskie/mount.py:137 #, python-brace-format msgid "not opening terminal {0}: not mounted" msgstr "Öffnen eines Terminals für {0} abgebrochen: nicht eingehängt" #: ../udiskie/mount.py:140 #, python-brace-format msgid "not opening terminal {0}: no program" msgstr "" "Öffnen eines Terminals für {0} abgebrochen: Programm nicht konfiguriert" #: ../udiskie/mount.py:158 #, python-brace-format msgid "not mounting {0}: unhandled device" msgstr "Einhängen von {0} abgebrochen: nicht betrachtetes Laufwerk" #: ../udiskie/mount.py:161 #, python-brace-format msgid "not mounting {0}: already mounted" msgstr "Einhängen von {0} abgebrochen: bereits eingehängt" #: ../udiskie/mount.py:165 #, python-brace-format msgid "mounting {0} with {1}" msgstr "Einhängen von {0} mit {1}" #: ../udiskie/mount.py:168 #, python-brace-format msgid "mounted {0} on {1}" msgstr "{0} wurde unter {1} eingehängt" #: ../udiskie/mount.py:174 msgid "" "Mounting NTFS device with default driver.\n" "Please install 'ntfs-3g' if you experience problems or the device is " "readonly." msgstr "" "Ein NTFS Laufwerk wird mit dem Standardtreiber eingehängt.\n" "Bitte installieren Sie 'ntfs-3g' falls Sie Probleme feststellen oder das " "Gerät schreibgeschützt ist." #: ../udiskie/mount.py:188 #, python-brace-format msgid "not unmounting {0}: unhandled device" msgstr "Aushängen von {0} abgebrochen: nicht betrachtetes Laufwerk" #: ../udiskie/mount.py:191 #, python-brace-format msgid "not unmounting {0}: not mounted" msgstr "Aushängen von {0} abgebrochen: nicht eingehängt" #: ../udiskie/mount.py:193 #, python-brace-format msgid "unmounting {0}" msgstr "{0} wird ausgehängt" #: ../udiskie/mount.py:195 #, python-brace-format msgid "unmounted {0}" msgstr "{0} wurde ausgehängt" #: ../udiskie/mount.py:209 #, python-brace-format msgid "not unlocking {0}: unhandled device" msgstr "Entsperren von {0} abgebrochen: nicht betrachtetes Laufwerk" #: ../udiskie/mount.py:212 #, python-brace-format msgid "not unlocking {0}: already unlocked" msgstr "Entsperren von {0} abgebrochen: bereits entsperrt" #: ../udiskie/mount.py:215 #, python-brace-format msgid "not unlocking {0}: no password prompt" msgstr "Entsperren von {0} abgebrochen: kein Passworteingabe-Programm" #: ../udiskie/mount.py:229 #, python-brace-format msgid "not unlocking {0}: cancelled by user" msgstr "Entsperren von {0} abgebrochen: durch Benutzer abgebrochen" #: ../udiskie/mount.py:234 #, python-brace-format msgid "unlocking {0} using keyfile" msgstr "{0} wird durch Schlüsseldatei entsperrt" #: ../udiskie/mount.py:237 #, python-brace-format msgid "unlocking {0}" msgstr "{0} wird entsperrt" #: ../udiskie/mount.py:240 #, python-brace-format msgid "unlocked {0}" msgstr "{0} wurde entsperrt" #: ../udiskie/mount.py:249 #, python-brace-format msgid "no cached key for {0}" msgstr "Kein gespeicherter Schlüssel für {0}" #: ../udiskie/mount.py:251 #, python-brace-format msgid "unlocking {0} using cached password" msgstr "{0} wird mit gespeichertem Passwort entsperrt" #: ../udiskie/mount.py:255 #, python-brace-format msgid "failed to unlock {0} using cached password" msgstr "Fehler beim Entsperren von {0} mit gespeichertem Passwort" #: ../udiskie/mount.py:258 #, python-brace-format msgid "unlocked {0} using cached password" msgstr "{0} wurde mit gespeichertem Passwort entsperrt" #: ../udiskie/mount.py:266 msgid "No matching keyfile rule for {}." msgstr "Keine passende Schlüsseldatei Regel für {}." #: ../udiskie/mount.py:272 #, python-brace-format msgid "keyfile for {0} not found: {1}" msgstr "Schlüsseldatei für {0} nicht gefunden: {1}" #: ../udiskie/mount.py:274 #, python-brace-format msgid "unlocking {0} using keyfile {1}" msgstr "{0} wird durch Schlüsseldatei {1} entsperrt" #: ../udiskie/mount.py:278 #, python-brace-format msgid "failed to unlock {0} using keyfile" msgstr "Fehler beim Entsperren von {0} mit Schlüsseldatei" #: ../udiskie/mount.py:281 #, python-brace-format msgid "unlocked {0} using keyfile" msgstr "{0} wurde mit Schlüsseldatei entsperrt" #: ../udiskie/mount.py:307 #, python-brace-format msgid "not locking {0}: unhandled device" msgstr "Sperren von {0} abgebrochen: nicht betrachtetes Laufwerk" #: ../udiskie/mount.py:310 #, python-brace-format msgid "not locking {0}: not unlocked" msgstr "Sperren von {0} abgebrochen: nicht entsperrt" #: ../udiskie/mount.py:312 #, python-brace-format msgid "locking {0}" msgstr "{0} wird gesperrt" #: ../udiskie/mount.py:314 #, python-brace-format msgid "locked {0}" msgstr "{0} wurde gesperrt" #: ../udiskie/mount.py:351 ../udiskie/mount.py:394 #, python-brace-format msgid "not adding {0}: unhandled device" msgstr "Hinzufügen von {0} abgebrochen: nicht betrachtetes Laufwerk" #: ../udiskie/mount.py:430 ../udiskie/mount.py:480 #, python-brace-format msgid "not removing {0}: unhandled device" msgstr "Entfernen von {0} abgebrochen: nicht betrachtetes Laufwerk" #: ../udiskie/mount.py:505 #, python-brace-format msgid "not ejecting {0}: unhandled device" msgstr "Auswerfen von {0} abgebrochen: nicht betrachtetes Laufwerk" #: ../udiskie/mount.py:509 #, python-brace-format msgid "not ejecting {0}: drive not ejectable" msgstr "Auswerfen von {0} abgebrochen: nicht auswerfbar" #: ../udiskie/mount.py:515 #, python-brace-format msgid "ejecting {0}" msgstr "{0} wird ausgeworfen" #: ../udiskie/mount.py:517 #, python-brace-format msgid "ejected {0}" msgstr "{0} wurde ausgeworfen" #: ../udiskie/mount.py:531 #, python-brace-format msgid "not detaching {0}: unhandled device" msgstr "Abschalten von {0} abgebrochen: nicht betrachtetes Laufwerk" #: ../udiskie/mount.py:535 #, python-brace-format msgid "not detaching {0}: drive not detachable" msgstr "Abschalten von {0} abgebrochen: nicht abschaltbar" #: ../udiskie/mount.py:539 #, python-brace-format msgid "detaching {0}" msgstr "{0} wird abgeschalten" #: ../udiskie/mount.py:544 #, python-brace-format msgid "detached {0}" msgstr "{0} wurde abgeschalten" #: ../udiskie/mount.py:595 #, python-brace-format msgid "not setting up {0}: already up" msgstr "Einrichtung von {0} abgebrochen: bereits eingerichtet" #: ../udiskie/mount.py:598 #, python-brace-format msgid "not setting up {0}: not a file" msgstr "Einrichtung von {0} abgebrochen: nur für Dateien möglich" #: ../udiskie/mount.py:600 #, python-brace-format msgid "setting up loop device {0}" msgstr "Loop device für {0} wird eingerichtet" #: ../udiskie/mount.py:618 #, python-brace-format msgid "" "Insufficient permission to open {0} in read-write mode. Retrying in read-" "only mode." msgstr "" "Ungenügende Rechte um {0} im Schreibmodus zu öffnen. Erneuter Versuch " "schreibgeschützten Modus." #: ../udiskie/mount.py:630 #, python-brace-format msgid "set up {0} as {1}" msgstr "{0} wurde eingerichtet als {1}" #: ../udiskie/mount.py:645 #, python-brace-format msgid "not deleting {0}: unhandled device" msgstr "Löschen von {0} abgebrochen: nicht betrachtetes Laufwerk" #: ../udiskie/mount.py:649 #, python-brace-format msgid "deleting {0}" msgstr "{0} wird gelöscht" #: ../udiskie/mount.py:651 #, python-brace-format msgid "deleted {0}" msgstr "{0} wurde gelöscht" #: ../udiskie/mount.py:777 #, python-brace-format msgid "Browse {0}" msgstr "Öffne {0}" #: ../udiskie/mount.py:778 #, python-brace-format msgid "Hack on {0}" msgstr "Terminal für {0}" #: ../udiskie/mount.py:779 #, python-brace-format msgid "Mount {0}" msgstr "Einhängen {0}" #: ../udiskie/mount.py:780 #, python-brace-format msgid "Unmount {0}" msgstr "Aushängen {0}" #: ../udiskie/mount.py:781 #, python-brace-format msgid "Unlock {0}" msgstr "Entsperren {0}" #: ../udiskie/mount.py:782 #, python-brace-format msgid "Lock {0}" msgstr "Sperren {0}" #: ../udiskie/mount.py:783 #, python-brace-format msgid "Eject {1}" msgstr "Auswerfen {1}" #: ../udiskie/mount.py:784 #, python-brace-format msgid "Unpower {1}" msgstr "Abschalten {1}" #: ../udiskie/mount.py:785 #, python-brace-format msgid "Clear password for {0}" msgstr "Passwort löschen für {0}" #: ../udiskie/mount.py:786 #, python-brace-format msgid "Detach {0}" msgstr "Abschalten {0}" #: ../udiskie/notify.py:62 msgid "Browse directory" msgstr "Verzeichnis öffnen" #: ../udiskie/notify.py:64 msgid "Open terminal" msgstr "Terminal öffnen" #: ../udiskie/notify.py:68 msgid "Device mounted" msgstr "Gerät eingehängt" #: ../udiskie/notify.py:69 #, python-brace-format msgid "{0.ui_label} mounted on {0.mount_paths[0]}" msgstr "{0.ui_label} wurde unter {0.mount_paths[0]} eingehängt" #: ../udiskie/notify.py:80 msgid "Device unmounted" msgstr "Gerät ausgehängt" #: ../udiskie/notify.py:81 #, python-brace-format msgid "{0.ui_label} unmounted" msgstr "{0.ui_label} ausgehängt" #: ../udiskie/notify.py:90 msgid "Device locked" msgstr "Gerät gesperrt" #: ../udiskie/notify.py:91 #, python-brace-format msgid "{0.device_presentation} locked" msgstr "{0.device_presentation} gesperrt" #: ../udiskie/notify.py:100 msgid "Device unlocked" msgstr "Gerät entsperrt" #: ../udiskie/notify.py:101 #, python-brace-format msgid "{0.device_presentation} unlocked" msgstr "{0.device_presentation} entsperrt" #: ../udiskie/notify.py:135 msgid "Device added" msgstr "Gerät erkannt" #: ../udiskie/notify.py:136 #, python-brace-format msgid "device appeared on {0.device_presentation}" msgstr "Gerät ist unter {0.device_presentation} erschienen" #: ../udiskie/notify.py:155 msgid "Device removed" msgstr "Gerät entfernt" #: ../udiskie/notify.py:156 #, python-brace-format msgid "device disappeared on {0.device_presentation}" msgstr "Gerät ist von {0.device_presentation} verschwunden" #: ../udiskie/notify.py:165 #, python-brace-format msgid "" "failed to {0} {1}:\n" "{2}" msgstr "" "Fehler bei {0} {1}:\n" "{2}" #: ../udiskie/notify.py:167 #, python-brace-format msgid "failed to {0} device {1}." msgstr "Fehler bei {0} von Gerät {1}." #: ../udiskie/notify.py:173 msgid "Retry" msgstr "Wiederholen" #: ../udiskie/notify.py:176 msgid "Job failed" msgstr "Auftrag gescheitert" #: ../udiskie/notify.py:207 #, python-brace-format msgid "Failed to show notification: {0}" msgstr "Fehler beim Anzeigen von Benachrichtigung: {0}" #: ../udiskie/prompt.py:96 msgid "Show password" msgstr "Passwort zeigen" #: ../udiskie/prompt.py:101 msgid "Open keyfile…" msgstr "Öffne Schlüsseldatei…" #: ../udiskie/prompt.py:108 msgid "Cache password" msgstr "Passwort merken" #: ../udiskie/prompt.py:123 msgid "Open a keyfile to unlock the LUKS device" msgstr "Schlüsseldatei zum Entsperren von LUKS Gerät öffnen" #: ../udiskie/prompt.py:157 ../udiskie/prompt.py:167 #, python-brace-format msgid "Enter password for {0.device_presentation}: " msgstr "Passwort für {0.device_presentation} eingeben: " #: ../udiskie/prompt.py:203 msgid "Unknown device attribute {!r} in format string: {!r}" msgstr "Unbekannte Geräteeigenschaft {!r} in Formatstring: {!r}" #: ../udiskie/prompt.py:255 msgid "" "Can't find file browser: {0!r}. You may want to change the value for the '-" "f' option." msgstr "" "Dateimanager konnte nicht gefunden werden: {0!r}. Programm kann mit dem '-f' " "Parameter festgelegt werden." #: ../udiskie/tray.py:182 msgid "Managed devices" msgstr "Verwaltete Geräte" #: ../udiskie/tray.py:198 msgid "Mount disc image" msgstr "Abbild einhängen" #: ../udiskie/tray.py:204 msgid "Enable automounting" msgstr "Automatisches Einhängen" #: ../udiskie/tray.py:210 msgid "Enable notifications" msgstr "Benachrichtigungen" #: ../udiskie/tray.py:219 msgid "Quit" msgstr "Beenden" #: ../udiskie/tray.py:226 msgid "Open disc image" msgstr "Datenträgerabbild öffnen" #: ../udiskie/tray.py:228 msgid "Open" msgstr "Öffnen" #: ../udiskie/tray.py:229 msgid "Cancel" msgstr "Abbrechen" #: ../udiskie/tray.py:269 msgid "Invalid node!" msgstr "Ungültiger Knoten!" #: ../udiskie/tray.py:271 msgid "No external devices" msgstr "Keine externen Geräte" #: ../udiskie/tray.py:387 msgid "udiskie" msgstr "udiskie" #: ../udiskie/udisks2.py:661 #, python-brace-format msgid "found device owning \"{0}\": \"{1}\"" msgstr "Besitzendes Gerät für \"{0}\" gefunden: \"{1}\"" #: ../udiskie/udisks2.py:664 #, python-brace-format msgid "no device found owning \"{0}\"" msgstr "Kein besitzendes Gerät für \"{0}\" gefunden" #: ../udiskie/udisks2.py:683 #, python-brace-format msgid "Daemon version: {0}" msgstr "Daemon Version: {0}" #: ../udiskie/udisks2.py:688 #, python-brace-format msgid "Keyfile support: {0}" msgstr "Unterstützung für Schlüsseldateien: {0}" #: ../udiskie/udisks2.py:767 #, python-brace-format msgid "+++ {0}: {1}" msgstr "+++ {0}: {1}" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/lang/en_US.po0000644000175100001770000004144214612243552015066 0ustar00runnerdocker# English translations for udiskie package. # Copyright (C) 2024 Thomas Gläßle # This file is distributed under the same license as the udiskie package. # Automatically generated, 2024. # msgid "" msgstr "" "Project-Id-Version: udiskie 2.5.2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-04-16 21:10+0000\n" "PO-Revision-Date: 2024-04-16 21:10+0000\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" "Language: en_US\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../udiskie/cli.py:46 #, python-brace-format msgid "These options are mutually exclusive: {0}" msgstr "These options are mutually exclusive: {0}" #: ../udiskie/cli.py:119 msgid "" "\n" " Note, that the options in the individual groups are mutually exclusive.\n" "\n" " The config file can be a JSON or preferably a YAML file. For an\n" " example, see the MAN page (or doc/udiskie.8.txt in the repository).\n" " " msgstr "" "\n" " Note, that the options in the individual groups are mutually exclusive.\n" "\n" " The config file can be a JSON or preferably a YAML file. For an\n" " example, see the MAN page (or doc/udiskie.8.txt in the repository).\n" " " #: ../udiskie/cli.py:139 #, python-format msgid "%(message)s" msgstr "%(message)s" #: ../udiskie/cli.py:141 #, python-format msgid "%(levelname)s [%(asctime)s] %(name)s: %(message)s" msgstr "%(levelname)s [%(asctime)s] %(name)s: %(message)s" #: ../udiskie/cli.py:386 msgid "" "Typelib for 'libnotify' is not available. Possible causes include:\n" "\t- libnotify is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- libnotify was built with introspection disabled\n" "\n" "Starting udiskie without notifications." msgstr "" "Typelib for 'libnotify' is not available. Possible causes include:\n" "\t- libnotify is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- libnotify was built with introspection disabled\n" "\n" "Starting udiskie without notifications." #: ../udiskie/cli.py:400 msgid "" "Not run within X or Wayland session.\n" "Starting udiskie without tray icon.\n" msgstr "" "Not run within X or Wayland session.\n" "Starting udiskie without tray icon.\n" #: ../udiskie/cli.py:407 msgid "" "Typelib for 'Gtk 3.0' is not available. Possible causes include:\n" "\t- GTK3 is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- GTK3 was built with introspection disabled\n" "Starting udiskie without tray icon.\n" msgstr "" "Typelib for 'Gtk 3.0' is not available. Possible causes include:\n" "\t- GTK3 is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- GTK3 was built with introspection disabled\n" "Starting udiskie without tray icon.\n" #: ../udiskie/cli.py:417 msgid "" "Typelib for 'AppIndicator3 0.1' is not available. Possible causes include:\n" "\t- libappindicator is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- it was built with introspection disabled\n" "Starting udiskie without appindicator icon.\n" msgstr "" "Typelib for 'AppIndicator3 0.1' is not available. Possible causes include:\n" "\t- libappindicator is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- it was built with introspection disabled\n" "Starting udiskie without appindicator icon.\n" #: ../udiskie/cli.py:436 msgid "" "The 'notify_command' option was renamed to 'event_hook'. The old name still " "works, but may be removed in a future version. Please change your command " "line and config to use the new name." msgstr "" "The 'notify_command' option was renamed to 'event_hook'. The old name still " "works, but may be removed in a future version. Please change your command " "line and config to use the new name." #: ../udiskie/cli.py:443 msgid "Ignoring 'notify_command' in favor of 'event_hook'." msgstr "Ignoring 'notify_command' in favor of 'event_hook'." #: ../udiskie/config.py:129 msgid "Unknown matching attribute: {!r}" msgstr "Unknown matching attribute: {!r}" #: ../udiskie/config.py:131 #, python-brace-format msgid "new rule: {0}" msgstr "new rule: {0}" #: ../udiskie/config.py:134 #, python-brace-format msgid "{0} -> {1}" msgstr "{0} -> {1}" #: ../udiskie/config.py:153 #, python-brace-format msgid "{0} matched {1}" msgstr "{0} matched {1}" #: ../udiskie/config.py:230 #, python-brace-format msgid "Failed to read config file: {0}" msgstr "Failed to read config file: {0}" #: ../udiskie/config.py:233 msgid "Failed to read {0!r}: {1}" msgstr "Failed to read {0!r}: {1}" #: ../udiskie/depend.py:59 msgid "" "Missing runtime dependency GTK 3. Falling back to GTK 2 for password prompt" msgstr "" "Missing runtime dependency GTK 3. Falling back to GTK 2 for password prompt" #: ../udiskie/depend.py:65 msgid "X server not connected!" msgstr "X server not connected!" #: ../udiskie/mount.py:29 #, python-brace-format msgid "failed to {0} {1}: {2}" msgstr "failed to {0} {1}: {2}" #: ../udiskie/mount.py:117 #, python-brace-format msgid "not browsing {0}: not mounted" msgstr "not browsing {0}: not mounted" #: ../udiskie/mount.py:120 #, python-brace-format msgid "not browsing {0}: no program" msgstr "not browsing {0}: no program" #: ../udiskie/mount.py:122 ../udiskie/mount.py:142 #, python-brace-format msgid "opening {0} on {0.mount_paths[0]}" msgstr "opening {0} on {0.mount_paths[0]}" #: ../udiskie/mount.py:124 ../udiskie/mount.py:144 #, python-brace-format msgid "opened {0} on {0.mount_paths[0]}" msgstr "opened {0} on {0.mount_paths[0]}" #: ../udiskie/mount.py:137 #, python-brace-format msgid "not opening terminal {0}: not mounted" msgstr "not opening terminal {0}: not mounted" #: ../udiskie/mount.py:140 #, python-brace-format msgid "not opening terminal {0}: no program" msgstr "not opening terminal {0}: no program" #: ../udiskie/mount.py:158 #, python-brace-format msgid "not mounting {0}: unhandled device" msgstr "not mounting {0}: unhandled device" #: ../udiskie/mount.py:161 #, python-brace-format msgid "not mounting {0}: already mounted" msgstr "not mounting {0}: already mounted" #: ../udiskie/mount.py:165 #, python-brace-format msgid "mounting {0} with {1}" msgstr "mounting {0} with {1}" #: ../udiskie/mount.py:168 #, python-brace-format msgid "mounted {0} on {1}" msgstr "mounted {0} on {1}" #: ../udiskie/mount.py:174 msgid "" "Mounting NTFS device with default driver.\n" "Please install 'ntfs-3g' if you experience problems or the device is " "readonly." msgstr "" "Mounting NTFS device with default driver.\n" "Please install 'ntfs-3g' if you experience problems or the device is " "readonly." #: ../udiskie/mount.py:188 #, python-brace-format msgid "not unmounting {0}: unhandled device" msgstr "not unmounting {0}: unhandled device" #: ../udiskie/mount.py:191 #, python-brace-format msgid "not unmounting {0}: not mounted" msgstr "not unmounting {0}: not mounted" #: ../udiskie/mount.py:193 #, python-brace-format msgid "unmounting {0}" msgstr "unmounting {0}" #: ../udiskie/mount.py:195 #, python-brace-format msgid "unmounted {0}" msgstr "unmounted {0}" #: ../udiskie/mount.py:209 #, python-brace-format msgid "not unlocking {0}: unhandled device" msgstr "not unlocking {0}: unhandled device" #: ../udiskie/mount.py:212 #, python-brace-format msgid "not unlocking {0}: already unlocked" msgstr "not unlocking {0}: already unlocked" #: ../udiskie/mount.py:215 #, python-brace-format msgid "not unlocking {0}: no password prompt" msgstr "not unlocking {0}: no password prompt" #: ../udiskie/mount.py:229 #, python-brace-format msgid "not unlocking {0}: cancelled by user" msgstr "not unlocking {0}: cancelled by user" #: ../udiskie/mount.py:234 #, python-brace-format msgid "unlocking {0} using keyfile" msgstr "unlocking {0} using keyfile" #: ../udiskie/mount.py:237 #, python-brace-format msgid "unlocking {0}" msgstr "unlocking {0}" #: ../udiskie/mount.py:240 #, python-brace-format msgid "unlocked {0}" msgstr "unlocked {0}" #: ../udiskie/mount.py:249 #, python-brace-format msgid "no cached key for {0}" msgstr "no cached key for {0}" #: ../udiskie/mount.py:251 #, python-brace-format msgid "unlocking {0} using cached password" msgstr "unlocking {0} using cached password" #: ../udiskie/mount.py:255 #, python-brace-format msgid "failed to unlock {0} using cached password" msgstr "failed to unlock {0} using cached password" #: ../udiskie/mount.py:258 #, python-brace-format msgid "unlocked {0} using cached password" msgstr "unlocked {0} using cached password" #: ../udiskie/mount.py:266 msgid "No matching keyfile rule for {}." msgstr "No matching keyfile rule for {}." #: ../udiskie/mount.py:272 #, python-brace-format msgid "keyfile for {0} not found: {1}" msgstr "keyfile for {0} not found: {1}" #: ../udiskie/mount.py:274 #, python-brace-format msgid "unlocking {0} using keyfile {1}" msgstr "unlocking {0} using keyfile {1}" #: ../udiskie/mount.py:278 #, python-brace-format msgid "failed to unlock {0} using keyfile" msgstr "failed to unlock {0} using keyfile" #: ../udiskie/mount.py:281 #, python-brace-format msgid "unlocked {0} using keyfile" msgstr "unlocked {0} using keyfile" #: ../udiskie/mount.py:307 #, python-brace-format msgid "not locking {0}: unhandled device" msgstr "not locking {0}: unhandled device" #: ../udiskie/mount.py:310 #, python-brace-format msgid "not locking {0}: not unlocked" msgstr "not locking {0}: not unlocked" #: ../udiskie/mount.py:312 #, python-brace-format msgid "locking {0}" msgstr "locking {0}" #: ../udiskie/mount.py:314 #, python-brace-format msgid "locked {0}" msgstr "locked {0}" #: ../udiskie/mount.py:351 ../udiskie/mount.py:394 #, python-brace-format msgid "not adding {0}: unhandled device" msgstr "not adding {0}: unhandled device" #: ../udiskie/mount.py:430 ../udiskie/mount.py:480 #, python-brace-format msgid "not removing {0}: unhandled device" msgstr "not removing {0}: unhandled device" #: ../udiskie/mount.py:505 #, python-brace-format msgid "not ejecting {0}: unhandled device" msgstr "not ejecting {0}: unhandled device" #: ../udiskie/mount.py:509 #, python-brace-format msgid "not ejecting {0}: drive not ejectable" msgstr "not ejecting {0}: drive not ejectable" #: ../udiskie/mount.py:515 #, python-brace-format msgid "ejecting {0}" msgstr "ejecting {0}" #: ../udiskie/mount.py:517 #, python-brace-format msgid "ejected {0}" msgstr "ejected {0}" #: ../udiskie/mount.py:531 #, python-brace-format msgid "not detaching {0}: unhandled device" msgstr "not detaching {0}: unhandled device" #: ../udiskie/mount.py:535 #, python-brace-format msgid "not detaching {0}: drive not detachable" msgstr "not detaching {0}: drive not detachable" #: ../udiskie/mount.py:539 #, python-brace-format msgid "detaching {0}" msgstr "detaching {0}" #: ../udiskie/mount.py:544 #, python-brace-format msgid "detached {0}" msgstr "detached {0}" #: ../udiskie/mount.py:595 #, python-brace-format msgid "not setting up {0}: already up" msgstr "not setting up {0}: already up" #: ../udiskie/mount.py:598 #, python-brace-format msgid "not setting up {0}: not a file" msgstr "not setting up {0}: not a file" #: ../udiskie/mount.py:600 #, python-brace-format msgid "setting up loop device {0}" msgstr "setting up loop device {0}" #: ../udiskie/mount.py:618 #, python-brace-format msgid "" "Insufficient permission to open {0} in read-write mode. Retrying in read-" "only mode." msgstr "" "Insufficient permission to open {0} in read-write mode. Retrying in read-" "only mode." #: ../udiskie/mount.py:630 #, python-brace-format msgid "set up {0} as {1}" msgstr "set up {0} as {1}" #: ../udiskie/mount.py:645 #, python-brace-format msgid "not deleting {0}: unhandled device" msgstr "not deleting {0}: unhandled device" #: ../udiskie/mount.py:649 #, python-brace-format msgid "deleting {0}" msgstr "deleting {0}" #: ../udiskie/mount.py:651 #, python-brace-format msgid "deleted {0}" msgstr "deleted {0}" #: ../udiskie/mount.py:777 #, python-brace-format msgid "Browse {0}" msgstr "Browse {0}" #: ../udiskie/mount.py:778 #, python-brace-format msgid "Hack on {0}" msgstr "Hack on {0}" #: ../udiskie/mount.py:779 #, python-brace-format msgid "Mount {0}" msgstr "Mount {0}" #: ../udiskie/mount.py:780 #, python-brace-format msgid "Unmount {0}" msgstr "Unmount {0}" #: ../udiskie/mount.py:781 #, python-brace-format msgid "Unlock {0}" msgstr "Unlock {0}" #: ../udiskie/mount.py:782 #, python-brace-format msgid "Lock {0}" msgstr "Lock {0}" #: ../udiskie/mount.py:783 #, python-brace-format msgid "Eject {1}" msgstr "Eject {1}" #: ../udiskie/mount.py:784 #, python-brace-format msgid "Unpower {1}" msgstr "Unpower {1}" #: ../udiskie/mount.py:785 #, python-brace-format msgid "Clear password for {0}" msgstr "Clear password for {0}" #: ../udiskie/mount.py:786 #, python-brace-format msgid "Detach {0}" msgstr "Detach {0}" #: ../udiskie/notify.py:62 msgid "Browse directory" msgstr "Browse directory" #: ../udiskie/notify.py:64 msgid "Open terminal" msgstr "Open terminal" #: ../udiskie/notify.py:68 msgid "Device mounted" msgstr "Device mounted" #: ../udiskie/notify.py:69 #, python-brace-format msgid "{0.ui_label} mounted on {0.mount_paths[0]}" msgstr "{0.ui_label} mounted on {0.mount_paths[0]}" #: ../udiskie/notify.py:80 msgid "Device unmounted" msgstr "Device unmounted" #: ../udiskie/notify.py:81 #, python-brace-format msgid "{0.ui_label} unmounted" msgstr "{0.ui_label} unmounted" #: ../udiskie/notify.py:90 msgid "Device locked" msgstr "Device locked" #: ../udiskie/notify.py:91 #, python-brace-format msgid "{0.device_presentation} locked" msgstr "{0.device_presentation} locked" #: ../udiskie/notify.py:100 msgid "Device unlocked" msgstr "Device unlocked" #: ../udiskie/notify.py:101 #, python-brace-format msgid "{0.device_presentation} unlocked" msgstr "{0.device_presentation} unlocked" #: ../udiskie/notify.py:135 msgid "Device added" msgstr "Device added" #: ../udiskie/notify.py:136 #, python-brace-format msgid "device appeared on {0.device_presentation}" msgstr "device appeared on {0.device_presentation}" #: ../udiskie/notify.py:155 msgid "Device removed" msgstr "Device removed" #: ../udiskie/notify.py:156 #, python-brace-format msgid "device disappeared on {0.device_presentation}" msgstr "device disappeared on {0.device_presentation}" #: ../udiskie/notify.py:165 #, python-brace-format msgid "" "failed to {0} {1}:\n" "{2}" msgstr "" "failed to {0} {1}:\n" "{2}" #: ../udiskie/notify.py:167 #, python-brace-format msgid "failed to {0} device {1}." msgstr "failed to {0} device {1}." #: ../udiskie/notify.py:173 msgid "Retry" msgstr "Retry" #: ../udiskie/notify.py:176 msgid "Job failed" msgstr "Job failed" #: ../udiskie/notify.py:207 #, python-brace-format msgid "Failed to show notification: {0}" msgstr "Failed to show notification: {0}" #: ../udiskie/prompt.py:96 msgid "Show password" msgstr "Show password" #: ../udiskie/prompt.py:101 msgid "Open keyfile…" msgstr "Open keyfile…" #: ../udiskie/prompt.py:108 msgid "Cache password" msgstr "Cache password" #: ../udiskie/prompt.py:123 msgid "Open a keyfile to unlock the LUKS device" msgstr "Open a keyfile to unlock the LUKS device" #: ../udiskie/prompt.py:157 ../udiskie/prompt.py:167 #, python-brace-format msgid "Enter password for {0.device_presentation}: " msgstr "Enter password for {0.device_presentation}: " #: ../udiskie/prompt.py:203 msgid "Unknown device attribute {!r} in format string: {!r}" msgstr "Unknown device attribute {!r} in format string: {!r}" #: ../udiskie/prompt.py:255 msgid "" "Can't find file browser: {0!r}. You may want to change the value for the '-" "f' option." msgstr "" "Can't find file browser: {0!r}. You may want to change the value for the '-" "f' option." #: ../udiskie/tray.py:182 msgid "Managed devices" msgstr "Managed devices" #: ../udiskie/tray.py:198 msgid "Mount disc image" msgstr "Mount disc image" #: ../udiskie/tray.py:204 msgid "Enable automounting" msgstr "Enable automounting" #: ../udiskie/tray.py:210 msgid "Enable notifications" msgstr "Enable notifications" #: ../udiskie/tray.py:219 msgid "Quit" msgstr "Quit" #: ../udiskie/tray.py:226 msgid "Open disc image" msgstr "Open disc image" #: ../udiskie/tray.py:228 msgid "Open" msgstr "Open" #: ../udiskie/tray.py:229 msgid "Cancel" msgstr "Cancel" #: ../udiskie/tray.py:269 msgid "Invalid node!" msgstr "Invalid node!" #: ../udiskie/tray.py:271 msgid "No external devices" msgstr "No external devices" #: ../udiskie/tray.py:387 msgid "udiskie" msgstr "udiskie" #: ../udiskie/udisks2.py:661 #, python-brace-format msgid "found device owning \"{0}\": \"{1}\"" msgstr "found device owning \"{0}\": \"{1}\"" #: ../udiskie/udisks2.py:664 #, python-brace-format msgid "no device found owning \"{0}\"" msgstr "no device found owning \"{0}\"" #: ../udiskie/udisks2.py:683 #, python-brace-format msgid "Daemon version: {0}" msgstr "Daemon version: {0}" #: ../udiskie/udisks2.py:688 #, python-brace-format msgid "Keyfile support: {0}" msgstr "Keyfile support: {0}" #: ../udiskie/udisks2.py:767 #, python-brace-format msgid "+++ {0}: {1}" msgstr "+++ {0}: {1}" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/lang/es_ES.po0000644000175100001770000004053414612243552015054 0ustar00runnerdocker# Spanish translations for udiskie package. # Copyright (C) 2021 Thomas Gläßle # This file is distributed under the same license as the udiskie package. # Alejandro Pérez , 2014. # msgid "" msgstr "" "Project-Id-Version: udiskie\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-04-16 21:10+0000\n" "PO-Revision-Date: 2014-07-23 18:43+0100\n" "Last-Translator: Alejandro Pérez \n" "Language-Team: Spanish alejandro.perez.mendez@gmail.com\n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 1.6.5\n" #: ../udiskie/cli.py:46 #, python-brace-format msgid "These options are mutually exclusive: {0}" msgstr "Estas opciones son excluyentes: {0}" #: ../udiskie/cli.py:119 #, fuzzy msgid "" "\n" " Note, that the options in the individual groups are mutually exclusive.\n" "\n" " The config file can be a JSON or preferably a YAML file. For an\n" " example, see the MAN page (or doc/udiskie.8.txt in the repository).\n" " " msgstr "" "\n" " Nótese que las opciones de los grupos individuales son excluyentes.\n" "\n" " El fichero de configuración puede ser un fichero JSON o YAML " "(preferiblemente).\n" " Para un ejemplo, refiérase a la pagína del manual (o doc/udiskie.8.txt " "in the repository).\n" #: ../udiskie/cli.py:139 #, python-format msgid "%(message)s" msgstr "%(message)s" #: ../udiskie/cli.py:141 #, python-format msgid "%(levelname)s [%(asctime)s] %(name)s: %(message)s" msgstr "%(levelname)s [%(asctime)s] %(name)s: %(message)s" #: ../udiskie/cli.py:386 msgid "" "Typelib for 'libnotify' is not available. Possible causes include:\n" "\t- libnotify is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- libnotify was built with introspection disabled\n" "\n" "Starting udiskie without notifications." msgstr "" #: ../udiskie/cli.py:400 msgid "" "Not run within X or Wayland session.\n" "Starting udiskie without tray icon.\n" msgstr "" #: ../udiskie/cli.py:407 msgid "" "Typelib for 'Gtk 3.0' is not available. Possible causes include:\n" "\t- GTK3 is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- GTK3 was built with introspection disabled\n" "Starting udiskie without tray icon.\n" msgstr "" #: ../udiskie/cli.py:417 msgid "" "Typelib for 'AppIndicator3 0.1' is not available. Possible causes include:\n" "\t- libappindicator is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- it was built with introspection disabled\n" "Starting udiskie without appindicator icon.\n" msgstr "" #: ../udiskie/cli.py:436 msgid "" "The 'notify_command' option was renamed to 'event_hook'. The old name still " "works, but may be removed in a future version. Please change your command " "line and config to use the new name." msgstr "" #: ../udiskie/cli.py:443 msgid "Ignoring 'notify_command' in favor of 'event_hook'." msgstr "" #: ../udiskie/config.py:129 msgid "Unknown matching attribute: {!r}" msgstr "Atributo de filtrado desconocido: {!r}" #: ../udiskie/config.py:131 #, python-brace-format msgid "new rule: {0}" msgstr "" #: ../udiskie/config.py:134 #, fuzzy, python-brace-format msgid "{0} -> {1}" msgstr "+++ {0}: {1}" #: ../udiskie/config.py:153 #, fuzzy, python-brace-format msgid "{0} matched {1}" msgstr "{0} usado para {1}" #: ../udiskie/config.py:230 #, python-brace-format msgid "Failed to read config file: {0}" msgstr "" #: ../udiskie/config.py:233 #, fuzzy msgid "Failed to read {0!r}: {1}" msgstr "Fallo al {0} {1}: {2}" #: ../udiskie/depend.py:59 msgid "" "Missing runtime dependency GTK 3. Falling back to GTK 2 for password prompt" msgstr "" #: ../udiskie/depend.py:65 msgid "X server not connected!" msgstr "¡Servidor X no conectado!" #: ../udiskie/mount.py:29 #, python-brace-format msgid "failed to {0} {1}: {2}" msgstr "Fallo al {0} {1}: {2}" #: ../udiskie/mount.py:117 #, python-brace-format msgid "not browsing {0}: not mounted" msgstr "no se exploró {0}: no está montado" #: ../udiskie/mount.py:120 #, python-brace-format msgid "not browsing {0}: no program" msgstr "no se exploró {0}: no hay programa configurado" #: ../udiskie/mount.py:122 ../udiskie/mount.py:142 #, python-brace-format msgid "opening {0} on {0.mount_paths[0]}" msgstr "abriendo {0} en {0.mount_paths[0]}" #: ../udiskie/mount.py:124 ../udiskie/mount.py:144 #, python-brace-format msgid "opened {0} on {0.mount_paths[0]}" msgstr "se abrió {0} en {0.mount_paths[0]}" #: ../udiskie/mount.py:137 #, fuzzy, python-brace-format msgid "not opening terminal {0}: not mounted" msgstr "no se desmontó {0}: no estaba montado" #: ../udiskie/mount.py:140 #, fuzzy, python-brace-format msgid "not opening terminal {0}: no program" msgstr "no se exploró {0}: no hay programa configurado" #: ../udiskie/mount.py:158 #, python-brace-format msgid "not mounting {0}: unhandled device" msgstr "no se montó {0}: dispositivo no gestionado" #: ../udiskie/mount.py:161 #, python-brace-format msgid "not mounting {0}: already mounted" msgstr "no se montó {0}: ya está montado" #: ../udiskie/mount.py:165 #, python-brace-format msgid "mounting {0} with {1}" msgstr "montando {0} en {1}" #: ../udiskie/mount.py:168 #, python-brace-format msgid "mounted {0} on {1}" msgstr "montado {0} en {1}" #: ../udiskie/mount.py:174 msgid "" "Mounting NTFS device with default driver.\n" "Please install 'ntfs-3g' if you experience problems or the device is " "readonly." msgstr "" #: ../udiskie/mount.py:188 #, python-brace-format msgid "not unmounting {0}: unhandled device" msgstr "no se desmontó {0}: dispositivo no gestionado" #: ../udiskie/mount.py:191 #, python-brace-format msgid "not unmounting {0}: not mounted" msgstr "no se desmontó {0}: no estaba montado" #: ../udiskie/mount.py:193 #, python-brace-format msgid "unmounting {0}" msgstr "desmontando {0}" #: ../udiskie/mount.py:195 #, python-brace-format msgid "unmounted {0}" msgstr "desmontado {0}" #: ../udiskie/mount.py:209 #, python-brace-format msgid "not unlocking {0}: unhandled device" msgstr "no se desbloqueó {0}: dispositivo no gestionado" #: ../udiskie/mount.py:212 #, python-brace-format msgid "not unlocking {0}: already unlocked" msgstr "no se desbloqueó {0}: ya está desbloqueado" #: ../udiskie/mount.py:215 #, python-brace-format msgid "not unlocking {0}: no password prompt" msgstr "no se desbloqueó {0}: no se introdujo la clave" #: ../udiskie/mount.py:229 #, python-brace-format msgid "not unlocking {0}: cancelled by user" msgstr "no se desbloqueó {0}: cancellado por el usuario" #: ../udiskie/mount.py:234 #, fuzzy, python-brace-format msgid "unlocking {0} using keyfile" msgstr "no se desbloqueó {0}: no se introdujo la clave" #: ../udiskie/mount.py:237 #, python-brace-format msgid "unlocking {0}" msgstr "desbloqueando {0}" #: ../udiskie/mount.py:240 #, python-brace-format msgid "unlocked {0}" msgstr "desbloqueado {0}" #: ../udiskie/mount.py:249 #, python-brace-format msgid "no cached key for {0}" msgstr "" #: ../udiskie/mount.py:251 #, fuzzy, python-brace-format msgid "unlocking {0} using cached password" msgstr "no se desbloqueó {0}: no se introdujo la clave" #: ../udiskie/mount.py:255 #, python-brace-format msgid "failed to unlock {0} using cached password" msgstr "" #: ../udiskie/mount.py:258 #, python-brace-format msgid "unlocked {0} using cached password" msgstr "" #: ../udiskie/mount.py:266 msgid "No matching keyfile rule for {}." msgstr "" #: ../udiskie/mount.py:272 #, fuzzy, python-brace-format msgid "keyfile for {0} not found: {1}" msgstr "Dispositivo no encontrado: {0}" #: ../udiskie/mount.py:274 #, fuzzy, python-brace-format msgid "unlocking {0} using keyfile {1}" msgstr "no se desbloqueó {0}: no se introdujo la clave" #: ../udiskie/mount.py:278 #, python-brace-format msgid "failed to unlock {0} using keyfile" msgstr "" #: ../udiskie/mount.py:281 #, fuzzy, python-brace-format msgid "unlocked {0} using keyfile" msgstr "desbloqueado {0}" #: ../udiskie/mount.py:307 #, python-brace-format msgid "not locking {0}: unhandled device" msgstr "no se bloqueó {0}: dispositivo no gestionado" #: ../udiskie/mount.py:310 #, python-brace-format msgid "not locking {0}: not unlocked" msgstr "no se bloqueó {0}: no estaba desbloqueado" #: ../udiskie/mount.py:312 #, python-brace-format msgid "locking {0}" msgstr "bloqueando {0}" #: ../udiskie/mount.py:314 #, python-brace-format msgid "locked {0}" msgstr "bloqueado {0}" #: ../udiskie/mount.py:351 ../udiskie/mount.py:394 #, python-brace-format msgid "not adding {0}: unhandled device" msgstr "no se añadió {0}: dispositivo no gestionado" #: ../udiskie/mount.py:430 ../udiskie/mount.py:480 #, python-brace-format msgid "not removing {0}: unhandled device" msgstr "no se eliminó {0}: dispositivo no gestionado" #: ../udiskie/mount.py:505 #, python-brace-format msgid "not ejecting {0}: unhandled device" msgstr "no se expulsó {0}: dispositivo no gestionado" #: ../udiskie/mount.py:509 #, python-brace-format msgid "not ejecting {0}: drive not ejectable" msgstr "no se expulsó {0}: dispositivo no expulsable" #: ../udiskie/mount.py:515 #, python-brace-format msgid "ejecting {0}" msgstr "expulsando {0}" #: ../udiskie/mount.py:517 #, python-brace-format msgid "ejected {0}" msgstr "expulsado {0}" #: ../udiskie/mount.py:531 #, python-brace-format msgid "not detaching {0}: unhandled device" msgstr "no se desconectó {0}: unhandled device" #: ../udiskie/mount.py:535 #, python-brace-format msgid "not detaching {0}: drive not detachable" msgstr "no se desconectó {0}: dispositivo no desconectable" #: ../udiskie/mount.py:539 #, python-brace-format msgid "detaching {0}" msgstr "desconectando {0}" #: ../udiskie/mount.py:544 #, python-brace-format msgid "detached {0}" msgstr "desconectado {0}" #: ../udiskie/mount.py:595 #, fuzzy, python-brace-format msgid "not setting up {0}: already up" msgstr "no se montó {0}: ya está montado" #: ../udiskie/mount.py:598 #, fuzzy, python-brace-format msgid "not setting up {0}: not a file" msgstr "no se expulsó {0}: dispositivo no expulsable" #: ../udiskie/mount.py:600 #, fuzzy, python-brace-format msgid "setting up loop device {0}" msgstr "expulsando {0}" #: ../udiskie/mount.py:618 #, python-brace-format msgid "" "Insufficient permission to open {0} in read-write mode. Retrying in read-" "only mode." msgstr "" #: ../udiskie/mount.py:630 #, python-brace-format msgid "set up {0} as {1}" msgstr "" #: ../udiskie/mount.py:645 #, fuzzy, python-brace-format msgid "not deleting {0}: unhandled device" msgstr "no se expulsó {0}: dispositivo no gestionado" #: ../udiskie/mount.py:649 #, fuzzy, python-brace-format msgid "deleting {0}" msgstr "expulsando {0}" #: ../udiskie/mount.py:651 #, fuzzy, python-brace-format msgid "deleted {0}" msgstr "expulsado {0}" #: ../udiskie/mount.py:777 #, python-brace-format msgid "Browse {0}" msgstr "Explorar {0}" #: ../udiskie/mount.py:778 #, fuzzy, python-brace-format msgid "Hack on {0}" msgstr "bloqueando {0}" #: ../udiskie/mount.py:779 #, python-brace-format msgid "Mount {0}" msgstr "Montar {0}" #: ../udiskie/mount.py:780 #, python-brace-format msgid "Unmount {0}" msgstr "Desmontar {0}" #: ../udiskie/mount.py:781 #, python-brace-format msgid "Unlock {0}" msgstr "Desbloquear {0}" #: ../udiskie/mount.py:782 #, python-brace-format msgid "Lock {0}" msgstr "Bloquear {0}" #: ../udiskie/mount.py:783 #, fuzzy, python-brace-format msgid "Eject {1}" msgstr "Expulsar {1}" #: ../udiskie/mount.py:784 #, fuzzy, python-brace-format msgid "Unpower {1}" msgstr "Apagar {1}" #: ../udiskie/mount.py:785 #, python-brace-format msgid "Clear password for {0}" msgstr "" #: ../udiskie/mount.py:786 #, fuzzy, python-brace-format msgid "Detach {0}" msgstr "desconectado {0}" #: ../udiskie/notify.py:62 msgid "Browse directory" msgstr "Navegar directorio" #: ../udiskie/notify.py:64 msgid "Open terminal" msgstr "" #: ../udiskie/notify.py:68 msgid "Device mounted" msgstr "Dispositivo montado" #: ../udiskie/notify.py:69 #, fuzzy, python-brace-format msgid "{0.ui_label} mounted on {0.mount_paths[0]}" msgstr "{0.ui_label} montado en {0.mount_paths[0]}" #: ../udiskie/notify.py:80 msgid "Device unmounted" msgstr "Dispositivo desmontado" #: ../udiskie/notify.py:81 #, fuzzy, python-brace-format msgid "{0.ui_label} unmounted" msgstr "{0.ui_label} desmontado" #: ../udiskie/notify.py:90 msgid "Device locked" msgstr "Dispositivo bloqueado" #: ../udiskie/notify.py:91 #, python-brace-format msgid "{0.device_presentation} locked" msgstr "{0.device_presentation} bloqueado" #: ../udiskie/notify.py:100 msgid "Device unlocked" msgstr "Dispositivo desbloqueado" #: ../udiskie/notify.py:101 #, python-brace-format msgid "{0.device_presentation} unlocked" msgstr "{0.device_presentation} desbloqueado" #: ../udiskie/notify.py:135 msgid "Device added" msgstr "Dispositivo añadido" #: ../udiskie/notify.py:136 #, python-brace-format msgid "device appeared on {0.device_presentation}" msgstr "Dispositivo apareció en {0.device_presentation}" #: ../udiskie/notify.py:155 msgid "Device removed" msgstr "Dispositivo retirado" #: ../udiskie/notify.py:156 #, python-brace-format msgid "device disappeared on {0.device_presentation}" msgstr "el dispositivo desapareció en {0.device_presentation} " #: ../udiskie/notify.py:165 #, python-brace-format msgid "" "failed to {0} {1}:\n" "{2}" msgstr "" "fallo al {0} {1}:\n" "{2}" #: ../udiskie/notify.py:167 #, python-brace-format msgid "failed to {0} device {1}." msgstr "fallo al {0} el dispositivo {1}." #: ../udiskie/notify.py:173 msgid "Retry" msgstr "Reintentar" #: ../udiskie/notify.py:176 msgid "Job failed" msgstr "Falló la tarea." #: ../udiskie/notify.py:207 #, python-brace-format msgid "Failed to show notification: {0}" msgstr "" #: ../udiskie/prompt.py:96 msgid "Show password" msgstr "" #: ../udiskie/prompt.py:101 msgid "Open keyfile…" msgstr "" #: ../udiskie/prompt.py:108 msgid "Cache password" msgstr "" #: ../udiskie/prompt.py:123 msgid "Open a keyfile to unlock the LUKS device" msgstr "" #: ../udiskie/prompt.py:157 ../udiskie/prompt.py:167 #, python-brace-format msgid "Enter password for {0.device_presentation}: " msgstr "Introduce la clave para {0.device_presentation}: " #: ../udiskie/prompt.py:203 msgid "Unknown device attribute {!r} in format string: {!r}" msgstr "" #: ../udiskie/prompt.py:255 #, fuzzy msgid "" "Can't find file browser: {0!r}. You may want to change the value for the '-" "f' option." msgstr "" "No se encontró el gestor de ficheros: {0!r}. Puede que necesites cambiar el " "valor de la opción '-b'." #: ../udiskie/tray.py:182 msgid "Managed devices" msgstr "" #: ../udiskie/tray.py:198 msgid "Mount disc image" msgstr "" #: ../udiskie/tray.py:204 msgid "Enable automounting" msgstr "" #: ../udiskie/tray.py:210 msgid "Enable notifications" msgstr "" #: ../udiskie/tray.py:219 msgid "Quit" msgstr "Salir" #: ../udiskie/tray.py:226 msgid "Open disc image" msgstr "" #: ../udiskie/tray.py:228 msgid "Open" msgstr "" #: ../udiskie/tray.py:229 msgid "Cancel" msgstr "" #: ../udiskie/tray.py:269 msgid "Invalid node!" msgstr "¡Nodo inválido!" #: ../udiskie/tray.py:271 msgid "No external devices" msgstr "" #: ../udiskie/tray.py:387 msgid "udiskie" msgstr "udiskie" #: ../udiskie/udisks2.py:661 #, python-brace-format msgid "found device owning \"{0}\": \"{1}\"" msgstr "Se encontró el dispositivo maestro \"{0}\": \"{1}\"" #: ../udiskie/udisks2.py:664 #, python-brace-format msgid "no device found owning \"{0}\"" msgstr "no se encontró dispositivo maestro para \"{0}\"" #: ../udiskie/udisks2.py:683 #, python-brace-format msgid "Daemon version: {0}" msgstr "" #: ../udiskie/udisks2.py:688 #, python-brace-format msgid "Keyfile support: {0}" msgstr "" #: ../udiskie/udisks2.py:767 #, python-brace-format msgid "+++ {0}: {1}" msgstr "+++ {0}: {1}" #~ msgid "{0} created" #~ msgstr "{0} creado" #~ msgid "{0}(match={1!r}, value={2!r})" #~ msgstr "{0}(match={1!r}, value={2!r})" #, fuzzy #~ msgid "{0}(match={1!r}, {2}={3!r}) used for {4}" #~ msgstr "{0}(match={1!r}, value={2!r})" #~ msgid "{0} operation failed for device: {1}" #~ msgstr "Falló la operación {0} para el dispositovo: {1}" #, fuzzy #~ msgid "" #~ "Failed to connect UDisks2 dbus service..\n" #~ "Falling back to UDisks1." #~ msgstr "" #~ "Fallo al conectar al servicio dbus UDisks1.\n" #~ "Usando UDisk2 [experimental]." #~ msgid "UDisks version not supported: {0}!" #~ msgstr "¡Versión de UDisks no soportada: {0}!" #~ msgid "Interface {0!r} not available for {1}" #~ msgstr "Interfaz {0!r} no disponible para {1}" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/lang/it_IT.po0000644000175100001770000004257714612243552015077 0ustar00runnerdocker# Italian translations for udiskie package. # Copyright (C) 2021 Thomas Gläßle # This file is distributed under the same license as the udiskie package. # Stefano Mecocci , 2020. # msgid "" msgstr "" "Project-Id-Version: udiskie\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-04-16 21:10+0000\n" "PO-Revision-Date: 2022-04-17 13:10+0000\n" "Last-Translator: Weblate Admin \n" "Language-Team: Italian \n" "Language: it_IT\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.11.2\n" #: ../udiskie/cli.py:46 #, python-brace-format msgid "These options are mutually exclusive: {0}" msgstr "Queste opzioni sono mutualmente esclusive: {0}" #: ../udiskie/cli.py:119 msgid "" "\n" " Note, that the options in the individual groups are mutually exclusive.\n" "\n" " The config file can be a JSON or preferably a YAML file. For an\n" " example, see the MAN page (or doc/udiskie.8.txt in the repository).\n" " " msgstr "" "\n" " Attenzione, le opzioni nei gruppi individuali sono mutualmente " "esclusive\n" "\n" " Il file di configurazione può essere un file JSON o preferibilmente un " "file YAML.\n" " Per un esempio, guarda la pagina MAN (oppure doc/udiskie.8.txt nella " "repository).\n" " " #: ../udiskie/cli.py:139 #, python-format msgid "%(message)s" msgstr "%(message)s" #: ../udiskie/cli.py:141 #, python-format msgid "%(levelname)s [%(asctime)s] %(name)s: %(message)s" msgstr "%(levelname)s [%(asctime)s] %(name)s: %(message)s" #: ../udiskie/cli.py:386 msgid "" "Typelib for 'libnotify' is not available. Possible causes include:\n" "\t- libnotify is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- libnotify was built with introspection disabled\n" "\n" "Starting udiskie without notifications." msgstr "" "Typelib per 'libnotify' non è disponibile. Fra le possibili cause:\n" "\t- libnotify non è installato\n" "\t- typelib è fornito da un pacchetto separato\n" "\t- La build di libnotify è stata eseguita con introspezione disabilitata\n" "\n" "Avvio di udiskie con notifiche disattivate." #: ../udiskie/cli.py:400 #, fuzzy msgid "" "Not run within X or Wayland session.\n" "Starting udiskie without tray icon.\n" msgstr "" "Non è stata rilevata una sessione X. \n" "Avvio di udiskie senza icona di sistema.\n" #: ../udiskie/cli.py:407 msgid "" "Typelib for 'Gtk 3.0' is not available. Possible causes include:\n" "\t- GTK3 is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- GTK3 was built with introspection disabled\n" "Starting udiskie without tray icon.\n" msgstr "" "Typelib per 'Gtk 3.0' non è disponibile. Fra le possibili cause:\n" "\t- GTK3 non è installato\n" "\t- typelib è fornito da un pacchetto separato\n" "\t- La build di GTK3 è stata eseguita con introspezione disabilitata\n" "Avvio di udiskie senza icona di sistema.\n" #: ../udiskie/cli.py:417 msgid "" "Typelib for 'AppIndicator3 0.1' is not available. Possible causes include:\n" "\t- libappindicator is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- it was built with introspection disabled\n" "Starting udiskie without appindicator icon.\n" msgstr "" "Typelib per 'AppIndicator3 0.1' non è disponibile. Fra le possibili cause:\n" "\t- libappindicator non è installato\n" "\t- typelib è fornita da un pacchetto separato\n" "\t- La build è stata eseguita con introspezione disabilitata\n" "Avvio di udiskie senza icona di sistema.\n" #: ../udiskie/cli.py:436 msgid "" "The 'notify_command' option was renamed to 'event_hook'. The old name still " "works, but may be removed in a future version. Please change your command " "line and config to use the new name." msgstr "" #: ../udiskie/cli.py:443 msgid "Ignoring 'notify_command' in favor of 'event_hook'." msgstr "" #: ../udiskie/config.py:129 msgid "Unknown matching attribute: {!r}" msgstr "" #: ../udiskie/config.py:131 #, python-brace-format msgid "new rule: {0}" msgstr "nuova regola: {0}" #: ../udiskie/config.py:134 #, python-brace-format msgid "{0} -> {1}" msgstr "{0} -> {1}" #: ../udiskie/config.py:153 #, python-brace-format msgid "{0} matched {1}" msgstr "{0} è uguale ad {1}" #: ../udiskie/config.py:230 #, python-brace-format msgid "Failed to read config file: {0}" msgstr "Impossibile leggere il file di configurazione: {0}" #: ../udiskie/config.py:233 msgid "Failed to read {0!r}: {1}" msgstr "Impossibile leggere {0!r}: {1}" #: ../udiskie/depend.py:59 msgid "" "Missing runtime dependency GTK 3. Falling back to GTK 2 for password prompt" msgstr "" "Dipendenza mancante: GTK 3. Viene usato GTK 2 per richiedere la password" #: ../udiskie/depend.py:65 msgid "X server not connected!" msgstr "server X non connesso!" #: ../udiskie/mount.py:29 #, python-brace-format msgid "failed to {0} {1}: {2}" msgstr "Errore nel {0} {1}: {2}" #: ../udiskie/mount.py:117 #, python-brace-format msgid "not browsing {0}: not mounted" msgstr "Impossibile aprire {0}: non montato" #: ../udiskie/mount.py:120 #, python-brace-format msgid "not browsing {0}: no program" msgstr "Impossibile aprire {0}: nessun programma" #: ../udiskie/mount.py:122 ../udiskie/mount.py:142 #, python-brace-format msgid "opening {0} on {0.mount_paths[0]}" msgstr "Apertura di {0} su {0.mount_paths[0]}" #: ../udiskie/mount.py:124 ../udiskie/mount.py:144 #, python-brace-format msgid "opened {0} on {0.mount_paths[0]}" msgstr "Aperto {0} su {0.mount_paths[0]}" #: ../udiskie/mount.py:137 #, python-brace-format msgid "not opening terminal {0}: not mounted" msgstr "Impossibile aprire il terminale {0}: non montato" #: ../udiskie/mount.py:140 #, python-brace-format msgid "not opening terminal {0}: no program" msgstr "Impossibile aprire il terminale {0}: nessun programma" #: ../udiskie/mount.py:158 #, python-brace-format msgid "not mounting {0}: unhandled device" msgstr "Impossibile montare {0}: dispositivo non gestibile" #: ../udiskie/mount.py:161 #, python-brace-format msgid "not mounting {0}: already mounted" msgstr "Impossibile montare {0}: è stato già montato" #: ../udiskie/mount.py:165 #, python-brace-format msgid "mounting {0} with {1}" msgstr "montato {0} con {1}" #: ../udiskie/mount.py:168 #, python-brace-format msgid "mounted {0} on {1}" msgstr "montato {0} in {1}" #: ../udiskie/mount.py:174 msgid "" "Mounting NTFS device with default driver.\n" "Please install 'ntfs-3g' if you experience problems or the device is " "readonly." msgstr "" "Montatura dispositivo NTFS con il driver di default.\n" "Per favore, installa 'ntfs-3g' se riscontri problemi o il dispositivo è di " "sola lettura." #: ../udiskie/mount.py:188 #, python-brace-format msgid "not unmounting {0}: unhandled device" msgstr "Impossibile montare {0}: dispositivo non gestibile" #: ../udiskie/mount.py:191 #, python-brace-format msgid "not unmounting {0}: not mounted" msgstr "Impossibile smontare {0}: non è montato" #: ../udiskie/mount.py:193 #, python-brace-format msgid "unmounting {0}" msgstr "Smontando {0}" #: ../udiskie/mount.py:195 #, fuzzy, python-brace-format msgid "unmounted {0}" msgstr "Smontato {0}" #: ../udiskie/mount.py:209 #, python-brace-format msgid "not unlocking {0}: unhandled device" msgstr "Impossibile sbloccare {0}: dispositivo non gestibile" #: ../udiskie/mount.py:212 #, python-brace-format msgid "not unlocking {0}: already unlocked" msgstr "Impossibile sbloccare {0}: dispositivo già sbloccato" #: ../udiskie/mount.py:215 #, python-brace-format msgid "not unlocking {0}: no password prompt" msgstr "Impossibile sbloccare {0}: niente input password" #: ../udiskie/mount.py:229 #, python-brace-format msgid "not unlocking {0}: cancelled by user" msgstr "Impossibile sbloccare {0}: annullato dall'utente" #: ../udiskie/mount.py:234 #, python-brace-format msgid "unlocking {0} using keyfile" msgstr "sblocco di {0} con keyfile" #: ../udiskie/mount.py:237 #, python-brace-format msgid "unlocking {0}" msgstr "sblocco di {0}" #: ../udiskie/mount.py:240 #, python-brace-format msgid "unlocked {0}" msgstr "{0} é stato sbloccato" #: ../udiskie/mount.py:249 #, python-brace-format msgid "no cached key for {0}" msgstr "nessuna chiave memorizzata per {0}" #: ../udiskie/mount.py:251 #, python-brace-format msgid "unlocking {0} using cached password" msgstr "Sblocco di {0} con password memorizzata" #: ../udiskie/mount.py:255 #, python-brace-format msgid "failed to unlock {0} using cached password" msgstr "Impossibile sbloccare {0} usando la password memorizzata" #: ../udiskie/mount.py:258 #, python-brace-format msgid "unlocked {0} using cached password" msgstr "{0} é stato sbloccato con la password memorizzata" #: ../udiskie/mount.py:266 msgid "No matching keyfile rule for {}." msgstr "Nessuna regola keyfile corrispondente per {}." #: ../udiskie/mount.py:272 #, python-brace-format msgid "keyfile for {0} not found: {1}" msgstr "keyfile per {0} non trovato: {1}" #: ../udiskie/mount.py:274 #, python-brace-format msgid "unlocking {0} using keyfile {1}" msgstr "Sto sbloccando {0} usando il keyfile {1}" #: ../udiskie/mount.py:278 #, python-brace-format msgid "failed to unlock {0} using keyfile" msgstr "Impossibile sbloccare {0} usando il keyfile" #: ../udiskie/mount.py:281 #, python-brace-format msgid "unlocked {0} using keyfile" msgstr "{0} è stato sbloccato usando il keyfile" #: ../udiskie/mount.py:307 #, python-brace-format msgid "not locking {0}: unhandled device" msgstr "Impossibile bloccare {0}: dispositivo non gestibile" #: ../udiskie/mount.py:310 #, python-brace-format msgid "not locking {0}: not unlocked" msgstr "Impossibile bloccare {0}: è già bloccato" #: ../udiskie/mount.py:312 #, python-brace-format msgid "locking {0}" msgstr "Sto bloccando {0}" #: ../udiskie/mount.py:314 #, python-brace-format msgid "locked {0}" msgstr "{0} è stato bloccato" #: ../udiskie/mount.py:351 ../udiskie/mount.py:394 #, python-brace-format msgid "not adding {0}: unhandled device" msgstr "Impossibile aggiungere {0}: dispositivo non gestibile" #: ../udiskie/mount.py:430 ../udiskie/mount.py:480 #, python-brace-format msgid "not removing {0}: unhandled device" msgstr "Impossibile rimuovere {0}: dispositivo non gestibile" #: ../udiskie/mount.py:505 #, python-brace-format msgid "not ejecting {0}: unhandled device" msgstr "Impossibile espellere {0}: dispositivo non gestibile" #: ../udiskie/mount.py:509 #, python-brace-format msgid "not ejecting {0}: drive not ejectable" msgstr "Impossibile espellere {0}: dispositivo non rimovibile" #: ../udiskie/mount.py:515 #, python-brace-format msgid "ejecting {0}" msgstr "Sto espellendo {0}" #: ../udiskie/mount.py:517 #, python-brace-format msgid "ejected {0}" msgstr "{0} è stato espulso" #: ../udiskie/mount.py:531 #, python-brace-format msgid "not detaching {0}: unhandled device" msgstr "Impossibile staccare {0}: dispositivo non gestibile" #: ../udiskie/mount.py:535 #, python-brace-format msgid "not detaching {0}: drive not detachable" msgstr "Impossibile staccare {0}: dispositivo non staccabile" #: ../udiskie/mount.py:539 #, python-brace-format msgid "detaching {0}" msgstr "Sto staccando {0}" #: ../udiskie/mount.py:544 #, python-brace-format msgid "detached {0}" msgstr "{0} è stato staccato" #: ../udiskie/mount.py:595 #, python-brace-format msgid "not setting up {0}: already up" msgstr "Impossibile impostare {0}: è già impostato" #: ../udiskie/mount.py:598 #, python-brace-format msgid "not setting up {0}: not a file" msgstr "Impossibile impostare {0}: non è un file" #: ../udiskie/mount.py:600 #, fuzzy, python-brace-format msgid "setting up loop device {0}" msgstr "Sto impostando {0}" #: ../udiskie/mount.py:618 #, python-brace-format msgid "" "Insufficient permission to open {0} in read-write mode. Retrying in read-" "only mode." msgstr "" #: ../udiskie/mount.py:630 #, python-brace-format msgid "set up {0} as {1}" msgstr "Imposta {0} come {1}" #: ../udiskie/mount.py:645 #, python-brace-format msgid "not deleting {0}: unhandled device" msgstr "Impossibile cancellare {0}: dispositivo non gestibile" #: ../udiskie/mount.py:649 #, python-brace-format msgid "deleting {0}" msgstr "Sto cancellando {0}" #: ../udiskie/mount.py:651 #, python-brace-format msgid "deleted {0}" msgstr "{0} è stato cancellato" #: ../udiskie/mount.py:777 #, python-brace-format msgid "Browse {0}" msgstr "Apri {0}" #: ../udiskie/mount.py:778 #, python-brace-format msgid "Hack on {0}" msgstr "" #: ../udiskie/mount.py:779 #, python-brace-format msgid "Mount {0}" msgstr "Monta {0}" #: ../udiskie/mount.py:780 #, python-brace-format msgid "Unmount {0}" msgstr "Smonta {0}" #: ../udiskie/mount.py:781 #, python-brace-format msgid "Unlock {0}" msgstr "Sblocca {0}" #: ../udiskie/mount.py:782 #, python-brace-format msgid "Lock {0}" msgstr "Blocca {0}" #: ../udiskie/mount.py:783 #, python-brace-format msgid "Eject {1}" msgstr "Espelli {1}" #: ../udiskie/mount.py:784 #, python-brace-format msgid "Unpower {1}" msgstr "Rimuovi {1}" #: ../udiskie/mount.py:785 #, python-brace-format msgid "Clear password for {0}" msgstr "Dimentica la password per {0}" #: ../udiskie/mount.py:786 #, python-brace-format msgid "Detach {0}" msgstr "Stacca {0}" #: ../udiskie/notify.py:62 msgid "Browse directory" msgstr "Apri la cartella" #: ../udiskie/notify.py:64 msgid "Open terminal" msgstr "Apri il terminale" #: ../udiskie/notify.py:68 msgid "Device mounted" msgstr "Dispositivo montato" #: ../udiskie/notify.py:69 #, python-brace-format msgid "{0.ui_label} mounted on {0.mount_paths[0]}" msgstr "{0.ui_label} montato in {0.mount_paths[0]}" #: ../udiskie/notify.py:80 msgid "Device unmounted" msgstr "Dispositivo smontato" #: ../udiskie/notify.py:81 #, python-brace-format msgid "{0.ui_label} unmounted" msgstr "{0.ui_label} smontato" #: ../udiskie/notify.py:90 msgid "Device locked" msgstr "Dispositivo bloccato" #: ../udiskie/notify.py:91 #, python-brace-format msgid "{0.device_presentation} locked" msgstr "{0.device_presentation} bloccato" #: ../udiskie/notify.py:100 msgid "Device unlocked" msgstr "Dispositivo sbloccato" #: ../udiskie/notify.py:101 #, python-brace-format msgid "{0.device_presentation} unlocked" msgstr "{0.device_presentation} sbloccato" #: ../udiskie/notify.py:135 msgid "Device added" msgstr "Nuovo dispositivo" #: ../udiskie/notify.py:136 #, python-brace-format msgid "device appeared on {0.device_presentation}" msgstr "Aggiunto dispositivo da {0.device_presentation}" #: ../udiskie/notify.py:155 msgid "Device removed" msgstr "Dispositivo rimosso" #: ../udiskie/notify.py:156 #, python-brace-format msgid "device disappeared on {0.device_presentation}" msgstr "Dispositivo rimosso da {0.device_presentation}" #: ../udiskie/notify.py:165 #, python-brace-format msgid "" "failed to {0} {1}:\n" "{2}" msgstr "" "Errore in {0} {1}:\n" "{2}" #: ../udiskie/notify.py:167 #, python-brace-format msgid "failed to {0} device {1}." msgstr "Errore in {0} dispositivo {1}." #: ../udiskie/notify.py:173 msgid "Retry" msgstr "Riprova" #: ../udiskie/notify.py:176 msgid "Job failed" msgstr "Lavoro fallito" #: ../udiskie/notify.py:207 #, python-brace-format msgid "Failed to show notification: {0}" msgstr "Impossibile mostrare la notifica: {0}" #: ../udiskie/prompt.py:96 msgid "Show password" msgstr "Mostra password" #: ../udiskie/prompt.py:101 msgid "Open keyfile…" msgstr "Apri keyfile…" #: ../udiskie/prompt.py:108 msgid "Cache password" msgstr "Memorizza password" #: ../udiskie/prompt.py:123 msgid "Open a keyfile to unlock the LUKS device" msgstr "Apri un keyfile per sbloccare il dispositivo LUKS" #: ../udiskie/prompt.py:157 ../udiskie/prompt.py:167 #, python-brace-format msgid "Enter password for {0.device_presentation}: " msgstr "Immetti la password per {0.device_presentation}: " #: ../udiskie/prompt.py:203 msgid "Unknown device attribute {!r} in format string: {!r}" msgstr "Attributo dispositivo sconosciuto {!r} nella stringa di formato: {!r}" #: ../udiskie/prompt.py:255 msgid "" "Can't find file browser: {0!r}. You may want to change the value for the '-" "f' option." msgstr "" "Impossibile trovare esplora risorse: {0!r}. Potresti voler cambiare il " "valore per l'opzione '-f'." #: ../udiskie/tray.py:182 msgid "Managed devices" msgstr "" #: ../udiskie/tray.py:198 msgid "Mount disc image" msgstr "Monta l'immagine del disco" #: ../udiskie/tray.py:204 msgid "Enable automounting" msgstr "Abilita l'automontaggio" #: ../udiskie/tray.py:210 msgid "Enable notifications" msgstr "Abilita le notifiche" #: ../udiskie/tray.py:219 msgid "Quit" msgstr "Esci" #: ../udiskie/tray.py:226 msgid "Open disc image" msgstr "Apri l'immagine del disco" #: ../udiskie/tray.py:228 msgid "Open" msgstr "Apri" #: ../udiskie/tray.py:229 msgid "Cancel" msgstr "Annulla" #: ../udiskie/tray.py:269 msgid "Invalid node!" msgstr "Nodo invalido!" #: ../udiskie/tray.py:271 msgid "No external devices" msgstr "Nessun dispositivo esterno" #: ../udiskie/tray.py:387 msgid "udiskie" msgstr "udiskie" #: ../udiskie/udisks2.py:661 #, python-brace-format msgid "found device owning \"{0}\": \"{1}\"" msgstr "Trovato dispositivo a cui appartiene \"{0}\": \"{1}\"" #: ../udiskie/udisks2.py:664 #, python-brace-format msgid "no device found owning \"{0}\"" msgstr "Nessun dispositivo trovato a cui appartiene \"{0}\"" #: ../udiskie/udisks2.py:683 #, python-brace-format msgid "Daemon version: {0}" msgstr "Versione demone: {0}" #: ../udiskie/udisks2.py:688 #, python-brace-format msgid "Keyfile support: {0}" msgstr "Supporto keyfile: {0}" #: ../udiskie/udisks2.py:767 #, python-brace-format msgid "+++ {0}: {1}" msgstr "+++ {0}: {1}" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/lang/ru_RU.po0000644000175100001770000005114114612243552015106 0ustar00runnerdocker# Russian translations for udiskie package. # Copyright (C) 2021 Thomas Gläßle # This file is distributed under the same license as the udiskie package. # mr-GreyWolf , 2019. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-04-16 21:10+0000\n" "PO-Revision-Date: 2022-04-17 13:10+0000\n" "Last-Translator: Weblate Admin \n" "Language-Team: Russian \n" "Language: ru_RU\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 4.11.2\n" #: ../udiskie/cli.py:46 #, python-brace-format msgid "These options are mutually exclusive: {0}" msgstr "Эти параметры являются взаимоисключающими: {0}" #: ../udiskie/cli.py:119 #, fuzzy msgid "" "\n" " Note, that the options in the individual groups are mutually exclusive.\n" "\n" " The config file can be a JSON or preferably a YAML file. For an\n" " example, see the MAN page (or doc/udiskie.8.txt in the repository).\n" " " msgstr "" "\n" " Обратите внимание, что параметры в отдельных группах являются " "взаимоисключающими.\n" "\n" " Файл конфигурации может быть JSON или, предпочтительно, YAML-файлом\n" " Примеры приведены в документации.\n" " " #: ../udiskie/cli.py:139 #, python-format msgid "%(message)s" msgstr "%(message)s" #: ../udiskie/cli.py:141 #, python-format msgid "%(levelname)s [%(asctime)s] %(name)s: %(message)s" msgstr "%(levelname)s [%(asctime)s] %(name)s: %(message)s" #: ../udiskie/cli.py:386 msgid "" "Typelib for 'libnotify' is not available. Possible causes include:\n" "\t- libnotify is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- libnotify was built with introspection disabled\n" "\n" "Starting udiskie without notifications." msgstr "" "Typelib для 'libnotify' недоступна. Возможные причины:\n" "\t- libnotify не установлена\n" "\t- typelib поставляется в виде отдельного пакета\n" "\t- libnotify была собрана с отключенным самоанализом\n" "\n" "Запуск udiskie выполнен без уведомлений." #: ../udiskie/cli.py:400 #, fuzzy msgid "" "Not run within X or Wayland session.\n" "Starting udiskie without tray icon.\n" msgstr "" "Не запущено в сессии X-Windows. \n" "Запустите udiskie без иконки в трее.\n" #: ../udiskie/cli.py:407 msgid "" "Typelib for 'Gtk 3.0' is not available. Possible causes include:\n" "\t- GTK3 is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- GTK3 was built with introspection disabled\n" "Starting udiskie without tray icon.\n" msgstr "" "Typelib для GTK 3.0 недоступен. Возможные причины:\n" "\t- GTK3 не установлен\n" "\t- typelib поставляется в виде отдельного пакета\n" "\t- GTK3 был собран с отключенным самоанализом\n" "Запуск udiskie выполнен без иконки в трее.\n" #: ../udiskie/cli.py:417 #, fuzzy msgid "" "Typelib for 'AppIndicator3 0.1' is not available. Possible causes include:\n" "\t- libappindicator is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- it was built with introspection disabled\n" "Starting udiskie without appindicator icon.\n" msgstr "" "Typelib для GTK 3.0 недоступен. Возможные причины:\n" "\t- GTK3 не установлен\n" "\t- typelib поставляется в виде отдельного пакета\n" "\t- GTK3 был собран с отключенным самоанализом\n" "Запуск udiskie выполнен без иконки в трее.\n" #: ../udiskie/cli.py:436 msgid "" "The 'notify_command' option was renamed to 'event_hook'. The old name still " "works, but may be removed in a future version. Please change your command " "line and config to use the new name." msgstr "" #: ../udiskie/cli.py:443 msgid "Ignoring 'notify_command' in favor of 'event_hook'." msgstr "" #: ../udiskie/config.py:129 msgid "Unknown matching attribute: {!r}" msgstr "Неизвестный атрибут соответствия: {!r}" #: ../udiskie/config.py:131 #, python-brace-format msgid "new rule: {0}" msgstr "" #: ../udiskie/config.py:134 #, fuzzy, python-brace-format msgid "{0} -> {1}" msgstr "+++ {0}: {1}" #: ../udiskie/config.py:153 #, fuzzy, python-brace-format msgid "{0} matched {1}" msgstr "{0} создан(о)" #: ../udiskie/config.py:230 #, python-brace-format msgid "Failed to read config file: {0}" msgstr "Невозможно прочитать файл настроек: {0}" #: ../udiskie/config.py:233 msgid "Failed to read {0!r}: {1}" msgstr "Невозможно прочитать {0!r}: {1}" #: ../udiskie/depend.py:59 msgid "" "Missing runtime dependency GTK 3. Falling back to GTK 2 for password prompt" msgstr "Отсутствуют библиотеки GTK 3. Возврат к GTK 2 для запроса пароля" #: ../udiskie/depend.py:65 msgid "X server not connected!" msgstr "X-сервер не подключен!" #: ../udiskie/mount.py:29 #, python-brace-format msgid "failed to {0} {1}: {2}" msgstr "Невозможно {0} {1}: {2}" #: ../udiskie/mount.py:117 #, python-brace-format msgid "not browsing {0}: not mounted" msgstr "обзор недоступен т.к. {0}: не смонтирован" #: ../udiskie/mount.py:120 #, python-brace-format msgid "not browsing {0}: no program" msgstr "обзор {0} недоступен т.к. нет программы" #: ../udiskie/mount.py:122 ../udiskie/mount.py:142 #, python-brace-format msgid "opening {0} on {0.mount_paths[0]}" msgstr "открытие {0} на {0.mount_paths[0]}" #: ../udiskie/mount.py:124 ../udiskie/mount.py:144 #, python-brace-format msgid "opened {0} on {0.mount_paths[0]}" msgstr "открыто {0} on {0.mount_paths[0]}" #: ../udiskie/mount.py:137 #, fuzzy, python-brace-format msgid "not opening terminal {0}: not mounted" msgstr "не размонтировано {0}: не смонтировано" #: ../udiskie/mount.py:140 #, fuzzy, python-brace-format msgid "not opening terminal {0}: no program" msgstr "обзор {0} недоступен т.к. нет программы" #: ../udiskie/mount.py:158 #, python-brace-format msgid "not mounting {0}: unhandled device" msgstr "не смонтировано {0}: необработанное устройство" #: ../udiskie/mount.py:161 #, python-brace-format msgid "not mounting {0}: already mounted" msgstr "не смонтировано {0}: уже смонтировано" #: ../udiskie/mount.py:165 #, python-brace-format msgid "mounting {0} with {1}" msgstr "монтируется {0} с {1}" #: ../udiskie/mount.py:168 #, python-brace-format msgid "mounted {0} on {1}" msgstr "смонтировано {0} в {1}" #: ../udiskie/mount.py:174 msgid "" "Mounting NTFS device with default driver.\n" "Please install 'ntfs-3g' if you experience problems or the device is " "readonly." msgstr "" "Устройство с NTFS смонтировано используя драйвер по умолчанию.\n" "Пожалуйста установите 'ntfs-3g' при возникновении проблем или устройство " "доступно только для чтения." #: ../udiskie/mount.py:188 #, python-brace-format msgid "not unmounting {0}: unhandled device" msgstr "не размонтировано {0}: необработанное устройство" #: ../udiskie/mount.py:191 #, python-brace-format msgid "not unmounting {0}: not mounted" msgstr "не размонтировано {0}: не смонтировано" #: ../udiskie/mount.py:193 #, python-brace-format msgid "unmounting {0}" msgstr "размонтируется {0}" #: ../udiskie/mount.py:195 #, python-brace-format msgid "unmounted {0}" msgstr "размонтировно {0}" #: ../udiskie/mount.py:209 #, python-brace-format msgid "not unlocking {0}: unhandled device" msgstr "не разблокировано {0}: необработанное устройство" #: ../udiskie/mount.py:212 #, python-brace-format msgid "not unlocking {0}: already unlocked" msgstr "не разблокировано {0}: уже разблокировано" #: ../udiskie/mount.py:215 #, python-brace-format msgid "not unlocking {0}: no password prompt" msgstr "не разблокировано {0}: нет пароля" #: ../udiskie/mount.py:229 #, python-brace-format msgid "not unlocking {0}: cancelled by user" msgstr "не разблокировано {0}: отменено пользователем" #: ../udiskie/mount.py:234 #, python-brace-format msgid "unlocking {0} using keyfile" msgstr "разблокировано {0} с помощью ключевого файла" #: ../udiskie/mount.py:237 #, python-brace-format msgid "unlocking {0}" msgstr "разблокировка {0}" #: ../udiskie/mount.py:240 #, python-brace-format msgid "unlocked {0}" msgstr "разблокировано {0}" #: ../udiskie/mount.py:249 #, fuzzy, python-brace-format msgid "no cached key for {0}" msgstr "Нет соответствующего правила ключевого файла для {}." #: ../udiskie/mount.py:251 #, python-brace-format msgid "unlocking {0} using cached password" msgstr "разблокировка {0} с помощью кэшированного пароля" #: ../udiskie/mount.py:255 #, python-brace-format msgid "failed to unlock {0} using cached password" msgstr "не удалось разблокировать {0} с помощью кэшированного пароля" #: ../udiskie/mount.py:258 #, python-brace-format msgid "unlocked {0} using cached password" msgstr "разблокировано {0} с помощью кэшированного пароля" #: ../udiskie/mount.py:266 msgid "No matching keyfile rule for {}." msgstr "Нет соответствующего правила ключевого файла для {}." #: ../udiskie/mount.py:272 #, python-brace-format msgid "keyfile for {0} not found: {1}" msgstr "ключевой файл для {0} не найден: {1}" #: ../udiskie/mount.py:274 #, python-brace-format msgid "unlocking {0} using keyfile {1}" msgstr "разблокируется {0} с помощью ключевого файла {1}" #: ../udiskie/mount.py:278 #, python-brace-format msgid "failed to unlock {0} using keyfile" msgstr "не удалось разблокировать {0} с помощью ключевого файла" #: ../udiskie/mount.py:281 #, python-brace-format msgid "unlocked {0} using keyfile" msgstr "разблокировно {0} с помощью ключевого файла" #: ../udiskie/mount.py:307 #, python-brace-format msgid "not locking {0}: unhandled device" msgstr "не блокировано {0}: необработанное устройство" #: ../udiskie/mount.py:310 #, python-brace-format msgid "not locking {0}: not unlocked" msgstr "разблокирован {0} с помощью ключевого файла" #: ../udiskie/mount.py:312 #, python-brace-format msgid "locking {0}" msgstr "заперто {0}" #: ../udiskie/mount.py:314 #, python-brace-format msgid "locked {0}" msgstr "заблокировано {0}" #: ../udiskie/mount.py:351 ../udiskie/mount.py:394 #, python-brace-format msgid "not adding {0}: unhandled device" msgstr "не добавлено {0}: необработанное устройство" #: ../udiskie/mount.py:430 ../udiskie/mount.py:480 #, python-brace-format msgid "not removing {0}: unhandled device" msgstr "не удалено {0}: необработанное устройство" #: ../udiskie/mount.py:505 #, python-brace-format msgid "not ejecting {0}: unhandled device" msgstr "не извлечено {0}: необработанное устройство" #: ../udiskie/mount.py:509 #, python-brace-format msgid "not ejecting {0}: drive not ejectable" msgstr "не извлечено {0}: лоток привод не извлекается" #: ../udiskie/mount.py:515 #, python-brace-format msgid "ejecting {0}" msgstr "выбросить {0}" #: ../udiskie/mount.py:517 #, python-brace-format msgid "ejected {0}" msgstr "выброшено {0}" #: ../udiskie/mount.py:531 #, python-brace-format msgid "not detaching {0}: unhandled device" msgstr "не отсоединено {0}: необработанное устройство" #: ../udiskie/mount.py:535 #, python-brace-format msgid "not detaching {0}: drive not detachable" msgstr "не отсоединено{0}: устройство не отсоединяемое" #: ../udiskie/mount.py:539 #, python-brace-format msgid "detaching {0}" msgstr "отсоединение {0}" #: ../udiskie/mount.py:544 #, python-brace-format msgid "detached {0}" msgstr "отсоединено {0}" #: ../udiskie/mount.py:595 #, python-brace-format msgid "not setting up {0}: already up" msgstr "не включено {0}: уже включено" #: ../udiskie/mount.py:598 #, python-brace-format msgid "not setting up {0}: not a file" msgstr "не включено {0}: это не файл" #: ../udiskie/mount.py:600 #, fuzzy, python-brace-format msgid "setting up loop device {0}" msgstr "настройка {0}" #: ../udiskie/mount.py:618 #, python-brace-format msgid "" "Insufficient permission to open {0} in read-write mode. Retrying in read-" "only mode." msgstr "" #: ../udiskie/mount.py:630 #, python-brace-format msgid "set up {0} as {1}" msgstr "установлено {0} как {1}" #: ../udiskie/mount.py:645 #, python-brace-format msgid "not deleting {0}: unhandled device" msgstr "не удаляется {0}: необработанное устройство" #: ../udiskie/mount.py:649 #, python-brace-format msgid "deleting {0}" msgstr "удаление {0}" #: ../udiskie/mount.py:651 #, python-brace-format msgid "deleted {0}" msgstr "удалён {0}" #: ../udiskie/mount.py:777 #, python-brace-format msgid "Browse {0}" msgstr "Обзор {0}" #: ../udiskie/mount.py:778 #, fuzzy, python-brace-format msgid "Hack on {0}" msgstr "заперто {0}" #: ../udiskie/mount.py:779 #, python-brace-format msgid "Mount {0}" msgstr "Монтировать {0}" #: ../udiskie/mount.py:780 #, python-brace-format msgid "Unmount {0}" msgstr "Размонтировать {0}" #: ../udiskie/mount.py:781 #, python-brace-format msgid "Unlock {0}" msgstr "Разблокировать {0}" #: ../udiskie/mount.py:782 #, python-brace-format msgid "Lock {0}" msgstr "Заблокировать {0}" #: ../udiskie/mount.py:783 #, python-brace-format msgid "Eject {1}" msgstr "Извлечь {1}" #: ../udiskie/mount.py:784 #, python-brace-format msgid "Unpower {1}" msgstr "Отключить питание {1}" #: ../udiskie/mount.py:785 #, python-brace-format msgid "Clear password for {0}" msgstr "Очистить пароль для {0}" #: ../udiskie/mount.py:786 #, python-brace-format msgid "Detach {0}" msgstr "Отсоединить {0}" #: ../udiskie/notify.py:62 msgid "Browse directory" msgstr "Обзор папки" #: ../udiskie/notify.py:64 msgid "Open terminal" msgstr "" #: ../udiskie/notify.py:68 msgid "Device mounted" msgstr "Устройство смонтировано" #: ../udiskie/notify.py:69 #, python-brace-format msgid "{0.ui_label} mounted on {0.mount_paths[0]}" msgstr "{0.ui_label} смонтировано в {0.mount_paths[0]}" #: ../udiskie/notify.py:80 msgid "Device unmounted" msgstr "Устройство размонтировано" #: ../udiskie/notify.py:81 #, python-brace-format msgid "{0.ui_label} unmounted" msgstr "{0.ui_label} размонтировано" #: ../udiskie/notify.py:90 msgid "Device locked" msgstr "Устройство заблокировано" #: ../udiskie/notify.py:91 #, python-brace-format msgid "{0.device_presentation} locked" msgstr "{0.device_presentation} заблокированно" #: ../udiskie/notify.py:100 msgid "Device unlocked" msgstr "Устройство разблокировано" #: ../udiskie/notify.py:101 #, python-brace-format msgid "{0.device_presentation} unlocked" msgstr "{0.device_presentation} разблокированно" #: ../udiskie/notify.py:135 msgid "Device added" msgstr "Устройство добавлено" #: ../udiskie/notify.py:136 #, python-brace-format msgid "device appeared on {0.device_presentation}" msgstr "устройство появилось в {0.device_presentation}" #: ../udiskie/notify.py:155 msgid "Device removed" msgstr "Устройство удалено" #: ../udiskie/notify.py:156 #, python-brace-format msgid "device disappeared on {0.device_presentation}" msgstr "устройство исчезло в {0.device_presentation}" #: ../udiskie/notify.py:165 #, python-brace-format msgid "" "failed to {0} {1}:\n" "{2}" msgstr "" "неудачно {0} {1}:\n" "{2}" #: ../udiskie/notify.py:167 #, python-brace-format msgid "failed to {0} device {1}." msgstr "неудачно {0} устройство {1}." #: ../udiskie/notify.py:173 msgid "Retry" msgstr "Повторить попытку" #: ../udiskie/notify.py:176 msgid "Job failed" msgstr "Задание не выполнено" #: ../udiskie/notify.py:207 #, python-brace-format msgid "Failed to show notification: {0}" msgstr "Не удалось показать уведомление: {0}" #: ../udiskie/prompt.py:96 msgid "Show password" msgstr "" #: ../udiskie/prompt.py:101 msgid "Open keyfile…" msgstr "" #: ../udiskie/prompt.py:108 #, fuzzy msgid "Cache password" msgstr "Очистить пароль для {0}" #: ../udiskie/prompt.py:123 msgid "Open a keyfile to unlock the LUKS device" msgstr "" #: ../udiskie/prompt.py:157 ../udiskie/prompt.py:167 #, python-brace-format msgid "Enter password for {0.device_presentation}: " msgstr "Введите пароль для {0.device_presentation}: " #: ../udiskie/prompt.py:203 msgid "Unknown device attribute {!r} in format string: {!r}" msgstr "Неизвестный атрибут устройства {!r} в формате строки: {!r}" #: ../udiskie/prompt.py:255 msgid "" "Can't find file browser: {0!r}. You may want to change the value for the '-" "f' option." msgstr "" "Не найдена программа для работы с файлами: {0!r}. Вы можете изменить " "значение указанное после '-f'." #: ../udiskie/tray.py:182 msgid "Managed devices" msgstr "" #: ../udiskie/tray.py:198 msgid "Mount disc image" msgstr "Монтировать образ диска" #: ../udiskie/tray.py:204 msgid "Enable automounting" msgstr "Включить автоматическое монтирование" #: ../udiskie/tray.py:210 msgid "Enable notifications" msgstr "Включить уведомления" #: ../udiskie/tray.py:219 msgid "Quit" msgstr "Выход" #: ../udiskie/tray.py:226 msgid "Open disc image" msgstr "Открыть образ диска" #: ../udiskie/tray.py:228 msgid "Open" msgstr "Открыть" #: ../udiskie/tray.py:229 msgid "Cancel" msgstr "Отмена" #: ../udiskie/tray.py:269 msgid "Invalid node!" msgstr "" #: ../udiskie/tray.py:271 msgid "No external devices" msgstr "Нет внешних устройств" #: ../udiskie/tray.py:387 msgid "udiskie" msgstr "udiskie" #: ../udiskie/udisks2.py:661 #, python-brace-format msgid "found device owning \"{0}\": \"{1}\"" msgstr "найдено устройство, владеющее \"{0}\": \"{1}\"" #: ../udiskie/udisks2.py:664 #, python-brace-format msgid "no device found owning \"{0}\"" msgstr "не найдены устройства, владеющие \"{0}\"" #: ../udiskie/udisks2.py:683 #, python-brace-format msgid "Daemon version: {0}" msgstr "Версия демона: {0}" #: ../udiskie/udisks2.py:688 #, python-brace-format msgid "Keyfile support: {0}" msgstr "Поддержка ключевых файлов: {0}" #: ../udiskie/udisks2.py:767 #, python-brace-format msgid "+++ {0}: {1}" msgstr "+++ {0}: {1}" #~ msgid "Positional field in format string {!r} is deprecated." #~ msgstr "Позиционное поле в формате строки {!r} является устаревшим." #~ msgid "{0}(match={1!r}, value={2!r})" #~ msgstr "{0}(соответствие={1!r}, значение={2!r})" #~ msgid "{0}(match={1!r}, {2}={3!r}) used for {4}" #~ msgstr "{0}(соответствие={1!r}, {2}={3!r}) использовано в {4}" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/lang/sk_SK.po0000644000175100001770000004010114612243552015056 0ustar00runnerdocker# Slovak translations for udiskie package # Slovenské preklady pre balík udiskie. # Copyright (C) 2021 Thomas Gläßle # This file is distributed under the same license as the udiskie package. # oli , 2019. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-04-16 21:10+0000\n" "PO-Revision-Date: 2019-05-02 14:20+0200\n" "Last-Translator: Jose Riha \n" "Language-Team: Slovak\n" "Language: sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "X-Generator: Poedit 2.2.1\n" #: ../udiskie/cli.py:46 #, python-brace-format msgid "These options are mutually exclusive: {0}" msgstr "Tieto voľby sa navzájom vylučujú: {0}" #: ../udiskie/cli.py:119 msgid "" "\n" " Note, that the options in the individual groups are mutually exclusive.\n" "\n" " The config file can be a JSON or preferably a YAML file. For an\n" " example, see the MAN page (or doc/udiskie.8.txt in the repository).\n" " " msgstr "" #: ../udiskie/cli.py:139 #, python-format msgid "%(message)s" msgstr "%(message)s" #: ../udiskie/cli.py:141 #, python-format msgid "%(levelname)s [%(asctime)s] %(name)s: %(message)s" msgstr "%(levelname)s [%(asctime)s] %(name)s: %(message)s" #: ../udiskie/cli.py:386 msgid "" "Typelib for 'libnotify' is not available. Possible causes include:\n" "\t- libnotify is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- libnotify was built with introspection disabled\n" "\n" "Starting udiskie without notifications." msgstr "" #: ../udiskie/cli.py:400 msgid "" "Not run within X or Wayland session.\n" "Starting udiskie without tray icon.\n" msgstr "" #: ../udiskie/cli.py:407 msgid "" "Typelib for 'Gtk 3.0' is not available. Possible causes include:\n" "\t- GTK3 is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- GTK3 was built with introspection disabled\n" "Starting udiskie without tray icon.\n" msgstr "" #: ../udiskie/cli.py:417 msgid "" "Typelib for 'AppIndicator3 0.1' is not available. Possible causes include:\n" "\t- libappindicator is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- it was built with introspection disabled\n" "Starting udiskie without appindicator icon.\n" msgstr "" #: ../udiskie/cli.py:436 msgid "" "The 'notify_command' option was renamed to 'event_hook'. The old name still " "works, but may be removed in a future version. Please change your command " "line and config to use the new name." msgstr "" #: ../udiskie/cli.py:443 msgid "Ignoring 'notify_command' in favor of 'event_hook'." msgstr "" #: ../udiskie/config.py:129 msgid "Unknown matching attribute: {!r}" msgstr "Neznámy atribút pre vyhľadávanie: {!r}" #: ../udiskie/config.py:131 #, python-brace-format msgid "new rule: {0}" msgstr "" #: ../udiskie/config.py:134 #, fuzzy, python-brace-format msgid "{0} -> {1}" msgstr "+++ {0}: {1}" #: ../udiskie/config.py:153 #, fuzzy, python-brace-format msgid "{0} matched {1}" msgstr "{0} vytvorené" #: ../udiskie/config.py:230 #, python-brace-format msgid "Failed to read config file: {0}" msgstr "Nepodarilo sa načítať konfiguračný súbor: {0}" #: ../udiskie/config.py:233 msgid "Failed to read {0!r}: {1}" msgstr "Nepodarilo sa načítať {0!r}: {1}" #: ../udiskie/depend.py:59 msgid "" "Missing runtime dependency GTK 3. Falling back to GTK 2 for password prompt" msgstr "" #: ../udiskie/depend.py:65 msgid "X server not connected!" msgstr "X server nie je pripojený!" #: ../udiskie/mount.py:29 #, python-brace-format msgid "failed to {0} {1}: {2}" msgstr "chyba pri {0} {1}: {2}" #: ../udiskie/mount.py:117 #, python-brace-format msgid "not browsing {0}: not mounted" msgstr "neprezerám {0}: nepripojené" #: ../udiskie/mount.py:120 #, python-brace-format msgid "not browsing {0}: no program" msgstr "neprezerám {0}: chýba program" #: ../udiskie/mount.py:122 ../udiskie/mount.py:142 #, python-brace-format msgid "opening {0} on {0.mount_paths[0]}" msgstr "otváram {0} na {0.mount_paths[0]}" #: ../udiskie/mount.py:124 ../udiskie/mount.py:144 #, python-brace-format msgid "opened {0} on {0.mount_paths[0]}" msgstr "otvorené {0} na {0.mount_paths[0]}" #: ../udiskie/mount.py:137 #, fuzzy, python-brace-format msgid "not opening terminal {0}: not mounted" msgstr "neodpájam {0}: nepripojené" #: ../udiskie/mount.py:140 #, fuzzy, python-brace-format msgid "not opening terminal {0}: no program" msgstr "neprezerám {0}: chýba program" #: ../udiskie/mount.py:158 #, python-brace-format msgid "not mounting {0}: unhandled device" msgstr "nepripám {0}: nepodporované zariadenie" #: ../udiskie/mount.py:161 #, python-brace-format msgid "not mounting {0}: already mounted" msgstr "nepripájam {0}: už pripojené" #: ../udiskie/mount.py:165 #, python-brace-format msgid "mounting {0} with {1}" msgstr "pripájam {0} s {1}" #: ../udiskie/mount.py:168 #, python-brace-format msgid "mounted {0} on {1}" msgstr "pripojené {0} na {1}" #: ../udiskie/mount.py:174 msgid "" "Mounting NTFS device with default driver.\n" "Please install 'ntfs-3g' if you experience problems or the device is " "readonly." msgstr "" "Pripájam zariadenie NTFS s východzím ovládačom.\n" "Prosím, nainštalujte 'ntfs-3g', ak narazíte na problémy alebo nemôžete na " "zariadenie zapisovať." #: ../udiskie/mount.py:188 #, python-brace-format msgid "not unmounting {0}: unhandled device" msgstr "neodpájam {0}: nepodporované zariadenie" #: ../udiskie/mount.py:191 #, python-brace-format msgid "not unmounting {0}: not mounted" msgstr "neodpájam {0}: nepripojené" #: ../udiskie/mount.py:193 #, python-brace-format msgid "unmounting {0}" msgstr "odpájam {0}" #: ../udiskie/mount.py:195 #, python-brace-format msgid "unmounted {0}" msgstr "odpojené {0}" #: ../udiskie/mount.py:209 #, python-brace-format msgid "not unlocking {0}: unhandled device" msgstr "nezamykám {0}: nepodporované zariadenie" #: ../udiskie/mount.py:212 #, python-brace-format msgid "not unlocking {0}: already unlocked" msgstr "nezamykám {0}: už odomknuté" #: ../udiskie/mount.py:215 #, python-brace-format msgid "not unlocking {0}: no password prompt" msgstr "nezamykám {0}: chýba dialógové okno pre heslo" #: ../udiskie/mount.py:229 #, python-brace-format msgid "not unlocking {0}: cancelled by user" msgstr "nezamykám {0}: zrušené používateľom" #: ../udiskie/mount.py:234 #, python-brace-format msgid "unlocking {0} using keyfile" msgstr "odomykám {0} použitím kľúča" #: ../udiskie/mount.py:237 #, python-brace-format msgid "unlocking {0}" msgstr "odomykám {0}" #: ../udiskie/mount.py:240 #, python-brace-format msgid "unlocked {0}" msgstr "odomknuté {0}" #: ../udiskie/mount.py:249 #, fuzzy, python-brace-format msgid "no cached key for {0}" msgstr "Nepodarilo sa nájsť zodpovedajúce pravidlo pri kľúči pre {}." #: ../udiskie/mount.py:251 #, python-brace-format msgid "unlocking {0} using cached password" msgstr "odomykám {0} použitím kešovaného hesla" #: ../udiskie/mount.py:255 #, python-brace-format msgid "failed to unlock {0} using cached password" msgstr "nepodarilo sa odomknúť {0} použitím kešovaného hesla" #: ../udiskie/mount.py:258 #, python-brace-format msgid "unlocked {0} using cached password" msgstr "odomknuté {0} použitím kešovaného hesla" #: ../udiskie/mount.py:266 msgid "No matching keyfile rule for {}." msgstr "Nepodarilo sa nájsť zodpovedajúce pravidlo pri kľúči pre {}." #: ../udiskie/mount.py:272 #, python-brace-format msgid "keyfile for {0} not found: {1}" msgstr "kľúč pre {0} nebol nájdený: {1}" #: ../udiskie/mount.py:274 #, python-brace-format msgid "unlocking {0} using keyfile {1}" msgstr "odomykám {0} použitím kľúča {1}" #: ../udiskie/mount.py:278 #, python-brace-format msgid "failed to unlock {0} using keyfile" msgstr "nepodarilo sa odomknúť {0} použitím kľúča" #: ../udiskie/mount.py:281 #, python-brace-format msgid "unlocked {0} using keyfile" msgstr "odomknuté {0} použitím kľúča" #: ../udiskie/mount.py:307 #, python-brace-format msgid "not locking {0}: unhandled device" msgstr "neodomykám {0}: nepodporované zariadenie" #: ../udiskie/mount.py:310 #, python-brace-format msgid "not locking {0}: not unlocked" msgstr "neodomykám {0}: nie je zamknuté" #: ../udiskie/mount.py:312 #, python-brace-format msgid "locking {0}" msgstr "zamykám {0}" #: ../udiskie/mount.py:314 #, python-brace-format msgid "locked {0}" msgstr "zamknuté {0}" #: ../udiskie/mount.py:351 ../udiskie/mount.py:394 #, python-brace-format msgid "not adding {0}: unhandled device" msgstr "nepridávam {0}: nepodporované zariadenie" #: ../udiskie/mount.py:430 ../udiskie/mount.py:480 #, python-brace-format msgid "not removing {0}: unhandled device" msgstr "neodstraňujem {0}: nepodporované zariadenie" #: ../udiskie/mount.py:505 #, python-brace-format msgid "not ejecting {0}: unhandled device" msgstr "nevysúvam {0}: nepodporované zariadenie" #: ../udiskie/mount.py:509 #, python-brace-format msgid "not ejecting {0}: drive not ejectable" msgstr "nevysúvam {0}: zariadenie to nepodporuje" #: ../udiskie/mount.py:515 #, python-brace-format msgid "ejecting {0}" msgstr "vysúvam {0}" #: ../udiskie/mount.py:517 #, python-brace-format msgid "ejected {0}" msgstr "vysunuté {0}" #: ../udiskie/mount.py:531 #, python-brace-format msgid "not detaching {0}: unhandled device" msgstr "" #: ../udiskie/mount.py:535 #, python-brace-format msgid "not detaching {0}: drive not detachable" msgstr "" #: ../udiskie/mount.py:539 #, python-brace-format msgid "detaching {0}" msgstr "" #: ../udiskie/mount.py:544 #, python-brace-format msgid "detached {0}" msgstr "" #: ../udiskie/mount.py:595 #, python-brace-format msgid "not setting up {0}: already up" msgstr "nenastavujem {0}: už je nastavené" #: ../udiskie/mount.py:598 #, python-brace-format msgid "not setting up {0}: not a file" msgstr "nenastavujem {0}: nie je to súbor" #: ../udiskie/mount.py:600 #, fuzzy, python-brace-format msgid "setting up loop device {0}" msgstr "nastavujem {0}" #: ../udiskie/mount.py:618 #, python-brace-format msgid "" "Insufficient permission to open {0} in read-write mode. Retrying in read-" "only mode." msgstr "" #: ../udiskie/mount.py:630 #, python-brace-format msgid "set up {0} as {1}" msgstr "nastaviť {0} ako {1}" #: ../udiskie/mount.py:645 #, python-brace-format msgid "not deleting {0}: unhandled device" msgstr "nemažem {0}: nepodporované zariadenie" #: ../udiskie/mount.py:649 #, python-brace-format msgid "deleting {0}" msgstr "mažem {0}" #: ../udiskie/mount.py:651 #, python-brace-format msgid "deleted {0}" msgstr "vymazané {0}" #: ../udiskie/mount.py:777 #, python-brace-format msgid "Browse {0}" msgstr "Prehliadať {0}" #: ../udiskie/mount.py:778 #, fuzzy, python-brace-format msgid "Hack on {0}" msgstr "zamykám {0}" #: ../udiskie/mount.py:779 #, python-brace-format msgid "Mount {0}" msgstr "Pripojiť {0}" #: ../udiskie/mount.py:780 #, python-brace-format msgid "Unmount {0}" msgstr "Odpojiť {0}" #: ../udiskie/mount.py:781 #, python-brace-format msgid "Unlock {0}" msgstr "Odomknúť {0}" #: ../udiskie/mount.py:782 #, python-brace-format msgid "Lock {0}" msgstr "Uzamknúť {0}" #: ../udiskie/mount.py:783 #, python-brace-format msgid "Eject {1}" msgstr "Vysunúť {1}" #: ../udiskie/mount.py:784 #, python-brace-format msgid "Unpower {1}" msgstr "Vypnúť {1}" #: ../udiskie/mount.py:785 #, python-brace-format msgid "Clear password for {0}" msgstr "Vymazať heslo pre {0}" #: ../udiskie/mount.py:786 #, python-brace-format msgid "Detach {0}" msgstr "" #: ../udiskie/notify.py:62 msgid "Browse directory" msgstr "Prehliadať adresár" #: ../udiskie/notify.py:64 msgid "Open terminal" msgstr "" #: ../udiskie/notify.py:68 msgid "Device mounted" msgstr "Zariadenie pripojené" #: ../udiskie/notify.py:69 #, python-brace-format msgid "{0.ui_label} mounted on {0.mount_paths[0]}" msgstr "{0.ui_label} pripojené na {0.mount_paths[0]}" #: ../udiskie/notify.py:80 msgid "Device unmounted" msgstr "Zariadenie odpojené" #: ../udiskie/notify.py:81 #, python-brace-format msgid "{0.ui_label} unmounted" msgstr "{0.ui_label} odpojené" #: ../udiskie/notify.py:90 msgid "Device locked" msgstr "Zariadenie zamknuté" #: ../udiskie/notify.py:91 #, python-brace-format msgid "{0.device_presentation} locked" msgstr "{0.device_presentation} zamknuté" #: ../udiskie/notify.py:100 msgid "Device unlocked" msgstr "Zariadenie odomknuté" #: ../udiskie/notify.py:101 #, python-brace-format msgid "{0.device_presentation} unlocked" msgstr "{0.device_presentation} odomknuté" #: ../udiskie/notify.py:135 msgid "Device added" msgstr "Zariadenie pridané" #: ../udiskie/notify.py:136 #, python-brace-format msgid "device appeared on {0.device_presentation}" msgstr "zariadenie sa objavilo na {0.device_presentation}" #: ../udiskie/notify.py:155 msgid "Device removed" msgstr "Zariadenie odstránené" #: ../udiskie/notify.py:156 #, python-brace-format msgid "device disappeared on {0.device_presentation}" msgstr "zariadenie zmizlo z {0.device_presentation}" #: ../udiskie/notify.py:165 #, python-brace-format msgid "" "failed to {0} {1}:\n" "{2}" msgstr "" "nepodarilo sa {0} {1}:\n" "{2}" #: ../udiskie/notify.py:167 #, python-brace-format msgid "failed to {0} device {1}." msgstr "chyba pri {0} zariadenia {1}." #: ../udiskie/notify.py:173 msgid "Retry" msgstr "Skúsiť znova" #: ../udiskie/notify.py:176 msgid "Job failed" msgstr "Úloha skončila s chybou" #: ../udiskie/notify.py:207 #, python-brace-format msgid "Failed to show notification: {0}" msgstr "Nepodarilo sa zobraziť notifikáciu: {0}" #: ../udiskie/prompt.py:96 msgid "Show password" msgstr "" #: ../udiskie/prompt.py:101 msgid "Open keyfile…" msgstr "" #: ../udiskie/prompt.py:108 msgid "Cache password" msgstr "" #: ../udiskie/prompt.py:123 msgid "Open a keyfile to unlock the LUKS device" msgstr "" #: ../udiskie/prompt.py:157 ../udiskie/prompt.py:167 #, python-brace-format msgid "Enter password for {0.device_presentation}: " msgstr "Zadajte heslo pre {0.device_presentation}: " #: ../udiskie/prompt.py:203 msgid "Unknown device attribute {!r} in format string: {!r}" msgstr "Neznámy atribút zariadenia {!r} vo formátovacom reťazci: {!r}" #: ../udiskie/prompt.py:255 msgid "" "Can't find file browser: {0!r}. You may want to change the value for the '-" "f' option." msgstr "" "Nepodarilo sa nájsť prehliadač súborov: {0!r}. Možno budete chcieť zmeniť " "hodnotu pre voľbu '-f'." #: ../udiskie/tray.py:182 msgid "Managed devices" msgstr "" #: ../udiskie/tray.py:198 msgid "Mount disc image" msgstr "Pripojiť obraz disku" #: ../udiskie/tray.py:204 msgid "Enable automounting" msgstr "Povoliť automatické pripájanie" #: ../udiskie/tray.py:210 msgid "Enable notifications" msgstr "Povoliť notifikácie" #: ../udiskie/tray.py:219 msgid "Quit" msgstr "Ukončiť" #: ../udiskie/tray.py:226 msgid "Open disc image" msgstr "Otvoriť obraz disku" #: ../udiskie/tray.py:228 msgid "Open" msgstr "Otvoriť" #: ../udiskie/tray.py:229 msgid "Cancel" msgstr "Zrušiť" #: ../udiskie/tray.py:269 msgid "Invalid node!" msgstr "" #: ../udiskie/tray.py:271 msgid "No external devices" msgstr "Žiadne externé zariadenia" #: ../udiskie/tray.py:387 msgid "udiskie" msgstr "udiskie" #: ../udiskie/udisks2.py:661 #, python-brace-format msgid "found device owning \"{0}\": \"{1}\"" msgstr "našiel som zariadenie vlastniace \"{0}\": \"{1}\"" #: ../udiskie/udisks2.py:664 #, python-brace-format msgid "no device found owning \"{0}\"" msgstr "nenašiel som zariadenie vlastniace \"{0}\"" #: ../udiskie/udisks2.py:683 #, python-brace-format msgid "Daemon version: {0}" msgstr "Verzia daemona: {0}" #: ../udiskie/udisks2.py:688 #, python-brace-format msgid "Keyfile support: {0}" msgstr "Podpora pre kľúč (keyfile): {0}" #: ../udiskie/udisks2.py:767 #, python-brace-format msgid "+++ {0}: {1}" msgstr "+++ {0}: {1}" #~ msgid "{0}(match={1!r}, value={2!r})" #~ msgstr "{0}(zhoda={1!r}, hodnota={2!r})" #~ msgid "{0}(match={1!r}, {2}={3!r}) used for {4}" #~ msgstr "{0}(zhoda={1!r}, {2}={3!r}) použité pre {4}" #~ msgid "Positional field in format string {!r} is deprecated." #~ msgstr "Pozičné pole vo formátovacom reťazci {!r} je zastaralé." ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/lang/tr_TR.po0000644000175100001770000004304614612243552015111 0ustar00runnerdocker# Turkish translations for udiskie package. # Copyright (C) 2021 Thomas Gläßle # This file is distributed under the same license as the udiskie package. # Oğuz Ersen , 2021-2022. # msgid "" msgstr "" "Project-Id-Version: udiskie\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-04-16 21:10+0000\n" "PO-Revision-Date: 2022-02-18 23:04+0300\n" "Last-Translator: Oğuz Ersen \n" "Language-Team: Turkish\n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0\n" #: ../udiskie/cli.py:46 #, python-brace-format msgid "These options are mutually exclusive: {0}" msgstr "Bu seçenekler birlikte kullanılamaz: {0}" #: ../udiskie/cli.py:119 msgid "" "\n" " Note, that the options in the individual groups are mutually exclusive.\n" "\n" " The config file can be a JSON or preferably a YAML file. For an\n" " example, see the MAN page (or doc/udiskie.8.txt in the repository).\n" " " msgstr "" "\n" " Aynı gruptaki seçeneklerin birlikte kullanılamayacağını unutmayın.\n" "\n" " Yapılandırma dosyası bir JSON ya da tercihen YAML dosyası olabilir. " "Örnek\n" " için kılavuz sayfasına (veya depodaki doc/udiskie.8.txt dosyasına) " "bakın.\n" " " #: ../udiskie/cli.py:139 #, python-format msgid "%(message)s" msgstr "%(message)s" #: ../udiskie/cli.py:141 #, python-format msgid "%(levelname)s [%(asctime)s] %(name)s: %(message)s" msgstr "%(levelname)s [%(asctime)s] %(name)s: %(message)s" #: ../udiskie/cli.py:386 msgid "" "Typelib for 'libnotify' is not available. Possible causes include:\n" "\t- libnotify is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- libnotify was built with introspection disabled\n" "\n" "Starting udiskie without notifications." msgstr "" "'libnotify' için typelib kullanılamıyor. Olası nedenler şunlardır:\n" "\t- libnotify kurulu değil\n" "\t- typelib ayrı bir paket tarafından sağlanıyor\n" "\t- libnotify, iç gözlem devre dışı bırakılarak oluşturuldu\n" "\n" "udiskie, bildirimler olmadan başlatılıyor." #: ../udiskie/cli.py:400 msgid "" "Not run within X or Wayland session.\n" "Starting udiskie without tray icon.\n" msgstr "" "X veya Wayland oturumu içinde çalıştırılmadı.\n" "udiskie, tepsi simgesi olmadan başlatılıyor.\n" #: ../udiskie/cli.py:407 msgid "" "Typelib for 'Gtk 3.0' is not available. Possible causes include:\n" "\t- GTK3 is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- GTK3 was built with introspection disabled\n" "Starting udiskie without tray icon.\n" msgstr "" "'Gtk 3.0' için typelib kullanılamıyor. Olası nedenler şunlardır:\n" "\t- GTK3 kurulu değil\n" "\t- typelib ayrı bir paket tarafından sağlanıyor\n" "\t- GTK3, iç gözlem devre dışı bırakılarak oluşturuldu\n" "\n" "udiskie, tepsi simgesi olmadan başlatılıyor.\n" #: ../udiskie/cli.py:417 msgid "" "Typelib for 'AppIndicator3 0.1' is not available. Possible causes include:\n" "\t- libappindicator is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- it was built with introspection disabled\n" "Starting udiskie without appindicator icon.\n" msgstr "" "'AppIndicator3 0.1' için typelib kullanılamıyor. Olası nedenler şunlardır:\n" "\t- libappindicator kurulu değil\n" "\t- typelib ayrı bir paket tarafından sağlanıyor\n" "\t- iç gözlem devre dışı bırakılarak oluşturuldu\n" "\n" "udiskie, appindicator simgesi olmadan başlatılıyor.\n" #: ../udiskie/cli.py:436 msgid "" "The 'notify_command' option was renamed to 'event_hook'. The old name still " "works, but may be removed in a future version. Please change your command " "line and config to use the new name." msgstr "" #: ../udiskie/cli.py:443 msgid "Ignoring 'notify_command' in favor of 'event_hook'." msgstr "" #: ../udiskie/config.py:129 msgid "Unknown matching attribute: {!r}" msgstr "Bilinmeyen eşleşen özellik: {!r}" #: ../udiskie/config.py:131 #, python-brace-format msgid "new rule: {0}" msgstr "yeni kural: {0}" #: ../udiskie/config.py:134 #, python-brace-format msgid "{0} -> {1}" msgstr "{0} -> {1}" #: ../udiskie/config.py:153 #, python-brace-format msgid "{0} matched {1}" msgstr "{0}, {1} ile eşleşti" #: ../udiskie/config.py:230 #, python-brace-format msgid "Failed to read config file: {0}" msgstr "Yapılandırma dosyası okunamadı: {0}" #: ../udiskie/config.py:233 msgid "Failed to read {0!r}: {1}" msgstr "{0!r} okunamadı: {1}" #: ../udiskie/depend.py:59 msgid "" "Missing runtime dependency GTK 3. Falling back to GTK 2 for password prompt" msgstr "" "Çalışma zamanı bağımlılığı GTK 3 eksik. Parola istemi için GTK 2 kullanılıyor" #: ../udiskie/depend.py:65 msgid "X server not connected!" msgstr "X sunucusu bağlı değil!" #: ../udiskie/mount.py:29 #, python-brace-format msgid "failed to {0} {1}: {2}" msgstr "{0} {1} başarısız oldu: {2}" #: ../udiskie/mount.py:117 #, python-brace-format msgid "not browsing {0}: not mounted" msgstr "{0} tarayıcıda açılmıyor: bağlı değil" #: ../udiskie/mount.py:120 #, python-brace-format msgid "not browsing {0}: no program" msgstr "{0} tarayıcıda açılmıyor: program yok" #: ../udiskie/mount.py:122 ../udiskie/mount.py:142 #, python-brace-format msgid "opening {0} on {0.mount_paths[0]}" msgstr "{0}, {0.mount_paths[0]} yolunda açılıyor" #: ../udiskie/mount.py:124 ../udiskie/mount.py:144 #, python-brace-format msgid "opened {0} on {0.mount_paths[0]}" msgstr "{0}, {0.mount_paths[0]} yolunda açıldı" #: ../udiskie/mount.py:137 #, python-brace-format msgid "not opening terminal {0}: not mounted" msgstr "{0} terminalde açılmıyor: bağlı değil" #: ../udiskie/mount.py:140 #, python-brace-format msgid "not opening terminal {0}: no program" msgstr "{0} terminalde açılmıyor: program yok" #: ../udiskie/mount.py:158 #, python-brace-format msgid "not mounting {0}: unhandled device" msgstr "{0} bağlanmıyor: beklenmeyen aygıt" #: ../udiskie/mount.py:161 #, python-brace-format msgid "not mounting {0}: already mounted" msgstr "{0} bağlanmıyor: zaten bağlı" #: ../udiskie/mount.py:165 #, python-brace-format msgid "mounting {0} with {1}" msgstr "{0}, {1} ile bağlanıyor" #: ../udiskie/mount.py:168 #, python-brace-format msgid "mounted {0} on {1}" msgstr "{0}, {1} yolunda bağlandı" #: ../udiskie/mount.py:174 msgid "" "Mounting NTFS device with default driver.\n" "Please install 'ntfs-3g' if you experience problems or the device is " "readonly." msgstr "" "NTFS aygıtı öntanımlı sürücüyle bağlanıyor\n" "Sorun yaşarsanız veya aygıt salt okunursa lütfen 'ntfs-3g' programını kurun." #: ../udiskie/mount.py:188 #, python-brace-format msgid "not unmounting {0}: unhandled device" msgstr "{0} bağı kesilmiyor: beklenmeyen aygıt" #: ../udiskie/mount.py:191 #, python-brace-format msgid "not unmounting {0}: not mounted" msgstr "{0} bağı kesilmiyor: bağlı değil" #: ../udiskie/mount.py:193 #, python-brace-format msgid "unmounting {0}" msgstr "{0} bağı kesiliyor" #: ../udiskie/mount.py:195 #, python-brace-format msgid "unmounted {0}" msgstr "{0} bağı kesildi" #: ../udiskie/mount.py:209 #, python-brace-format msgid "not unlocking {0}: unhandled device" msgstr "{0} kilidi açılmıyor: beklenmeyen aygıt" #: ../udiskie/mount.py:212 #, python-brace-format msgid "not unlocking {0}: already unlocked" msgstr "{0} kilidi açılmıyor: zaten kilidi açık" #: ../udiskie/mount.py:215 #, python-brace-format msgid "not unlocking {0}: no password prompt" msgstr "{0} kilidi açılmıyor: parola istemi yok" #: ../udiskie/mount.py:229 #, python-brace-format msgid "not unlocking {0}: cancelled by user" msgstr "{0} kilidi açılmıyor: kullanıcı iptal etti" #: ../udiskie/mount.py:234 #, python-brace-format msgid "unlocking {0} using keyfile" msgstr "anahtar dosyası kullanılarak {0} kilidi açılıyor" #: ../udiskie/mount.py:237 #, python-brace-format msgid "unlocking {0}" msgstr "{0} kilidi açılıyor" #: ../udiskie/mount.py:240 #, python-brace-format msgid "unlocked {0}" msgstr "{0} kilidi açıldı" #: ../udiskie/mount.py:249 #, python-brace-format msgid "no cached key for {0}" msgstr "{0} için önbelleğe alınan anahtar yok" #: ../udiskie/mount.py:251 #, python-brace-format msgid "unlocking {0} using cached password" msgstr "önbelleğe alınan parola kullanılarak {0} kilidi açılıyor" #: ../udiskie/mount.py:255 #, python-brace-format msgid "failed to unlock {0} using cached password" msgstr "önbelleğe alınan parola kullanılarak {0} kilidi açılamadı" #: ../udiskie/mount.py:258 #, python-brace-format msgid "unlocked {0} using cached password" msgstr "önbelleğe alınan parola kullanılarak {0} kilidi açıldı" #: ../udiskie/mount.py:266 msgid "No matching keyfile rule for {}." msgstr "{} için eşleşen anahtar dosyası kuralı yok." #: ../udiskie/mount.py:272 #, python-brace-format msgid "keyfile for {0} not found: {1}" msgstr "{0} için anahtar dosyası bulunamadı: {1}" #: ../udiskie/mount.py:274 #, python-brace-format msgid "unlocking {0} using keyfile {1}" msgstr "{0} kilidi, {1} anahtar dosyası kullanılarak açılıyor" #: ../udiskie/mount.py:278 #, python-brace-format msgid "failed to unlock {0} using keyfile" msgstr "anahtar dosyası kullanılarak {0} kilidi açılamadı" #: ../udiskie/mount.py:281 #, python-brace-format msgid "unlocked {0} using keyfile" msgstr "anahtar dosyası kullanılarak {0} kilidi açıldı" #: ../udiskie/mount.py:307 #, python-brace-format msgid "not locking {0}: unhandled device" msgstr "{0} kilitlenmiyor: beklenmeyen aygıt" #: ../udiskie/mount.py:310 #, python-brace-format msgid "not locking {0}: not unlocked" msgstr "{0} kilitlenmiyor: kilidi açık değil" #: ../udiskie/mount.py:312 #, python-brace-format msgid "locking {0}" msgstr "{0} kilitleniyor" #: ../udiskie/mount.py:314 #, python-brace-format msgid "locked {0}" msgstr "{0} kilitlendi" #: ../udiskie/mount.py:351 ../udiskie/mount.py:394 #, python-brace-format msgid "not adding {0}: unhandled device" msgstr "{0} eklenmiyor: beklenmeyen aygıt" #: ../udiskie/mount.py:430 ../udiskie/mount.py:480 #, python-brace-format msgid "not removing {0}: unhandled device" msgstr "{0} kaldırılmıyor: beklenmeyen aygıt" #: ../udiskie/mount.py:505 #, python-brace-format msgid "not ejecting {0}: unhandled device" msgstr "{0} çıkartılmıyor: beklenmeyen aygıt" #: ../udiskie/mount.py:509 #, python-brace-format msgid "not ejecting {0}: drive not ejectable" msgstr "{0} çıkartılmıyor: sürücü çıkartılabilir değil" #: ../udiskie/mount.py:515 #, python-brace-format msgid "ejecting {0}" msgstr "{0} çıkartılıyor" #: ../udiskie/mount.py:517 #, python-brace-format msgid "ejected {0}" msgstr "{0} çıkartıldı" #: ../udiskie/mount.py:531 #, python-brace-format msgid "not detaching {0}: unhandled device" msgstr "{0} ayrılmıyor: beklenmeyen aygıt" #: ../udiskie/mount.py:535 #, python-brace-format msgid "not detaching {0}: drive not detachable" msgstr "{0} ayrılmıyor: sürücü ayrılabilir değil" #: ../udiskie/mount.py:539 #, python-brace-format msgid "detaching {0}" msgstr "{0} ayrılıyor" #: ../udiskie/mount.py:544 #, python-brace-format msgid "detached {0}" msgstr "{0} ayrıldı" #: ../udiskie/mount.py:595 #, python-brace-format msgid "not setting up {0}: already up" msgstr "{0} kurulmuyor: zaten kurulu" #: ../udiskie/mount.py:598 #, python-brace-format msgid "not setting up {0}: not a file" msgstr "{0} kurulmuyor: bir dosya değil" #: ../udiskie/mount.py:600 #, python-brace-format msgid "setting up loop device {0}" msgstr "{0} döngü aygıtı kuruluyor" #: ../udiskie/mount.py:618 #, python-brace-format msgid "" "Insufficient permission to open {0} in read-write mode. Retrying in read-" "only mode." msgstr "" "{0} okuma-yazma modunda açılması için yetersiz izin. Salt okunur modda " "yeniden deneniyor." #: ../udiskie/mount.py:630 #, python-brace-format msgid "set up {0} as {1}" msgstr "{0}, {1} olarak kuruldu" #: ../udiskie/mount.py:645 #, python-brace-format msgid "not deleting {0}: unhandled device" msgstr "{0} silinmiyor: beklenmeyen aygıt" #: ../udiskie/mount.py:649 #, python-brace-format msgid "deleting {0}" msgstr "{0} siliniyor" #: ../udiskie/mount.py:651 #, python-brace-format msgid "deleted {0}" msgstr "{0} silindi" #: ../udiskie/mount.py:777 #, python-brace-format msgid "Browse {0}" msgstr "Tarayıcıda aç: {0}" #: ../udiskie/mount.py:778 #, python-brace-format msgid "Hack on {0}" msgstr "Terminalde aç: {0}" #: ../udiskie/mount.py:779 #, python-brace-format msgid "Mount {0}" msgstr "Bağla: {0}" #: ../udiskie/mount.py:780 #, python-brace-format msgid "Unmount {0}" msgstr "Bağını kes: {0}" #: ../udiskie/mount.py:781 #, python-brace-format msgid "Unlock {0}" msgstr "Kilidini aç: {0}" #: ../udiskie/mount.py:782 #, python-brace-format msgid "Lock {0}" msgstr "Kilitle: {0}" #: ../udiskie/mount.py:783 #, python-brace-format msgid "Eject {1}" msgstr "Çıkart: {1}" #: ../udiskie/mount.py:784 #, python-brace-format msgid "Unpower {1}" msgstr "Gücünü kapat: {1}" #: ../udiskie/mount.py:785 #, python-brace-format msgid "Clear password for {0}" msgstr "{0} için parolayı temizle" #: ../udiskie/mount.py:786 #, python-brace-format msgid "Detach {0}" msgstr "Ayır: {0}" #: ../udiskie/notify.py:62 msgid "Browse directory" msgstr "Dizine göz at" #: ../udiskie/notify.py:64 msgid "Open terminal" msgstr "Terminali aç" #: ../udiskie/notify.py:68 msgid "Device mounted" msgstr "Aygıt bağlandı" #: ../udiskie/notify.py:69 #, python-brace-format msgid "{0.ui_label} mounted on {0.mount_paths[0]}" msgstr "{0.ui_label}, {0.mount_paths[0]} yolunda bağlandı" #: ../udiskie/notify.py:80 msgid "Device unmounted" msgstr "Aygıtın bağı kesildi" #: ../udiskie/notify.py:81 #, python-brace-format msgid "{0.ui_label} unmounted" msgstr "{0.ui_label} bağı kesildi" #: ../udiskie/notify.py:90 msgid "Device locked" msgstr "Aygıt kilitlendi" #: ../udiskie/notify.py:91 #, python-brace-format msgid "{0.device_presentation} locked" msgstr "{0.device_presentation} kilitlendi" #: ../udiskie/notify.py:100 msgid "Device unlocked" msgstr "Aygıtın kilidi açıldı" #: ../udiskie/notify.py:101 #, python-brace-format msgid "{0.device_presentation} unlocked" msgstr "{0.device_presentation} kilidi açıldı" #: ../udiskie/notify.py:135 msgid "Device added" msgstr "Aygıt eklendi" #: ../udiskie/notify.py:136 #, python-brace-format msgid "device appeared on {0.device_presentation}" msgstr "Aygıt {0.device_presentation} yolunda göründü" #: ../udiskie/notify.py:155 msgid "Device removed" msgstr "Aygıt kaldırıldı" #: ../udiskie/notify.py:156 #, python-brace-format msgid "device disappeared on {0.device_presentation}" msgstr "Aygıt {0.device_presentation} yolunda kayboldu" #: ../udiskie/notify.py:165 #, python-brace-format msgid "" "failed to {0} {1}:\n" "{2}" msgstr "" "{0} {1} başarısız oldu:\n" "{2}" #: ../udiskie/notify.py:167 #, python-brace-format msgid "failed to {0} device {1}." msgstr "{0} aygıt {1} başarısız oldu." #: ../udiskie/notify.py:173 msgid "Retry" msgstr "Yeniden dene" #: ../udiskie/notify.py:176 msgid "Job failed" msgstr "Görev başarısız oldu" #: ../udiskie/notify.py:207 #, python-brace-format msgid "Failed to show notification: {0}" msgstr "Bildirim gösterilemedi: {0}" #: ../udiskie/prompt.py:96 msgid "Show password" msgstr "Parolayı göster" #: ../udiskie/prompt.py:101 msgid "Open keyfile…" msgstr "Anahtar dosyasını aç…" #: ../udiskie/prompt.py:108 msgid "Cache password" msgstr "Parolayı önbelleğe al" #: ../udiskie/prompt.py:123 msgid "Open a keyfile to unlock the LUKS device" msgstr "LUKS aygıtının kilidini açmak için bir anahtar dosyası açın" #: ../udiskie/prompt.py:157 ../udiskie/prompt.py:167 #, python-brace-format msgid "Enter password for {0.device_presentation}: " msgstr "{0.device_presentation} için parolayı girin: " #: ../udiskie/prompt.py:203 msgid "Unknown device attribute {!r} in format string: {!r}" msgstr "Biçim dizgesinde bilinmeyen aygıt özelliği {!r}: {!r}" #: ../udiskie/prompt.py:255 msgid "" "Can't find file browser: {0!r}. You may want to change the value for the '-" "f' option." msgstr "" "{0!r} dosya tarayıcısı bulunamıyor. '-f' seçeneğinin değerini değiştirmek " "isteyebilirsiniz." #: ../udiskie/tray.py:182 msgid "Managed devices" msgstr "Yönetilen aygıtlar" #: ../udiskie/tray.py:198 msgid "Mount disc image" msgstr "Disk kalıbını bağla" #: ../udiskie/tray.py:204 msgid "Enable automounting" msgstr "Otomatik bağlamayı etkinleştir" #: ../udiskie/tray.py:210 msgid "Enable notifications" msgstr "Bildirimleri etkinleştir" #: ../udiskie/tray.py:219 msgid "Quit" msgstr "Çıkış" #: ../udiskie/tray.py:226 msgid "Open disc image" msgstr "Disk kalıbı aç" #: ../udiskie/tray.py:228 msgid "Open" msgstr "Aç" #: ../udiskie/tray.py:229 msgid "Cancel" msgstr "İptal" #: ../udiskie/tray.py:269 msgid "Invalid node!" msgstr "Geçersiz düğüm!" #: ../udiskie/tray.py:271 msgid "No external devices" msgstr "Harici aygıt yok" #: ../udiskie/tray.py:387 msgid "udiskie" msgstr "udiskie" #: ../udiskie/udisks2.py:661 #, python-brace-format msgid "found device owning \"{0}\": \"{1}\"" msgstr "\"{0}\" sahibi aygıt bulundu: \"{1}\"" #: ../udiskie/udisks2.py:664 #, python-brace-format msgid "no device found owning \"{0}\"" msgstr "\"{0}\" sahibi aygıt bulunamadı" #: ../udiskie/udisks2.py:683 #, python-brace-format msgid "Daemon version: {0}" msgstr "Arka plan programı sürümü: {0}" #: ../udiskie/udisks2.py:688 #, python-brace-format msgid "Keyfile support: {0}" msgstr "Anahtar dosyası desteği: {0}" #: ../udiskie/udisks2.py:767 #, python-brace-format msgid "+++ {0}: {1}" msgstr "+++ {0}: {1}" ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/lang/udiskie.pot0000644000175100001770000003076614612243552015705 0ustar00runnerdocker# SOME DESCRIPTIVE TITLE. # Copyright (C) 2024 Thomas Gläßle # This file is distributed under the same license as the udiskie package. # FIRST AUTHOR , 2024. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: udiskie 2.5.2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-04-16 21:10+0000\n" "PO-Revision-Date: 2024-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: ../udiskie/cli.py:46 #, python-brace-format msgid "These options are mutually exclusive: {0}" msgstr "" #: ../udiskie/cli.py:119 msgid "" "\n" " Note, that the options in the individual groups are mutually exclusive.\n" "\n" " The config file can be a JSON or preferably a YAML file. For an\n" " example, see the MAN page (or doc/udiskie.8.txt in the repository).\n" " " msgstr "" #: ../udiskie/cli.py:139 #, python-format msgid "%(message)s" msgstr "" #: ../udiskie/cli.py:141 #, python-format msgid "%(levelname)s [%(asctime)s] %(name)s: %(message)s" msgstr "" #: ../udiskie/cli.py:386 msgid "" "Typelib for 'libnotify' is not available. Possible causes include:\n" "\t- libnotify is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- libnotify was built with introspection disabled\n" "\n" "Starting udiskie without notifications." msgstr "" #: ../udiskie/cli.py:400 msgid "" "Not run within X or Wayland session.\n" "Starting udiskie without tray icon.\n" msgstr "" #: ../udiskie/cli.py:407 msgid "" "Typelib for 'Gtk 3.0' is not available. Possible causes include:\n" "\t- GTK3 is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- GTK3 was built with introspection disabled\n" "Starting udiskie without tray icon.\n" msgstr "" #: ../udiskie/cli.py:417 msgid "" "Typelib for 'AppIndicator3 0.1' is not available. Possible causes include:\n" "\t- libappindicator is not installed\n" "\t- the typelib is provided by a separate package\n" "\t- it was built with introspection disabled\n" "Starting udiskie without appindicator icon.\n" msgstr "" #: ../udiskie/cli.py:436 msgid "" "The 'notify_command' option was renamed to 'event_hook'. The old name still " "works, but may be removed in a future version. Please change your command " "line and config to use the new name." msgstr "" #: ../udiskie/cli.py:443 msgid "Ignoring 'notify_command' in favor of 'event_hook'." msgstr "" #: ../udiskie/config.py:129 msgid "Unknown matching attribute: {!r}" msgstr "" #: ../udiskie/config.py:131 #, python-brace-format msgid "new rule: {0}" msgstr "" #: ../udiskie/config.py:134 #, python-brace-format msgid "{0} -> {1}" msgstr "" #: ../udiskie/config.py:153 #, python-brace-format msgid "{0} matched {1}" msgstr "" #: ../udiskie/config.py:230 #, python-brace-format msgid "Failed to read config file: {0}" msgstr "" #: ../udiskie/config.py:233 msgid "Failed to read {0!r}: {1}" msgstr "" #: ../udiskie/depend.py:59 msgid "" "Missing runtime dependency GTK 3. Falling back to GTK 2 for password prompt" msgstr "" #: ../udiskie/depend.py:65 msgid "X server not connected!" msgstr "" #: ../udiskie/mount.py:29 #, python-brace-format msgid "failed to {0} {1}: {2}" msgstr "" #: ../udiskie/mount.py:117 #, python-brace-format msgid "not browsing {0}: not mounted" msgstr "" #: ../udiskie/mount.py:120 #, python-brace-format msgid "not browsing {0}: no program" msgstr "" #: ../udiskie/mount.py:122 ../udiskie/mount.py:142 #, python-brace-format msgid "opening {0} on {0.mount_paths[0]}" msgstr "" #: ../udiskie/mount.py:124 ../udiskie/mount.py:144 #, python-brace-format msgid "opened {0} on {0.mount_paths[0]}" msgstr "" #: ../udiskie/mount.py:137 #, python-brace-format msgid "not opening terminal {0}: not mounted" msgstr "" #: ../udiskie/mount.py:140 #, python-brace-format msgid "not opening terminal {0}: no program" msgstr "" #: ../udiskie/mount.py:158 #, python-brace-format msgid "not mounting {0}: unhandled device" msgstr "" #: ../udiskie/mount.py:161 #, python-brace-format msgid "not mounting {0}: already mounted" msgstr "" #: ../udiskie/mount.py:165 #, python-brace-format msgid "mounting {0} with {1}" msgstr "" #: ../udiskie/mount.py:168 #, python-brace-format msgid "mounted {0} on {1}" msgstr "" #: ../udiskie/mount.py:174 msgid "" "Mounting NTFS device with default driver.\n" "Please install 'ntfs-3g' if you experience problems or the device is " "readonly." msgstr "" #: ../udiskie/mount.py:188 #, python-brace-format msgid "not unmounting {0}: unhandled device" msgstr "" #: ../udiskie/mount.py:191 #, python-brace-format msgid "not unmounting {0}: not mounted" msgstr "" #: ../udiskie/mount.py:193 #, python-brace-format msgid "unmounting {0}" msgstr "" #: ../udiskie/mount.py:195 #, python-brace-format msgid "unmounted {0}" msgstr "" #: ../udiskie/mount.py:209 #, python-brace-format msgid "not unlocking {0}: unhandled device" msgstr "" #: ../udiskie/mount.py:212 #, python-brace-format msgid "not unlocking {0}: already unlocked" msgstr "" #: ../udiskie/mount.py:215 #, python-brace-format msgid "not unlocking {0}: no password prompt" msgstr "" #: ../udiskie/mount.py:229 #, python-brace-format msgid "not unlocking {0}: cancelled by user" msgstr "" #: ../udiskie/mount.py:234 #, python-brace-format msgid "unlocking {0} using keyfile" msgstr "" #: ../udiskie/mount.py:237 #, python-brace-format msgid "unlocking {0}" msgstr "" #: ../udiskie/mount.py:240 #, python-brace-format msgid "unlocked {0}" msgstr "" #: ../udiskie/mount.py:249 #, python-brace-format msgid "no cached key for {0}" msgstr "" #: ../udiskie/mount.py:251 #, python-brace-format msgid "unlocking {0} using cached password" msgstr "" #: ../udiskie/mount.py:255 #, python-brace-format msgid "failed to unlock {0} using cached password" msgstr "" #: ../udiskie/mount.py:258 #, python-brace-format msgid "unlocked {0} using cached password" msgstr "" #: ../udiskie/mount.py:266 msgid "No matching keyfile rule for {}." msgstr "" #: ../udiskie/mount.py:272 #, python-brace-format msgid "keyfile for {0} not found: {1}" msgstr "" #: ../udiskie/mount.py:274 #, python-brace-format msgid "unlocking {0} using keyfile {1}" msgstr "" #: ../udiskie/mount.py:278 #, python-brace-format msgid "failed to unlock {0} using keyfile" msgstr "" #: ../udiskie/mount.py:281 #, python-brace-format msgid "unlocked {0} using keyfile" msgstr "" #: ../udiskie/mount.py:307 #, python-brace-format msgid "not locking {0}: unhandled device" msgstr "" #: ../udiskie/mount.py:310 #, python-brace-format msgid "not locking {0}: not unlocked" msgstr "" #: ../udiskie/mount.py:312 #, python-brace-format msgid "locking {0}" msgstr "" #: ../udiskie/mount.py:314 #, python-brace-format msgid "locked {0}" msgstr "" #: ../udiskie/mount.py:351 ../udiskie/mount.py:394 #, python-brace-format msgid "not adding {0}: unhandled device" msgstr "" #: ../udiskie/mount.py:430 ../udiskie/mount.py:480 #, python-brace-format msgid "not removing {0}: unhandled device" msgstr "" #: ../udiskie/mount.py:505 #, python-brace-format msgid "not ejecting {0}: unhandled device" msgstr "" #: ../udiskie/mount.py:509 #, python-brace-format msgid "not ejecting {0}: drive not ejectable" msgstr "" #: ../udiskie/mount.py:515 #, python-brace-format msgid "ejecting {0}" msgstr "" #: ../udiskie/mount.py:517 #, python-brace-format msgid "ejected {0}" msgstr "" #: ../udiskie/mount.py:531 #, python-brace-format msgid "not detaching {0}: unhandled device" msgstr "" #: ../udiskie/mount.py:535 #, python-brace-format msgid "not detaching {0}: drive not detachable" msgstr "" #: ../udiskie/mount.py:539 #, python-brace-format msgid "detaching {0}" msgstr "" #: ../udiskie/mount.py:544 #, python-brace-format msgid "detached {0}" msgstr "" #: ../udiskie/mount.py:595 #, python-brace-format msgid "not setting up {0}: already up" msgstr "" #: ../udiskie/mount.py:598 #, python-brace-format msgid "not setting up {0}: not a file" msgstr "" #: ../udiskie/mount.py:600 #, python-brace-format msgid "setting up loop device {0}" msgstr "" #: ../udiskie/mount.py:618 #, python-brace-format msgid "" "Insufficient permission to open {0} in read-write mode. Retrying in read-" "only mode." msgstr "" #: ../udiskie/mount.py:630 #, python-brace-format msgid "set up {0} as {1}" msgstr "" #: ../udiskie/mount.py:645 #, python-brace-format msgid "not deleting {0}: unhandled device" msgstr "" #: ../udiskie/mount.py:649 #, python-brace-format msgid "deleting {0}" msgstr "" #: ../udiskie/mount.py:651 #, python-brace-format msgid "deleted {0}" msgstr "" #: ../udiskie/mount.py:777 #, python-brace-format msgid "Browse {0}" msgstr "" #: ../udiskie/mount.py:778 #, python-brace-format msgid "Hack on {0}" msgstr "" #: ../udiskie/mount.py:779 #, python-brace-format msgid "Mount {0}" msgstr "" #: ../udiskie/mount.py:780 #, python-brace-format msgid "Unmount {0}" msgstr "" #: ../udiskie/mount.py:781 #, python-brace-format msgid "Unlock {0}" msgstr "" #: ../udiskie/mount.py:782 #, python-brace-format msgid "Lock {0}" msgstr "" #: ../udiskie/mount.py:783 #, python-brace-format msgid "Eject {1}" msgstr "" #: ../udiskie/mount.py:784 #, python-brace-format msgid "Unpower {1}" msgstr "" #: ../udiskie/mount.py:785 #, python-brace-format msgid "Clear password for {0}" msgstr "" #: ../udiskie/mount.py:786 #, python-brace-format msgid "Detach {0}" msgstr "" #: ../udiskie/notify.py:62 msgid "Browse directory" msgstr "" #: ../udiskie/notify.py:64 msgid "Open terminal" msgstr "" #: ../udiskie/notify.py:68 msgid "Device mounted" msgstr "" #: ../udiskie/notify.py:69 #, python-brace-format msgid "{0.ui_label} mounted on {0.mount_paths[0]}" msgstr "" #: ../udiskie/notify.py:80 msgid "Device unmounted" msgstr "" #: ../udiskie/notify.py:81 #, python-brace-format msgid "{0.ui_label} unmounted" msgstr "" #: ../udiskie/notify.py:90 msgid "Device locked" msgstr "" #: ../udiskie/notify.py:91 #, python-brace-format msgid "{0.device_presentation} locked" msgstr "" #: ../udiskie/notify.py:100 msgid "Device unlocked" msgstr "" #: ../udiskie/notify.py:101 #, python-brace-format msgid "{0.device_presentation} unlocked" msgstr "" #: ../udiskie/notify.py:135 msgid "Device added" msgstr "" #: ../udiskie/notify.py:136 #, python-brace-format msgid "device appeared on {0.device_presentation}" msgstr "" #: ../udiskie/notify.py:155 msgid "Device removed" msgstr "" #: ../udiskie/notify.py:156 #, python-brace-format msgid "device disappeared on {0.device_presentation}" msgstr "" #: ../udiskie/notify.py:165 #, python-brace-format msgid "" "failed to {0} {1}:\n" "{2}" msgstr "" #: ../udiskie/notify.py:167 #, python-brace-format msgid "failed to {0} device {1}." msgstr "" #: ../udiskie/notify.py:173 msgid "Retry" msgstr "" #: ../udiskie/notify.py:176 msgid "Job failed" msgstr "" #: ../udiskie/notify.py:207 #, python-brace-format msgid "Failed to show notification: {0}" msgstr "" #: ../udiskie/prompt.py:96 msgid "Show password" msgstr "" #: ../udiskie/prompt.py:101 msgid "Open keyfile…" msgstr "" #: ../udiskie/prompt.py:108 msgid "Cache password" msgstr "" #: ../udiskie/prompt.py:123 msgid "Open a keyfile to unlock the LUKS device" msgstr "" #: ../udiskie/prompt.py:157 ../udiskie/prompt.py:167 #, python-brace-format msgid "Enter password for {0.device_presentation}: " msgstr "" #: ../udiskie/prompt.py:203 msgid "Unknown device attribute {!r} in format string: {!r}" msgstr "" #: ../udiskie/prompt.py:255 msgid "" "Can't find file browser: {0!r}. You may want to change the value for the '-" "f' option." msgstr "" #: ../udiskie/tray.py:182 msgid "Managed devices" msgstr "" #: ../udiskie/tray.py:198 msgid "Mount disc image" msgstr "" #: ../udiskie/tray.py:204 msgid "Enable automounting" msgstr "" #: ../udiskie/tray.py:210 msgid "Enable notifications" msgstr "" #: ../udiskie/tray.py:219 msgid "Quit" msgstr "" #: ../udiskie/tray.py:226 msgid "Open disc image" msgstr "" #: ../udiskie/tray.py:228 msgid "Open" msgstr "" #: ../udiskie/tray.py:229 msgid "Cancel" msgstr "" #: ../udiskie/tray.py:269 msgid "Invalid node!" msgstr "" #: ../udiskie/tray.py:271 msgid "No external devices" msgstr "" #: ../udiskie/tray.py:387 msgid "udiskie" msgstr "" #: ../udiskie/udisks2.py:661 #, python-brace-format msgid "found device owning \"{0}\": \"{1}\"" msgstr "" #: ../udiskie/udisks2.py:664 #, python-brace-format msgid "no device found owning \"{0}\"" msgstr "" #: ../udiskie/udisks2.py:683 #, python-brace-format msgid "Daemon version: {0}" msgstr "" #: ../udiskie/udisks2.py:688 #, python-brace-format msgid "Keyfile support: {0}" msgstr "" #: ../udiskie/udisks2.py:767 #, python-brace-format msgid "+++ {0}: {1}" msgstr "" ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1713981306.3709414 udiskie-2.5.3/setup.cfg0000644000175100001770000000326114612243572014414 0ustar00runnerdocker[metadata] name = udiskie version = attr: udiskie.__version__ description = Removable disk automounter for udisks url = https://github.com/coldfix/udiskie long_description = file: README.rst, HACKING.rst, TRANSLATIONS.rst, CHANGES.rst author = Byron Clark author_email = byron@theclarkfamily.name maintainer = Thomas Gläßle maintainer_email = t_glaessle@gmx.de license = MIT license_file = COPYING project_urls = Bug Tracker = https://github.com/coldfix/udiskie/issues Source Code = https://github.com/coldfix/udiskie classifiers = Development Status :: 5 - Production/Stable Environment :: Console Environment :: X11 Applications :: GTK Intended Audience :: Developers Intended Audience :: End Users/Desktop Operating System :: POSIX :: Linux Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 License :: OSI Approved :: MIT License Topic :: Desktop Environment Topic :: Software Development Topic :: System :: Filesystems Topic :: System :: Hardware Topic :: Utilities long_description_content_type = text/x-rst [options] packages = udiskie udiskie.icons zip_safe = true include_package_data = true python_requires = >=3.5 install_requires = PyYAML docopt importlib_resources;python_version<'3.7' PyGObject [options.extras_require] password-cache = keyutils==0.3 [options.entry_points] console_scripts = udiskie = udiskie.cli:Daemon.main udiskie-mount = udiskie.cli:Mount.main udiskie-umount = udiskie.cli:Umount.main udiskie-info = udiskie.cli:Info.main [flake8] ignore = E126,E221,E226,E241,E731,E741,W503,W504 max-line-length = 84 max-complexity = 14 exclude = docs,.git,build,__pycache__,dist,hit_models [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/setup.py0000644000175100001770000000715614612243552014312 0ustar00runnerdockerfrom setuptools import setup, Command from setuptools.command.easy_install import ScriptWriter from setuptools.command.install import install as orig_install from distutils.command.build import build as orig_build from textwrap import dedent from subprocess import call import logging from os import path from glob import glob completions_zsh = glob('completions/zsh/_*') completions_bash = glob('completions/bash/*') languages = [path.splitext(path.split(po_file)[1])[0] for po_file in glob('lang/*.po')] class build(orig_build): """Subclass build command to add a subcommand for building .mo files.""" sub_commands = orig_build.sub_commands + [('build_mo', None)] class build_mo(Command): """Create machine specific translation files (for i18n via gettext).""" description = 'Compile .po files into .mo files' def initialize_options(self): pass def finalize_options(self): pass def run(self): for lang in languages: po_file = 'lang/{}.po'.format(lang) mo_file = 'build/locale/{}/LC_MESSAGES/udiskie.mo'.format(lang) self.mkpath(path.dirname(mo_file)) self.make_file( po_file, mo_file, self.make_mo, [po_file, mo_file]) def make_mo(self, po_filename, mo_filename): """Create a machine object (.mo) from a portable object (.po) file.""" try: call(['msgfmt', po_filename, '-o', mo_filename]) except OSError as e: # ignore failures since i18n support is optional: logging.warning(e) # NOTE: Subclassing the setuptools install command alters its behaviour to use # the distutils code. This is due to some really odd call-context checks in # the setuptools command. # # In fact this is desirable because distutils (correctly) installs data files # to `sys.prefix` whereas setuptools by default installs to the egg folder # (which is pretty much useless) and doesn't invoke build commands before # install. The only real drawback with the distutils behaviour is that it does # not automatically install dependencies, but we can easily live with that. # # Note further that we need to subclass the *setuptools* install command # rather than the *distutils* one to prevent errors when installing with pip # from the source distribution. class install(orig_install): pass def fast_entrypoint_script_template(): """ Replacement for ``easy_install.ScriptWriter.template`` to generate faster entry points that don't depend on and import pkg_resources. NOTE: `pip install` already does the right thing (at least for pip 19.0) without our help, but this is still needed for setuptools install, i.e. ``python setup.py install`` or develop. """ SCRIPT_TEMPLATE = dedent(r''' # encoding: utf-8 import sys from {ep.module_name} import {ep.attrs[0]} if __name__ == '__main__': sys.exit({func}()) ''').lstrip() class ScriptTemplate(str): def __mod__(self, context): func = '.'.join(context['ep'].attrs) return self.format(func=func, **context) return ScriptTemplate(SCRIPT_TEMPLATE) ScriptWriter.template = fast_entrypoint_script_template() setup( cmdclass={ 'install': install, 'build': build, 'build_mo': build_mo, }, data_files=[ ('share/bash-completion/completions', completions_bash), ('share/zsh/site-functions', completions_zsh), *[('share/locale/{}/LC_MESSAGES'.format(lang), ['build/locale/{}/LC_MESSAGES/udiskie.mo'.format(lang)]) for lang in languages], ], ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1713981306.3629415 udiskie-2.5.3/test/0000755000175100001770000000000014612243572013550 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/test/test_cache.py0000644000175100001770000000420014612243552016216 0ustar00runnerdocker""" Tests for the udiskie.cache module. """ import unittest import time from udiskie.cache import PasswordCache class TestDev: def __init__(self, id_uuid): self.id_uuid = id_uuid class TestPasswordCache(unittest.TestCase): """ Tests for the udiskie.cache.PasswordCache class. """ # NOTE: The device names are different in each test so that they do not # interfere accidentally. def test_timeout(self): """The cached password expires after the specified timeout.""" device = TestDev('ALPHA') password = '{<}hëllo ωορλδ!{>}' cache = PasswordCache(1) cache[device] = password self.assertEqual(cache[device], password.encode('utf-8')) time.sleep(1.5) with self.assertRaises(KeyError): cache[device] def test_touch(self): """Key access refreshes the timeout.""" device = TestDev('BETA') password = '{<}hëllo ωορλδ!{>}' cache = PasswordCache(3) cache[device] = password time.sleep(2) self.assertEqual(cache[device], password.encode('utf-8')) time.sleep(2) self.assertEqual(cache[device], password.encode('utf-8')) time.sleep(4) with self.assertRaises(KeyError): cache[device] def test_revoke(self): """A key can be deleted manually.""" device = TestDev('GAMMA') password = '{<}hëllo ωορλδ!{>}' cache = PasswordCache(0) cache[device] = password self.assertEqual(cache[device], password.encode('utf-8')) del cache[device] with self.assertRaises(KeyError): cache[device] def test_update(self): device = TestDev('DELTA') password = '{<}hëllo ωορλδ!{>}' cache = PasswordCache(0) cache[device] = password self.assertEqual(cache[device], password.encode('utf-8')) cache[device] = password * 2 self.assertEqual(cache[device], password.encode('utf-8')*2) del cache[device] with self.assertRaises(KeyError): cache[device] if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/test/test_match.py0000644000175100001770000000517014612243552016256 0ustar00runnerdocker""" Tests for the udiskie.match module. These tests are intended to demonstrate and ensure the correct usage of the config file used by udiskie for custom device options. """ import unittest import tempfile import shutil import os.path import gc from udiskie.config import Config, match_config class TestDev: def __init__(self, object_path, id_type, id_uuid): self.device_file = object_path self.id_type = id_type self.id_uuid = id_uuid self.partition_slave = None self.luks_cleartext_slave = None class TestFilterMatcher(unittest.TestCase): """ Tests for the udiskie.match.FilterMatcher class. """ def setUp(self): """Create a temporary config file.""" self.base = tempfile.mkdtemp() self.config_file = os.path.join(self.base, 'filters.conf') with open(self.config_file, 'wt') as f: f.write(''' mount_options: - id_uuid: device-with-options options: noatime,nouser - id_type: vfat options: ro,nouser ignore_device: - id_uuid: ignored-DEVICE ''') self.filters = Config.from_file(self.config_file).device_config def mount_options(self, device): return match_config(self.filters, device, 'options', None) def ignore_device(self, device): return match_config(self.filters, device, 'ignore', False) def tearDown(self): """Remove the config file.""" gc.collect() shutil.rmtree(self.base) def test_ignored(self): """Test the FilterMatcher.is_ignored() method.""" self.assertTrue( self.ignore_device( TestDev('/ignore', 'vfat', 'IGNORED-device'))) self.assertFalse( self.ignore_device( TestDev('/options', 'vfat', 'device-with-options'))) self.assertFalse( self.ignore_device( TestDev('/nomatch', 'vfat', 'no-matching-id'))) def test_options(self): """Test the FilterMatcher.get_mount_options() method.""" self.assertEqual( ['noatime', 'nouser'], self.mount_options( TestDev('/options', 'vfat', 'device-with-options'))) self.assertEqual( ['noatime', 'nouser'], self.mount_options( TestDev('/optonly', 'ext', 'device-with-options'))) self.assertEqual( ['ro', 'nouser'], self.mount_options( TestDev('/fsonly', 'vfat', 'no-matching-id'))) self.assertEqual( None, self.mount_options( TestDev('/nomatch', 'ext', 'no-matching-id'))) if __name__ == '__main__': unittest.main() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1713981306.3669415 udiskie-2.5.3/udiskie/0000755000175100001770000000000014612243572014226 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/__init__.py0000644000175100001770000000002614612243552016333 0ustar00runnerdocker__version__ = '2.5.3' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/appindicator.py0000644000175100001770000000402514612243552017254 0ustar00runnerdocker""" Status icon using AppIndicator3. """ from gi.repository import Gtk from .async_ import Future from .depend import require_AppIndicator3 AppIndicator3 = require_AppIndicator3() class AppIndicatorIcon: """ Show status icon using AppIndicator as backend. Replaces `udiskie.tray.StatusIcon` on ubuntu/unity. """ def __init__(self, menumaker, _icons): self._maker = menumaker self._menu = Gtk.Menu() self._indicator = AppIndicator3.Indicator.new( 'udiskie', _icons.get_icon_name('media'), AppIndicator3.IndicatorCategory.HARDWARE) self._indicator.set_status(AppIndicator3.IndicatorStatus.PASSIVE) self._indicator.set_menu(self._menu) # Get notified before menu is shown, see: # https://bugs.launchpad.net/screenlets/+bug/522152/comments/15 dbusmenuserver = self._indicator.get_property('dbus-menu-server') self._dbusmenuitem = dbusmenuserver.get_property('root-node') self._conn = self._dbusmenuitem.connect('about-to-show', self._on_show) self.task = Future() menumaker._quit_action = self.destroy # Populate menu initially, so libdbusmenu does not ignore the # 'about-to-show': self._maker(self._menu) self._menu.show_all() def destroy(self): self.show(False) self._dbusmenuitem.disconnect(self._conn) self.task.set_result(True) @property def visible(self): status = self._indicator.get_status() return status == AppIndicator3.IndicatorStatus.ACTIVE def show(self, show=True): if show == self.visible: return status = (AppIndicator3.IndicatorStatus.ACTIVE if show else AppIndicator3.IndicatorStatus.PASSIVE) self._indicator.set_status(status) def _on_show(self, menu): # clear menu: for item in self._menu.get_children(): self._menu.remove(item) # repopulate: self._maker(self._menu) self._menu.show_all() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/async_.py0000644000175100001770000001520414612243552016054 0ustar00runnerdocker""" Lightweight asynchronous framework. This module defines the protocol used for asynchronous operations in udiskie. It is based on ideas from "Twisted" and the "yield from" expression in python3, but more lightweight (incomplete) and compatible with python2. """ import traceback from functools import partial from subprocess import CalledProcessError from gi.repository import GLib from gi.repository import Gio from .common import cachedproperty, wraps __all__ = [ 'pack', 'to_coro', 'run_bg', 'Future', 'gather', 'Task', ] ACTIVE_TASKS = set() def pack(*values): """Unpack a return tuple to a yield expression return value.""" # Schizophrenic returns from asyncs. Inspired by # gi.overrides.Gio.DBusProxy. if len(values) == 0: return None elif len(values) == 1: return values[0] else: return values class Future: """ Base class for asynchronous operations. One `Future' object represents an asynchronous operation. It allows for separate result and error handlers which can be set by appending to the `callbacks` and `errbacks` lists. Implementations must conform to the following very lightweight protocol: The task is started on initialization, but most not finish immediately. Success/error exit is signaled to the observer by calling exactly one of `self.set_result(value)` or `self.set_exception(exception)` when the operation finishes. For implementations, see :class:`Task` or :class:`Dialog`. """ @cachedproperty def callbacks(self): """Functions to be called on successful completion.""" return [] @cachedproperty def errbacks(self): """Functions to be called on error completion.""" return [] def _finish(self, callbacks, *args): """Set finished state and invoke specified callbacks [internal].""" return [fn(*args) for fn in callbacks] def set_result(self, value): """Signal successful completion.""" self._finish(self.callbacks, value) def set_exception(self, exception): """Signal unsuccessful completion.""" was_handled = self._finish(self.errbacks, exception) if not was_handled: traceback.print_exception( type(exception), exception, exception.__traceback__) def __await__(self): ACTIVE_TASKS.add(self) try: return (yield self) finally: ACTIVE_TASKS.remove(self) def to_coro(func): @wraps(func) async def coro(*args, **kwargs): return func(*args, **kwargs) return coro def run_bg(func): @wraps(func) def runner(*args, **kwargs): return ensure_future(func(*args, **kwargs)) return runner class gather(Future): """ Manages a collection of asynchronous tasks. The callbacks are executed when all of the subtasks have completed. """ def __init__(self, *tasks): """Create from a list of `Future`-s.""" tasks = list(tasks) self._done = False self._results = {} self._num_tasks = len(tasks) if not tasks: run_soon(self.set_result, []) for idx, task in enumerate(tasks): task = ensure_future(task) task.callbacks.append(partial(self._subtask_result, idx)) task.errbacks.append(partial(self._subtask_error, idx)) def _subtask_result(self, idx, value): """Receive a result from a single subtask.""" self._results[idx] = value if len(self._results) == self._num_tasks: self.set_result([ self._results[i] for i in range(self._num_tasks) ]) def _subtask_error(self, idx, error): """Receive an error from a single subtask.""" self.set_exception(error) self.errbacks.clear() def call_func(fn, *args): """ Call the function with the specified arguments but return None. This rather boring helper function is used by run_soon to make sure the function is executed only once. """ # NOTE: Apparently, idle_add does not re-execute its argument if an # exception is raised. So it's okay to let exceptions propagate. fn(*args) def run_soon(fn, *args): """Run the function once.""" GLib.idle_add(call_func, fn, *args) def sleep(seconds): future = Future() GLib.timeout_add(int(seconds*1000), future.set_result, True) return future def ensure_future(awaitable): if isinstance(awaitable, Future): return awaitable return Task(iter(awaitable.__await__())) class Task(Future): """Turns a generator into a Future.""" def __init__(self, generator): """Create and start a ``Task`` from the specified generator.""" self._generator = generator run_soon(self._resume, next, self._generator) def _resume(self, func, *args): """Resume the coroutine by throwing a value or returning a value from the ``await`` and handle further awaits.""" try: value = func(*args) except StopIteration as e: self._generator.close() self.set_result(e.value) except Exception as e: self._generator.close() self.set_exception(e) else: assert isinstance(value, Future) value.callbacks.append(partial(self._resume, self._generator.send)) value.errbacks.append(partial(self._resume, self._generator.throw)) def gio_callback(proxy, result, future): future.set_result(result) async def exec_subprocess(argv, capture=True): """ An Future task that represents a subprocess. If successful, the task's result is set to the collected STDOUT of the subprocess. :raises subprocess.CalledProcessError: if the subprocess returns a non-zero exit code """ future = Future() flags = ((Gio.SubprocessFlags.STDOUT_PIPE if capture else Gio.SubprocessFlags.NONE) | Gio.SubprocessFlags.STDIN_INHERIT) process = Gio.Subprocess.new(argv, flags) stdin_buf = None cancellable = None process.communicate_async( stdin_buf, cancellable, gio_callback, future) result = await future success, stdout, stderr = process.communicate_finish(result) stdout = stdout.get_data() if capture else None # GLib.Bytes -> bytes if not success: raise RuntimeError("Subprocess did not exit normally!") exit_code = process.get_exit_status() if exit_code != 0: raise CalledProcessError( "Subprocess returned a non-zero exit-status!", exit_code, stdout) return stdout ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/automount.py0000644000175100001770000000273314612243552016636 0ustar00runnerdocker""" Automount utility. """ from .common import DaemonBase from .async_ import run_bg __all__ = ['AutoMounter'] class AutoMounter(DaemonBase): """ Automount utility. Being connected to the udiskie daemon, this component automatically mounts newly discovered external devices. Instances are constructed with a Mounter object, like so: >>> automounter = AutoMounter(Mounter(udisks=Daemon())) >>> automounter.activate() """ def __init__(self, mounter, automount=True): """Store mounter as member variable.""" self._mounter = mounter self._automount = automount self.events = { 'device_changed': self.device_changed, 'device_added': self.auto_add, 'media_added': self.auto_add, } def is_on(self): return self._automount def toggle_on(self): self._automount = not self._automount def device_changed(self, old_state, new_state): """Mount newly mountable devices.""" # udisks2 sometimes adds empty devices and later updates them - which # makes is_external become true at a time later than device_added: if (self._mounter.is_addable(new_state) and not self._mounter.is_addable(old_state) and not self._mounter.is_removable(old_state)): self.auto_add(new_state) @run_bg def auto_add(self, device): return self._mounter.auto_add(device, automount=self._automount) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/cache.py0000644000175100001770000000325714612243552015650 0ustar00runnerdocker""" Utility for temporarily caching passwords. """ import keyutils # This import fails in python-keyring-keyutils, which is a not (yet) supported # alternative for the python-keyring package. This lets us distinguish between # the two identically named python packages (=keyring). from keyutils import KEY_SPEC_PROCESS_KEYRING class PasswordCache: def __init__(self, timeout): self.timeout = timeout self.keyring = KEY_SPEC_PROCESS_KEYRING def _key(self, device): return device.id_uuid.encode('utf-8') def _key_id(self, device): key = self._key(device) try: key_id = keyutils.request_key(key, self.keyring) except keyutils.Error: raise KeyError("Key has been revoked!") if key_id is None: raise KeyError("Key not cached!") return key_id def __contains__(self, device): try: self._key_id(device) return True except KeyError: return False def __getitem__(self, device): key_id = self._key_id(device) self._touch(key_id) try: return keyutils.read_key(key_id) except keyutils.Error: raise KeyError("Key not cached!") def __setitem__(self, device, value): key = self._key(device) if isinstance(value, str): value = value.encode('utf-8') key_id = keyutils.add_key(key, value, self.keyring) self._touch(key_id) def __delitem__(self, device): key_id = self._key_id(device) keyutils.revoke(key_id) def _touch(self, key_id): if self.timeout > 0: keyutils.set_timeout(key_id, self.timeout) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/cli.py0000644000175100001770000006161214612243552015353 0ustar00runnerdocker""" Command line interface logic. The application classes in this module are installed as executables via setuptools entry points. """ # import udiskie.depend first - for side effects! from .depend import has_Notify, has_Gtk, _in_X, _in_Wayland, has_AppIndicator3 import inspect import logging.config import traceback from gi.repository import GLib from docopt import docopt, DocoptExit import udiskie import udiskie.config import udiskie.mount import udiskie.udisks2 from .common import extend, ObjDictView from .locale import _ from .async_ import Future, ensure_future, gather __all__ = [ 'Daemon', 'Mount', 'Umount', ] class Choice: """Mapping of command line arguments to option values.""" def __init__(self, mapping): """Set mapping between arguments and values.""" self._mapping = mapping def _check(self, args): """Exit in case of multiple exclusive arguments.""" if sum(bool(args[arg]) for arg in self._mapping) > 1: raise DocoptExit(_('These options are mutually exclusive: {0}', ', '.join(self._mapping))) def __call__(self, args): """Get the option value from the parsed arguments.""" self._check(args) for arg, val in self._mapping.items(): if args[arg] not in (None, False): return val def Switch(name): """Negatable option.""" return Choice({'--' + name: True, '--no-' + name: False}) class Value: """Option which is given as value of a command line argument.""" def __init__(self, name): """Set argument name.""" self._name = name def __call__(self, args): """Get the value of the command line argument.""" return args[self._name] class OptionalValue: def __init__(self, name): """Set argument name.""" self._name = name self._choice = Switch(name.lstrip('-')) def __call__(self, args): """Get the value of the command line argument.""" return self._choice(args) and args[self._name] class SelectLevel(logging.Filter): def __init__(self, level): self.level = level def filter(self, record): return record.levelno == self.level class _EntryPoint: """ Abstract base class for program entry points. Implementations need to - implement :meth:`_init` - provide a docstring - extend :cvar:`option_defaults` and :cvar:`option_rules`. """ option_defaults = { 'log_level': logging.INFO, } option_rules = { 'log_level': Choice({ '--verbose': logging.DEBUG, '--quiet': logging.ERROR}), } usage_remarks = _(""" Note, that the options in the individual groups are mutually exclusive. The config file can be a JSON or preferably a YAML file. For an example, see the MAN page (or doc/udiskie.8.txt in the repository). """) def __init__(self, argv=None): """Parse command line options, read config and initialize members.""" # parse program options (retrieve log level and config file name): args = docopt(self.usage, version='udiskie ' + self.version) default_opts = self.option_defaults program_opts = self.program_options(args) # initialize logging configuration: log_level = program_opts.get('log_level', default_opts['log_level']) debug = log_level <= logging.DEBUG logging.config.dictConfig({ 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'plain': {'format': _('%(message)s')}, 'detail': {'format': _( '%(levelname)s [%(asctime)s] %(name)s: %(message)s')}, }, 'filters': { 'info': {'()': 'udiskie.cli.SelectLevel', 'level': logging.INFO}, }, 'handlers': { 'info': {'class': 'logging.StreamHandler', 'stream': 'ext://sys.stdout', 'formatter': 'plain', 'filters': ['info']}, 'error': {'class': 'logging.StreamHandler', 'stream': 'ext://sys.stderr', 'formatter': 'plain', 'level': 'WARNING'}, 'debug': {'class': 'logging.StreamHandler', 'stream': 'ext://sys.stderr', 'formatter': 'detail'}, }, # configure root logger: 'root': { 'handlers': ['info', 'debug' if debug else 'error'], 'level': log_level, }, }) # parse config options config_file = OptionalValue('--config')(args) config = udiskie.config.Config.from_file(config_file) options = {} options.update(default_opts) options.update(config.program_options) options.update(program_opts) # initialize instance variables self.config = config self.options = options def program_options(self, args): """Get program options from docopt parsed options.""" options = {} for name, rule in self.option_rules.items(): val = rule(args) if val is not None: options[name] = val return options @classmethod def main(cls, argv=None): """Run program. Returns program exit code.""" return cls(argv).run() @property def version(self): """Version from setuptools metadata.""" return udiskie.__version__ @property def usage(self): """Full usage string.""" return inspect.cleandoc(self.__doc__ + self.usage_remarks) def _init(self): """Return the application main task as Future.""" raise NotImplementedError() def run(self): """Run the main loop. Returns exit code.""" self.exit_code = 1 self.mainloop = GLib.MainLoop() try: future = ensure_future(self._start_async_tasks()) future.callbacks.append(self.set_exit_code) self.mainloop.run() return self.exit_code except KeyboardInterrupt: return 1 def set_exit_code(self, exit_code): self.exit_code = exit_code async def _start_async_tasks(self): """Start asynchronous operations.""" try: self.udisks = await udiskie.udisks2.Daemon.create() results = await self._init() return 0 if all(results) else 1 except Exception: traceback.print_exc() return 1 finally: self.mainloop.quit() class Component: def __init__(self, create): self.create = create self.instance = None @property def active(self): return self.instance is not None and self.instance.active def activate(self): if self.instance is None: self.instance = self.create() if not self.instance.active: self.instance.activate() def deactivate(self): if self.active: self.instance.deactivate() def toggle(self): if self.active: self.deactivate() else: self.activate() class Daemon(_EntryPoint): """ udiskie: a user-level daemon for auto-mounting. Usage: udiskie [options] udiskie (--help | --version) General options: -c FILE, --config=FILE Set config file -C, --no-config Don't use config file -v, --verbose Increase verbosity (DEBUG) -q, --quiet Decrease verbosity -h, --help Show this help -V, --version Show version information Daemon options: -a, --automount Automount new devices -A, --no-automount Disable automounting -n, --notify Show popup notifications -N, --no-notify Disable notifications -t, --tray Show tray icon -s, --smart-tray Auto hide tray icon -T, --no-tray Disable tray icon -m MENU, --menu MENU Tray menu [flat/nested] --appindicator Use appindicator for status icon --no-appindicator Don't use appindicator --password-cache MINUTES Set password cache timeout --no-password-cache Disable password cache -f PROGRAM, --file-manager PROGRAM Set program for browsing -F, --no-file-manager Disable browsing --terminal COMMAND Set terminal command line (e.g. "termite -d") --no-terminal Disable terminal action -p COMMAND, --password-prompt COMMAND Command for password retrieval -P, --no-password-prompt Disable unlocking --event-hook COMMAND Command to execute on events --no-event-hook Disable command notifications --menu-checkbox-workaround Use checkbox workaround --no-menu-checkbox-workaround Disable checkbox workaround --menu-update-workaround Use wayland menu workaround --no-menu-update-workaround Disable wayland menu workaround [DEPRECATED]: --notify-command COMMAND Renamed to --event-hook --no-notify-command Renamed to --no-event-hook """ option_defaults = extend(_EntryPoint.option_defaults, { 'automount': True, 'notify': True, 'tray': False, 'menu': 'flat', 'appindicator': None, 'file_manager': 'xdg-open', 'terminal': '', 'password_prompt': 'builtin:gui', 'password_cache': False, 'notify_command': None, 'event_hook': None, 'menu_checkbox_workaround': None, 'menu_update_workaround': None, }) option_rules = extend(_EntryPoint.option_rules, { 'automount': Switch('automount'), 'notify': Switch('notify'), 'tray': Choice({ '--tray': True, '--no-tray': False, '--smart-tray': 'auto'}), 'menu': Value('--menu'), 'appindicator': Switch('appindicator'), 'file_manager': OptionalValue('--file-manager'), 'password_prompt': OptionalValue('--password-prompt'), 'password_cache': OptionalValue('--password-cache'), 'terminal': OptionalValue('--terminal'), 'notify_command': OptionalValue('--notify-command'), 'event_hook': OptionalValue('--event-hook'), 'menu_checkbox_workaround': OptionalValue('--menu-checkbox-workaround'), 'menu_update_workaround': OptionalValue('--menu-update-workaround'), }) def _init(self): import udiskie.prompt config = self.config options = self.options # prepare mounter object prompt = udiskie.prompt.password(options['password_prompt']) browser = udiskie.prompt.browser(options['file_manager']) terminal = udiskie.prompt.browser(options['terminal']) cache = None try: import udiskie.cache timeout = int(options['password_cache']) * 60 cache = udiskie.cache.PasswordCache(timeout) except ImportError: cache = None self.mounter = udiskie.mount.Mounter( config=config.device_config, prompt=prompt, browser=browser, terminal=terminal, cache=cache, cache_hint=options['password_cache'], udisks=self.udisks) # check component availability if options['notify'] and not has_Notify(): libnotify_not_available = _( "Typelib for 'libnotify' is not available. Possible causes include:" "\n\t- libnotify is not installed" "\n\t- the typelib is provided by a separate package" "\n\t- libnotify was built with introspection disabled" "\n\nStarting udiskie without notifications.") logging.getLogger(__name__).error(libnotify_not_available) options['notify'] = False show_tray = options['tray'] or options['appindicator'] if show_tray and _in_Wayland and options['appindicator'] is None: options['appindicator'] = True if show_tray and not (_in_X or _in_Wayland): no_tray_support = _( "Not run within X or Wayland session." "\nStarting udiskie without tray icon.\n") logging.getLogger(__name__).error(no_tray_support) show_tray = False if show_tray and not has_Gtk(3): gtk3_not_available = _( "Typelib for 'Gtk 3.0' is not available. Possible causes include:" "\n\t- GTK3 is not installed" "\n\t- the typelib is provided by a separate package" "\n\t- GTK3 was built with introspection disabled" "\nStarting udiskie without tray icon.\n") logging.getLogger(__name__).error(gtk3_not_available) show_tray = False if show_tray and options['appindicator'] and not has_AppIndicator3(): appindicator_not_available = _( "Typelib for 'AppIndicator3 0.1' is not available. Possible " "causes include:" "\n\t- libappindicator is not installed" "\n\t- the typelib is provided by a separate package" "\n\t- it was built with introspection disabled" "\nStarting udiskie without appindicator icon.\n") logging.getLogger(__name__).error(appindicator_not_available) options['appindicator'] = False # start components tasks = [] self.notify = Component(self._load_notify) self.statusicon = Component(self._load_statusicon) self.automounter = self._load_automounter(options['automount']) self.automounter.activate() if options['notify_command']: logging.getLogger(__name__).warn(_( "The 'notify_command' option was renamed to 'event_hook'. " "The old name still works, but may be removed in a future version. " "Please change your command line and config to use the new name.")) if options['event_hook'] is None: options['event_hook'] = options['notify_command'] else: logging.getLogger(__name__).warn(_( "Ignoring 'notify_command' in favor of 'event_hook'.")) if options['notify']: self.notify.activate() if options['event_hook']: # is currently enabled/disabled statically only once: self.connect_event_hook(options['event_hook']) if show_tray: self.statusicon.activate() tasks.append(self.statusicon.instance._icon.task) else: tasks.append(Future()) if options['automount']: tasks.append(self.mounter.add_all()) return gather(*tasks) def _load_notify(self): import udiskie.notify from gi.repository import Notify Notify.init('udiskie') aconfig = self.config.notification_actions if self.options['automount']: aconfig.setdefault('device_added', []) else: aconfig.setdefault('device_added', ['mount']) return udiskie.notify.Notify( Notify.Notification.new, mounter=self.mounter, timeout=self.config.notifications, aconfig=aconfig) def connect_event_hook(self, command): import udiskie.prompt return udiskie.prompt.connect_event_hook(command, self.mounter) def _load_statusicon(self): import udiskie.tray options = self.options config = self.config smart = options['tray'] == 'auto' if options['tray'] not in ('auto', True, False): raise ValueError("Invalid tray: %s" % (options['tray'],)) icons = udiskie.tray.Icons(self.config.icon_names) actions = udiskie.mount.DeviceActions(self.mounter) checkbox_workaround = options['menu_checkbox_workaround'] if checkbox_workaround is None: checkbox_workaround = options['appindicator'] and _in_Wayland update_workaround = options['menu_update_workaround'] if update_workaround is None: update_workaround = options['appindicator'] and _in_Wayland if options['menu'] == 'flat': flat = True # dropped legacy 'nested' mode: elif options['menu'] in ('smart', 'nested'): flat = False else: raise ValueError("Invalid menu: %s" % (options['menu'],)) menu_maker = udiskie.tray.TrayMenu( self, icons, actions, flat, config.quickmenu_actions, checkbox_workaround=checkbox_workaround, update_workaround=update_workaround) if options['appindicator']: import udiskie.appindicator TrayIcon = udiskie.appindicator.AppIndicatorIcon else: TrayIcon = udiskie.tray.TrayIcon trayicon = TrayIcon(menu_maker, icons) return udiskie.tray.UdiskieStatusIcon(trayicon, menu_maker, smart) def _load_automounter(self, automount): import udiskie.automount return udiskie.automount.AutoMounter(self.mounter, automount) class Mount(_EntryPoint): """ udiskie-mount: a user-level command line utility for mounting. Usage: udiskie-mount [options] (-a | DEVICE...) udiskie-mount (--help | --version) General options: -c FILE, --config=FILE Set config file -C, --no-config Don't use config file -v, --verbose Increase verbosity (DEBUG) -q, --quiet Decrease verbosity -h, --help Show this help -V, --version Show version information Mount options: -a, --all Mount all handleable devices -r, --recursive Recursively mount partitions -R, --no-recursive Disable recursive mounting -o OPTIONS, --options OPTIONS Mount option list -p COMMAND, --password-prompt COMMAND Command for password retrieval -P, --no-password-prompt Disable unlocking """ option_defaults = extend(_EntryPoint.option_defaults, { 'recursive': None, 'options': None, '': None, 'password_prompt': 'builtin:tty', }) option_rules = extend(_EntryPoint.option_rules, { 'recursive': Switch('recursive'), 'options': Value('--options'), '': Value('DEVICE'), 'password_prompt': OptionalValue('--password-prompt'), }) def _init(self): import udiskie.prompt config = self.config options = self.options device_config = config.device_config if options['options']: device_config.insert(0, udiskie.config.MountOptions({ 'options': [o.strip() for o in options['options'].split(',')], })) prompt = udiskie.prompt.password(options['password_prompt']) mounter = udiskie.mount.Mounter( config=device_config, prompt=prompt, udisks=self.udisks) recursive = options['recursive'] if options['']: tasks = [mounter.add(path, recursive=recursive) for path in options['']] else: tasks = [mounter.add_all(recursive=recursive)] return gather(*tasks) class Umount(_EntryPoint): """ udiskie-umount: a user-level command line utility for unmounting. Usage: udiskie-umount [options] (-a | DEVICE...) udiskie-umount (--help | --version) General options: -c FILE, --config=FILE Set config file -C, --no-config Don't use config file -v, --verbose Increase verbosity (DEBUG) -q, --quiet Decrease verbosity -h, --help Show this help -V, --version Show version information Unmount options: -a, --all Unmount all handleable devices -d, --detach Power off drive if possible -D, --no-detach Don't power off drive -e, --eject Eject media from device if possible -E, --no-eject Don't eject media -f, --force Force removal (recursive unmounting) -F, --no-force Don't force removal -l, --lock Lock device after unmounting -L, --no-lock Don't lock device """ option_defaults = extend(_EntryPoint.option_defaults, { 'detach': None, 'eject': False, 'force': False, 'lock': True, '': None, }) option_rules = extend(_EntryPoint.option_rules, { 'detach': Switch('detach'), 'eject': Switch('eject'), 'force': Switch('force'), 'lock': Switch('lock'), '': Value('DEVICE'), }) def _init(self): config = self.config options = self.options mounter = udiskie.mount.Mounter( self.udisks, config=config.device_config) strategy = dict(detach=options['detach'], eject=options['eject'], lock=options['lock']) if options['']: strategy['force'] = options['force'] tasks = [mounter.remove(path, **strategy) for path in options['']] else: tasks = [mounter.remove_all(**strategy)] return gather(*tasks) def _parse_filter(spec): try: key, val = spec.split('=', 1) except ValueError: if spec.startswith('!'): val = False key = spec[1:] else: val = True key = spec return key, val class Info(_EntryPoint): """ udiskie-info: get information about handleable devices. Usage: udiskie-info [options] [-o OUTPUT] [-f FILTER]... (-a | DEVICE...) udiskie-info (--help | --version) General options: -c FILE, --config=FILE Set config file -C, --no-config Don't use config file -v, --verbose Increase verbosity (DEBUG) -q, --quiet Decrease verbosity -h, --help Show this help -V, --version Show version information Unmount options: -a, --all List all handleable devices -o COL, --output COL Specify output columns in a format string containing the allowed device attributes, e.g.: "{ui_label} {is_luks}" [default: device_presentation]. -f FILT, --filter FILT Print only devices that match the given filter. """ option_defaults = extend(_EntryPoint.option_defaults, { 'output': '', 'filter': '', '': None, }) option_rules = extend(_EntryPoint.option_rules, { 'output': Value('--output'), 'filter': Value('--filter'), '': Value('DEVICE'), }) def _init(self): config = self.config options = self.options mounter = udiskie.mount.Mounter( self.udisks, config=config.device_config) if options['']: devices = [self.udisks.find(path) for path in options['']] else: devices = mounter.get_all_handleable() DeviceFilter = udiskie.config.DeviceFilter output = options['output'] # old behaviour: single attribute if output in DeviceFilter.VALID_PARAMETERS: def format_output(device): return getattr(device, output) # new behaviour: format string else: def format_output(device): view = ObjDictView(device, DeviceFilter.VALID_PARAMETERS) return output.format_map(view) filters = [_parse_filter(spec) for spec in options['filter']] matcher = DeviceFilter(dict(filters)) for device in devices: if matcher.match(device): print(format_output(device)) return gather() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/common.py0000644000175100001770000001143214612243552016067 0ustar00runnerdocker""" Common utilities. """ import os.path import sys import traceback __all__ = [ 'wraps', 'Emitter', 'samefile', 'sameuuid', 'setdefault', 'extend', 'cachedproperty', 'decode_ay', 'exc_message', 'format_exc', ] try: from black_magic.decorator import wraps except ImportError: from functools import wraps class Emitter: """Simple event emitter for a known finite set of events.""" def __init__(self, event_names=(), *args, **kwargs): """Initialize with empty lists of event handlers.""" super().__init__(*args, **kwargs) self._event_handlers = {} for evt in event_names: self._event_handlers[evt] = [] def trigger(self, event, *args): """Trigger event by name.""" for handler in self._event_handlers[event]: handler(*args) def connect(self, event, handler): """Connect an event handler.""" self._event_handlers[event].append(handler) def disconnect(self, event, handler): """Disconnect an event handler.""" self._event_handlers[event].remove(handler) def samefile(a: str, b: str) -> bool: """Check if two paths represent the same file.""" try: return os.path.samefile(a, b) except OSError: return os.path.normpath(a) == os.path.normpath(b) def sameuuid(a: str, b: str) -> bool: """Compare two UUIDs.""" return a and b and a.lower() == b.lower() def setdefault(self: dict, other: dict): """Like .update() but values in self take priority.""" for k, v in other.items(): self.setdefault(k, v) def extend(a: dict, b: dict) -> dict: """Merge two dicts and return a new dict. Much like subclassing works.""" res = a.copy() res.update(b) return res def cachedmethod(func): """A memoize decorator for object methods.""" key = '_' + func.__name__ @wraps(func) def get(self, *args): try: cache = getattr(self, key) except AttributeError: cache = {} setattr(self, key, cache) try: val = cache[args] except KeyError: val = cache[args] = func(self, *args) return val return get def cachedproperty(func): """A memoize decorator for class properties.""" key = '_' + func.__name__ @wraps(func) def get(self): try: return getattr(self, key) except AttributeError: val = func(self) setattr(self, key, val) return val return property(get) # ---------------------------------------- # udisks.Device helper classes # ---------------------------------------- class AttrDictView: """Provide attribute access view to a dictionary.""" def __init__(self, data): self.__data = data def __getattr__(self, key): try: return self.__data[key] except KeyError: raise AttributeError class ObjDictView: """Provide dict-like access view to the attributes of an object.""" def __init__(self, object, valid=None): self._object = object self._valid = valid def __getitem__(self, key): if self._valid is None or key in self._valid: try: return getattr(self._object, key) except AttributeError: raise KeyError(key) raise KeyError("Unknown key: {}".format(key)) class DaemonBase: active = False def activate(self): udisks = self._mounter.udisks for event, handler in self.events.items(): udisks.connect(event, handler) self.active = True def deactivate(self): udisks = self._mounter.udisks for event, handler in self.events.items(): udisks.disconnect(event, handler) self.active = False # ---------------------------------------- # byte array to string conversion # ---------------------------------------- def decode_ay(ay): """Convert binary blob from DBus queries to strings.""" if ay is None: return '' elif isinstance(ay, str): return ay elif isinstance(ay, bytes): return ay.decode('utf-8') else: # dbus.Array([dbus.Byte]) or any similar sequence type: return bytearray(ay).rstrip(bytearray((0,))).decode('utf-8') def is_utf8(bs): """Check if the given bytes string is utf-8 decodable.""" try: bs.decode('utf-8') return True except UnicodeDecodeError: return False def exc_message(exc): """Get an exception message.""" message = getattr(exc, 'message', None) return message or str(exc) def format_exc(*exc_info): """Show exception with traceback.""" typ, exc, tb = exc_info or sys.exc_info() error = traceback.format_exception(typ, exc, tb) return "".join(error) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/config.py0000644000175100001770000002042214612243552016043 0ustar00runnerdocker""" Config utilities. For an example config file, see the manual. If you don't have the man page installed, a raw version is available in doc/udiskie.8.txt. """ import logging import os import fnmatch from .common import exc_message from .locale import _ __all__ = ['DeviceFilter', 'match_config', 'Config'] def lower(s): try: return s.lower() except AttributeError: return s def format_dict(d): return '{' + ', '.join([ _format_item(k, v) for k, v in d.items() ]) + '}' def _format_item(k, v): if isinstance(v, bool): return k if v else '!' + k else: return '{}={}'.format(k, v) def match_value(value, pattern): if isinstance(value, (list, tuple)): return any(match_value(v, pattern) for v in value) if isinstance(pattern, (list, tuple)): return any(match_value(value, p) for p in pattern) if isinstance(value, str) and isinstance(pattern, str): return fnmatch.fnmatch(value.lower(), pattern.lower()) return lower(value) == lower(pattern) class DeviceFilter: """Associate a certain value to matching devices.""" VALID_PARAMETERS = [ 'is_drive', 'is_block', 'is_partition_table', 'is_partition', 'is_filesystem', 'is_luks', 'is_loop', # FIXME: experimental/undocumented 'is_loopfile', 'is_toplevel', 'is_detachable', 'is_ejectable', 'has_media', 'device_file', 'device_presentation', 'device_size', 'device_id', 'id_usage', 'is_crypto', 'is_ignored', 'id_type', 'id_label', 'id_uuid', 'is_luks_cleartext', 'is_external', 'is_systeminternal', 'is_mounted', 'mount_paths', 'mount_path', 'is_unlocked', 'in_use', 'should_automount', 'ui_label', 'loop_file', 'setup_by_uid', 'autoclear', 'symlinks', 'drive_model', 'drive_vendor', 'drive_label', 'ui_device_label', 'ui_device_presentation', 'ui_id_label', 'ui_id_uuid', ] def __init__(self, match): """Construct from dict of device matching attributes.""" self._log = logging.getLogger(__name__) self._match = match = match.copy() self._values = {} # mount options: if 'options' in match: options = match.pop('options') if isinstance(options, str): options = [o.strip() for o in options.split(',')] self._values['options'] = options # ignore device: if 'ignore' in match: self._values['ignore'] = match.pop('ignore') # automount: if 'automount' in match: self._values['automount'] = match.pop('automount') # keyfile: if 'keyfile' in match: keyfile = match.pop('keyfile') keyfile = os.path.expandvars(keyfile) keyfile = os.path.expanduser(keyfile) self._values['keyfile'] = keyfile if 'skip' in match: self._values['skip'] = match.pop('skip') # the use of list() makes deletion inside the loop safe: for k in list(self._match): if k not in self.VALID_PARAMETERS: self._log.error(_('Unknown matching attribute: {!r}', k)) del self._match[k] self._log.debug(_('new rule: {0}', self)) def __str__(self): return _('{0} -> {1}', format_dict(self._match), format_dict(self._values)) def match(self, device): """Check if the device object matches this filter.""" return all(match_value(getattr(device, k), v) for k, v in self._match.items()) def has_value(self, kind): return kind in self._values def value(self, kind, device): """ Get the value for the device object associated with this filter. If :meth:`match` is False for the device, the return value of this method is undefined. """ self._log.debug(_('{0} matched {1}', device.device_file or device.object_path, self)) return self._values[kind] class MountOptions(DeviceFilter): """Associate a list of mount options to matched devices.""" def __init__(self, config_item): config_item.setdefault('options', None) super().__init__(config_item) class IgnoreDevice(DeviceFilter): """Associate a boolean ignore flag to matched devices.""" def __init__(self, config_item): config_item.setdefault('ignore', True) super().__init__(config_item) def match_config(filters, device, kind, default): """ Matches devices against multiple :class:`DeviceFilter`s. :param list filters: device filters :param Device device: device to be mounted :param str kind: value kind :param default: default value :returns: value of the first matching filter """ while device is not None: for f in filters: if f.has_value(kind) and f.match(device): return f.value(kind, device) # 'skip' allows skipping further rules and directly moving on # lookup on the parent device: if f.has_value('skip') and f.match(device) and ( f.value('skip', device) in (True, 'all', kind)): break device = device.partition_slave or device.luks_cleartext_slave return default class Config: """Udiskie config in memory representation.""" def __init__(self, data): """Initialize with preparsed data dict.""" self._data = data or {} @classmethod def default_pathes(cls): """Return the default config file paths as a list.""" config_home = ( os.environ.get('XDG_CONFIG_HOME') or os.path.expanduser('~/.config')) return [os.path.join(config_home, 'udiskie', 'config.yml'), os.path.join(config_home, 'udiskie', 'config.json')] @classmethod def from_file(cls, path=None): """ Read YAML config file. Returns Config object. :raises IOError: if the path does not exist """ # None => use default if path is None: for path in cls.default_pathes(): try: return cls.from_file(path) except IOError as e: logging.getLogger(__name__).debug( _("Failed to read config file: {0}", exc_message(e))) except ImportError as e: logging.getLogger(__name__).warn( _("Failed to read {0!r}: {1}", path, exc_message(e))) return cls({}) # False/'' => no config if not path: return cls({}) if os.path.splitext(path)[1].lower() == '.json': from json import load else: from yaml import safe_load as load with open(path) as f: return cls(load(f)) @property def device_config(self): device_config = map(DeviceFilter, self._data.get('device_config', [])) mount_options = map(MountOptions, self._data.get('mount_options', [])) ignore_device = map(IgnoreDevice, self._data.get('ignore_device', [])) return list(device_config) + list(mount_options) + list(ignore_device) @property def program_options(self): """Get the program options dictionary from the config file.""" return self._data.get('program_options', {}).copy() @property def notifications(self): """Get the notification timeouts dictionary from the config file.""" return self._data.get('notifications', {}).copy() @property def icon_names(self): """Get the icon names dictionary from the config file.""" return self._data.get('icon_names', {}).copy() @property def notification_actions(self): """Get the notification actions dictionary from the config file.""" return self._data.get('notification_actions', {}).copy() @property def quickmenu_actions(self): """Get the set of actions to be shown in the quickmenu (left-click).""" return self._data.get('quickmenu_actions', None) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/dbus.py0000644000175100001770000002137314612243552015541 0ustar00runnerdocker""" Common DBus utilities. """ from functools import partial from gi.repository import Gio from gi.repository import GLib from .async_ import gio_callback, pack, Future __all__ = [ 'InterfaceProxy', 'PropertiesProxy', 'ObjectProxy', 'BusProxy', 'connect_service', 'MethodsProxy', ] unpack_variant = GLib.Variant.unpack async def call(proxy, method_name, signature, args, flags=0, timeout_msec=-1): """ Asynchronously call the specified method on a DBus proxy object. :param Gio.DBusProxy proxy: :param str method_name: :param str signature: :param tuple args: :param int flags: :param int timeout_msec: """ future = Future() cancellable = None proxy.call( method_name, GLib.Variant(signature, tuple(args)), flags, timeout_msec, cancellable, gio_callback, future, ) result = await future value = proxy.call_finish(result) return pack(*unpack_variant(value)) async def call_with_fd_list(proxy, method_name, signature, args, fds, flags=0, timeout_msec=-1): """ Asynchronously call the specified method on a DBus proxy object. :param Gio.DBusProxy proxy: :param str method_name: :param str signature: :param tuple args: :param list fds: :param int flags: :param int timeout_msec: """ future = Future() cancellable = None fd_list = Gio.UnixFDList.new_from_array(fds) proxy.call_with_unix_fd_list( method_name, GLib.Variant(signature, tuple(args)), flags, timeout_msec, fd_list, cancellable, gio_callback, future, ) result = await future value, fds = proxy.call_with_unix_fd_list_finish(result) return pack(*unpack_variant(value)) class InterfaceProxy: """ DBus proxy object for a specific interface. :ivar str object_path: object path of the DBus object :ivar Gio.DBusProxy _proxy: underlying proxy object """ def __init__(self, proxy): """ Initialize property and method attribute accessors for the interface. :param Gio.DBusProxy proxy: accessed object """ self._proxy = proxy self.object_path = proxy.get_object_path() @property def object(self): """Get an ObjectProxy instanec for the underlying object.""" proxy = self._proxy return ObjectProxy(proxy.get_connection(), proxy.get_name(), proxy.get_object_path()) def connect(self, event, handler): """Connect to a DBus signal, returns subscription id (int).""" interface = self._proxy.get_interface_name() return self.object.connect(interface, event, handler) def call(self, method_name, signature='()', *args): return call(self._proxy, method_name, signature, args) class PropertiesProxy(InterfaceProxy): Interface = 'org.freedesktop.DBus.Properties' def __init__(self, proxy, interface_name=None): super().__init__(proxy) self.interface_name = interface_name def GetAll(self, interface_name=None): return self.call('GetAll', '(s)', interface_name or self.interface_name) class ObjectProxy: """Simple proxy class for a DBus object.""" def __init__(self, connection, bus_name, object_path): """ Initialize member variables. :ivar Gio.DBusConnection connection: :ivar str bus_name: :ivar str object_path: This performs no IO at all. """ self.connection = connection self.bus_name = bus_name self.object_path = object_path def _get_interface(self, name): """Get a Future(Gio.DBusProxy) for the specified interface.""" return proxy_new( self.connection, Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES | Gio.DBusProxyFlags.DO_NOT_CONNECT_SIGNALS, info=None, name=self.bus_name, object_path=self.object_path, interface_name=name, ) async def get_interface(self, name): """Get an InterfaceProxy for the specified interface.""" proxy = await self._get_interface(name) return InterfaceProxy(proxy) async def get_property_interface(self, interface_name=None): proxy = await self._get_interface(PropertiesProxy.Interface) return PropertiesProxy(proxy, interface_name) @property def bus(self): """Get a BusProxy for the underlying bus.""" return BusProxy(self.connection, self.bus_name) def connect(self, interface, event, handler): """Connect to a DBus signal. Returns subscription id (int).""" object_path = self.object_path return self.bus.connect(interface, event, object_path, handler) async def call(self, interface_name, method_name, signature='()', *args): proxy = await self.get_interface(interface_name) result = await proxy.call(method_name, signature, *args) return result class BusProxy: """ Simple proxy class for a connected bus. :ivar Gio.DBusConnection connection: :ivar str bus_name: """ def __init__(self, connection, bus_name): """ Initialize member variables. :param Gio.DBusConnection connection: :param str bus_name: This performs IO at all. """ self.connection = connection self.bus_name = bus_name def get_object(self, object_path): """Get an ObjectProxy representing the specified object.""" return ObjectProxy(self.connection, self.bus_name, object_path) def connect(self, interface, event, object_path, handler): """ Connect to a DBus signal. If ``object_path`` is None, subscribe for all objects and invoke the callback with the object_path as its first argument. """ if object_path: def callback(connection, sender_name, object_path, interface_name, signal_name, parameters): return handler(*unpack_variant(parameters)) else: def callback(connection, sender_name, object_path, interface_name, signal_name, parameters): return handler(object_path, *unpack_variant(parameters)) return self.connection.signal_subscribe( self.bus_name, interface, event, object_path, None, Gio.DBusSignalFlags.NONE, callback, ) def disconnect(self, subscription_id): """Disconnect a DBus signal subscription.""" self.connection.signal_unsubscribe(subscription_id) async def proxy_new(connection, flags, info, name, object_path, interface_name): """Asynchronously call the specified method on a DBus proxy object.""" future = Future() cancellable = None Gio.DBusProxy.new( connection, flags, info, name, object_path, interface_name, cancellable, gio_callback, future, ) result = await future value = Gio.DBusProxy.new_finish(result) if value is None: raise RuntimeError("Failed to connect DBus object!") return value async def proxy_new_for_bus(bus_type, flags, info, name, object_path, interface_name): """Asynchronously call the specified method on a DBus proxy object.""" future = Future() cancellable = None Gio.DBusProxy.new_for_bus( bus_type, flags, info, name, object_path, interface_name, cancellable, gio_callback, future, ) result = await future value = Gio.DBusProxy.new_for_bus_finish(result) if value is None: raise RuntimeError("Failed to connect DBus object!") return value async def connect_service(bus_name, object_path, interface): """Connect to the service object on DBus, return InterfaceProxy.""" proxy = await proxy_new_for_bus( Gio.BusType.SYSTEM, Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES | Gio.DBusProxyFlags.DO_NOT_CONNECT_SIGNALS, info=None, name=bus_name, object_path=object_path, interface_name=interface, ) return InterfaceProxy(proxy) class MethodsProxy: """Provide methods as attributes for one interface of a DBus object.""" def __init__(self, object_proxy, interface_name): """Initialize from (ObjectProxy, str).""" self._object_proxy = object_proxy self._interface_name = interface_name def __getattr__(self, name): """Get a proxy for the specified method on this interface.""" return partial(self._object_proxy.call, self._interface_name, name) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/depend.py0000644000175100001770000000526214612243552016042 0ustar00runnerdocker""" Make sure that the correct versions of gobject introspection dependencies are installed. """ import os import logging from gi import require_version from .locale import _ require_version('Gio', '2.0') require_version('GLib', '2.0') def check_call(exc_type, func, *args): try: func(*args) return True except exc_type: return False def check_version(package, version): if check_call(ValueError, require_version, package, version): return (package, version) _in_X = bool(os.environ.get('DISPLAY')) _in_Wayland = bool(os.environ.get('WAYLAND_DISPLAY')) if not _in_X and not _in_Wayland and os.environ.get('XDG_RUNTIME_DIR'): _in_Wayland = os.path.exists(os.path.join( os.environ.get('XDG_RUNTIME_DIR'), 'wayland-0')) _has_Gtk = (3 if check_version('Gtk', '3.0') else 2 if check_version('Gtk', '2.0') else 0) _has_Notify = check_version('Notify', '0.7') _has_AppIndicator3 = ( check_version('AyatanaAppIndicator3', '0.1') or check_version('AppIndicator3', '0.1') ) def require_Gtk(min_version=2): """ Make sure Gtk is properly initialized. :raises RuntimeError: if Gtk can not be properly initialized """ if not (_in_X or _in_Wayland): raise RuntimeError('Not in X or Wayland session.') if _has_Gtk < min_version: raise RuntimeError('Module gi.repository.Gtk not available!') if _has_Gtk == 2: logging.getLogger(__name__).warn( _("Missing runtime dependency GTK 3. Falling back to GTK 2 " "for password prompt")) from gi.repository import Gtk # if we attempt to create any GUI elements with no X server running the # program will just crash, so let's make a way to catch this case: if not Gtk.init_check(None)[0]: raise RuntimeError(_("X server not connected!")) return Gtk def require_Notify(): if not _has_Notify: raise RuntimeError('Module gi.repository.Notify not available!') from gi.repository import Notify return Notify def require_AppIndicator3(): if _has_AppIndicator3 == ('AppIndicator3', '0.1'): from gi.repository import AppIndicator3 elif _has_AppIndicator3 == ('AyatanaAppIndicator3', '0.1'): from gi.repository import AyatanaAppIndicator3 as AppIndicator3 else: raise RuntimeError('Module gi.repository.AppIndicator3 not available!') return AppIndicator3 def has_Notify(): return check_call((RuntimeError, ImportError), require_Notify) def has_Gtk(min_version=2): return check_call((RuntimeError, ImportError), require_Gtk, min_version) def has_AppIndicator3(): return check_call((RuntimeError, ImportError), require_AppIndicator3) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1713981306.3669415 udiskie-2.5.3/udiskie/icons/0000755000175100001770000000000014612243572015341 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/icons/__init__.py0000644000175100001770000000146114612243552017452 0ustar00runnerdockerimport contextlib try: import importlib.resources as resources except ImportError: # for Python<3.7 import importlib_resources as resources class IconDist: def __init__(self): self._context = contextlib.ExitStack() self._paths = {} def patch_list(self, icons): return [ path for icon in icons for path in [icon, self.lookup(icon)] if path ] def lookup(self, name): if name in self._paths: return self._paths[name] try: path = self._context.enter_context( resources.path('udiskie.icons', name + '.svg')) path = str(path.absolute()) except FileNotFoundError: path = None self._paths[name] = path return path ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/icons/udiskie-checkbox-checked.svg0000644000175100001770000000136214612243552022667 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/icons/udiskie-checkbox-unchecked.svg0000644000175100001770000000102514612243552023226 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/icons/udiskie-detach.svg0000644000175100001770000001325714612243552020753 0ustar00runnerdocker image/svg+xml ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/icons/udiskie-eject.svg0000644000175100001770000002244514612243552020614 0ustar00runnerdocker image/svg+xml ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/icons/udiskie-lock.svg0000644000175100001770000002625414612243552020454 0ustar00runnerdocker image/svg+xml ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/icons/udiskie-mount.svg0000644000175100001770000007163214612243552020666 0ustar00runnerdocker image/svg+xml ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/icons/udiskie-submenu.svg0000644000175100001770000000114214612243552021167 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/icons/udiskie-unlock.svg0000644000175100001770000002626514612243552021021 0ustar00runnerdocker image/svg+xml ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/icons/udiskie-unmount.svg0000644000175100001770000010147314612243552021226 0ustar00runnerdocker image/svg+xml ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/locale.py0000644000175100001770000000141014612243552016031 0ustar00runnerdocker""" I18n utilities. """ import os import sys from gettext import translation testdirs = [ # manual override: os.environ.get('TEXTDOMAINDIR'), # editable installation: os.path.join(os.path.dirname(__file__), '../build/locale'), # user or virtualenv installation: os.path.join(sys.prefix, 'share/locale'), ] testfile = 'en_US/LC_MESSAGES/udiskie.mo' localedir = next( (d for d in testdirs if d and os.path.exists(os.path.join(d, testfile))), None) _t = translation('udiskie', localedir, languages=None, fallback=True) def _(text, *args, **kwargs): """Translate and then and format the text with ``str.format``.""" msg = _t.gettext(text) if args or kwargs: return msg.format(*args, **kwargs) else: return msg ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/mount.py0000644000175100001770000010170314612243552015742 0ustar00runnerdocker""" Mount utilities. """ from collections import namedtuple from functools import partial from shutil import which import logging import os from .async_ import to_coro, gather, sleep from .common import wraps, setdefault, exc_message, format_exc from .config import IgnoreDevice, match_config from .locale import _ __all__ = ['Mounter'] # TODO: add / remove / XXX_all should make proper use of the asynchronous # execution. def _error_boundary(fn): @wraps(fn) async def wrapper(self, device, *args, **kwargs): try: return await fn(self, device, *args, **kwargs) except Exception as e: self._log.error(_('failed to {0} {1}: {2}', fn.__name__, device, exc_message(e))) self._log.debug(format_exc()) return False return wrapper def _is_parent_of(parent, child): """Check whether the first device is the parent of the second device.""" if child.is_partition: return child.partition_slave == parent if child.is_toplevel: return child.drive == parent and child != parent return False class Mounter: """ Mount utility. Stores environment variables (filter, prompt, browser, udisks) to use across multiple mount operations. :ivar udisks: adapter to the udisks service NOTE: The optional parameters are not guaranteed to keep their order and should always be passed as keyword arguments. """ def __init__(self, udisks, config=None, prompt=None, browser=None, terminal=None, cache=None, cache_hint=False): """ Initialize mounter with the given defaults. :param udisks: udisks service object. May be a Sniffer or a Daemon. :param list config: list of :class:`DeviceFilter` :param callable prompt: retrieve passwords for devices :param callable browser: open devices :param callable terminal: open devices in terminal If prompt is None, device unlocking will not work. If browser is None, browse will not work. """ self.udisks = udisks self._config = (config or []) + [ IgnoreDevice({'symlinks': '/dev/mapper/docker-*', 'ignore': True}), IgnoreDevice({'symlinks': '/dev/disk/by-id/dm-name-docker-*', 'ignore': True}), IgnoreDevice({'loop_file': '/var/lib/snapd/snaps/*', 'ignore': True}), IgnoreDevice({'is_loop': True, 'is_ignored': False, 'loop_file': '/*', 'ignore': False}), IgnoreDevice({'is_block': False, 'ignore': True}), IgnoreDevice({'is_external': False, 'is_toplevel': True, 'ignore': True}), IgnoreDevice({'is_ignored': True, 'ignore': True})] self._prompt = prompt self._browser = browser self._terminal = terminal self._cache = cache self._cache_hint = cache_hint self._log = logging.getLogger(__name__) def _find_device(self, device_or_path): """Find device object from path.""" return self.udisks.find(device_or_path) async def _find_device_losetup(self, device_or_path): try: device = self.udisks.find(device_or_path) return device, False except FileNotFoundError: if not os.path.isfile(device_or_path): raise device = await self.losetup(device_or_path) return device, True @_error_boundary async def browse(self, device): """ Launch file manager on the mount path of the specified device. :param device: device object, block device path or mount path :returns: whether the program was successfully launched. """ device = self._find_device(device) if not device.is_mounted: self._log.error(_("not browsing {0}: not mounted", device)) return False if not self._browser: self._log.error(_("not browsing {0}: no program", device)) return False self._log.debug(_('opening {0} on {0.mount_paths[0]}', device)) self._browser(device.mount_paths[0]) self._log.info(_('opened {0} on {0.mount_paths[0]}', device)) return True @_error_boundary async def terminal(self, device): """ Launch terminal on the mount path of the specified device. :param device: device object, block device path or mount path :returns: whether the program was successfully launched. """ device = self._find_device(device) if not device.is_mounted: self._log.error(_("not opening terminal {0}: not mounted", device)) return False if not self._terminal: self._log.error(_("not opening terminal {0}: no program", device)) return False self._log.debug(_('opening {0} on {0.mount_paths[0]}', device)) self._terminal(device.mount_paths[0]) self._log.info(_('opened {0} on {0.mount_paths[0]}', device)) return True # mount/unmount @_error_boundary async def mount(self, device): """ Mount the device if not already mounted. :param device: device object, block device path or mount path :returns: whether the device is mounted. """ device = self._find_device(device) if not self.is_handleable(device) or not device.is_filesystem: self._log.warn(_('not mounting {0}: unhandled device', device)) return False if device.is_mounted: self._log.info(_('not mounting {0}: already mounted', device)) return True options = match_config(self._config, device, 'options', None) kwargs = dict(options=options) self._log.debug(_('mounting {0} with {1}', device, kwargs)) self._check_device_before_mount(device) mount_path = await device.mount(**kwargs) self._log.info(_('mounted {0} on {1}', device, mount_path)) return True def _check_device_before_mount(self, device): if device.id_type == 'ntfs' and not which('ntfs-3g'): self._log.warn(_( "Mounting NTFS device with default driver.\n" "Please install 'ntfs-3g' if you experience problems or the " "device is readonly.")) @_error_boundary async def unmount(self, device): """ Unmount a Device if mounted. :param device: device object, block device path or mount path :returns: whether the device is unmounted """ device = self._find_device(device) if not self.is_handleable(device) or not device.is_filesystem: self._log.warn(_('not unmounting {0}: unhandled device', device)) return False if not device.is_mounted: self._log.info(_('not unmounting {0}: not mounted', device)) return True self._log.debug(_('unmounting {0}', device)) await device.unmount() self._log.info(_('unmounted {0}', device)) return True # unlock/lock (LUKS) @_error_boundary async def unlock(self, device): """ Unlock the device if not already unlocked. :param device: device object, block device path or mount path :returns: whether the device is unlocked """ device = self._find_device(device) if not self.is_handleable(device) or not device.is_crypto: self._log.warn(_('not unlocking {0}: unhandled device', device)) return False if device.is_unlocked: self._log.info(_('not unlocking {0}: already unlocked', device)) return True if not self._prompt: self._log.error(_('not unlocking {0}: no password prompt', device)) return False unlocked = await self._unlock_from_cache(device) if unlocked: return True unlocked = await self._unlock_from_keyfile(device) if unlocked: return True options = dict(allow_keyfile=self.udisks.keyfile_support, allow_cache=self._cache is not None, cache_hint=self._cache_hint) password = await self._prompt(device, options) # password is either None or udiskie.prompt.PasswordResult: if password is None: self._log.debug(_('not unlocking {0}: cancelled by user', device)) return False cache_hint = password.cache_hint password = password.password if isinstance(password, bytes): self._log.debug(_('unlocking {0} using keyfile', device)) await device.unlock_keyfile(password) else: self._log.debug(_('unlocking {0}', device)) await device.unlock(password) self._update_cache(device, password, cache_hint) self._log.info(_('unlocked {0}', device)) return True async def _unlock_from_cache(self, device): if not self._cache: return False try: password = self._cache[device] except KeyError: self._log.debug(_("no cached key for {0}", device)) return False self._log.debug(_('unlocking {0} using cached password', device)) try: await device.unlock_keyfile(password) except Exception: self._log.debug(_('failed to unlock {0} using cached password', device)) self._log.debug(format_exc()) return False self._log.info(_('unlocked {0} using cached password', device)) return True async def _unlock_from_keyfile(self, device): if not self.udisks.keyfile_support: return False filename = match_config(self._config, device, 'keyfile', None) if filename is None: self._log.debug(_('No matching keyfile rule for {}.', device)) return False try: with open(filename, 'rb') as f: keyfile = f.read() except IOError: self._log.warn(_('keyfile for {0} not found: {1}', device, filename)) return False self._log.debug(_('unlocking {0} using keyfile {1}', device, filename)) try: await device.unlock_keyfile(keyfile) except Exception: self._log.debug(_('failed to unlock {0} using keyfile', device)) self._log.debug(format_exc()) return False self._log.info(_('unlocked {0} using keyfile', device)) return True def _update_cache(self, device, password, cache_hint): if not self._cache: return # TODO: could allow numeric cache_hint (=timeout)… if cache_hint or cache_hint is None: self._cache[device] = password def forget_password(self, device): try: del self._cache[device] except KeyError: pass @_error_boundary async def lock(self, device): """ Lock device if unlocked. :param device: device object, block device path or mount path :returns: whether the device is locked """ device = self._find_device(device) if not self.is_handleable(device) or not device.is_crypto: self._log.warn(_('not locking {0}: unhandled device', device)) return False if not device.is_unlocked: self._log.info(_('not locking {0}: not unlocked', device)) return True self._log.debug(_('locking {0}', device)) await device.lock() self._log.info(_('locked {0}', device)) return True # add/remove (unlock/lock or mount/unmount) @_error_boundary async def add(self, device, recursive=None): """ Mount or unlock the device depending on its type. :param device: device object, block device path or mount path :param bool recursive: recursively mount and unlock child devices :returns: whether all attempted operations succeeded """ device, created = await self._find_device_losetup(device) if created and recursive is False: return device if device.is_filesystem: success = await self.mount(device) elif device.is_crypto: success = await self.unlock(device) if success and recursive: await self.udisks._sync() device = self.udisks[device.object_path] success = await self.add( device.luks_cleartext_holder, recursive=True) elif (recursive and device.is_partition_table and self.is_handleable(device)): tasks = [ self.add(dev, recursive=True) for dev in self.get_all_handleable() if dev.is_partition and dev.partition_slave == device ] results = await gather(*tasks) success = all(results) else: self._log.info(_('not adding {0}: unhandled device', device)) return False return success @_error_boundary async def auto_add(self, device, recursive=None, automount=True): """ Automatically attempt to mount or unlock a device, but be quiet if the device is not supported. :param device: device object, block device path or mount path :param bool recursive: recursively mount and unlock child devices :returns: whether all attempted operations succeeded """ device, created = await self._find_device_losetup(device) if created and recursive is False: return device if device.is_luks_cleartext and self.udisks.version_info >= (2, 7, 0): await sleep(1.5) # temporary workaround for #153, unreliable success = True if not self.is_automount(device, automount): pass elif device.is_filesystem: if not device.is_mounted: success = await self.mount(device) elif device.is_crypto: if self._prompt and not device.is_unlocked: success = await self.unlock(device) if success and recursive: await self.udisks._sync() device = self.udisks[device.object_path] success = await self.auto_add( device.luks_cleartext_holder, recursive=True) elif recursive and device.is_partition_table: tasks = [ self.auto_add(dev, recursive=True) for dev in self.get_all_handleable() if dev.is_partition and dev.partition_slave == device ] results = await gather(*tasks) success = all(results) else: self._log.debug(_('not adding {0}: unhandled device', device)) return success @_error_boundary async def remove(self, device, force=False, detach=False, eject=False, lock=False): """ Unmount or lock the device depending on device type. :param device: device object, block device path or mount path :param bool force: recursively remove all child devices :param bool detach: detach the root drive :param bool eject: remove media from the root drive :param bool lock: lock the associated LUKS cleartext slave :returns: whether all attempted operations succeeded """ device = self._find_device(device) if device.is_filesystem: if device.is_mounted or not device.is_loop or detach is False: success = await self.unmount(device) elif device.is_crypto: if force and device.is_unlocked: await self.auto_remove(device.luks_cleartext_holder, force=True) success = await self.lock(device) elif (force and (device.is_partition_table or device.is_drive) and self.is_handleable(device)): kw = dict(force=True, detach=detach, eject=eject, lock=lock) tasks = [ self.auto_remove(child, **kw) for child in self.get_all_handleable() if _is_parent_of(device, child) ] results = await gather(*tasks) success = all(results) else: self._log.info(_('not removing {0}: unhandled device', device)) success = False # if these operations work, everything is fine, we can return True: if lock and device.is_luks_cleartext: device = device.luks_cleartext_slave if self.is_handleable(device): success = await self.lock(device) if eject: success = await self.eject(device) if (detach or detach is None) and device.is_loop: success = await self.delete(device, remove=False) elif detach: success = await self.detach(device) return success @_error_boundary async def auto_remove(self, device, force=False, detach=False, eject=False, lock=False): """ Unmount or lock the device depending on device type. :param device: device object, block device path or mount path :param bool force: recursively remove all child devices :param bool detach: detach the root drive :param bool eject: remove media from the root drive :param bool lock: lock the associated LUKS cleartext slave :returns: whether all attempted operations succeeded """ device = self._find_device(device) success = True if not self.is_handleable(device): pass elif device.is_filesystem: if device.is_mounted: success = await self.unmount(device) elif device.is_crypto: if force and device.is_unlocked: await self.auto_remove(device.luks_cleartext_holder, force=True) if device.is_unlocked: success = await self.lock(device) elif force and (device.is_partition_table or device.is_drive): kw = dict(force=True, detach=detach, eject=eject, lock=lock) tasks = [ self.auto_remove(child, **kw) for child in self.get_all_handleable() if _is_parent_of(device, child) ] results = await gather(*tasks) success = all(results) else: self._log.debug(_('not removing {0}: unhandled device', device)) # if these operations work, everything is fine, we can return True: if lock and device.is_luks_cleartext: device = device.luks_cleartext_slave success = await self.lock(device) if eject and device.has_media: success = await self.eject(device) if (detach or detach is None) and device.is_loop: success = await self.delete(device, remove=False) elif detach and device.is_detachable: success = await self.detach(device) return success # eject/detach device @_error_boundary async def eject(self, device, force=False): """ Eject a device after unmounting all its mounted filesystems. :param device: device object, block device path or mount path :param bool force: remove child devices before trying to eject :returns: whether the operation succeeded """ device = self._find_device(device) if not self.is_handleable(device): self._log.warn(_('not ejecting {0}: unhandled device')) return False drive = device.drive if not (drive.is_drive and drive.is_ejectable): self._log.warn(_('not ejecting {0}: drive not ejectable', drive)) return False if force: # Can't autoremove 'device.drive', because that will be filtered # due to block=False: await self.auto_remove(device.root, force=True) self._log.debug(_('ejecting {0}', device)) await drive.eject() self._log.info(_('ejected {0}', device)) return True @_error_boundary async def detach(self, device, force=False): """ Detach a device after unmounting all its mounted filesystems. :param device: device object, block device path or mount path :param bool force: remove child devices before trying to detach :returns: whether the operation succeeded """ device = self._find_device(device) if not self.is_handleable(device): self._log.warn(_('not detaching {0}: unhandled device', device)) return False drive = device.root if not drive.is_detachable and not drive.is_loop: self._log.warn(_('not detaching {0}: drive not detachable', drive)) return False if force: await self.auto_remove(drive, force=True) self._log.debug(_('detaching {0}', device)) if drive.is_detachable: await drive.detach() else: await drive.delete() self._log.info(_('detached {0}', device)) return True # mount_all/unmount_all async def add_all(self, recursive=False): """ Add all handleable devices that available at start. :param bool recursive: recursively mount and unlock child devices :returns: whether all attempted operations succeeded """ tasks = [self.auto_add(device, recursive=recursive) for device in self.get_all_handleable_leaves()] results = await gather(*tasks) success = all(results) return success async def remove_all(self, detach=False, eject=False, lock=False): """ Remove all filesystems handleable by udiskie. :param bool detach: detach the root drive :param bool eject: remove media from the root drive :param bool lock: lock the associated LUKS cleartext slave :returns: whether all attempted operations succeeded """ kw = dict(force=True, detach=detach, eject=eject, lock=lock) tasks = [self.auto_remove(device, **kw) for device in self.get_all_handleable_roots()] results = await gather(*tasks) success = all(results) return success # loop devices async def losetup(self, image, read_only=None, offset=None, size=None, no_part_scan=None): """ Setup a loop device. :param str image: path of the image file :param bool read_only: :param int offset: :param int size: :param bool no_part_scan: :returns: the device object for the loop device """ try: device = self.udisks.find(image) except FileNotFoundError: pass else: self._log.info(_('not setting up {0}: already up', device)) return device if not os.path.isfile(image): self._log.error(_('not setting up {0}: not a file', image)) return None self._log.debug(_('setting up loop device {0}', image)) # Create pseudo Device object to enable config matching: loopfile = self.udisks.loopfile_device(image) options = match_config(self._config, loopfile, 'options', []) # TODO: also set 'offset', 'size', 'no_part_scan' from config if read_only is None: if 'ro' in options: read_only = True if 'rw' in options: read_only = False if not read_only: try: fd = os.open(image, os.O_RDWR) except PermissionError: self._log.debug(_( 'Insufficient permission to open {0} in read-write mode. ' 'Retrying in read-only mode.', image)) read_only = True if read_only: fd = os.open(image, os.O_RDONLY) device = await self.udisks.loop_setup(fd, { 'offset': offset, 'size': size, 'read-only': bool(read_only), 'no-part-scan': no_part_scan, }) self._log.info(_('set up {0} as {1}', image, device.device_presentation)) return device @_error_boundary async def delete(self, device, remove=True): """ Detach the loop device. :param device: device object, block device path or mount path :param bool remove: whether to unmount the partition etc. :returns: whether the loop device is deleted """ device = self._find_device(device) if not self.is_handleable(device) or not device.is_loop: self._log.warn(_('not deleting {0}: unhandled device', device)) return False if remove: await self.auto_remove(device, force=True) self._log.debug(_('deleting {0}', device)) await device.delete() self._log.info(_('deleted {0}', device)) return True # iterate devices def is_handleable(self, device): # TODO: handle paths in first argument """ Check whether this device should be handled by udiskie. :param device: device object, block device path or mount path :returns: handleability Currently this just means that the device is removable and holds a filesystem or the device is a LUKS encrypted volume. """ return not self._ignore_device(device) def is_automount(self, device, default=True): if not self.is_handleable(device): return False return match_config(self._config, device, 'automount', default) def _ignore_device(self, device): return match_config(self._config, device, 'ignore', False) def is_addable(self, device, automount=True): """Check if device can be added with ``auto_add``.""" if not self.is_automount(device, automount): return False if device.is_filesystem: return not device.is_mounted if device.is_crypto: return self._prompt and not device.is_unlocked if device.is_partition_table: return any(self.is_addable(dev) for dev in self.get_all_handleable() if dev.partition_slave == device) return False def is_removable(self, device): """Check if device can be removed with ``auto_remove``.""" if not self.is_handleable(device): return False if device.is_filesystem: return device.is_mounted if device.is_crypto: return device.is_unlocked if device.is_partition_table or device.is_drive: return any(self.is_removable(dev) for dev in self.get_all_handleable() if _is_parent_of(device, dev)) return False def get_all_handleable(self): """Get list of all known handleable devices.""" nodes = self.get_device_tree() return [node.device for node in sorted(nodes.values(), key=DevNode._sort_key) if not node.ignored and node.device] def get_all_handleable_roots(self): """ Get list of all handleable devices, return only those that represent root nodes within the filtered device tree. """ nodes = self.get_device_tree() return [node.device for node in sorted(nodes.values(), key=DevNode._sort_key) if not node.ignored and node.device and (node.root == '/' or nodes[node.root].ignored)] def get_all_handleable_leaves(self): """ Get list of all handleable devices, return only those that represent leaf nodes within the filtered device tree. """ nodes = self.get_device_tree() return [node.device for node in sorted(nodes.values(), key=DevNode._sort_key) if not node.ignored and node.device and all(child.ignored for child in node.children)] def get_device_tree(self): """Get a tree of all devices.""" root = DevNode(None, None, [], None) device_nodes = { dev.object_path: DevNode(dev, dev.parent_object_path, [], self._ignore_device(dev)) for dev in self.udisks } for node in device_nodes.values(): device_nodes.get(node.root, root).children.append(node) device_nodes['/'] = root for node in device_nodes.values(): node.children.sort(key=DevNode._sort_key) # use parent as fallback, update top->down: def propagate_ignored(node): for child in node.children: if child.ignored is None: child.ignored = node.ignored propagate_ignored(child) propagate_ignored(root) return device_nodes class DevNode: def __init__(self, device, root, children, ignored): self.device = device self.root = root self.children = children self.ignored = ignored def _sort_key(self): return self.device.device_presentation if self.device else '' # data structs containing the menu hierarchy: Device = namedtuple('Device', ['root', 'branches', 'device', 'label', 'methods']) Action = namedtuple('Action', ['method', 'device', 'label', 'action']) class DeviceActions: _labels = { 'browse': _('Browse {0}'), 'terminal': _('Hack on {0}'), 'mount': _('Mount {0}'), 'unmount': _('Unmount {0}'), 'unlock': _('Unlock {0}'), 'lock': _('Lock {0}'), 'eject': _('Eject {1}'), 'detach': _('Unpower {1}'), 'forget_password': _('Clear password for {0}'), 'delete': _('Detach {0}'), } def __init__(self, mounter, actions={}): self._mounter = mounter self._actions = _actions = actions.copy() setdefault(_actions, { 'browse': mounter.browse, 'terminal': mounter.terminal, 'mount': mounter.mount, 'unmount': mounter.unmount, 'unlock': mounter.unlock, 'lock': partial(mounter.remove, force=True), 'eject': partial(mounter.eject, force=True), 'detach': partial(mounter.detach, force=True), 'forget_password': to_coro(mounter.forget_password), 'delete': mounter.delete, }) def detect(self, root_device='/'): """ Detect all currently known devices. :param str root_device: object path of root device to return :returns: root node of device hierarchy """ root = Device(None, [], None, "", []) device_nodes = dict(map(self._device_node, self._mounter.get_all_handleable())) # insert child devices as branches into their roots: for node in device_nodes.values(): device_nodes.get(node.root, root).branches.append(node) device_nodes['/'] = root for node in device_nodes.values(): node.branches.sort(key=lambda node: node.label) return device_nodes[root_device] def _get_device_methods(self, device): """Return an iterable over all available methods the device has.""" if device.is_filesystem: if device.is_mounted: if self._mounter._browser: yield 'browse' if self._mounter._terminal: yield 'terminal' yield 'unmount' else: yield 'mount' elif device.is_crypto: if device.is_unlocked: yield 'lock' else: yield 'unlock' cache = self._mounter._cache if cache and device in cache: yield 'forget_password' if device.is_ejectable and device.has_media: yield 'eject' if device.is_detachable: yield 'detach' if device.is_loop: yield 'delete' def _device_node(self, device): """Create an empty menu node for the specified device.""" label = device.ui_label dev_label = device.ui_device_label # determine available methods methods = [Action(method, device, self._labels[method].format(label, dev_label), partial(self._actions[method], device)) for method in self._get_device_methods(device)] # find the root device: root = device.parent_object_path # in this first step leave branches empty return device.object_path, Device(root, [], device, dev_label, methods) def prune_empty_node(node, seen): """ Recursively remove empty branches and return whether this makes the node itself empty. The ``seen`` parameter is used to avoid infinite recursion due to cycles (you never know). """ if node.methods: return False if id(node) in seen: return True seen = seen | {id(node)} for branch in list(node.branches): if prune_empty_node(branch, seen): node.branches.remove(branch) else: return False return True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/notify.py0000644000175100001770000002307614612243552016116 0ustar00runnerdocker""" Notification utility. """ import logging from gi.repository import GLib from .async_ import run_bg from .common import exc_message, DaemonBase, format_exc from .mount import DeviceActions from .locale import _ __all__ = ['Notify'] class Notify(DaemonBase): """ Notification tool. Can be connected to udisks daemon in order to automatically issue notifications when system status has changed. NOTE: the action buttons in the notifications don't work with all notification services. """ EVENTS = ['device_mounted', 'device_unmounted', 'device_locked', 'device_unlocked', 'device_added', 'device_removed', 'job_failed'] def __init__(self, notify, mounter, timeout=None, aconfig=None): """ Initialize notifier and connect to service. :param notify: notification service module (gi.repository.Notify) :param mounter: Mounter object :param dict timeout: dictionary with timeouts for notifications """ self._notify = notify self._mounter = mounter self._actions = DeviceActions(mounter) self._timeout = timeout or {} self._aconfig = aconfig or {} self._default = self._timeout.get('timeout', -1) self._log = logging.getLogger(__name__) self._notifications = [] self.events = { event: getattr(self, event) for event in self.EVENTS if self._enabled(event) } # event handlers: def device_mounted(self, device): """Show mount notification for specified device object.""" if not self._mounter.is_handleable(device): return browse_action = (device, 'browse', _('Browse directory'), self._mounter.browse, device) terminal_action = (device, 'terminal', _('Open terminal'), self._mounter.terminal, device) self._show_notification( 'device_mounted', _('Device mounted'), _('{0.ui_label} mounted on {0.mount_paths[0]}', device), device.icon_name, self._mounter._browser and browse_action, self._mounter._terminal and terminal_action) def device_unmounted(self, device): """Show unmount notification for specified device object.""" if not self._mounter.is_handleable(device): return self._show_notification( 'device_unmounted', _('Device unmounted'), _('{0.ui_label} unmounted', device), device.icon_name) def device_locked(self, device): """Show lock notification for specified device object.""" if not self._mounter.is_handleable(device): return self._show_notification( 'device_locked', _('Device locked'), _('{0.device_presentation} locked', device), device.icon_name) def device_unlocked(self, device): """Show unlock notification for specified device object.""" if not self._mounter.is_handleable(device): return self._show_notification( 'device_unlocked', _('Device unlocked'), _('{0.device_presentation} unlocked', device), device.icon_name) def device_added(self, device): """Show discovery notification for specified device object.""" if not self._mounter.is_handleable(device): return if self._has_actions('device_added'): # wait for partitions etc to be reported to udiskie, otherwise we # can't discover the actions GLib.timeout_add(500, self._device_added, device) else: self._device_added(device) def _device_added(self, device): device_file = device.device_presentation if (device.is_drive or device.is_toplevel) and device_file: # On UDisks1: cannot invoke self._actions.detect() for newly added # LUKS devices. It should be okay if we had waited for the actions # to be added, though. if self._has_actions('device_added'): node_tree = self._actions.detect(device.object_path) flat_actions = self._flatten_node(node_tree) actions = [ (action.device, action.method, action.label.format(action.device.ui_label), action.action) for action in flat_actions ] else: actions = () self._show_notification( 'device_added', _('Device added'), _('device appeared on {0.device_presentation}', device), device.icon_name, *actions) def _flatten_node(self, node): actions = [action for branch in node.branches for action in self._flatten_node(branch)] actions += node.methods return actions def device_removed(self, device): """Show removal notification for specified device object.""" if not self._mounter.is_handleable(device): return device_file = device.device_presentation if (device.is_drive or device.is_toplevel) and device_file: self._show_notification( 'device_removed', _('Device removed'), _('device disappeared on {0.device_presentation}', device), device.icon_name) def job_failed(self, device, action, message): """Show 'Job failed' notification with 'Retry' button.""" if not self._mounter.is_handleable(device): return device_file = device.device_presentation or device.object_path if message: text = _('failed to {0} {1}:\n{2}', action, device_file, message) else: text = _('failed to {0} device {1}.', action, device_file) try: retry = getattr(self._mounter, action) except AttributeError: retry_action = None else: retry_action = (device, 'retry', _('Retry'), retry, device) self._show_notification( 'job_failed', _('Job failed'), text, device.icon_name, retry_action) def _show_notification(self, event, summary, message, icon, *actions): """ Show a notification. :param str event: event name :param str summary: notification title :param str message: notification body :param str icon: icon name :param actions: each item is a tuple with parameters for _add_action """ notification = self._notify(summary, message, icon) timeout = self._get_timeout(event) if timeout != -1: notification.set_timeout(int(timeout * 1000)) for action in actions: if action and self._action_enabled(event, action[1]): self._add_action(notification, *action) try: notification.show() except GLib.GError as exc: # Catch and log the exception. Starting udiskie with notifications # enabled while not having a notification service installed is a # mistake too easy to be made, but it should not render the rest of # udiskie's logic useless by raising an exception before the # automount handler gets invoked. self._log.error(_("Failed to show notification: {0}", exc_message(exc))) self._log.debug(format_exc()) def _add_action(self, notification, device, action, label, callback, *args): """ Show an action button button in mount notifications. Note, this only works with some libnotify services. """ action = action + ':' + device.device_file on_action_click = run_bg(lambda *_: callback(*args)) try: # this is the correct signature for Notify-0.7, the last argument # being 'user_data': notification.add_action(action, label, on_action_click, None) except TypeError: # this is the signature for some older version, I don't know what # the last argument is for. notification.add_action(action, label, on_action_click, None, None) # gi.Notify does not store hard references to the notification # objects. When a signal is received and the notification does not # exist anymore, no handler will be called. Therefore, we need to # prevent these notifications from being destroyed by storing # references: notification.connect('closed', self._notifications.remove) self._notifications.append(notification) def _enabled(self, event): """Check if the notification for an event is enabled.""" return self._get_timeout(event) not in (None, False) def _get_timeout(self, event): """Get the timeout for an event from the config or None.""" return self._timeout.get(event, self._default) def _action_enabled(self, event, action): """Check if an action for a notification is enabled.""" event_actions = self._aconfig.get(event) if event_actions is None: return True if event_actions is False: return False return action in event_actions def _has_actions(self, event): """Check if a notification type has any enabled actions.""" event_actions = self._aconfig.get(event) return event_actions is None or bool(event_actions) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/password_dialog.ui0000644000175100001770000000544614612243552017755 0ustar00runnerdocker 5 center dialog password-dialog 6 6 True 0 True False True True Show password False True Remember password False True gtk-cancel True True gtk-ok True True True True Open keyfile… False cancel_button ok_button ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/prompt.py0000644000175100001770000002176514612243552016132 0ustar00runnerdocker""" User prompt utility. """ from udiskie.depend import has_Gtk, require_Gtk from udiskie.common import is_utf8 from shutil import which import getpass import logging import shlex import string import subprocess import sys try: from importlib.resources import read_text except ImportError: # for Python<3.7 from importlib_resources import read_text from .async_ import exec_subprocess, run_bg, Future from .locale import _ from .config import DeviceFilter Gtk = None __all__ = ['password', 'browser'] dialog_definition = read_text(__package__, 'password_dialog.ui') class Dialog(Future): def __init__(self, window): self._enter_count = 0 self.window = window self.window.connect("response", self._result_handler) def _result_handler(self, window, response): self.set_result(response) def __enter__(self): self._enter_count += 1 self._awaken() return self def __exit__(self, *exc_info): self._enter_count -= 1 if self._enter_count == 0: self._cleanup() def _awaken(self): self.window.present() def _cleanup(self): self.window.hide() self.window.destroy() class PasswordResult: def __init__(self, password=None, cache_hint=None): self.password = password self.cache_hint = cache_hint class PasswordDialog(Dialog): INSTANCES = {} content = None @classmethod def create(cls, key, title, message, options): if key in cls.INSTANCES: return cls.INSTANCES[key] return cls(key, title, message, options) def _awaken(self): self.INSTANCES[self.key] = self super()._awaken() def _cleanup(self): del self.INSTANCES[self.key] super()._cleanup() def __init__(self, key, title, message, options): self.key = key global Gtk Gtk = require_Gtk() builder = Gtk.Builder.new() builder.add_from_string(dialog_definition) window = builder.get_object('entry_dialog') self.entry = builder.get_object('entry') show_password = builder.get_object('show_password') show_password.set_label(_('Show password')) show_password.connect('clicked', self.on_show_password) allow_keyfile = options.get('allow_keyfile') keyfile_button = builder.get_object('keyfile_button') keyfile_button.set_label(_('Open keyfile…')) keyfile_button.set_visible(allow_keyfile) keyfile_button.connect('clicked', run_bg(self.on_open_keyfile)) allow_cache = options.get('allow_cache') cache_hint = options.get('cache_hint') self.use_cache = builder.get_object('remember') self.use_cache.set_label(_('Cache password')) self.use_cache.set_visible(allow_cache) self.use_cache.set_active(cache_hint) label = builder.get_object('message') label.set_label(message) window.set_title(title) window.set_keep_above(True) super().__init__(window) def on_show_password(self, button): self.entry.set_visibility(button.get_active()) async def on_open_keyfile(self, button): gtk_dialog = Gtk.FileChooserDialog( _("Open a keyfile to unlock the LUKS device"), self.window, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) with Dialog(gtk_dialog) as dialog: response = await dialog if response == Gtk.ResponseType.OK: with open(dialog.window.get_filename(), 'rb') as f: self.content = f.read() self.window.response(response) def get_text(self): if self.content is not None: return self.content return self.entry.get_text() async def password_dialog(key, title, message, options): """ Show a Gtk password dialog. :returns: the password or ``None`` if the user aborted the operation :raises RuntimeError: if Gtk can not be properly initialized """ with PasswordDialog.create(key, title, message, options) as dialog: response = await dialog if response == Gtk.ResponseType.OK: return PasswordResult(dialog.get_text(), dialog.use_cache.get_active()) return None def get_password_gui(device, options): """Get the password to unlock a device from GUI.""" text = _('Enter password for {0.device_presentation}: ', device) try: return password_dialog(device.id_uuid, 'udiskie', text, options) except RuntimeError: return None async def get_password_tty(device, options): """Get the password to unlock a device from terminal.""" # TODO: make this a TRUE async text = _('Enter password for {0.device_presentation}: ', device) try: return PasswordResult(getpass.getpass(text)) except EOFError: print("") return None class DeviceCommand: """ Launcher that starts user-defined password prompts. The command can be specified in terms of a command line template. """ def __init__(self, argv, capture=False, **extra): """Create the launcher object from the command line template.""" if isinstance(argv, str): self.argv = shlex.split(argv) else: self.argv = argv self.capture = capture self.extra = extra.copy() # obtain a list of used fields names formatter = string.Formatter() self.used_attrs = set() for arg in self.argv: for text, kwd, spec, conv in formatter.parse(arg): if kwd is None: continue if kwd in DeviceFilter.VALID_PARAMETERS: self.used_attrs.add(kwd) if kwd not in DeviceFilter.VALID_PARAMETERS and \ kwd not in self.extra: self.extra[kwd] = None logging.getLogger(__name__).error(_( 'Unknown device attribute {!r} in format string: {!r}', kwd, arg)) async def __call__(self, device): """ Invoke the subprocess to ask the user to enter a password for unlocking the specified device. """ attrs = {attr: getattr(device, attr) for attr in self.used_attrs} attrs.update(self.extra) argv = [arg.format(**attrs) for arg in self.argv] try: stdout = await exec_subprocess(argv, self.capture) except subprocess.CalledProcessError: return None # Remove trailing newline for text answers, but not for binary # keyfiles. This logic is a guess that may cause bugs for some users:( if stdout and stdout.endswith(b'\n') and is_utf8(stdout): stdout = stdout[:-1] return stdout async def password(self, device, options): text = await self(device) return PasswordResult(text) def password(password_command): """Create a password prompt function.""" gui = lambda: has_Gtk() and get_password_gui tty = lambda: sys.stdin.isatty() and get_password_tty if password_command == 'builtin:gui': return gui() or tty() elif password_command == 'builtin:tty': return tty() or gui() elif password_command: return DeviceCommand(password_command, capture=True).password else: return None def browser(browser_name='xdg-open'): """Create a browse-directory function.""" if not browser_name: return None argv = shlex.split(browser_name) executable = which(argv[0]) if executable is None: # Why not raise an exception? -I think it is more convenient (for # end users) to have a reasonable default, without enforcing it. logging.getLogger(__name__).warn( _("Can't find file browser: {0!r}. " "You may want to change the value for the '-f' option.", browser_name)) return None def browse(path): return subprocess.Popen(argv + [path]) return browse def connect_event_hook(command_format, mounter): """ Command notification tool. This works similar to Notify, but will issue command instead of showing the notifications on the desktop. This can then be used to react to events from shell scripts. The command can contain modern pythonic format placeholders like: {device_file}. The following placeholders are supported: event, device_file, device_id, device_size, drive, drive_label, id_label, id_type, id_usage, id_uuid, mount_path, root :param str command_format: command to run when an event occurs. :param mounter: Mounter object """ udisks = mounter.udisks for event in ['device_mounted', 'device_unmounted', 'device_locked', 'device_unlocked', 'device_added', 'device_removed', 'job_failed']: udisks.connect(event, run_bg(DeviceCommand( command_format, event=event, capture=False))) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/tray.py0000644000175100001770000004017414612243552015563 0ustar00runnerdocker""" Tray icon for udiskie. """ from gi.repository import Gio from gi.repository import Gtk from .async_ import run_bg, Future from .common import setdefault, DaemonBase, cachedmethod from .locale import _ from .mount import Action, prune_empty_node from .prompt import Dialog from .icons import IconDist import os __all__ = ['TrayMenu', 'TrayIcon'] class MenuFolder: def __init__(self, label, items): self.label = label self.items = items def __bool__(self): return bool(self.items) __nonzero__ = __bool__ class MenuSection(MenuFolder): pass class SubMenu(MenuFolder): pass class Icons: """Encapsulates the responsibility to load icons.""" _icon_names = { 'media': [ 'drive-removable-media-usb-panel', 'drive-removable-media-usb-pendrive', 'drive-removable-media-usb', 'drive-removable-media', 'media-optical', 'media-flash', ], 'browse': ['document-open', 'folder-open'], 'terminal': ['terminal', 'utilities-terminal'], 'mount': ['udiskie-mount'], 'unmount': ['udiskie-unmount'], 'unlock': ['udiskie-unlock'], 'lock': ['udiskie-lock'], 'eject': ['udiskie-eject', 'media-eject'], 'detach': ['udiskie-detach'], 'quit': ['application-exit'], 'forget_password': ['edit-delete'], 'delete': ['udiskie-eject'], 'losetup': ['udiskie-mount'], # checkbox workaround: 'checked': ['checkbox-checked', 'udiskie-checkbox-checked'], 'unchecked': ['checkbox', 'udiskie-checkbox-unchecked'], 'submenu': ['udiskie-submenu', 'pan-end-symbolic'], } def __init__(self, icon_names={}): """Merge ``icon_names`` into default icon names.""" self._icon_dist = IconDist() _icon_names = icon_names.copy() setdefault(_icon_names, self.__class__._icon_names) self._icon_names = _icon_names for k, v in _icon_names.items(): if isinstance(v, str): self._icon_names[k] = v = [v] self._icon_names[k] = self._icon_dist.patch_list(v) @cachedmethod def get_icon_name(self, icon_id: str) -> str: """Lookup the system icon name from udisie-internal id.""" icon_theme = Gtk.IconTheme.get_default() for name in self._icon_names[icon_id]: if icon_theme.has_icon(name): return name elif os.path.exists(name): return name return 'not-available' def get_icon(self, icon_id: str, size: "Gtk.IconSize") -> "Gtk.Image": """Load Gtk.Image from udiskie-internal id.""" return Gtk.Image.new_from_gicon(self.get_gicon(icon_id), size) def get_gicon(self, icon_id: str) -> "Gio.Icon": """Lookup Gio.Icon from udiskie-internal id.""" name = self.get_icon_name(icon_id) if os.path.exists(name): # TODO (?): we could also add the icon to the theme using # Gtk.IconTheme.append_search_path or .add_resource_path: file = Gio.File.new_for_path(name) return Gio.FileIcon.new(file) else: return Gio.ThemedIcon.new(name) class TrayMenu: """ Builder for udiskie menus. Objects of this class generate action menus when being called. """ def __init__(self, daemon, icons, actions, flat=True, quickmenu_actions=None, checkbox_workaround=False, update_workaround=False): """ Initialize a new menu maker. :param object mounter: mount operation provider :param Icons icons: icon provider :param DeviceActions actions: device actions discovery :returns: a new menu maker :rtype: cls Required keys for the ``_labels``, ``_menu_icons`` and ``actions`` dictionaries are: - browse Open mount location - terminal Open mount location in terminal - mount Mount a device - unmount Unmount a device - unlock Unlock a LUKS device - lock Lock a LUKS device - eject Eject a drive - detach Detach (power down) a drive - quit Exit the application NOTE: If using a main loop other than ``Gtk.main`` the 'quit' action must be customized. """ self._icons = icons self._daemon = daemon self._mounter = daemon.mounter self._actions = actions self._quit_action = daemon.mainloop.quit self.flat = flat # actions shown in the quick-menu ("flat", left-click): self._quickmenu_actions = quickmenu_actions or [ 'mount', 'browse', 'terminal', 'unlock', 'detach', 'delete', # suppressed: # 'unmount', # 'lock', # 'eject', # 'forget_password', ] self._checkbox_workaround = checkbox_workaround self._update_workaround = update_workaround def __call__(self, menu, extended=True): """Populate the Gtk.Menu with udiskie mount operations.""" # create actions items flat = self.flat and not extended if self._update_workaround: # When showing menus via AppIndicator3 on sway, the menu geometry # seems to be calculated before the 'about-to-show' event, and # therefore cannot take into account newly inserted menu items. # For this reason, we have to keep the top-level menu fixed-size # and insert dynamic entries into a submenu. devmenu = Gtk.Menu() menu.append(self._menuitem( label=_('Managed devices'), icon=None, onclick=devmenu, )) else: devmenu = menu self._create_menu_items( devmenu, self._prepare_menu(self.detect(), flat)) if extended: self._insert_options(menu) return menu def _insert_options(self, menu): """Add configuration options to menu.""" menu.append(Gtk.SeparatorMenuItem()) menu.append(self._menuitem( _('Mount disc image'), self._icons.get_icon('losetup', Gtk.IconSize.MENU), run_bg(lambda _: self._losetup()) )) menu.append(Gtk.SeparatorMenuItem()) menu.append(self._menuitem( _("Enable automounting"), icon=None, onclick=lambda _: self._daemon.automounter.toggle_on(), checked=self._daemon.automounter.is_on(), )) menu.append(self._menuitem( _("Enable notifications"), icon=None, onclick=lambda _: self._daemon.notify.toggle(), checked=self._daemon.notify.active, )) # append menu item for closing the application if self._quit_action: menu.append(Gtk.SeparatorMenuItem()) menu.append(self._menuitem( _('Quit'), self._icons.get_icon('quit', Gtk.IconSize.MENU), lambda _: self._quit_action() )) async def _losetup(self): gtk_dialog = Gtk.FileChooserDialog( _('Open disc image'), None, Gtk.FileChooserAction.OPEN, (_('Open'), Gtk.ResponseType.OK, _('Cancel'), Gtk.ResponseType.CANCEL)) with Dialog(gtk_dialog) as dialog: response = await dialog if response == Gtk.ResponseType.OK: await self._mounter.losetup(dialog.window.get_filename()) def detect(self): """Detect all currently known devices. Returns the root device.""" root = self._actions.detect() prune_empty_node(root, set()) return root def _create_menu(self, items): """ Create a menu from the given node. :param list items: list of menu items :returns: a new Gtk.Menu object holding all items of the node """ menu = Gtk.Menu() self._create_menu_items(menu, items) return menu def _create_menu_items(self, menu, items): def make_action_callback(node): return run_bg(lambda _: node.action()) for node in items: if isinstance(node, Action): menu.append(self._menuitem( node.label, self._icons.get_icon(node.method, Gtk.IconSize.MENU), make_action_callback(node))) elif isinstance(node, SubMenu): menu.append(self._menuitem( node.label, icon=None, onclick=self._create_menu(node.items))) elif isinstance(node, MenuSection): self._create_menu_section(menu, node) else: raise ValueError(_("Invalid node!")) if len(menu) == 0: mi = self._menuitem(_("No external devices"), None, None) mi.set_sensitive(False) menu.append(mi) def _create_menu_section(self, menu, section): if len(menu) > 0: menu.append(Gtk.SeparatorMenuItem()) if section.label: mi = self._menuitem(section.label, None, None) mi.set_sensitive(False) menu.append(mi) self._create_menu_items(menu, section.items) def _menuitem(self, label, icon, onclick, checked=None): """ Create a generic menu item. :param str label: text :param Gtk.Image icon: icon (may be ``None``) :param onclick: onclick handler, either a callable or Gtk.Menu :returns: the menu item object :rtype: Gtk.MenuItem """ if self._checkbox_workaround: if checked is not None: icon_name = 'checked' if checked else 'unchecked' icon = self._icons.get_icon(icon_name, Gtk.IconSize.MENU) checked = None elif isinstance(onclick, Gtk.Menu): icon = self._icons.get_icon('submenu', Gtk.IconSize.MENU) if checked is not None: item = Gtk.CheckMenuItem() item.set_active(checked) elif icon is None: item = Gtk.MenuItem() else: item = Gtk.ImageMenuItem() item.set_image(icon) # I don't really care for the "show icons only for nouns, not # for verbs" policy: item.set_always_show_image(True) if label is not None: item.set_label(label) if isinstance(onclick, Gtk.Menu): item.set_submenu(onclick) elif onclick is not None: item.connect('activate', onclick) return item def _prepare_menu(self, node, flat=None): """ Prepare the menu hierarchy from the given device tree. :param Device node: root node of device hierarchy :returns: menu hierarchy as list """ if flat is None: flat = self.flat ItemGroup = MenuSection if flat else SubMenu return [ ItemGroup(branch.label, self._collapse_device(branch, flat)) for branch in node.branches if branch.methods or branch.branches ] def _collapse_device(self, node, flat): """Collapse device hierarchy into a flat folder.""" items = [item for branch in node.branches for item in self._collapse_device(branch, flat) if item] show_all = not flat or self._quickmenu_actions == 'all' methods = node.methods if show_all else [ method for method in node.methods if method.method in self._quickmenu_actions ] if flat: items.extend(methods) else: items.append(MenuSection(None, methods)) return items class TrayIcon: """Default TrayIcon class.""" def __init__(self, menumaker, icons, statusicon=None): """ Create an object managing a tray icon. The actual Gtk.StatusIcon is only created as soon as you call show() for the first time. The reason to delay its creation is that the GTK icon will be initially visible, which results in a perceptable flickering. :param TrayMenu menumaker: menu factory :param Gtk.StatusIcon statusicon: status icon """ self._icons = icons self._icon = statusicon self._menu = menumaker self._conn_left = None self._conn_right = None self.task = Future() menumaker._quit_action = self.destroy def destroy(self): self.show(False) self.task.set_result(True) def _create_statusicon(self): """Return a new Gtk.StatusIcon.""" statusicon = Gtk.StatusIcon() statusicon.set_from_gicon(self._icons.get_gicon('media')) statusicon.set_tooltip_text(_("udiskie")) return statusicon @property def visible(self): """Return visibility state of icon.""" return bool(self._conn_left) def show(self, show=True): """Show or hide the tray icon.""" if show and not self.visible: self._show() if not show and self.visible: self._hide() def _show(self): """Show the tray icon.""" if not self._icon: self._icon = self._create_statusicon() widget = self._icon widget.set_visible(True) self._conn_left = widget.connect("activate", self._activate) self._conn_right = widget.connect("popup-menu", self._popup_menu) def _hide(self): """Hide the tray icon.""" self._icon.set_visible(False) self._icon.disconnect(self._conn_left) self._icon.disconnect(self._conn_right) self._conn_left = None self._conn_right = None def create_context_menu(self, extended): """Create the context menu.""" menu = Gtk.Menu() self._menu(menu, extended) return menu def _activate(self, icon): """Handle a left click event (show the menu).""" self._popup_menu(icon, button=0, time=Gtk.get_current_event_time(), extended=False) def _popup_menu(self, icon, button, time, extended=True): """Handle a right click event (show the menu).""" m = self.create_context_menu(extended) m.show_all() m.popup(parent_menu_shell=None, parent_menu_item=None, func=icon.position_menu, data=icon, button=button, activate_time=time) # need to store reference or menu will be destroyed before showing: self._m = m class UdiskieStatusIcon(DaemonBase): """ Manage a status icon. When `smart` is on, the icon will automatically hide if there is no action available and the menu will have no 'Quit' item. """ def __init__(self, icon, menumaker, smart=False): self._icon = icon self._menumaker = menumaker self._mounter = menumaker._mounter self._quit_action = menumaker._quit_action self.smart = smart self.active = False self.events = { 'device_changed': self.update, 'device_added': self.update, 'device_removed': self.update, } def activate(self): super().activate() self.update() def deactivate(self): super().deactivate() self._icon.show(False) @property def smart(self): return getattr(self, '_smart', None) @smart.setter def smart(self, smart): if smart == self.smart: return if smart: self._menumaker._quit_action = None else: self._menumaker._quit_action = self._quit_action self._smart = smart self.update() def has_menu(self): """Check if a menu action is available.""" return any(self._menumaker._prepare_menu(self._menumaker.detect())) def update(self, *args): """Show/hide icon depending on whether there are devices.""" if self.smart: self._icon.show(self.has_menu()) else: self._icon.show(True) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981290.0 udiskie-2.5.3/udiskie/udisks2.py0000644000175100001770000007543214612243552016175 0ustar00runnerdocker""" UDisks2 wrapper utilities. These act as a convenience abstraction layer on the UDisks2 DBus service. Requires UDisks2 2.1.1 as described here: http://udisks.freedesktop.org/docs/latest This wraps the DBus API of Udisks2. """ from copy import copy, deepcopy import logging from gi.repository import GLib import udiskie.dbus as dbus from .common import Emitter, AttrDictView, decode_ay, samefile, sameuuid from .locale import _ __all__ = ['Daemon'] def object_kind(object_path): """ Parse the kind of object from an UDisks2 object path. Example: /org/freedesktop/UDisks2/block_devices/sdb1 => device /org/freedesktop/UDisks2/drives/WDC_WD... => drive /org/freedesktop/UDisks2/jobs/5 => job """ try: return { 'block_devices': 'device', 'drives': 'drive', 'jobs': 'job', }.get(object_path.split('/')[4]) except IndexError: return None def filter_opt(opt): """Remove ``None`` values from a dictionary.""" return {k: GLib.Variant(*v) for k, v in opt.items() if v[1] is not None} Interface = { 'Manager': 'org.freedesktop.UDisks2.Manager', 'Drive': 'org.freedesktop.UDisks2.Drive', 'DriveAta': 'org.freedesktop.UDisks2.Drive.Ata', 'MDRaid': 'org.freedesktop.UDisks2.MDRaid', 'Block': 'org.freedesktop.UDisks2.Block', 'Partition': 'org.freedesktop.UDisks2.Partition', 'PartitionTable': 'org.freedesktop.UDisks2.PartitionTable', 'Filesystem': 'org.freedesktop.UDisks2.Filesystem', 'Swapspace': 'org.freedesktop.UDisks2.Swapspace', 'Encrypted': 'org.freedesktop.UDisks2.Encrypted', 'Loop': 'org.freedesktop.UDisks2.Loop', 'Job': 'org.freedesktop.UDisks2.Job', 'ObjectManager': 'org.freedesktop.DBus.ObjectManager', 'Properties': 'org.freedesktop.DBus.Properties', # (experimental) pseudo-interface to represent loop files: # TODO: Should this be improved and receive additional properties such as # parent device (using mount point)? 'LoopFile': 'LoopFile', } # ---------------------------------------- # Internal helper classes # ---------------------------------------- class MethodHub: """Provide MethodsProxies for queried interfaces of a DBus object.""" def __init__(self, object_proxy): """Initialize from (ObjectProxy).""" self._object_proxy = object_proxy def __getattr__(self, key): """Return a MethodsProxy for the requested interface.""" return dbus.MethodsProxy(self._object_proxy, Interface[key]) class PropertyHub: """Provide attribute accessors for queried interfaces of a DBus object.""" def __init__(self, interfaces_and_properties): """Initialize from (dict).""" self._interfaces_and_properties = interfaces_and_properties def __getattr__(self, key): """Return an AttrDictView for properties on the requested interface.""" interface = Interface[key] try: return AttrDictView(self._interfaces_and_properties[interface]) except KeyError: return PropertiesNotAvailable() class PropertiesNotAvailable: """Null class for properties of an unavailable interface.""" def __bool__(self): return False def __getattr__(self, key): """Return None when asked for any attribute.""" return None # ---------------------------------------- # Device wrapper # ---------------------------------------- class Device: """ Proxy class for UDisks2 devices. Properties are read from the cached values retrieved by the Daemon class. Methods are executed asynchronously, and hence return Asyncs instead of returning the result directly. """ def __init__(self, daemon, object_path, property_hub, method_hub): """Initialize from (Daemon, str, PropertyHub, MethodHub).""" self._daemon = daemon self.object_path = object_path self._P = property_hub self._M = method_hub def __str__(self): """Show as object_path.""" return self.object_path def __eq__(self, other): """Comparison by object_path.""" return self.object_path == str(other) def __ne__(self, other): """Comparison by object_path.""" return not (self == other) # availability of interfaces @property def is_drive(self): """Check if the device is a drive.""" return bool(self._P.Drive) @property def is_block(self): """Check if the device is a block device.""" return bool(self._P.Block) @property def is_partition_table(self): """Check if the device is a partition table.""" return bool(self._P.PartitionTable) @property def is_partition(self): """Check if the device has a partition slave.""" # Sometimes udisks2 empties the Partition interface before removing # the device. In this case, we want to report .is_partition=False, so # properties like .partition_slave will not be used. return bool(self._P.Partition and self.partition_slave) @property def is_filesystem(self): """Check if the device is a filesystem.""" return bool(self._P.Filesystem) @property def is_luks(self): """Check if the device is a LUKS container.""" return bool(self._P.Encrypted) @property def is_loop(self): """Check if the device is a loop device.""" return bool(self._P.Loop) @property def is_loopfile(self): """Check if this object is a pseudo-device that represents an image file to be used for setting up a loop device.""" return bool(self._P.LoopFile) # ---------------------------------------- # Drive # ---------------------------------------- # Drive properties @property def is_toplevel(self): """Check if the device is not a child device.""" return not self.is_partition and not self.is_luks_cleartext @property def parent(self): """Return the device of which this one is a child.""" return self.partition_slave or self.luks_cleartext_slave @property def _assocdrive(self): """ Return associated drive if this is a top level block device. This method is used internally to unify the behaviour of top level devices in udisks1 and udisks2. """ # NOTE: always fallback to `self` because udisks2 doesn't report # CryptoBackingDevice nor Drive for logical volumes: return self.is_toplevel and not self.is_loop and self.drive or self @property def is_detachable(self): """Check if the drive that owns this device can be detached.""" return bool(self._assocdrive._P.Drive.CanPowerOff) @property def is_ejectable(self): """Check if the drive that owns this device can be ejected.""" return bool(self._assocdrive._P.Drive.Ejectable) @property def has_media(self): """Check if there is media available in the drive.""" return bool(self._assocdrive._P.Drive.MediaAvailable) @property def drive_vendor(self): """Return drive vendor.""" return self._assocdrive._P.Drive.Vendor @property def drive_model(self): """Return drive model.""" return self._assocdrive._P.Drive.Model # Drive methods def eject(self, auth_no_user_interaction=None): """Eject media from the device.""" return self._assocdrive._M.Drive.Eject( '(a{sv})', filter_opt({ 'auth.no_user_interaction': ('b', auth_no_user_interaction), }) ) def detach(self, auth_no_user_interaction=None): """Detach the device by e.g. powering down the physical port.""" return self._assocdrive._M.Drive.PowerOff( '(a{sv})', filter_opt({ 'auth.no_user_interaction': ('b', auth_no_user_interaction), }) ) # ---------------------------------------- # Block # ---------------------------------------- # Block properties @property def device_file(self): """The filesystem path of the device block file.""" return ( decode_ay(self._P.Block.Device) or self._P.LoopFile.Filename or '') @property def device_presentation(self): """The device file path to present to the user.""" return decode_ay(self._P.Block.PreferredDevice) @property def device_size(self): """The size of the device in bytes.""" return self._P.Block.Size @property def id_usage(self): """Device usage class, for example 'filesystem' or 'crypto'.""" return self._P.Block.IdUsage @property def is_crypto(self): """Check if the device is a crypto device.""" return self.id_usage == 'crypto' @property def is_ignored(self): """Check if the device should be ignored.""" return self._P.Block.HintIgnore @property def device_id(self): """ Return a unique and persistent identifier for the device. This is the basename (last path component) of the symlink in `/dev/disk/by-id/`. """ if self.is_block: for filename in self._P.Block.Symlinks: parts = decode_ay(filename).split('/') if parts[-2] == 'by-id': return parts[-1] elif self.is_drive: return self._assocdrive._P.Drive.Id return '' @property def id_type(self): """" Return IdType property. This field provides further detail on IdUsage, for example: IdUsage 'filesystem' 'crypto' IdType 'ext4' 'crypto_LUKS' """ return self._P.Block.IdType @property def id_label(self): """Label of the device if available.""" return self._P.Block.IdLabel @property def id_uuid(self): """Device UUID.""" return self._P.Block.IdUUID @property def luks_cleartext_slave(self): """Get wrapper to the LUKS crypto device.""" return self._daemon[self._P.Block.CryptoBackingDevice] @property def is_luks_cleartext(self): """Check whether this is a luks cleartext device.""" return bool(self.luks_cleartext_slave) @property def is_external(self): """Check if the device is external.""" return not self.is_systeminternal @property def is_systeminternal(self): """Check if the device is internal.""" return bool(self._P.Block.HintSystem) @property def drive(self): """Get wrapper to the drive containing this device.""" if self.is_drive: return self elif self.is_block: return self._daemon[self._P.Block.Drive] else: return None @property def root(self): """Get the top level block device in the ancestry of this device.""" return self if self.is_toplevel else self.parent.root @property def should_automount(self): """Check if the device should be automounted.""" return bool(self._P.Block.HintAuto) @property def icon_name(self): """Return the recommended device icon name.""" return self._P.Block.HintIconName or 'drive-removable-media' @property def symbolic_icon_name(self): """Return the recommended device symbolic icon name.""" return self._P.Block.HintSymbolicIconName or 'drive-removable-media' @property def symlinks(self): """Known symlinks of the block device.""" if not self._P.Block.Symlinks: return [] return [decode_ay(path) for path in self._P.Block.Symlinks] # ---------------------------------------- # Partition # ---------------------------------------- # Partition properties @property def partition_slave(self): """Get the partition slave (container).""" return self._daemon[self._P.Partition.Table] @property def partition_uuid(self): """Get the partition UUID.""" return self._P.Partition.UUID # ---------------------------------------- # Filesystem # ---------------------------------------- # Filesystem properties @property def is_mounted(self): """Check if the device is mounted.""" return bool(self._P.Filesystem.MountPoints) @property def mount_paths(self): """Return list of active mount paths.""" return list(map(decode_ay, self._P.Filesystem.MountPoints or ())) # Filesystem methods def mount(self, fstype=None, options=None, auth_no_user_interaction=None): """Mount filesystem.""" return self._M.Filesystem.Mount( '(a{sv})', filter_opt({ 'fstype': ('s', fstype), 'options': ('s', ','.join(options or [])), 'auth.no_user_interaction': ('b', auth_no_user_interaction), }) ) def unmount(self, force=None, auth_no_user_interaction=None): """Unmount filesystem.""" return self._M.Filesystem.Unmount( '(a{sv})', filter_opt({ 'force': ('b', force), 'auth.no_user_interaction': ('b', auth_no_user_interaction), }) ) # ---------------------------------------- # Encrypted # ---------------------------------------- # Encrypted properties @property def luks_cleartext_holder(self): """Get wrapper to the unlocked luks cleartext device.""" if not self.is_luks: return None for device in self._daemon: if device.luks_cleartext_slave == self: return device return None @property def is_unlocked(self): """Check if device is already unlocked.""" return bool(self.luks_cleartext_holder) # Encrypted methods def unlock(self, password, auth_no_user_interaction=None): """Unlock Luks device.""" return self._M.Encrypted.Unlock( '(sa{sv})', password, filter_opt({ 'auth.no_user_interaction': ('b', auth_no_user_interaction), }) ) def unlock_keyfile(self, password, auth_no_user_interaction=None): return self._M.Encrypted.Unlock( '(sa{sv})', '', filter_opt({ 'keyfile_contents': ('ay', password), 'auth.no_user_interaction': ('b', auth_no_user_interaction), }) ) def lock(self, auth_no_user_interaction=None): """Lock Luks device.""" return self._M.Encrypted.Lock( '(a{sv})', filter_opt({ 'auth.no_user_interaction': ('b', auth_no_user_interaction), }) ) # ---------------------------------------- # Loop # ---------------------------------------- @property def loop_file(self): """Get the file backing the loop device.""" return ( decode_ay(self._P.Loop.BackingFile) or self._P.LoopFile.Filename or '') @property def setup_by_uid(self): """Get the ID of the user who set up the loop device.""" return self._P.Loop.SetupByUID @property def autoclear(self): """If True the loop device will be deleted after unmounting.""" return self._P.Loop.Autoclear def delete(self, auth_no_user_interaction=None): """Delete loop partition.""" return self._M.Loop.Delete( '(a{sv})', filter_opt({ 'auth.no_user_interaction': ('b', auth_no_user_interaction), }) ) def set_autoclear(self, value, auth_no_user_interaction=None): """Set autoclear flag for loop partition.""" return self._M.Loop.SetAutoclear( '(ba{sv})', value, filter_opt({ 'auth.no_user_interaction': ('b', auth_no_user_interaction), }) ) # ---------------------------------------- # derived properties # ---------------------------------------- def is_file(self, path): """Comparison by mount and device file path.""" return (samefile(path, self.device_file) or samefile(path, self.loop_file) or any(samefile(path, mp) for mp in self.mount_paths) or sameuuid(path, self.id_uuid) or sameuuid(path, self.partition_uuid)) @property def parent_object_path(self): return (self._P.Partition.Table or self._P.Block.CryptoBackingDevice or '/') @property def mount_path(self): """Return any mount path.""" try: return self.mount_paths[0] except IndexError: return '' @property def in_use(self): """Check whether this device is in use, i.e. mounted or unlocked.""" if self.is_mounted or self.is_unlocked: return True if self.is_partition_table: for device in self._daemon: if device.partition_slave == self and device.in_use: return True return False @property def ui_id_label(self): """Label of the unlocked partition or the device itself.""" return (self.luks_cleartext_holder or self).id_label @property def ui_id_uuid(self): """UUID of the unlocked partition or the device itself.""" return (self.luks_cleartext_holder or self).id_uuid @property def ui_device_presentation(self): """Path of the crypto backing device or the device itself.""" return (self.luks_cleartext_slave or self).device_presentation @property def ui_label(self): """UI string identifying the partition if possible.""" return ': '.join(filter(None, [ self.ui_device_presentation, self.ui_id_label or self.ui_id_uuid or self.drive_label ])) @property def ui_device_label(self): """UI string identifying the device (drive) if toplevel.""" return ': '.join(filter(None, [ self.ui_device_presentation, self.loop_file or self.drive_label or self.ui_id_label or self.ui_id_uuid ])) @property def drive_label(self): """Return drive label.""" return ' '.join(filter(None, [ self.drive_vendor, self.drive_model, ])) # ---------------------------------------- # UDisks2 service wrapper # ---------------------------------------- class Daemon(Emitter): """ Listen to state changes to provide automatic synchronization. Listens to UDisks2 events. When a change occurs this class detects what has changed and triggers an appropriate event. Valid events are: - device_added / device_removed - device_unlocked / device_locked - device_mounted / device_unmounted - media_added / media_removed - device_changed / job_failed """ BusName = 'org.freedesktop.UDisks2' ObjectPath = '/org/freedesktop/UDisks2' Interface = Interface['ObjectManager'] def __iter__(self): """Iterate over all devices.""" return (self[path] for path in self.paths() if object_kind(path) in ('device', 'drive')) def __getitem__(self, object_path): return self.get(object_path) def find(self, path): """ Get a device proxy by device name or any mount path of the device. This searches through all accessible devices and compares device path as well as mount paths. """ if isinstance(path, Device): return path for device in self: if device.is_file(path): self._log.debug(_('found device owning "{0}": "{1}"', path, device)) return device raise FileNotFoundError(_('no device found owning "{0}"', path)) def __init__(self, proxy, version): """Initialize object and start listening to UDisks2 events.""" event_names = ['device_added', 'device_removed', 'device_mounted', 'device_unmounted', 'media_added', 'media_removed', 'device_unlocked', 'device_locked', 'device_changed', 'job_failed'] super().__init__(event_names) self._log = logging.getLogger(__name__) self._log.debug(_('Daemon version: {0}', version)) self.version = version self.version_info = tuple(map(int, version.split('.'))) self.keyfile_support = self.version_info >= (2, 6, 4) self._log.debug(_('Keyfile support: {0}', self.keyfile_support)) self._proxy = proxy self._objects = {} proxy.connect('InterfacesAdded', self._interfaces_added) proxy.connect('InterfacesRemoved', self._interfaces_removed) bus = proxy.object.bus bus.connect(Interface['Properties'], 'PropertiesChanged', None, self._properties_changed) bus.connect(Interface['Job'], 'Completed', None, self._job_completed) async def _sync(self): """Synchronize state.""" self._objects = await self._proxy.call('GetManagedObjects', '()') @classmethod async def create(cls): service = (cls.BusName, cls.ObjectPath, cls.Interface) proxy = await dbus.connect_service(*service) version = await cls.get_version() daemon = cls(proxy, version) await daemon._sync() return daemon @classmethod async def get_version(cls): service = (cls.BusName, '/org/freedesktop/UDisks2/Manager', Interface['Properties']) manager = await dbus.connect_service(*service) version = await dbus.call(manager._proxy, 'Get', '(ss)', ( Interface['Manager'], 'Version')) return version async def loop_setup(self, fd, options): service = (self.BusName, '/org/freedesktop/UDisks2/Manager', Interface['Manager']) manager = await dbus.connect_service(*service) object_path = await dbus.call_with_fd_list( manager._proxy, 'LoopSetup', '(ha{sv})', (0, filter_opt({ 'auth.no_user_interaction': ( 'b', options.get('auth.no_user_interaction')), 'offset': ('t', options.get('offset')), 'size': ('t', options.get('size')), 'read-only': ('b', options.get('read-only')), 'no-part-scan': ('b', options.get('no-part-scan')), })), [fd], ) await self._sync() return self[object_path] # UDisks2 interface def paths(self): return self._objects.keys() def get(self, object_path, interfaces_and_properties=None): """Create a Device instance from object path.""" # check this before creating the DBus object for more # controlled behaviour: if not interfaces_and_properties: interfaces_and_properties = self._objects.get(object_path) if not interfaces_and_properties: return None property_hub = PropertyHub(interfaces_and_properties) method_hub = MethodHub( self._proxy.object.bus.get_object(object_path)) return Device(self, object_path, property_hub, method_hub) def trigger(self, event, device, *args): self._log.debug(_("+++ {0}: {1}", event, device)) super().trigger(event, device, *args) # add objects / interfaces def _interfaces_added(self, object_path, interfaces_and_properties): """Internal method.""" added = object_path not in self._objects self._objects.setdefault(object_path, {}) old_state = copy(self._objects[object_path]) self._objects[object_path].update(interfaces_and_properties) new_state = self._objects[object_path] if added: kind = object_kind(object_path) if kind in ('device', 'drive'): self.trigger('device_added', self[object_path]) if Interface['Block'] in interfaces_and_properties: slave = self[object_path].luks_cleartext_slave if slave: if not self._has_job(slave.object_path, 'device_unlocked'): self.trigger('device_unlocked', slave) if not added: self.trigger('device_changed', self.get(object_path, old_state), self.get(object_path, new_state)) # remove objects / interfaces def _detect_toggle(self, property_name, old, new, add_name, del_name): old_valid = old and bool(getattr(old, property_name)) new_valid = new and bool(getattr(new, property_name)) if add_name and new_valid and not old_valid: if not self._has_job(old.object_path, add_name): self.trigger(add_name, new) elif del_name and old_valid and not new_valid: if not self._has_job(old.object_path, del_name): self.trigger(del_name, new) def _has_job(self, device_path, event_name): job_interface = Interface['Job'] for path, interfaces in self._objects.items(): try: job = interfaces[job_interface] job_objects = job['Objects'] job_operation = job['Operation'] job_action = self._action_by_operation[job_operation] job_event = self._event_by_action[job_action] if event_name == job_event and device_path in job_objects: return True except KeyError: pass return False def _interfaces_removed(self, object_path, interfaces): """Internal method.""" old_state = copy(self._objects[object_path]) for interface in interfaces: del self._objects[object_path][interface] new_state = self._objects[object_path] if Interface['Drive'] in interfaces: self._detect_toggle( 'has_media', self.get(object_path, old_state), self.get(object_path, new_state), None, 'media_removed') if Interface['Block'] in interfaces: slave = self.get(object_path, old_state).luks_cleartext_slave if slave: if not self._has_job(slave.object_path, 'device_locked'): self.trigger('device_locked', slave) if self._objects[object_path]: self.trigger('device_changed', self.get(object_path, old_state), self.get(object_path, new_state)) else: del self._objects[object_path] if object_kind(object_path) in ('device', 'drive'): self.trigger( 'device_removed', self.get(object_path, old_state)) # change interface properties def _properties_changed(self, object_path, interface_name, changed_properties, invalidated_properties): """ Internal method. Called when a DBusProperty of any managed object changes. """ # update device state: old_state = deepcopy(self._objects[object_path]) for property_name in invalidated_properties: try: del self._objects[object_path][interface_name][property_name] except KeyError: pass for key, value in changed_properties.items(): self._objects[object_path][interface_name][key] = value new_state = self._objects[object_path] # detect changes and trigger events: if interface_name == Interface['Drive']: self._detect_toggle( 'has_media', self.get(object_path, old_state), self.get(object_path, new_state), 'media_added', 'media_removed') elif interface_name == Interface['Filesystem']: self._detect_toggle( 'is_mounted', self.get(object_path, old_state), self.get(object_path, new_state), 'device_mounted', 'device_unmounted') self.trigger('device_changed', self.get(object_path, old_state), self.get(object_path, new_state)) # There is no PropertiesChanged for the crypto device when it is # unlocked/locked in UDisks2. Instead, this is handled by the # InterfaceAdded/Removed handlers. # jobs _action_by_operation = { 'filesystem-mount': 'mount', 'filesystem-unmount': 'unmount', 'encrypted-unlock': 'unlock', 'encrypted-lock': 'lock', 'power-off-drive': 'detach', 'eject-media': 'eject', } _event_by_action = { 'mount': 'device_mounted', 'unmount': 'device_unmounted', 'unlock': 'device_unlocked', 'lock': 'device_locked', 'eject': 'media_removed', 'detach': 'device_removed', } _check_action_success = { 'mount': lambda dev: dev.is_mounted, 'unmount': lambda dev: not dev or not dev.is_mounted, 'unlock': lambda dev: dev.is_unlocked, 'lock': lambda dev: not dev or not dev.is_unlocked, 'detach': lambda dev: not dev, 'eject': lambda dev: not dev or not dev.has_media, } def _job_completed(self, job_name, success, message): """ Internal method. Called when a job of a long running task completes. """ job = self._objects[job_name][Interface['Job']] action = self._action_by_operation.get(job['Operation']) if not action: return # We only handle events, which are associated to exactly one object: object_path, = job['Objects'] device = self[object_path] if success: # It rarely happens, but sometimes UDisks posts the # Job.Completed event before PropertiesChanged, so we have to # check if the operation has been carried out yet: if self._check_action_success[action](device): event_name = self._event_by_action[action] self.trigger(event_name, device) else: self.trigger('job_failed', device, action, message) def loopfile_device(self, path): properties = PropertyHub({'LoopFile': {'Filename': path}}) return Device(self, '', properties, None) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1713981306.3669415 udiskie-2.5.3/udiskie.egg-info/0000755000175100001770000000000014612243572015720 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981306.0 udiskie-2.5.3/udiskie.egg-info/PKG-INFO0000644000175100001770000005126214612243572017023 0ustar00runnerdockerMetadata-Version: 2.1 Name: udiskie Version: 2.5.3 Summary: Removable disk automounter for udisks Home-page: https://github.com/coldfix/udiskie Author: Byron Clark Author-email: byron@theclarkfamily.name Maintainer: Thomas Gläßle Maintainer-email: t_glaessle@gmx.de License: MIT Project-URL: Bug Tracker, https://github.com/coldfix/udiskie/issues Project-URL: Source Code, https://github.com/coldfix/udiskie Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Environment :: X11 Applications :: GTK Classifier: Intended Audience :: Developers Classifier: Intended Audience :: End Users/Desktop Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: License :: OSI Approved :: MIT License Classifier: Topic :: Desktop Environment Classifier: Topic :: Software Development Classifier: Topic :: System :: Filesystems Classifier: Topic :: System :: Hardware Classifier: Topic :: Utilities Requires-Python: >=3.5 Description-Content-Type: text/x-rst Provides-Extra: password-cache License-File: COPYING ======= udiskie ======= |Version| |License| |Translations| *udiskie* is a udisks2_ front-end that allows to manage removable media such as CDs or flash drives from userspace. |Screenshot| Its features include: - automount removable media - notifications - tray icon - command line tools for manual un-/mounting - LUKS encrypted devices - unlocking with keyfiles (requires udisks 2.6.4) - loop devices (mounting iso archives) - password caching (requires python keyutils 0.3) All features can be individually enabled or disabled. **NOTE:** support for python2 and udisks1 have been removed. If you need a version of udiskie that supports python2, please check out the ``1.7.X`` releases or the ``maint-1.7`` branch. .. _udisks2: https://www.freedesktop.org/wiki/Software/udisks - `Documentation`_ - Usage_ - Installation_ - `Debug Info`_ - Troubleshooting_ - FAQ_ - `Man page`_ - `Source Code`_ - `Latest Release`_ - `Issue Tracker`_ .. _Documentation: https://github.com/coldfix/udiskie/wiki .. _Usage: https://github.com/coldfix/udiskie/wiki/Usage .. _Installation: https://github.com/coldfix/udiskie/wiki/Installation .. _Debug Info: https://github.com/coldfix/udiskie/wiki/Debug-Info .. _Troubleshooting: https://github.com/coldfix/udiskie/wiki/Troubleshooting .. _FAQ: https://github.com/coldfix/udiskie/wiki/FAQ .. _Man Page: https://raw.githubusercontent.com/coldfix/udiskie/master/doc/udiskie.8.txt .. _Source Code: https://github.com/coldfix/udiskie .. _Latest Release: https://pypi.python.org/pypi/udiskie/ .. _Issue Tracker: https://github.com/coldfix/udiskie/issues .. _Roadmap: https://github.com/coldfix/udiskie/blob/master/HACKING.rst#roadmap .. Badges: .. |Version| image:: https://img.shields.io/pypi/v/udiskie.svg :target: https://pypi.python.org/pypi/udiskie :alt: Version .. |License| image:: https://img.shields.io/pypi/l/udiskie.svg :target: https://github.com/coldfix/udiskie/blob/master/COPYING :alt: License: MIT .. |Translations| image:: http://weblate.coldfix.de/widgets/udiskie/-/udiskie/svg-badge.svg :target: http://weblate.coldfix.de/engage/udiskie/ :alt: Translations .. |Screenshot| image:: https://raw.githubusercontent.com/coldfix/udiskie/master/screenshot.png :target: https://raw.githubusercontent.com/coldfix/udiskie/master/screenshot.png :alt: Screenshot Hacking ------- *udiskie* is developed on github_. Feel free to contribute patches as pull requests here. If you don't have nor want a github account, you can send me the relevant files via email. Further resources: - `UDisks1 API`_ - `UDisks2 API`_ - `PyGObject APIs`_ .. _github: https://github.com/coldfix/udiskie .. _PEP8: http://www.python.org/dev/peps/pep-0008/ .. _`unit tests`: http://docs.python.org/2/library/unittest.html .. _`UDisks1 API`: http://udisks.freedesktop.org/docs/1.0.5/ .. _`UDisks2 API`: http://udisks.freedesktop.org/docs/latest/ .. _`PyGObject APIs`: http://lazka.github.io/pgi-docs/index.html Translations ------------ Translations by users are always welcome. The corresponding files are in the `lang`_ subfolder. In order to create a new translation, find out the locale name for your language, e.g. ``es_ES`` for Spanish, and create a translation file in the ``lang`` folder as follows:: cd lang make es_ES.po or simply copy the `udiskie.pot`_ to a ``.po`` file with the name of the target locale and start editing. It's also best to fill in your name and email address. The translations may become outdated as udiskie changes. If you notice an outdated translation, please edit the corresponding ``.po`` file in submit a patch, even for very small changes. In order to test udiskie with your locally edited translation files, type (still from the ``lang`` folder):: export TEXTDOMAINDIR=$PWD/../build/locale export LANG=es_ES.UTF-8 make mo udiskie .. _lang: https://github.com/coldfix/udiskie/tree/master/lang .. _udiskie.pot: https://raw.githubusercontent.com/coldfix/udiskie/master/lang/udiskie.pot CHANGELOG --------- 2.5.3 ----- Date 24.04.2024 - allow matching against a list of values in config rules (``device_config``) [thanks @dajt1725] - fix ``--terminal`` command line option (was not doing anything) [@dajt1725] - fix AttributeError when *python-keyring-keyutils* is installed instead of *keyutils* [thanks @bubbleguuum] 2.5.2 ~~~~~ Date: 27.01.2024 - drop external dependency on distutils (#278) - rename ``--notify-command`` to ``--event-hook`` to prevent misunderstandings (#279) 2.5.1 ~~~~~ Date: 23.11.2023 - fix default install location for bash completion scripts (#275) - fix zsh completion for device names with spaces (#253) 2.5.0 ~~~~~ Date: 03.10.2023 - add bash completions for CLI options (thanks @oersen) - updates for translations - added German translation - add bright outlines for wayland icons in dark themes - ignore snap loop devices by default - maintenance on CI workflows - fix KeyError when properties are invalidated multiple times (#272) 2.4.2 ~~~~~ Date: 01.03.2022 - add support for AyatanaAppIndicator3 - update Turkish translation - fix some documentation typos and errors 2.4.1 ~~~~~ Date: 02.02.2022 - setup loop devices in read-write mode if sufficient permissions are available 2.4.0 ~~~~~ Date: 30.11.2021 - add icon name that is specifically designed for tray panel - add Turkish translations (thanks @ersen0!) - try to detect installed translation files also in non-system installations - make it possible to override localedir manually using the ``TEXTDOMAINDIR`` environment variable - add guide on how to contribute translations and improve the Makefile 2.3.3 ~~~~~ Date: 05.03.2021 - fix: buttons in "device-added" notifications always mounting the last partition instead of the selected one 2.3.2 ~~~~~ Date: 27.01.2021 - fix incorrect use of wayland settings (appindicator+workarounds) within X session, if a wayland session is or was active under the same user (even if that was in a different login session) - fix incorrect display of image menu items under X due to caching 2.3.1 ~~~~~ Date: 27.01.2021 - add workaround for missing menu checkboxes on sway/wayland - add workaround for menu not showing up with proper size on sway/wayland - distribute icons within udiskie package (allows displaying icons in local/venv installations) - fix ignored coroutine return values, fixes e.g. program exit code 2.3.0 ~~~~~ Date: 22.01.2021 - don't swallow STDOUT for notify-commands - default to appindicator if tray is requested under wayland - don't auto-disable tray when running in "pure" wayland session - set window role on the password dialog 2.2.0 ~~~~~ Date: 07.05.2020 - add Italian translation - remove unneeded ``xdg`` from optional dependencies - fix a typo in Spanish translation and update russian translation files 2.1.1 ~~~~~ Date: 17.04.2020 - handle XDG_CONFIG_HOME variable without external pyxdg dependency - silence warning when using AppIndicator - make ``--appindicator`` sufficient to show icon (previously additionally required ``--tray``) - improve wording in password dialog "Remember" -> "Cache" - make some of the logging more concise - fix recursive adding/removing of some child devices: - fix incorrect root device detection for devices without Drive property (e.g. children of loop devices) - fix ``--detach`` option when applied to partitions of loop devices 2.1.0 ~~~~~ Date: 02.02.2020 - fix some typos (thanks @torstehu, #197) - change how device rules are evaluated: lookup undecided rules on parent device (fixes issue with filters not applying to subdevices of a matched device, see #198) - change builtin rules to not show loop devices with ``HintIgnore``, see #181 - change how is_external attribute is compute: use the value from udisks directly (fixes issue with is_external property not behaving as expected, see #185) - add 'skip' keyword for rules to skip evaluation of further rules on this device, and continue directly on the parent 2.0.4 ~~~~~ Date: 21.01.2020 - fix user commands that output non-utf8 data 2.0.3 ~~~~~ Date: 20.01.2020 - fix exception when using non-device parameters with DeviceCommand (e.g. in --notify-command) 2.0.2 ~~~~~ Date: 30.12.2019 - hotfix for automounting being broken since 2.0.0 2.0.1 ~~~~~ Date: 28.12.2019 - use ``importlib.resources`` directly on py3.7 and above, rather than requiring ``importlib_resources`` as additional dependency 2.0.0 ~~~~~ Date: 26.12.2019 - require python >= 3.5 - drop python2 support - drop udisks1 support - drop command line options corresponding to udisks version selection (-1, -2) - use py35's ``async def`` functions -- improving stack traces upon exception - internal refactoring and simplifications - add "show password" checkbox in password dialog 1.7.7 ~~~~~ Date: 17.02.2019 - keep password dialog always on top - fix stdin-based password prompts 1.7.6 ~~~~~ Date: 17.02.2019 - add russian translations (thanks @mr-GreyWolf) - fixed deprecation warnings in setup.py (thanks @sealj553) 1.7.5 ~~~~~ Date: 24.05.2018 - fix "NameError: 'Async' is not defined" when starting without tray icon 1.7.4 ~~~~~ Date: 17.05.2018 - fix attribute error when using options in udiskie-mount (#159) - fix tray in appindicator mode (#156) - possibly fix non-deterministic bugs (due to garbage collection) by keeping global reference to all active asyncs 1.7.3 ~~~~~ Date: 13.12.2017 - temporary workaround for udisks2.7 requiring ``filesystem-mount-system`` when trying to mount a LUKS cleartext device diretcly after unlocking 1.7.2 ~~~~~ Date: 18.10.2017 - officially deprecate udisks1 - officially deprecate python2 (want python >= 3.5) - fix startup crash on py2 - fix exception when inserting LUKS device if ``--password-prompt`` or udisks1 is used - fix minor problem with zsh autocompletion 1.7.1 ~~~~~ Date: 02.10.2017 - add an "open keyfile" button to the password dialog - add warning if mounting device without ntfs-3g (#143) - fix problem with LVM devices 1.7.0 ~~~~~ Date: 26.03.2017 - add joined ``device_config`` list in the config file - deprecate ``mount_options`` and ``ignore_device`` in favor of ``device_config`` - can configure ``automount`` per device using the new ``device_config`` [#107] - can configure keyfiles (requires udisks 2.6.4) [#66] - remove mailing list 1.6.2 ~~~~~ Date: 06.03.2017 - Show losetup/quit actions only in ex-menu - Show note in menu if no devices are found 1.6.1 ~~~~~ Date: 24.02.2017 - add format strings for the undocumented ``udiskie-info`` utility - speed up autocompletion times, for ``udiskie-mount`` by about a factor three, for ``udiskie-umount`` by about a factor 10 1.6.0 ~~~~~ Date: 22.02.2017 - fix crash on startup if config file is empty - add ``--notify-command`` to notify external programs (@jgraef) [#127] - can enable/disable automounting via special right-click menu [#98] - do not explicitly specify filesystem when mounting [#131] 1.5.1 ~~~~~ Date: 03.06.2016 - fix unicode issue that occurs on python2 when stdout is redirected (in particular for zsh autocompletion) 1.5.0 ~~~~~ Date: 03.06.2016 - make systray menu flat (use ``udiskie --tray --menu smart`` to request the old menu) [#119] - extend support for loop devices (requires UDisks2) [#101] - support ubuntu/unity AppIndicator backend for status icon [#59] - add basic utility to obtain info on block devices [#122] - add zsh completions [#26] - improve UI menu labels for devices - fix error when force-ejecting device [#121] - respect configured ignore-rules in ``udiskie-umount`` - fix error message for empty task lists [#123] 1.4.12 ~~~~~~ Date: 15.05.2016 - log INFO events to STDOUT (#112) - fix exception in notifications when action is not available. This concerns the retry button in the ``job_failed`` notification, as well as the browse action in the ``device_mounted`` notification (#117) - don't show 'browse' action in tray menu if unavailable 1.4.11 ~~~~~~ Date: 13.05.2016 - protect password dialog against garbage collection (which makes the invoking coroutine hang up and not unlock the device) - fix add_all/remove_all operations: only consider leaf/root devices within the handleable devices hierarchy: - avoid considering the same device twice (#114) - makes sure every handleable device is considered at all in remove_all 1.4.10 ~~~~~~ Date: 11.05.2016 - signal failing mount/unmount operations with non-zero exit codes (#110) - suppress notifications for unhandled devices - add rules for docker devices marking them unhandled to avoid excessive notifications (#113) - allow mounting/unmounting using UUID (#90) - prevent warning when starting without X session (#102) - can now match against wildcards in config rules (#49) 1.4.9 ~~~~~ Date: 02.04.2016 - add is_loop and loop_file properties for devices - fix recursive mounting of crypto devices (udiskie-mount) - prevent empty submenus from showing 1.4.8 ~~~~~ Date: 09.02.2016 - fix problem with setupscript if utf8 is not the default encoding - fix crash when starting without X - basic support for loop devices (must be enabled explicitly at this time) - fix handling of 2 more error cases 1.4.7 ~~~~~ Date: 04.01.2016 - fix typo that prevents the yaml config file from being used - fix problem with glib/gio gir API on slackware (olders versions?) - fix bug when changing device state (e.g. when formatting existing device or burning ISO file to device) - improve handling of race conditions with udisks1 backend - fix notifications for devices without labels 1.4.6 ~~~~~ Date: 28.12.2015 - cleanup recent bugfixes - close some gates for more py2/unicode related bugs 1.4.5 ~~~~~ Date: 24.12.2015 - fix another bug with unicode data on command line (py2) - slightly improve stack traces in async code - further decrease verbosity while removing devices 1.4.4 ~~~~~ Date: 24.12.2015 - fix too narrow dependency enforcement - make udiskie slightly less verbose in default mode 1.4.3 ~~~~~ Date: 24.12.2015 - fix bug with unicode data on python2 - fix bug due to event ordering in udisks1 - fix bug due to inavailability of device data at specific time 1.4.2 ~~~~~ Date: 22.12.2015 - fix regression in get_password_tty 1.4.1 ~~~~~ Date: 19.12.2015 - fix problem in SmartTray due to recent transition to async 1.4.0 ~~~~~ Date: 19.12.2015 - go async (with self-made async module for now, until gbulb becomes ready) - specify GTK/Notify versions to be imported (hence fix warnings and a problem for the tray icon resulting from accidentally importing GTK2) - add optional password caching 1.3.2 ~~~~~ - revert "respect the automount flag for devices" - make dependency on Gtk optional 1.3.1 ~~~~~ - use icon hints from udev settings in notifications - respect the automount flag for devices - don't fail if libnotify is not available 1.3.0 ~~~~~ - add actions to "Device added" notification - allow to configure which actions should be added to notifications 1.2.1 ~~~~~ - fix unicode issue in setup script - update license/copyright notices 1.2.0 ~~~~~ - use UDisks2 by default - add --password-prompt command line argument and config file entry 1.1.3 ~~~~~ - fix password prompt for GTK2 (tray is still broken for GTK2) - fix minor documentation issues 1.1.2 ~~~~~ - add key ``device_id`` for matching devices rather than only file systems - improve documentation regarding dependencies 1.1.1 ~~~~~ - fix careless error in man page 1.1.0 ~~~~~ - implemented internationalization - added spanish translation - allow to choose icons from a configurable list 1.0.4 ~~~~~ - compatibility with older version of pygobject (e.g. in Slackware 14.1) 1.0.3 ~~~~~ - handle exception if no notification service is installed 1.0.2 ~~~~~ - fix crash when calling udiskie mount/unmount utilites without udisks1 installed 1.0.1 ~~~~~ - fix crash when calling udiskie without having udisks1 installed (regression) 1.0.0 ~~~~~ - port to PyGObject, removing dependencies on pygtk, zenity, dbus-python, python-notify - use a PyGObject based password dialog - remove --password-prompt parameter - rename command line parameters - add negations for all command line parameters 0.8.0 ~~~~~ - remove the '--filters' parameter for good - change config format to YAML - change default config path to $XDG_CONFIG_HOME/udiskie/config.yml - separate ignore filters from mount option filters - allow to match multiple attributes against a device (AND-wise) - allow to overwrite udiskies default handleability settings - raise exception if --config file doesn't exist - add --options parameter for udiskie-mount - simplify local installations 0.7.0 ~~~~~ There are some backward incompatible changes, hence the version break: - command line parameter '-f'/'--filters' renamed to '-C'/'--config' - add sections in config file to disable individual mount notifications and set defaults for some program options (udisks version, prompt, etc) - refactor ``udiskie.cli``, ``udiskie.config`` and ``udiskie.tray`` - revert 'make udiskie a namespace package' - add 'Browse folder' action to tray menu - add 'Browse folder' action button to mount notifications - add '--no-automounter' command line option to disable automounting - add '--auto-tray' command line option to use a tray icon that automatically disappears when no actions are available - show notifications when devices dis-/appear (can be disabled via config file) - show 'id_label' in tray menu, if available (instead of mount path or device path) - add 'Job failed' notifications - add 'Retry' button to failed notifications - remove automatic retries to unlock LUKS partitions - pass only device name to external password prompt - add '--quiet' command line option - ignore devices ignored by udev rules 0.6.4 ~~~~~ - fix logging in setup.py - more verbose log messages (with time) when having -v on - fix mounting devices that are added as 'external' and later changed to 'internal' [udisks1] (applies to LUKS devices that are opened by an udev rule for example) 0.6.3 (bug fix) ~~~~~~~~~~~~~~~ - fix exception in Mounter.detach_device if unable to detach - fix force-detach for UDisks2 backend - automatically use UDisks2 if UDisks1 is not available - mount unlocked devices only once, removes error message on UDisks2 - mention __ignore__ in man page 0.6.2 (aesthetic) ~~~~~~~~~~~~~~~~~ - add custom icons for the context menu of the system tray widget 0.6.1 (bug fix) ~~~~~~~~~~~~~~~ - fix udisks2 external device detection bug: all devices were considered external when using ``Sniffer`` (as done in the udiskie-mount and udiskie-umount tools) 0.6.0 (udisks2 support, bug fix) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - cache device states to avoid some race conditions - show filesystem label in mount/unmount notifications - retry to unlock LUKS devices when wrong password was entered twice - show 'eject' only if media is available (udisks1 ejects only in this case) - (un-) mount/lock notifications shown even when operations failed - refactor internal API - experimental support for udisks2 0.5.3 (feature, bug fix) ~~~~~~~~~~~~~~~~~~~~~~~~ - add '__ignore__' config file option to prevent handling specific devices - delay notifications until termination of long operations 0.5.2 (tray icon) ~~~~~~~~~~~~~~~~~ - add tray icon (pygtk based) - eject / detach drives from command line 0.5.1 (mainly internal changes) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - use setuptools entry points to create the executables - make udiskie a namespace package 0.5.0 (LUKS support) ~~~~~~~~~~~~~~~~~~~~ - support for LUKS devices (using zenity for password prompt) - major refactoring - use setuptools as installer ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981306.0 udiskie-2.5.3/udiskie.egg-info/SOURCES.txt0000644000175100001770000000254614612243572017613 0ustar00runnerdockerCHANGES.rst COPYING MANIFEST.in README.rst setup.cfg setup.py completions/bash/udiskie completions/bash/udiskie-info completions/bash/udiskie-mount completions/bash/udiskie-umount completions/zsh/_udiskie completions/zsh/_udiskie-canonical_paths completions/zsh/_udiskie-mount completions/zsh/_udiskie-umount doc/Makefile doc/asciidoc.conf doc/udiskie.8.txt lang/de.po lang/en_US.po lang/es_ES.po lang/it_IT.po lang/ru_RU.po lang/sk_SK.po lang/tr_TR.po lang/udiskie.pot test/test_cache.py test/test_match.py udiskie/__init__.py udiskie/appindicator.py udiskie/async_.py udiskie/automount.py udiskie/cache.py udiskie/cli.py udiskie/common.py udiskie/config.py udiskie/dbus.py udiskie/depend.py udiskie/locale.py udiskie/mount.py udiskie/notify.py udiskie/password_dialog.ui udiskie/prompt.py udiskie/tray.py udiskie/udisks2.py udiskie.egg-info/PKG-INFO udiskie.egg-info/SOURCES.txt udiskie.egg-info/dependency_links.txt udiskie.egg-info/entry_points.txt udiskie.egg-info/requires.txt udiskie.egg-info/top_level.txt udiskie.egg-info/zip-safe udiskie/icons/__init__.py udiskie/icons/udiskie-checkbox-checked.svg udiskie/icons/udiskie-checkbox-unchecked.svg udiskie/icons/udiskie-detach.svg udiskie/icons/udiskie-eject.svg udiskie/icons/udiskie-lock.svg udiskie/icons/udiskie-mount.svg udiskie/icons/udiskie-submenu.svg udiskie/icons/udiskie-unlock.svg udiskie/icons/udiskie-unmount.svg././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981306.0 udiskie-2.5.3/udiskie.egg-info/dependency_links.txt0000644000175100001770000000000114612243572021766 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981306.0 udiskie-2.5.3/udiskie.egg-info/entry_points.txt0000644000175100001770000000025214612243572021215 0ustar00runnerdocker[console_scripts] udiskie = udiskie.cli:Daemon.main udiskie-info = udiskie.cli:Info.main udiskie-mount = udiskie.cli:Mount.main udiskie-umount = udiskie.cli:Umount.main ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981306.0 udiskie-2.5.3/udiskie.egg-info/requires.txt0000644000175100001770000000014714612243572020322 0ustar00runnerdockerPyYAML docopt PyGObject [:python_version < "3.7"] importlib_resources [password-cache] keyutils==0.3 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981306.0 udiskie-2.5.3/udiskie.egg-info/top_level.txt0000644000175100001770000000001014612243572020441 0ustar00runnerdockerudiskie ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1713981306.0 udiskie-2.5.3/udiskie.egg-info/zip-safe0000644000175100001770000000000114612243572017350 0ustar00runnerdocker