diff options
author | segfault <segfault@riseup.net> | 2019-09-30 23:14:53 +0200 |
---|---|---|
committer | segfault <segfault@riseup.net> | 2019-10-01 23:40:20 +0200 |
commit | de5b80128f30eda27529bc880dc9ba19d8648c92 (patch) | |
tree | 710723ded81229f32b9aab0590d242df39822c02 | |
parent | 99e5d9b31832ea8ca7f940cecb02dda3f1d1b32c (diff) |
More refactoring (refs: #17098)wip/feature/17098-refactor-greeter
31 files changed, 520 insertions, 586 deletions
diff --git a/bin/delete-merged-git-branches b/bin/delete-merged-git-branches index 03ea380..d2e2036 100755 --- a/bin/delete-merged-git-branches +++ b/bin/delete-merged-git-branches @@ -15,7 +15,7 @@ def yes_no_input(prompt, default=True): options = 'Y/n' else: options = 'N/y' - sys.stdout.write('%s [%s] ' % (prompt, options)) + sys.stdout.apply_to_upcoming_session('%s [%s] ' % (prompt, options)) while True: answer = input().lower() if len(answer) == 0: diff --git a/config/chroot_local-includes/etc/gdm3/PostLogin/Default b/config/chroot_local-includes/etc/gdm3/PostLogin/Default index 0722b7e..a3fe997 100755 --- a/config/chroot_local-includes/etc/gdm3/PostLogin/Default +++ b/config/chroot_local-includes/etc/gdm3/PostLogin/Default @@ -10,7 +10,6 @@ # * /var/lib/gdm3/tails.locale : $TAILS_LOCALE_NAME, $TAILS_XKBMODEL, # $TAILS_XKBLAYOUT, $TAILS_XKBVARIANT, $TAILS_XKBOPTIONS, $CODESET # * /var/lib/gdm3/tails.password : $TAILS_USER_PASSWORD -# * /var/lib/gdm3/tails.physical_security : $TAILS_MACSPOOF_ENABLED # For whatever reason, /usr/sbin (needed by at least chpasswd) # is not in our PATH diff --git a/config/chroot_local-includes/etc/whisperback/debugging-info.json b/config/chroot_local-includes/etc/whisperback/debugging-info.json index 35903bc..b9c451a 100644 --- a/config/chroot_local-includes/etc/whisperback/debugging-info.json +++ b/config/chroot_local-includes/etc/whisperback/debugging-info.json @@ -16,7 +16,8 @@ ["file", {"user": "Debian-gdm", "path": "/var/log/gdm3/tails-greeter.errors"}], ["file", {"user": "root", "path": "/var/log/live/boot.log"}], ["file", {"user": "root", "path": "/var/log/live/config.log"}], -["file", {"user": "root", "path": "/var/lib/live/config/tails.physical_security"}], +["file", {"user": "root", "path": "/var/lib/live/config/tails.macspoof"}], +["file", {"user": "root", "path": "/var/lib/live/config/tails.network"}], ["file", {"user": "root", "path": "/var/lib/live/config/tails.persistence"}], ["file", {"user": "tails-persistence-setup", "path": "/live/persistence/TailsData_unlocked/persistence.conf"}], ["file", {"user": "tails-persistence-setup", "path": "/live/persistence/TailsData_unlocked/live-additional-software.conf"}], diff --git a/config/chroot_local-includes/lib/systemd/system/tails-unblock-network.service b/config/chroot_local-includes/lib/systemd/system/tails-unblock-network.service index e8feaca..eab9902 100644 --- a/config/chroot_local-includes/lib/systemd/system/tails-unblock-network.service +++ b/config/chroot_local-includes/lib/systemd/system/tails-unblock-network.service @@ -5,15 +5,18 @@ Documentation=https://tails.boum.org/contribute/design/MAC_address/ [Service] Type=oneshot RemainAfterExit=yes -EnvironmentFile=/var/lib/gdm3/tails.physical_security +EnvironmentFile=/var/lib/gdm3/tails.network -# It's important we "export" the settings from tails.physical_security -# before unblocking the network; doing so will make the user-set MAC spoofing +# It's important we "export" the settings from tails.macspoof before +# unblocking the network; doing so will make the user-set MAC spoofing # option apply (via the custom udev rule) when loading the modules for the # previously blocked network devices. ExecStartPre=/usr/bin/install -m 0640 -o root -g root \ - /var/lib/gdm3/tails.physical_security \ - /var/lib/live/config/tails.physical_security + /var/lib/gdm3/tails.macspoof \ + /var/lib/live/config/tails.macspoof && \ + /usr/bin/install -m 0640 -o root -g root \ + /var/lib/gdm3/tails.network \ + /var/lib/live/config/tails.network ExecStartPre=/bin/sync ExecStartPre=/bin/sh -c \ 'if [ "${TAILS_NETCONF}" = "obstacle" ] ; then \ diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/config.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/config.py index 8c7fe3b..3e63b1d 100644 --- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/config.py +++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/config.py @@ -27,19 +27,22 @@ LUSER = 'amnesia' data_path = '/usr/share/tails/greeter/' # File containing the language codes that are supported by the greeter -language_codes_path = os.path.join(data_path, 'language_codes') +locales_path = os.path.join(data_path, 'locales') # Locales path -locales_path = '/usr/share/locale/' +system_locale_dir = '/usr/share/locale/' # File where session locale settings are stored locale_output_path = '/var/lib/gdm3/tails.locale' # File where the session sudo password is stored -rootpassword_output_path = '/var/lib/gdm3/tails.password' +admin_password_output_path = '/var/lib/gdm3/tails.password' # World-readable file where Tails persistence status is stored persistence_state_file = '/var/lib/live/config/tails.persistence' -# File where settings related to physical security are stored -physical_security_settings = '/var/lib/gdm3/tails.physical_security' +# File where the network setting is stored +network_setting = '/var/lib/gdm3/tails.network' + +# File where the MAC address spoofing setting is stored +macspoof_setting = '/var/lib/gdm3/tails.macspoof' diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/greeter.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/greeter.py new file mode 100644 index 0000000..a09ccc7 --- /dev/null +++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/greeter.py @@ -0,0 +1,138 @@ +#!/usr/bin/python3 +# +# Copyright 2012-2019 Tails developers <tails@boum.org> +# Copyright 2011 Max <govnototalitarizm@gmail.com> +# Copyright 2011 Martin Owens +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/> +# + +import gi +import logging + +from tailsgreeter.gdmclient import GdmClient +from tailsgreeter.settings import localization +from tailsgreeter.settings.admin import AdminSetting +from tailsgreeter.settings.localization_settings import LocalisationSettings +from tailsgreeter.settings.macspoof import MacSpoofSetting +from tailsgreeter.settings.network import NetworkSetting +from tailsgreeter.settings.persistence import PersistenceSettings +from tailsgreeter.translatable_window import TranslatableWindow +from tailsgreeter.ui.additional_settings import AdminSettingUI, MACSpoofSettingUI, NetworkSettingUI +from tailsgreeter.ui.main_window import GreeterMainWindow +from tailsgreeter.ui.region_settings import LanguageSettingUI, KeyboardSettingUI, FormatsSettingUI +from tailsgreeter.ui.settings_collection import GreeterSettingsCollection + +gi.require_version('Gio', '2.0') +from gi.repository import Gio + + +class GreeterApplication(): + """Tails greeter main controller + + This class is the greeter dbus service""" + + def __init__(self): + self.session = None + self.forced = False + self.postponed = False + self.postponed_text = None + self.ready = False + self.translated = False + + self._sessionmanager = Gio.DBusProxy.new_for_bus_sync( + Gio.BusType.SESSION, + Gio.DBusProxyFlags.NONE, + None, + "org.gnome.SessionManager", + "/org/gnome/SessionManager", + "org.gnome.SessionManager") + + # Load models + self.gdmclient = GdmClient(session_opened_cb=self.close_app) + + persistence = PersistenceSettings() + self.localisationsettings = LocalisationSettings( + usermanager_loaded_cb=self.usermanager_loaded, + locale_selected_cb=self.on_language_changed + ) + self.admin_setting = AdminSetting() + self.network_setting = NetworkSetting() + self.macspoof_setting = MacSpoofSetting() + + # Initialize the settings + settings = GreeterSettingsCollection( + LanguageSettingUI(self.localisationsettings.language), + KeyboardSettingUI(self.localisationsettings.keyboard), + FormatsSettingUI(self.localisationsettings.formats), + AdminSettingUI(self.admin_setting), + MACSpoofSettingUI(self.macspoof_setting), + NetworkSettingUI(self.network_setting), + ) + + # Initialize main window + self.mainwindow = GreeterMainWindow(self, persistence, settings) + + # Inhibit the session being marked as idle + self.inhibit_idle() + + def translate_to(self, lang): + """Translate all windows to target language""" + TranslatableWindow.translate_all(lang) + + def login(self): + """Login GDM to the server""" + logging.debug("login called") + + # Apply settings + self.localisationsettings.apply_to_upcoming_session() + self.admin_setting.apply_to_upcoming_session() + self.macspoof_setting.apply_to_upcoming_session() + self.network_setting.apply_to_upcoming_session() + + self.mainwindow.hide() + self.gdmclient.do_login() + + def usermanager_loaded(self): + """UserManager is ready""" + logging.debug("Entering usermanager_loaded") + self.ready = True + logging.info("tails-greeter is ready.") + self.mainwindow.show() + + def on_language_changed(self, locale_code: str): + """Translate to the given locale""" + self.localisationsettings.formats.on_language_changed(locale_code) # XXX: notify + self.localisationsettings.keyboard.on_language_changed(locale_code) # XXX: notify + self.translate_to(locale_code) + self.mainwindow.current_language = localization.language_from_locale(locale_code) + + def close_app(self): + """We're done, quit gtk app""" + logging.info("Finished.") + Gtk.main_quit() + + def shutdown(self): + """Shuts down the computer using GNOME Session Manager""" + logging.info("Shutdown") + self._sessionmanager.Shutdown() + + def inhibit_idle(self): + cookie = self._sessionmanager.Inhibit( + "(susu)", + "org.boum.tails.Greeter", + 0, + "Greeter session shouldn't idle", + 8) # Inhibit the session being marked as idle + logging.debug("inhibitor cookie=%i", cookie) diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/admin.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/admin.py new file mode 100644 index 0000000..ae85d34 --- /dev/null +++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/admin.py @@ -0,0 +1,34 @@ + +import os +import os.path +import logging +import pipes + +import tailsgreeter.config + + +class AdminSetting(object): + """Setting controlling the sudo password""" + + def __init__(self): + self.password = None + + def apply_to_upcoming_session(self): + setting_file = tailsgreeter.config.admin_password_output_path + + if self.password: + with open(setting_file, 'w') as f: + os.chmod(setting_file, 0o600) + f.write('TAILS_USER_PASSWORD=%s\n' % pipes.quote(self.password)) + logging.debug('password written to %s', setting_file) + return + + # Try to remove the password file + try: + os.unlink(setting_file) + logging.debug('removed %s', setting_file) + except OSError: + # It's bad if the file exists and couldn't be removed, so we + # we raise the exception in that case (which prevents the login) + if os.path.exists(setting_file): + raise diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/formats.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/formats.py index 02f03f6..b7d3e7a 100644 --- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/formats.py +++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/formats.py @@ -1,65 +1,50 @@ import logging -from typing import TYPE_CHECKING import gi -from tailsgreeter.settings.localization import LocalizationSetting, language_from_locale, country_from_locale, \ - countries_from_locales +from tailsgreeter.settings.localization import LocalizationSetting, language_from_locale, country_from_locale gi.require_version('GObject', '2.0') gi.require_version('GnomeDesktop', '3.0') gi.require_version('Gtk', '3.0') from gi.repository import GObject, GnomeDesktop, Gtk -if TYPE_CHECKING: - from tailsgreeter.settings.localization_settings import LocalisationSettings - class FormatsSetting(LocalizationSetting): - def __init__(self, settings_object: "LocalisationSettings"): - super().__init__(settings_object) - super().set_value('en_US', is_default=True) + def __init__(self, language_codes: [str]): + super().__init__() + self.value = 'en_US' + self.locales_per_country = self._make_locales_per_country_dict(language_codes) - def get_tree(self): + def get_tree(self) -> Gtk.TreeStore: treestore = Gtk.TreeStore(GObject.TYPE_STRING, # id GObject.TYPE_STRING) # name - format_codes = countries_from_locales( - self._settings.system_locales_list) - format_codes.sort(key=lambda x: self._country_name(x).lower()) - logging.debug("format_codes=%s", format_codes) - for format_code in format_codes: - format_name = self._country_name(format_code) - if not format_name: + country_codes = list(self.locales_per_country.keys()) + country_codes.sort(key=lambda x: self._country_name(x).lower()) + logging.debug("format_codes=%s", country_codes) + for country_code in country_codes: + country_name = self._country_name(country_code) + if not country_name: # Don't display languages without a name continue + treeiter_format = treestore.append(parent=None) - treestore.set(treeiter_format, - 0, self.get_default_locale(format_code)) - treestore.set(treeiter_format, 1, format_name) - locale_codes = sorted( - self.get_default_locales(format_code), - key=lambda x: self._locale_name(x).lower()) - if len(locale_codes) > 1: - for locale_code in locale_codes: - treeiter_locale = treestore.append( - parent=treeiter_format) - treestore.set(treeiter_locale, 0, locale_code) - treestore.set(treeiter_locale, 1, - self._locale_name(locale_code)) + treestore.set(treeiter_format, 0, self.get_default_locale(country_code)) + treestore.set(treeiter_format, 1, country_name) + locales = sorted(self.locales_per_country[country_code], + key=lambda x: self._locale_name(x).lower()) + if len(locales) > 1: + for locale in locales: + treeiter_locale = treestore.append(parent=treeiter_format) + treestore.set(treeiter_locale, 0, locale) + treestore.set(treeiter_locale, 1, self._locale_name(locale)) return treestore - def get_name(self): + def get_name(self) -> str: return self._locale_name(self.get_value()) - def get_default_locales(self, country_code): - """Return available locales for given country - - """ - if country_code in self._settings.system_formats_dict: - return self._settings.system_formats_dict[country_code] - - def get_default_locale(self, country_code=None): + def get_default_locale(self, country_code=None) -> str: """Return default locale for given country Returns the 1st locale among: @@ -67,17 +52,18 @@ class FormatsSetting(LocalizationSetting): - the 1st locale for the language - en_US """ - default_locales = self.get_default_locales(country_code) - if default_locales: - for locale_code in default_locales: - if (country_from_locale(locale_code).lower() == - language_from_locale(locale_code)): - return locale_code - return default_locales[0] - else: + locales = self.locales_per_country[country_code] + if not locales: return 'en_US' - def _country_name(self, country_code): + # Get the default locale for the country + for locale_code in locales: + if country_from_locale(locale_code).lower() == language_from_locale(locale_code): + return locale_code + + return locales[0] + + def _country_name(self, country_code) -> str: default_locale = 'C' local_locale = self.get_default_locale(country_code) native_name = GnomeDesktop.get_country_from_code( @@ -93,7 +79,7 @@ class FormatsSetting(LocalizationSetting): native=native_name, localized=localized_name) @staticmethod - def _locale_name(locale_code): + def _locale_name(locale_code) -> str: lang_code = language_from_locale(locale_code) country_code = country_from_locale(locale_code) language_name_locale = GnomeDesktop.get_language_from_code(lang_code) @@ -118,11 +104,25 @@ class FormatsSetting(LocalizationSetting): except AttributeError: return locale_code - def set_default(self): - """Set default format for current language - - Select the same locale for formats that the language - """ - default_format = self._settings.language.get_value() - logging.debug("setting default formats to %s", default_format) - self.set_value(default_format, is_default=True) + @staticmethod + def _make_locales_per_country_dict(language_codes: [str]) -> {str: [str]}: + """assemble dictionary of country codes to corresponding locales list + + example {FR: [fr_FR, en_FR], ...}""" + res = {} + for language_code in language_codes: + country_code = country_from_locale(language_code) + if country_code not in res: + res[country_code] = [] + if language_code not in res[country_code]: + res[country_code].append(language_code) + return res + + def on_language_changed(self, language_code: str): + """Set the formats according to the new language""" + # Don't overwrite user chosen values + if self.value_changed_by_user: + return + + logging.debug("setting formats to %s", language_code) + self.set_value(language_code) diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/keyboard.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/keyboard.py index 65de965..5118f00 100644 --- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/keyboard.py +++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/keyboard.py @@ -14,10 +14,10 @@ from gi.repository import Gio, GLib, GnomeDesktop, GObject, Gtk class KeyboardSetting(LocalizationSetting): - def __init__(self, settings_object): - super().__init__(settings_object) + def __init__(self): + super().__init__() self.xkbinfo = GnomeDesktop.XkbInfo() - super().set_value('us', is_default=True) + self.value = 'us' def get_tree(self, layout_codes=None): if not layout_codes: @@ -27,9 +27,8 @@ class KeyboardSetting(LocalizationSetting): GObject.TYPE_STRING) # name layouts = self._layouts_split_names(layout_codes) for group_name in sorted(layouts.keys()): - layout_codes = sorted( - layouts[group_name], - key=lambda x: self._layout_name(x).lower()) + layout_codes = sorted(layouts[group_name], + key=lambda x: self._layout_name(x).lower()) treeiter_group = treestore.append(parent=None) # we fill the title with the 1st layout of the group treestore.set(treeiter_group, 0, layout_codes[0]) @@ -38,8 +37,7 @@ class KeyboardSetting(LocalizationSetting): for layout_code in layout_codes: treeiter_layout = treestore.append(parent=treeiter_group) treestore.set(treeiter_layout, 0, layout_code) - treestore.set(treeiter_layout, 1, - self._layout_name(layout_code)) + treestore.set(treeiter_layout, 1, self._layout_name(layout_code)) return treestore def get_name(self): @@ -51,21 +49,9 @@ class KeyboardSetting(LocalizationSetting): """ return self.xkbinfo.get_all_layouts() - def get_defaults(self): - """Return list of supported keyboard layouts for current language - - """ - lang_code = language_from_locale(self._settings.language.get_value()) - layouts = self._layouts_for_language(lang_code) - if not layouts: - country_code = country_from_locale(self._settings.language.get_value()) - layouts = self._layouts_for_country(country_code) - if not layouts: - layouts = ['us'] - return layouts - - def set_value(self, layout, is_default=False): - super().set_value(layout, is_default) + def set_value(self, layout, chosen_by_user=False): + super().set_value(layout) + self.value_changed_by_user = chosen_by_user self._apply_layout_to_current_screen() def _layout_name(self, layout_code): @@ -86,8 +72,8 @@ class KeyboardSetting(LocalizationSetting): layouts_names[country_name].add(layout_code) return layouts_names - def _layouts_for_language(self, lang_code): - """Return the list of available layouts for given language + def _layouts_for_language(self, lang_code) -> {str}: + """Return the set of available layouts for given language """ try: t_code = ln_iso639_tri(lang_code) @@ -111,10 +97,10 @@ class KeyboardSetting(LocalizationSetting): layouts = self.xkbinfo.get_layouts_for_language(b_code) logging.debug('got %d layouts for %s', len(layouts), lang_code) - return layouts + return set(layouts) - def _layouts_for_country(self, country): - """Return the list of available layouts for given country + def _layouts_for_country(self, country) -> {str}: + """Return the set of available layouts for given country """ # XXX: it would be logical to use: # self.__xklinfo.get_layouts_for_language(country) @@ -122,8 +108,7 @@ class KeyboardSetting(LocalizationSetting): # country. def country_filter(layout): cc = country.lower() - return ((layout == cc) - or ('+' in layout) and (layout.split('+')[0] == cc)) + return (layout == cc) or ('+' in layout) and (layout.split('+')[0] == cc) layouts = list(filter(country_filter, self.get_all())) @@ -168,35 +153,34 @@ class KeyboardSetting(LocalizationSetting): layouts = filtered_layouts return layouts - def set_default(self): - """Sets the best default layout for the current locale - """ - language = language_from_locale(self._settings.language.get_value()) - country = country_from_locale(self._settings.language.get_value()) + def on_language_changed(self, locale: str): + """Set the keyboard layout according to the new language""" + + # Don't overwrite a user chosen value + if self.value_changed_by_user: + return + + language = language_from_locale(locale) + country = country_from_locale(locale) # First, build a list of layouts to consider for the language language_layouts = self._layouts_for_language(language) logging.debug("Language %s layouts: %s", language, language_layouts) country_layouts = self._layouts_for_country(country) logging.debug("Country %s layouts: %s", country, country_layouts) - layouts = set(language_layouts).intersection(country_layouts) + layouts = language_layouts.intersection(country_layouts) logging.debug("Intersection of language %s and country %s: %s", language, country, layouts) - if not len(layouts) > 0: - def country_filter(layout): - layout_name, layout_variant = self._split_variant(layout) - return layout_name == country.lower() - layouts = list(filter(country_filter, language_layouts)) + + if not layouts: + layouts = set(l for l in language_layouts if self._split_variant(l)[0] == country.lower) logging.debug("Empty intersection of language and country, filter " "by country %s only: %s", country, layouts) - if not len(layouts) > 0: - def language_filter(layout): - layout_name, layout_variant = self._split_variant(layout) - return layout_name == language - layouts = list(filter(language_filter, language_layouts)) + if not layouts: + layouts = set(l for l in language_layouts if self._split_variant(l)[0] == language) logging.debug("List still empty, filter by language %s only: %s", language, layouts) - if not len(layouts) > 0: + if not layouts: layouts = language_layouts logging.debug("List still empty, use all language %s layouts: %s", language, layouts) @@ -220,7 +204,7 @@ class KeyboardSetting(LocalizationSetting): else: default_layout = 'us' logging.debug("Using us as fallback default layout") - self.set_value(default_layout, is_default=True) + self.set_value(default_layout) def _apply_layout_to_current_screen(self): layout = self.get_value() diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/language.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/language.py index f2f9409..ca57785 100644 --- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/language.py +++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/language.py @@ -20,6 +20,7 @@ import gi import logging import locale +from typing import Callable from tailsgreeter.settings.localization import LocalizationSetting, \ language_from_locale, languages_from_locales, country_from_locale @@ -33,16 +34,19 @@ from gi.repository import GLib, GObject, GnomeDesktop, Gtk class LanguageSetting(LocalizationSetting): - def __init__(self, settings_object): - super().__init__(settings_object) - super().set_value('en_US', is_default=True) + def __init__(self, locales: [str], language_changed_cb: Callable): + super().__init__() + self.value = 'en_US' + self.locales = locales + self.language_changed_cb = language_changed_cb + self._user_account = None + self.locales_per_language = self._make_language_to_locale_dict(locales) - def get_tree(self): + def get_tree(self) -> Gtk.TreeStore: treestore = Gtk.TreeStore(GObject.TYPE_STRING, # id GObject.TYPE_STRING) # name - lang_codes = languages_from_locales( - self._settings.system_locales_list) + lang_codes = languages_from_locales(self.locales) lang_codes.sort(key=lambda x: self._language_name(x).lower()) for lang_code in lang_codes: language_name = self._language_name(lang_code) @@ -50,58 +54,46 @@ class LanguageSetting(LocalizationSetting): # Don't display languages without a name continue treeiter_language = treestore.append(parent=None) - treestore.set(treeiter_language, - 0, self.get_default_locale(lang_code)) + treestore.set(treeiter_language, 0, self.get_default_locale(lang_code)) treestore.set(treeiter_language, 1, language_name) - locale_codes = sorted( - self.get_default_locales(lang_code), - key=lambda x: self._locale_name(x).lower()) - if len(locale_codes) > 1: - for locale_code in locale_codes: - treeiter_locale = treestore.append( - parent=treeiter_language) + locales = sorted(self.locales_per_language[lang_code], + key=lambda x: self._locale_name(x).lower()) + if len(locales) > 1: + for locale_code in locales: + treeiter_locale = treestore.append(parent=treeiter_language) treestore.set(treeiter_locale, 0, locale_code) - treestore.set(treeiter_locale, 1, - self._locale_name(locale_code)) + treestore.set(treeiter_locale, 1, self._locale_name(locale_code)) return treestore - def get_name(self): + def get_name(self) -> str: return self._locale_name(self.get_value()) - def get_default_locales(self, lang_code): - """Return available locales for given language - - """ - if lang_code in self._settings._system_languages_dict: - return self._settings._system_languages_dict[lang_code] - - def get_default_locale(self, lang_code=None): - """Return default locale for given language - - Returns the 1st locale among: - - the locale whose country name matches language name - - the 1st locale for the language - - en_US - """ - default_locales = self.get_default_locales(lang_code) - if default_locales: - for locale_code in default_locales: - if (country_from_locale(locale_code).lower() == - language_from_locale(locale_code)): - return locale_code - return default_locales[0] - else: + def get_default_locale(self, lang_code=None) -> str: + """Try to find a default locale for the given language""" + locales = self.locales_per_language[lang_code] + if not locales: return 'en_US' - def set_value(self, locale, is_default=False): - super().set_value(locale, is_default) - self.__apply_locale() - self._settings.formats.set_default_if_needed() # XXX: notify - self._settings.keyboard.set_default_if_needed() # XXX: notify - if self._settings._locale_selected_cb: - self._settings._locale_selected_cb(locale) + # Try using the default locale for the language as chosen by locale.normalize + default_locale = locale.normalize(lang_code).split('.')[0] + if default_locale in locales: + return default_locale + + # Try using the locale with the same country code as the language code + for locale_code in locales: + if (country_from_locale(locale_code).lower() == + language_from_locale(locale_code)): + return locale_code + + # None above worked, so just return the first locale in our list + return locales[0] - def _language_name(self, lang_code): + def set_value(self, locale_code: str, chosen_by_user=False): + super().set_value(locale_code, chosen_by_user) + self._apply_language(locale_code) + self.language_changed_cb(locale_code) + + def _language_name(self, lang_code) -> str: default_locale = 'C' local_locale = self.get_default_locale(lang_code) try: @@ -117,7 +109,8 @@ class LanguageSetting(LocalizationSetting): return "{native} ({localized})".format( native=native_name, localized=localized_name) - def _locale_name(self, locale_code): + @staticmethod + def _locale_name(locale_code) -> str: lang_code = language_from_locale(locale_code) country_code = country_from_locale(locale_code) language_name_locale = GnomeDesktop.get_language_from_code(lang_code) @@ -142,12 +135,28 @@ class LanguageSetting(LocalizationSetting): except AttributeError: return locale_code - def __apply_locale(self): - locale_code = locale.normalize( - self.get_value() + '.' + locale.getpreferredencoding()) - logging.debug("Setting session language to %s", locale_code) - if self._settings._act_user: - GLib.idle_add( - lambda: self._settings._act_user.set_language(locale_code)) + def _apply_language(self, language_code): + normalized_code = locale.normalize(language_code + '.' + locale.getpreferredencoding()) + logging.debug("Setting session language to %s", normalized_code) + if self._user_account: + # For some reason, this produces the following warning, but + # the language is actually applied. + # AccountsService-WARNING **: 19:29:39.181: SetLanguage for language de_DE.UTF-8 failed: + # GDBus.Error:org.freedesktop.Accounts.Error.PermissionDenied: Not authorized + GLib.idle_add(lambda: self._user_account.set_language(normalized_code)) else: logging.warning("AccountsManager not ready") + + @staticmethod + def _make_language_to_locale_dict(locale_codes): + """assemble dictionary of language codes to corresponding locales list + + example {en: [en_US, en_GB], ...}""" + languages_dict = {} + for locale_code in locale_codes: + lang_code = language_from_locale(locale_code) + if lang_code not in languages_dict: + languages_dict[lang_code] = [] + if locale_code not in languages_dict[lang_code]: + languages_dict[lang_code].append(locale_code) + return languages_dict diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/localization.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/localization.py index 665d551..b8d6313 100644 --- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/localization.py +++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/localization.py @@ -19,48 +19,31 @@ import gi import pycountry -from typing import TYPE_CHECKING gi.require_version('GObject', '2.0') from gi.repository import GObject -if TYPE_CHECKING: - from tailsgreeter.settings.localization_settings import LocalisationSettings +class LocalizationSetting(GObject.Object, object): -class LocalizationSetting(GObject.Object): + def __init__(self): + GObject.Object.__init__(self) + self.value = "" + self.value_changed_by_user = False - value = GObject.property(type=str) - is_default = GObject.property(type=bool, default=True) - - def __init__(self, settings_object: "LocalisationSettings"): - super().__init__() - self._settings = settings_object - - def get_value(self): + def get_value(self) -> str: return self.value - # is_default will be used by subclasses to register default value - def set_value(self, value, is_default=False): + def set_value(self, value, chosen_by_user=False): self.value = value - if not is_default: - self.is_default = False - self._settings.apply_settings_to_upcoming_session() + self.value_changed_by_user = chosen_by_user - def get_name(self): + def get_name(self) -> str: raise NotImplementedError - def get_tree(self): + def get_tree(self) -> str: raise NotImplementedError - def set_default(self): - raise NotImplementedError - - def set_default_if_needed(self): - """Update default value if it was not user choosen""" - if self.is_default: - self.set_default() - def ln_iso639_tri(ln_CC): """get iso639 3-letter code from a language code @@ -87,12 +70,7 @@ def languages_from_locales(locales): """Obtain a language code list from a locale code list example: [fr_FR, en_GB] -> [fr, en]""" - language_codes = [] - for l in locales: - language_code = language_from_locale(l) - if language_code not in language_codes: - language_codes.append(language_code) - return language_codes + return list({language_from_locale(l) for l in locales}) def country_from_locale(locale): @@ -102,13 +80,8 @@ def country_from_locale(locale): return locale.split('_')[1] -def countries_from_locales(locales): +def countries_from_locales(locales) -> [str]: """Obtain a country code list from a locale code list example: [fr_FR, en_GB] -> [FR, GB]""" - country_codes = [] - for l in locales: - country_code = country_from_locale(l) - if country_code not in country_codes: - country_codes.append(country_code) - return country_codes + return list({country_from_locale(l) for l in locales}) diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/localization_settings.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/localization_settings.py index 0f15c20..9f47318 100644 --- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/localization_settings.py +++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/localization_settings.py @@ -1,9 +1,9 @@ import gi import logging import os +from typing import Callable import tailsgreeter.config -from tailsgreeter.settings.localization import language_from_locale, country_from_locale from tailsgreeter.settings.formats import FormatsSetting from tailsgreeter.settings.keyboard import KeyboardSetting from tailsgreeter.settings.language import LanguageSetting @@ -16,88 +16,55 @@ class LocalisationSettings(object): """Controller for localisation settings """ - def __init__(self, usermanager_loaded_cb=None, locale_selected_cb=None): + def __init__(self, usermanager_loaded_cb: Callable, locale_selected_cb: Callable): self._usermanager_loaded_cb = usermanager_loaded_cb - self._locale_selected_cb = locale_selected_cb - self._act_user = None + self._user_account = None self._actusermanager_loadedid = None - self.system_locales_list = self.__get_langcodes() - self._system_languages_dict = self.__fill_languages_dict( - self.system_locales_list) - self.system_formats_dict = self.__fill_formats_dict( - self.system_locales_list) + locales = self._get_locales() self._actusermanager = AccountsService.UserManager.get_default() self._actusermanager_loadedid = self._actusermanager.connect( "notify::is-loaded", self.__on_usermanager_loaded) - self.language = LanguageSetting(self) - self.keyboard = KeyboardSetting(self) - self.formats = FormatsSetting(self) + self.language = LanguageSetting(locales, locale_selected_cb) + self.keyboard = KeyboardSetting() + self.formats = FormatsSetting(locales) def __del__(self): if self._actusermanager_loadedid: self._actusermanager.disconnect(self._actusermanager_loadedid) @staticmethod - def __get_langcodes(): - with open(tailsgreeter.config.language_codes_path, 'r') as f: + def _get_locales() -> [str]: + with open(tailsgreeter.config.locales_path, 'r') as f: return [line.rstrip('\n') for line in f.readlines()] def __on_usermanager_loaded(self, manager, pspec, data=None): logging.debug("Received AccountsManager signal is-loaded") - act_user = manager.get_user(tailsgreeter.config.LUSER) - if not act_user.is_loaded(): + user_account = manager.get_user(tailsgreeter.config.LUSER) + if not user_account.is_loaded(): raise RuntimeError("User manager for %s not loaded" % tailsgreeter.config.LUSER) - self._act_user = act_user + self.language._user_account = user_account + if self._usermanager_loaded_cb: self._usermanager_loaded_cb() - @staticmethod - def __fill_languages_dict(locale_codes): - """assemble dictionary of language codes to corresponding locales list - - example {en: [en_US, en_GB], ...}""" - languages_dict = {} - for locale_code in locale_codes: - lang_code = language_from_locale(locale_code) - if lang_code not in languages_dict: - languages_dict[lang_code] = [] - if locale_code not in languages_dict[lang_code]: - languages_dict[lang_code].append(locale_code) - return languages_dict - - @staticmethod - def __fill_formats_dict(locale_codes): - """assemble dictionary of country codes to corresponding locales list - - example {FR: [fr_FR, en_FR], ...}""" - formats_dict = {} - for locale_code in locale_codes: - country_code = country_from_locale(locale_code) - if country_code not in formats_dict: - formats_dict[country_code] = [] - if locale_code not in formats_dict[country_code]: - formats_dict[country_code].append(locale_code) - return formats_dict - - def apply_settings_to_upcoming_session(self): + def apply_to_upcoming_session(self): with open(tailsgreeter.config.locale_output_path, 'w') as f: os.chmod(tailsgreeter.config.locale_output_path, 0o600) - if hasattr(self, "text"): - f.write('TAILS_LOCALE_NAME=%s\n' % self.language.get_value()) - if hasattr(self, "formats"): - f.write('TAILS_FORMATS=%s\n' % self.formats.get_value()) - if hasattr(self, "layout"): - try: - layout, variant = self.keyboard.get_value().split('+') - except ValueError: - layout = self.keyboard.get_value() - variant = '' - # XXX: use default value from /etc/default/keyboard - f.write('TAILS_XKBMODEL=%s\n' % 'pc105') - f.write('TAILS_XKBLAYOUT=%s\n' % layout) - f.write('TAILS_XKBVARIANT=%s\n' % variant) + + f.write('TAILS_LOCALE_NAME=%s\n' % self.language.get_value()) + f.write('TAILS_FORMATS=%s\n' % self.formats.get_value()) + + try: + layout, variant = self.keyboard.get_value().split('+') + except ValueError: + layout = self.keyboard.get_value() + variant = '' + # XXX: use default value from /etc/default/keyboard + f.write('TAILS_XKBMODEL=%s\n' % 'pc105') + f.write('TAILS_XKBLAYOUT=%s\n' % layout) + f.write('TAILS_XKBVARIANT=%s\n' % variant) diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/macspoof.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/macspoof.py new file mode 100644 index 0000000..4132e39 --- /dev/null +++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/macspoof.py @@ -0,0 +1,19 @@ +import os +import logging +import pipes + +import tailsgreeter.config + + +class MacSpoofSetting(object): + """Setting controlling whether the MAC address is spoofed or not""" + + def __init__(self): + self.value = True + + def apply_to_upcoming_session(self): + setting_file = tailsgreeter.config.macspoof_setting + with open(setting_file, 'w') as f: + os.chmod(setting_file, 0o600) + f.write("TAILS_MACSPOOF_ENABLED=%s\n" % pipes.quote(str(self.value)).lower()) + logging.debug('macspoof setting written to %s', setting_file) diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/network.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/network.py new file mode 100644 index 0000000..ba257d3 --- /dev/null +++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/network.py @@ -0,0 +1,23 @@ +import os +import logging +import pipes + +import tailsgreeter.config + + +class NetworkSetting(object): + """Setting controlling how Tails connects to Tor""" + + NETCONF_DIRECT = "direct" + NETCONF_OBSTACLE = "obstacle" + NETCONF_DISABLED = "disabled" + + def __init__(self): + self.value = self.NETCONF_DIRECT + + def apply_to_upcoming_session(self): + setting_file = tailsgreeter.config.network_setting + with open(setting_file, 'w') as f: + os.chmod(setting_file, 0o600) + f.write("TAILS_NETCONF=%s\n" % pipes.quote(self.value)) + logging.debug('network setting written to %s', setting_file) diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/physicalsecurity.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/physicalsecurity.py deleted file mode 100644 index 75b744b..0000000 --- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/physicalsecurity.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright 2013-2016 Tails developers <tails@boum.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/> -# -"""Physical security settings - -""" -import os -import logging -import pipes - -import tailsgreeter.config - - -class PhysicalSecuritySettings(object): - """Model storing settings related to physical security - - """ - - NETCONF_DIRECT = "direct" - NETCONF_OBSTACLE = "obstacle" - NETCONF_DISABLED = "disabled" - - def __init__(self): - # Whether to run macspoof - self._netconf = self.NETCONF_DIRECT - self._macspoof = True - self.write_settings() - - def write_settings(self): - physical_security_settings_file = \ - tailsgreeter.config.physical_security_settings - with open(physical_security_settings_file, 'w') as f: - os.chmod(physical_security_settings_file, 0o600) - f.write('TAILS_NETCONF={0}\n'.format( - pipes.quote(self.netconf))) - f.write('TAILS_MACSPOOF_ENABLED={0}\n'.format( - pipes.quote(str(self.macspoof).lower()))) - logging.debug('physical security settings written to %s', - physical_security_settings_file) - - @property - def netconf(self): - return self._netconf - - @property - def macspoof(self): - return self._macspoof - - @netconf.setter - def netconf(self, new_state): - self._netconf = new_state - self.write_settings() - - @macspoof.setter - def macspoof(self, new_state): - self._macspoof = new_state - self.write_settings() diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/rootaccess.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/rootaccess.py deleted file mode 100644 index 057f8be..0000000 --- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/rootaccess.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright 2012-2016 Tails developers <tails@boum.org> -# Copyright 2011 Max <govnototalitarizm@gmail.com> -# Copyright 2011 Martin Owens -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/> -# -"""Root access handling - -""" -import os -import os.path -import logging -import pipes - -import tailsgreeter.config - - -class RootAccessSettings(object): - """Model storing settings related to root access - - """ - def __init__(self): - # Root password - self.password = None - # XXX: this should read the content of the setting file - - @property - def password(self): - return self._password - - @password.setter - def password(self, password): - self._password = password - if password: - with open(tailsgreeter.config.rootpassword_output_path, 'w') as f: - os.chmod(tailsgreeter.config.rootpassword_output_path, 0o600) - f.write('TAILS_USER_PASSWORD=%s\n' - % pipes.quote(self.password)) - logging.debug('password written to %s', - tailsgreeter.config.rootpassword_output_path) - else: - try: - os.unlink(tailsgreeter.config.rootpassword_output_path) - logging.debug('removed %s', - tailsgreeter.config.rootpassword_output_path) - except OSError: - if not os.path.exists( - tailsgreeter.config.rootpassword_output_path): - pass - else: - raise diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/translatable_window.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/translatable_window.py index 39c0988..a9577c3 100644 --- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/translatable_window.py +++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/translatable_window.py @@ -1,3 +1,23 @@ +#!/usr/bin/python3 +# +# Copyright 2012-2019 Tails developers <tails@boum.org> +# Copyright 2011 Max <govnototalitarizm@gmail.com> +# Copyright 2011 Martin Owens +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/> +# + import gettext import logging @@ -17,7 +37,7 @@ class TranslatableWindow(object): self.window_ = window self.translation = gettext.translation( TRANSLATION_DOMAIN, - tailsgreeter.config.locales_path, + tailsgreeter.config.system_locale_dir, fallback=True ) @@ -108,7 +128,7 @@ class TranslatableWindow(object): try: self.translation = gettext.translation( TRANSLATION_DOMAIN, - tailsgreeter.config.locales_path, [str(lang)]) + tailsgreeter.config.system_locale_dir, [str(lang)]) except IOError: self.translation = None diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/additional_settings.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/additional_settings.py index c7c471c..6a2883f 100644 --- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/additional_settings.py +++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/additional_settings.py @@ -13,8 +13,9 @@ gi.require_version('Gtk', '3.0') from gi.repository import Gdk, Gtk if TYPE_CHECKING: - from tailsgreeter.settings.rootaccess import RootAccessSettings - from tailsgreeter.settings.physicalsecurity import PhysicalSecuritySettings + from tailsgreeter.settings.admin import AdminSetting + from tailsgreeter.settings.macspoof import MacSpoofSetting + from tailsgreeter.settings.network import NetworkSetting ADDITIONAL_SETTINGS_UI_FILE = "additional_settings.ui" @@ -45,7 +46,7 @@ class AdditionalSetting(GreeterSetting): pass -class AdminSetting(AdditionalSetting): +class AdminSettingUI(AdditionalSetting): @property def id(self) -> str: return "admin" @@ -78,8 +79,8 @@ class AdminSetting(AdditionalSetting): self.dialog.button_add.set_sensitive(self.passwords_match()) self.password_entry.grab_focus() - def __init__(self, root_access_setting: "RootAccessSettings"): - self._root_access_setting = root_access_setting + def __init__(self, admin_setting: "AdminSetting"): + self._admin_setting = admin_setting self.password = None super().__init__() self.accel_key = Gdk.KEY_a @@ -116,7 +117,7 @@ class AdminSetting(AdditionalSetting): def apply(self): # This writes the password to a file from which it will be set # as the amnesia password when the greeter is closed. - self._root_access_setting.password = self.password + self._admin_setting.password = self.password def cb_entry_admin_changed(self, editable, user_data=None): self.update_check_icon() @@ -145,7 +146,7 @@ class AdminSetting(AdditionalSetting): self.popover.close(Gtk.ResponseType.YES) -class MACSpoofSetting(AdditionalSetting): +class MACSpoofSettingUI(AdditionalSetting): @property def id(self) -> str: return "macspoof" @@ -162,8 +163,8 @@ class MACSpoofSetting(AdditionalSetting): def value_for_display(self) -> str: return get_on_off_string(self.spoofing_enabled, default=True) - def __init__(self, physical_security_settings: "PhysicalSecuritySettings"): - self._physical_security_settings = physical_security_settings + def __init__(self, macspoof_setting: "MacSpoofSetting"): + self._macspoof_setting = macspoof_setting self.spoofing_enabled = True super().__init__() self.accel_key = Gdk.KEY_m @@ -177,7 +178,7 @@ class MACSpoofSetting(AdditionalSetting): self.listboxrow_macspoof_off = self.builder.get_object('listboxrow_macspoof_off') def apply(self): - self._physical_security_settings.macspoof = self.spoofing_enabled + self._macspoof_setting.value = self.spoofing_enabled def cb_listbox_macspoof_row_activated(self, listbox, row, user_data=None): self.spoofing_enabled = row == self.listboxrow_macspoof_on @@ -195,7 +196,7 @@ class MACSpoofSetting(AdditionalSetting): return False -class NetworkSetting(AdditionalSetting): +class NetworkSettingUI(AdditionalSetting): @property def id(self) -> str: return "network" @@ -210,16 +211,16 @@ class NetworkSetting(AdditionalSetting): @property def value_for_display(self) -> str: - if self.netconf == self._physical_security_settings.NETCONF_DIRECT: + if self.value == self._network_setting.NETCONF_DIRECT: return _("Direct (default)") - if self.netconf == self._physical_security_settings.NETCONF_OBSTACLE: + if self.value == self._network_setting.NETCONF_OBSTACLE: return _("Bridge & Proxy") - if self.netconf == self._physical_security_settings.NETCONF_DISABLED: + if self.value == self._network_setting.NETCONF_DISABLED: return _("Offline") - def __init__(self, physical_security_settings: "PhysicalSecuritySettings"): - self._physical_security_settings = physical_security_settings - self.netconf = self._physical_security_settings.NETCONF_DIRECT + def __init__(self, network_setting: "NetworkSetting"): + self._network_setting = network_setting + self.value = self._network_setting.NETCONF_DIRECT super().__init__() self.accel_key = Gdk.KEY_n self.icon_network_clear_chosen = self.builder.get_object('image_network_clear') @@ -233,8 +234,8 @@ class NetworkSetting(AdditionalSetting): self.listboxrow_network_off = self.builder.get_object('listboxrow_network_off') def apply(self): - self._physical_security_settings.netconf = self.netconf - is_bridge = self.netconf == self._physical_security_settings.NETCONF_OBSTACLE + self._network_setting.value = self.value + is_bridge = self.value == self._network_setting.NETCONF_OBSTACLE self.main_window.set_bridge_infobar_visibility(is_bridge) def cb_listbox_network_button_press(self, widget, event, user_data=None): @@ -249,13 +250,13 @@ class NetworkSetting(AdditionalSetting): self.icon_network_off_chosen.set_visible(False) if row == self.listboxrow_network_clear: - self.netconf = self._physical_security_settings.NETCONF_DIRECT + self.value = self._network_setting.NETCONF_DIRECT self.icon_network_clear_chosen.set_visible(True) elif row == self.listboxrow_network_specific: - self.netconf = self._physical_security_settings.NETCONF_OBSTACLE + self.value = self._network_setting.NETCONF_OBSTACLE self.icon_network_specific_chosen.set_visible(True) elif row == self.listboxrow_network_off: - self.netconf = self._physical_security_settings.NETCONF_DISABLED + self.value = self._network_setting.NETCONF_DISABLED self.icon_network_off_chosen.set_visible(True) if self.has_popover() and self.popover.is_open(): diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/main_window.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/main_window.py index 96fefe1..1bc3338 100644 --- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/main_window.py +++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/main_window.py @@ -30,7 +30,7 @@ from tailsgreeter.ui import _ from tailsgreeter.ui.add_settings_dialog import AddSettingsDialog from tailsgreeter.ui.additional_settings import AdditionalSetting from tailsgreeter.ui.help_window import GreeterHelpWindow -from tailsgreeter.ui.region_settings import RegionSetting, TextSetting +from tailsgreeter.ui.region_settings import LocalizationSettingUI, LanguageSettingUI from tailsgreeter import TRANSLATION_DOMAIN from tailsgreeter.ui.persistent_storage import PersistentStorage @@ -50,7 +50,7 @@ ICON_DIR = 'icons/' PREFERRED_WIDTH = 620 PREFERRED_HEIGHT = 470 -locale.bindtextdomain(TRANSLATION_DOMAIN, tailsgreeter.config.locales_path) +locale.bindtextdomain(TRANSLATION_DOMAIN, tailsgreeter.config.system_locale_dir) class GreeterMainWindow(Gtk.Window, TranslatableWindow): @@ -366,7 +366,7 @@ class GreeterMainWindow(Gtk.Window, TranslatableWindow): setting.popover.open(self.on_region_setting_popover_closed, setting) return False - def on_region_setting_popover_closed(self, popover: Popover, setting: RegionSetting): + def on_region_setting_popover_closed(self, popover: Popover, setting: LocalizationSettingUI): # Unselect the listbox row self.listbox_region.unselect_all() @@ -378,7 +378,7 @@ class GreeterMainWindow(Gtk.Window, TranslatableWindow): # If the language is changed, the values of the other region settings # are changed as well, so we have to update the value labels for all # region settings in that case. - if isinstance(setting, TextSetting): + if isinstance(setting, LanguageSettingUI): for s in self.settings.region_settings: s.update_value_label() else: diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/region_settings.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/region_settings.py index 16fd73b..ce28eef 100644 --- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/region_settings.py +++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/region_settings.py @@ -18,7 +18,7 @@ if TYPE_CHECKING: REGION_SETTINGS_UI_FILE = "region_settings.ui" -class RegionSetting(GreeterSetting): +class LocalizationSettingUI(GreeterSetting): def __init__(self, localization_setting: "LocalizationSetting"): self._localization_setting = localization_setting super().__init__() @@ -53,7 +53,7 @@ class RegionSetting(GreeterSetting): self.treeview.set_model(self.treestore_filtered) def apply(self): - self._localization_setting.set_value(self.selected_code) + self._localization_setting.set_value(self.selected_code, chosen_by_user=True) def cb_searchentry_activate(self, searchentry, user_data=None): """Selects the topmost item in the treeview when pressing Enter""" @@ -125,10 +125,10 @@ class RegionSetting(GreeterSetting): return False -class TextSetting(RegionSetting): +class LanguageSettingUI(LocalizationSettingUI): @property def id(self) -> str: - return "text" + return "language" @property def title(self) -> str: @@ -143,7 +143,7 @@ class TextSetting(RegionSetting): return self._localization_setting.get_name() -class KeyboardSetting(RegionSetting): +class KeyboardSettingUI(LocalizationSettingUI): @property def id(self) -> str: return "keyboard" @@ -161,7 +161,7 @@ class KeyboardSetting(RegionSetting): return self._localization_setting.get_name() -class FormatsSetting(RegionSetting): +class FormatsSettingUI(LocalizationSettingUI): @property def id(self) -> str: return "formats" diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/settings_collection.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/settings_collection.py index bc2619a..03bb463 100644 --- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/settings_collection.py +++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/settings_collection.py @@ -1,7 +1,7 @@ import gi from tailsgreeter.ui.setting import GreeterSetting -from tailsgreeter.ui.region_settings import RegionSetting +from tailsgreeter.ui.region_settings import LocalizationSettingUI from tailsgreeter.ui.additional_settings import AdditionalSetting gi.require_version('Gtk', '3.0') @@ -16,7 +16,7 @@ class GreeterSettingsCollection(object): return self.region_settings = GreeterSettingsCollection( - *(s for s in self.settings.values() if isinstance(s, RegionSetting)), + *(s for s in self.settings.values() if isinstance(s, LocalizationSettingUI)), no_subclassification = True, ) self.additional_settings = GreeterSettingsCollection( diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailslib/additionalsoftware.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailslib/additionalsoftware.py index ab6eaf1..89e4ca6 100644 --- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailslib/additionalsoftware.py +++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailslib/additionalsoftware.py @@ -36,7 +36,7 @@ def _write_config(packages, search_new_persistence=False): with atomicwrites.atomic_write(packages_list_path, overwrite=True) as f: for package in sorted(packages): - f.write(package + '\n') + f.apply_to_upcoming_session(package + '\n') os.chmod(packages_list_path, 0o0644) finally: os.seteuid(0) diff --git a/config/chroot_local-includes/usr/local/lib/onion-grater b/config/chroot_local-includes/usr/local/lib/onion-grater index b8369b9..5910302 100755 --- a/config/chroot_local-includes/usr/local/lib/onion-grater +++ b/config/chroot_local-includes/usr/local/lib/onion-grater @@ -245,9 +245,9 @@ class FilteredControlPortProxySession: if line.isspace(): return self.debug_log_send(line) - self.wfile.write(bytes(line, 'ascii')) + self.wfile.apply_to_upcoming_session(bytes(line, 'ascii')) if not raw: - self.wfile.write(bytes("\r\n", 'ascii')) + self.wfile.apply_to_upcoming_session(bytes("\r\n", 'ascii')) self.wfile.flush() def get_rule(self, cmd, arg_str): diff --git a/config/chroot_local-includes/usr/local/lib/tails-additional-software-notify b/config/chroot_local-includes/usr/local/lib/tails-additional-software-notify index e31e020..6893b63 100755 --- a/config/chroot_local-includes/usr/local/lib/tails-additional-software-notify +++ b/config/chroot_local-includes/usr/local/lib/tails-additional-software-notify @@ -45,7 +45,7 @@ class ASPNotifier(object): self.cb_notification_clicked, None) self.notification.connect("closed", self.cb_notification_closed) self.notification.show() - sys.stdout.write("id=%i" % self.notification.props.id) + sys.stdout.apply_to_upcoming_session("id=%i" % self.notification.props.id) if not (accept_label or deny_label or documentation_target): sys.exit(2) @@ -70,10 +70,10 @@ def print_help(): """The subcommand which displays help """ program_name = os.path.basename(sys.argv[0]) - sys.stderr.write( + sys.stderr.apply_to_upcoming_session( "Usage: %s <summary> <body> [<accept_label> [<deny_label> " "[documentation_target [<urgent>]]]]\n" % program_name) - sys.stderr.write( + sys.stderr.apply_to_upcoming_session( "Shows a notification with <summary>, <body> and optional " "buttons.\n" "\n" diff --git a/config/chroot_local-includes/usr/local/lib/tails-autotest-remote-shell b/config/chroot_local-includes/usr/local/lib/tails-autotest-remote-shell index 96ae7db..ee2d1a5 100755 --- a/config/chroot_local-includes/usr/local/lib/tails-autotest-remote-shell +++ b/config/chroot_local-includes/usr/local/lib/tails-autotest-remote-shell @@ -172,7 +172,7 @@ def main(): else: raise ValueError("unknown command type") response = (ret + "\n").encode('utf-8') - port.write(response) + port.apply_to_upcoming_session(response) port.flush() except Exception as e: print("Error caught while processing line:", file=sys.stderr) @@ -182,7 +182,7 @@ def main(): print("-----", file=sys.stderr) sys.stderr.flush() exc_str = '{}: {}'.format(type(e).__name__, str(e)) - port.write(json.dumps([id, 'error', exc_str]).encode('utf-8') + b"\n") + port.apply_to_upcoming_session(json.dumps([id, 'error', exc_str]).encode('utf-8') + b"\n") port.flush() continue diff --git a/config/chroot_local-includes/usr/local/lib/tails-shell-library/tails-greeter.sh b/config/chroot_local-includes/usr/local/lib/tails-shell-library/tails-greeter.sh index dc6cc26..ca0c4db 100644 --- a/config/chroot_local-includes/usr/local/lib/tails-shell-library/tails-greeter.sh +++ b/config/chroot_local-includes/usr/local/lib/tails-shell-library/tails-greeter.sh @@ -1,7 +1,8 @@ #!/bin/sh -PERSISTENCE_STATE='/var/lib/live/config/tails.persistence' -PHYSICAL_SECURITY_SETTINGS='/var/lib/live/config/tails.physical_security' +PERSISTENCE_SETTING='/var/lib/live/config/tails.persistence' +MACSPOOF_SETTING='/var/lib/live/config/tails.macspoof' +NETWORK_SETTING='/var/lib/live/config/tails.network' _get_tg_setting() { if [ -r "${1}" ]; then @@ -11,7 +12,7 @@ _get_tg_setting() { } persistence_is_enabled() { - [ "$(_get_tg_setting "${PERSISTENCE_STATE}" TAILS_PERSISTENCE_ENABLED)" = true ] + [ "$(_get_tg_setting "${PERSISTENCE_SETTING}" TAILS_PERSISTENCE_ENABLED)" = true ] } persistence_is_enabled_for() { @@ -20,15 +21,15 @@ persistence_is_enabled_for() { persistence_is_enabled_read_write() { persistence_is_enabled && \ - [ "$(_get_tg_setting "${PERSISTENCE_STATE}" TAILS_PERSISTENCE_READONLY)" != true ] + [ "$(_get_tg_setting "${PERSISTENCE_SETTING}" TAILS_PERSISTENCE_READONLY)" != true ] } mac_spoof_is_enabled() { # Only return false when explicitly told so to increase failure # safety. - [ "$(_get_tg_setting "${PHYSICAL_SECURITY_SETTINGS}" TAILS_MACSPOOF_ENABLED)" != false ] + [ "$(_get_tg_setting "${MACSPOOF_SETTING}" TAILS_MACSPOOF_ENABLED)" != false ] } tails_netconf() { - _get_tg_setting "${PHYSICAL_SECURITY_SETTINGS}" TAILS_NETCONF + _get_tg_setting "${NETWORK_SETTING}" TAILS_NETCONF } diff --git a/config/chroot_local-includes/usr/local/lib/tails-unblock-network b/config/chroot_local-includes/usr/local/lib/tails-unblock-network index 5f8e1b5..3e33d08 100755 --- a/config/chroot_local-includes/usr/local/lib/tails-unblock-network +++ b/config/chroot_local-includes/usr/local/lib/tails-unblock-network @@ -4,7 +4,7 @@ set -e set -u set -x -CONFIG_FILE=/var/lib/gdm3/tails.physical_security +CONFIG_FILE=/var/lib/gdm3/tails.network NET_MODULES_BLACKLIST=/etc/modprobe.d/all-net-blacklist.conf # Import the TAILS_NETCONF variable diff --git a/config/chroot_local-includes/usr/local/sbin/tails-additional-software b/config/chroot_local-includes/usr/local/sbin/tails-additional-software index a2fbc9b..9d8783a 100755 --- a/config/chroot_local-includes/usr/local/sbin/tails-additional-software +++ b/config/chroot_local-includes/usr/local/sbin/tails-additional-software @@ -627,8 +627,8 @@ def upgrade_additional_packages(): def print_help(): """Subcommand which displays help.""" - sys.stderr.write("Usage: %s <subcommand>\n" % program_name) - sys.stderr.write("""Subcommands: + sys.stderr.apply_to_upcoming_session("Usage: %s <subcommand>\n" % program_name) + sys.stderr.apply_to_upcoming_session("""Subcommands: install: install additional software upgrade: upgrade additional software\n""") diff --git a/config/chroot_local-includes/usr/local/sbin/tails-debugging-info b/config/chroot_local-includes/usr/local/sbin/tails-debugging-info index a0babde..9f5af7e 100755 --- a/config/chroot_local-includes/usr/local/sbin/tails-debugging-info +++ b/config/chroot_local-includes/usr/local/sbin/tails-debugging-info @@ -92,7 +92,7 @@ def debug_file(user, filename): >>> import tempfile, getpass >>> with tempfile.NamedTemporaryFile('w') as f: - ... _ = f.write("foo\\nbar") + ... _ = f.apply_to_upcoming_session("foo\\nbar") ... _ = f.seek(0) ... debug_file(getpass.getuser(), f.name) {...'content': ['foo', 'bar']...} @@ -122,7 +122,7 @@ def debug_directory(user, dir_name): >>> tmpdir = '/tmp/mytempdir' >>> os.makedirs(tmpdir) >>> with open(os.path.join(tmpdir, 'foo'), 'w') as f: - ... _ = f.write("foobar\\nbar") + ... _ = f.apply_to_upcoming_session("foobar\\nbar") ... _ = f.seek(0) ... result = debug_directory(getpass.getuser(), tmpdir) >>> os.remove(os.path.join(tmpdir, 'foo')) diff --git a/config/chroot_local-includes/usr/share/tails/greeter/language_codes b/config/chroot_local-includes/usr/share/tails/greeter/locales index 0b161b7..0b161b7 100644 --- a/config/chroot_local-includes/usr/share/tails/greeter/language_codes +++ b/config/chroot_local-includes/usr/share/tails/greeter/locales diff --git a/config/chroot_local-includes/usr/share/tails/greeter/tails-greeter.py b/config/chroot_local-includes/usr/share/tails/greeter/tails-greeter.py index 29cf327..a953808 100755 --- a/config/chroot_local-includes/usr/share/tails/greeter/tails-greeter.py +++ b/config/chroot_local-includes/usr/share/tails/greeter/tails-greeter.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 # -# Copyright 2012-2016 Tails developers <tails@boum.org> +# Copyright 2012-2019 Tails developers <tails@boum.org> # Copyright 2011 Max <govnototalitarizm@gmail.com> # Copyright 2011 Martin Owens # @@ -22,13 +22,21 @@ GDM greeter for Tails project using gtk """ import gettext +import gi import locale -import logging import logging.config import sys -import traceback # NOQA: F401 +import traceback +from tailsgreeter.greeter import GreeterApplication +import tailsgreeter.config +import tailsgreeter.gdmclient +gi.require_version('GLib', '2.0') +gi.require_version("Gtk", "3.0") +from gi.repository import GLib, Gtk + +# Logging logging.config.fileConfig('tails-logging.conf') # Set loglevel if debug is found in kernel command line with open('/proc/cmdline') as cmdline_fd: @@ -38,6 +46,7 @@ if "debug" in cmdline.split() or \ logging.getLogger().setLevel(logging.DEBUG) +# Hook to print exceptions with stack trace def log_exc(etype, value, tb): for line in traceback.format_exception(etype, value, tb): print(line, file=sys.stderr) @@ -46,132 +55,13 @@ def log_exc(etype, value, tb): sys.excepthook = log_exc -import gi # NOQA: F401 -gi.require_version('GLib', '2.0') -from gi.repository import GLib # NOQA: F401 -from gi.repository import Gio # NOQA: F401 -gi.require_version("Gtk", "3.0") -from gi.repository import Gtk # NOQA: F401 - -import tailsgreeter # NOQA: F401 -import tailsgreeter.config # NOQA: F401 -import tailsgreeter.gdmclient # NOQA: F401 - -from tailsgreeter.settings import localization -from tailsgreeter.settings.localization_settings import LocalisationSettings -from tailsgreeter.settings.persistence import PersistenceSettings -from tailsgreeter.settings.physicalsecurity import PhysicalSecuritySettings -from tailsgreeter.settings.rootaccess import RootAccessSettings -from tailsgreeter.translatable_window import TranslatableWindow -from tailsgreeter.ui.main_window import GreeterMainWindow # NOQA: F401 -from tailsgreeter.ui.settings_collection import GreeterSettingsCollection -from tailsgreeter.ui.region_settings import TextSetting, KeyboardSetting, FormatsSetting -from tailsgreeter.ui.additional_settings import AdminSetting, MACSpoofSetting, NetworkSetting - -gettext.install(tailsgreeter.__appname__, tailsgreeter.config.locales_path) -locale.bindtextdomain(tailsgreeter.__appname__, - tailsgreeter.config.locales_path) +gettext.install(tailsgreeter.__appname__, tailsgreeter.config.system_locale_dir) +locale.bindtextdomain(tailsgreeter.__appname__, tailsgreeter.config.system_locale_dir) _ = gettext.gettext - -class GreeterApplication(): - """Tails greeter main controller - - This class is the greeter dbus service""" - - def __init__(self): - self.language = 'en_US.UTF-8' - self.session = None - self.forced = False - self.postponed = False - self.postponed_text = None - self.ready = False - self.translated = False - - self._sessionmanager = Gio.DBusProxy.new_for_bus_sync( - Gio.BusType.SESSION, - Gio.DBusProxyFlags.NONE, - None, - "org.gnome.SessionManager", - "/org/gnome/SessionManager", - "org.gnome.SessionManager") - - # Load models - self.gdmclient = tailsgreeter.gdmclient.GdmClient( - session_opened_cb=self.close_app - ) - - persistence = PersistenceSettings() - self.localisationsettings = LocalisationSettings( - usermanager_loaded_cb=self.usermanager_loaded, - locale_selected_cb=self.locale_selected - ) - rootaccess = RootAccessSettings() - physical_security = PhysicalSecuritySettings() - - # Initialize the settings - settings = GreeterSettingsCollection( - TextSetting(self.localisationsettings.language), - KeyboardSetting(self.localisationsettings.keyboard), - FormatsSetting(self.localisationsettings.formats), - AdminSetting(rootaccess), - MACSpoofSetting(physical_security), - NetworkSetting(physical_security), - ) - - # Initialize main window - self.mainwindow = GreeterMainWindow(self, persistence, settings) - - # Inhibit the session being marked as idle - self.inhibit_idle() - - def translate_to(self, lang): - """Translate all windows to target language""" - TranslatableWindow.translate_all(lang) - - def login(self): - """Login GDM to the server""" - logging.debug("login called") - self.mainwindow.hide() - self.gdmclient.do_login() - - def usermanager_loaded(self): - """UserManager is ready""" - logging.debug("Entering usermanager_loaded") - self.ready = True - self.localisationsettings.language.set_value('en_US') - logging.info("tails-greeter is ready.") - self.mainwindow.show() - - def locale_selected(self, locale_code): - """Translate to the given locale""" - self.translate_to(locale_code) - self.mainwindow.current_language = localization.language_from_locale(locale_code) - - def close_app(self): - """We're done, quit gtk app""" - logging.info("Finished.") - Gtk.main_quit() - - def shutdown(self): - """Shuts down the computer using GNOME Session Manager""" - logging.info("Shutdown") - self._sessionmanager.Shutdown() - - def inhibit_idle(self): - cookie = self._sessionmanager.Inhibit( - "(susu)", - "org.boum.tails.Greeter", - 0, - "Greeter session shouldn't idle", - 8) # Inhibit the session being marked as idle - logging.debug("inhibitor cookie=%i", cookie) - - if __name__ == "__main__": GLib.set_prgname(tailsgreeter.APPLICATION_TITLE) - GLib.set_application_name( - _(tailsgreeter.APPLICATION_TITLE)) # NOQA: F821 + GLib.set_application_name(_(tailsgreeter.APPLICATION_TITLE)) Gtk.init(sys.argv) Gtk.Window.set_default_icon_name(tailsgreeter.APPLICATION_ICON_NAME) |