Commit 6ee4ba04 authored by segfault's avatar segfault
Browse files

Greeter: Support loading settings from persistence (refs: #17136)

parent 267ed10e
...@@ -78,7 +78,7 @@ class GreeterApplication(object): ...@@ -78,7 +78,7 @@ class GreeterApplication(object):
self.macspoof_setting = MacSpoofSetting() self.macspoof_setting = MacSpoofSetting()
# Initialize the settings # Initialize the settings
settings = GreeterSettingsCollection( self.settings = GreeterSettingsCollection(
LanguageSettingUI(self.localisationsettings.language), LanguageSettingUI(self.localisationsettings.language),
KeyboardSettingUI(self.localisationsettings.keyboard), KeyboardSettingUI(self.localisationsettings.keyboard),
FormatsSettingUI(self.localisationsettings.formats), FormatsSettingUI(self.localisationsettings.formats),
...@@ -88,7 +88,7 @@ class GreeterApplication(object): ...@@ -88,7 +88,7 @@ class GreeterApplication(object):
) )
# Initialize main window # Initialize main window
self.mainwindow = GreeterMainWindow(self, persistence, settings) self.mainwindow = GreeterMainWindow(self, persistence, self.settings)
# Inhibit the session being marked as idle # Inhibit the session being marked as idle
self.inhibit_idle() self.inhibit_idle()
...@@ -97,6 +97,29 @@ class GreeterApplication(object): ...@@ -97,6 +97,29 @@ class GreeterApplication(object):
"""Translate all windows to target language""" """Translate all windows to target language"""
TranslatableWindow.translate_all(lang) TranslatableWindow.translate_all(lang)
def load_settings(self):
if self.localisationsettings.language.load():
self.settings["language"].selected_code = self.localisationsettings.language.value
self.settings["language"].apply()
if self.localisationsettings.formats.load():
self.settings["formats"].selected_code = self.localisationsettings.formats.value
self.settings["formats"].apply()
if self.localisationsettings.keyboard.load():
self.settings["keyboard"].selected_code = self.localisationsettings.keyboard.value
self.settings["keyboard"].apply()
if self.admin_setting.load():
self.settings["admin"].password = self.admin_setting.password
self.mainwindow.add_setting("admin")
if self.network_setting.load():
if self.network_setting.value != self.settings["network"].value:
self.settings["network"].value = self.network_setting.value
self.mainwindow.add_setting("network")
if self.macspoof_setting.load():
if self.settings["macspoof"].spoofing_enabled != self.macspoof_setting.value:
self.settings["macspoof"].spoofing_enabled = self.macspoof_setting.value
self.mainwindow.add_setting("macspoof")
def login(self): def login(self):
"""Login GDM to the server""" """Login GDM to the server"""
logging.debug("login called") logging.debug("login called")
...@@ -120,9 +143,9 @@ class GreeterApplication(object): ...@@ -120,9 +143,9 @@ class GreeterApplication(object):
def on_language_changed(self, locale_code: str): def on_language_changed(self, locale_code: str):
"""Translate to the given locale""" """Translate to the given locale"""
self.localisationsettings.formats.on_language_changed(locale_code) # XXX: notify self.localisationsettings.formats.on_language_changed(locale_code) # XXX: notify
self.mainwindow.settings["formats"].update_value_label() self.settings["formats"].update_value_label()
self.localisationsettings.keyboard.on_language_changed(locale_code) # XXX: notify self.localisationsettings.keyboard.on_language_changed(locale_code) # XXX: notify
self.mainwindow.settings["keyboard"].update_value_label() self.settings["keyboard"].update_value_label()
self.translate_to(locale_code) self.translate_to(locale_code)
self.mainwindow.current_language = localization.language_from_locale(locale_code) self.mainwindow.current_language = localization.language_from_locale(locale_code)
......
...@@ -5,6 +5,7 @@ import logging ...@@ -5,6 +5,7 @@ import logging
import pipes import pipes
import tailsgreeter.config import tailsgreeter.config
from tailsgreeter.settings.utils import read_settings, write_settings
class AdminSetting(object): class AdminSetting(object):
...@@ -12,23 +13,35 @@ class AdminSetting(object): ...@@ -12,23 +13,35 @@ class AdminSetting(object):
def __init__(self): def __init__(self):
self.password = None self.password = None
self.settings_file = tailsgreeter.config.admin_password_path
def apply_to_upcoming_session(self): def apply_to_upcoming_session(self):
setting_file = tailsgreeter.config.admin_password_path
if self.password: if self.password:
with open(setting_file, 'w') as f: write_settings(self.settings_file, {
os.chmod(setting_file, 0o600) 'TAILS_USER_PASSWORD': pipes.quote(self.password),
f.write('TAILS_USER_PASSWORD=%s\n' % pipes.quote(self.password)) })
logging.debug('password written to %s', setting_file) logging.debug('password written to %s', self.settings_file)
return return
# Try to remove the password file # Try to remove the password file
try: try:
os.unlink(setting_file) os.unlink(self.settings_file)
logging.debug('removed %s', setting_file) logging.debug('removed %s', self.settings_file)
except OSError: except OSError:
# It's bad if the file exists and couldn't be removed, so we # 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) # we raise the exception in that case (which prevents the login)
if os.path.exists(setting_file): if os.path.exists(self.settings_file):
raise raise
def load(self) -> bool:
try:
settings = read_settings(self.settings_file)
except FileNotFoundError:
logging.debug("No persistent admin settings file found (path: %s)", self.settings_file)
return False
password = settings.get('TAILS_USER_PASSWORD')
if password:
self.password = password
logging.debug("Loaded admin password setting")
return True
...@@ -4,7 +4,7 @@ import os ...@@ -4,7 +4,7 @@ import os
import tailsgreeter.config import tailsgreeter.config
from tailsgreeter.settings.localization import LocalizationSetting, language_from_locale, country_from_locale from tailsgreeter.settings.localization import LocalizationSetting, language_from_locale, country_from_locale
from tailsgreeter.settings.utils import write_settings from tailsgreeter.settings.utils import read_settings, write_settings
gi.require_version('GObject', '2.0') gi.require_version('GObject', '2.0')
gi.require_version('GnomeDesktop', '3.0') gi.require_version('GnomeDesktop', '3.0')
...@@ -17,14 +17,29 @@ class FormatsSetting(LocalizationSetting): ...@@ -17,14 +17,29 @@ class FormatsSetting(LocalizationSetting):
super().__init__() super().__init__()
self.value = 'en_US' self.value = 'en_US'
self.locales_per_country = self._make_locales_per_country_dict(language_codes) self.locales_per_country = self._make_locales_per_country_dict(language_codes)
self.settings_file = tailsgreeter.config.formats_setting_path
def apply_to_upcoming_session(self): def apply_to_upcoming_session(self):
settings_file = tailsgreeter.config.formats_setting_path write_settings(self.settings_file, {
write_settings(settings_file, {
'TAILS_FORMATS': self.get_value(), 'TAILS_FORMATS': self.get_value(),
'IS_DEFAULT': (not self.value_changed_by_user), 'IS_DEFAULT': str(not self.value_changed_by_user).lower(),
}) })
def load(self) -> bool:
try:
settings = read_settings(self.settings_file)
except FileNotFoundError:
logging.debug("No persistent formats settings file found (path: %s)", self.settings_file)
return False
formats = settings.get('TAILS_FORMATS')
if not formats:
return False
is_default = settings.get('IS_DEFAULT') == 'true'
self.set_value(formats, chosen_by_user=not is_default)
logging.debug("Loaded formats setting '%s' (is default: %s)", formats, is_default)
return True
def get_tree(self) -> Gtk.TreeStore: def get_tree(self) -> Gtk.TreeStore:
treestore = Gtk.TreeStore(GObject.TYPE_STRING, # id treestore = Gtk.TreeStore(GObject.TYPE_STRING, # id
GObject.TYPE_STRING) # name GObject.TYPE_STRING) # name
......
...@@ -4,7 +4,7 @@ import logging ...@@ -4,7 +4,7 @@ import logging
import tailsgreeter.config import tailsgreeter.config
from tailsgreeter.settings.localization import LocalizationSetting, ln_iso639_tri, \ from tailsgreeter.settings.localization import LocalizationSetting, ln_iso639_tri, \
ln_iso639_2_T_to_B, language_from_locale, country_from_locale ln_iso639_2_T_to_B, language_from_locale, country_from_locale
from tailsgreeter.settings.utils import write_settings from tailsgreeter.settings.utils import read_settings, write_settings
gi.require_version('Gio', '2.0') gi.require_version('Gio', '2.0')
gi.require_version('GLib', '2.0') gi.require_version('GLib', '2.0')
...@@ -20,6 +20,7 @@ class KeyboardSetting(LocalizationSetting): ...@@ -20,6 +20,7 @@ class KeyboardSetting(LocalizationSetting):
super().__init__() super().__init__()
self.xkbinfo = GnomeDesktop.XkbInfo() self.xkbinfo = GnomeDesktop.XkbInfo()
self.value = 'us' self.value = 'us'
self.settings_file = tailsgreeter.config.keyboard_setting_path
def apply_to_upcoming_session(self): def apply_to_upcoming_session(self):
try: try:
...@@ -28,15 +29,35 @@ class KeyboardSetting(LocalizationSetting): ...@@ -28,15 +29,35 @@ class KeyboardSetting(LocalizationSetting):
layout = self.get_value() layout = self.get_value()
variant = '' variant = ''
settings_file = tailsgreeter.config.keyboard_setting_path write_settings(self.settings_file, {
write_settings(settings_file, {
# The default value from /etc/default/keyboard # The default value from /etc/default/keyboard
'TAILS_XKBMODEL': 'pc105', 'TAILS_XKBMODEL': 'pc105',
'TAILS_XKBLAYOUT': layout, 'TAILS_XKBLAYOUT': layout,
'TAILS_XKBVARIANT': variant, 'TAILS_XKBVARIANT': variant,
'IS_DEFAULT': (not self.value_changed_by_user), 'IS_DEFAULT': str(not self.value_changed_by_user).lower(),
}) })
def load(self) -> bool:
try:
settings = read_settings(self.settings_file)
except FileNotFoundError:
logging.debug("No persistent keyboard settings file found (path: %s)", self.settings_file)
return False
keyboard_layout = settings.get('TAILS_XKBLAYOUT')
if not keyboard_layout:
return False
keyboard_variant = settings.get('TAILS_XKBVARIANT')
if keyboard_variant:
keyboard_layout += "+" + keyboard_variant
is_default = settings.get('IS_DEFAULT') == 'true'
self.set_value(keyboard_layout, chosen_by_user=not is_default)
logging.debug("Loaded keyboard setting '%s' (is default: %s)", keyboard_layout, is_default)
return True
def get_tree(self, layout_codes=None) -> Gtk.TreeStore: def get_tree(self, layout_codes=None) -> Gtk.TreeStore:
if not layout_codes: if not layout_codes:
layout_codes = self.get_all() layout_codes = self.get_all()
......
...@@ -26,7 +26,7 @@ from typing import Callable ...@@ -26,7 +26,7 @@ from typing import Callable
import tailsgreeter.config import tailsgreeter.config
from tailsgreeter.settings.localization import LocalizationSetting, \ from tailsgreeter.settings.localization import LocalizationSetting, \
language_from_locale, country_from_locale language_from_locale, country_from_locale
from tailsgreeter.settings.utils import write_settings from tailsgreeter.settings.utils import read_settings, write_settings
gi.require_version('GLib', '2.0') gi.require_version('GLib', '2.0')
gi.require_version('GObject', '2.0') gi.require_version('GObject', '2.0')
...@@ -43,18 +43,33 @@ class LanguageSetting(LocalizationSetting): ...@@ -43,18 +43,33 @@ class LanguageSetting(LocalizationSetting):
self.locales = locales self.locales = locales
self.language_changed_cb = language_changed_cb self.language_changed_cb = language_changed_cb
self._user_account = None self._user_account = None
self.settings_file = tailsgreeter.config.language_setting_path
self.lang_codes = self._languages_from_locales(locales) self.lang_codes = self._languages_from_locales(locales)
self.locales_per_language = self._make_language_to_locale_dict(locales) self.locales_per_language = self._make_language_to_locale_dict(locales)
self.language_names_per_language = self._make_language_to_language_name_dict(self.lang_codes) self.language_names_per_language = self._make_language_to_language_name_dict(self.lang_codes)
def apply_to_upcoming_session(self): def apply_to_upcoming_session(self):
settings_file = tailsgreeter.config.language_setting_path write_settings(self.settings_file, {
write_settings(settings_file, {
'TAILS_LOCALE_NAME': self.get_value(), 'TAILS_LOCALE_NAME': self.get_value(),
'IS_DEFAULT': (not self.value_changed_by_user), 'IS_DEFAULT': str(not self.value_changed_by_user).lower(),
}) })
def load(self) -> bool:
try:
settings = read_settings(self.settings_file)
except FileNotFoundError:
logging.debug("No persistent language settings file found (path: %s)", self.settings_file)
return False
language = settings.get('TAILS_LOCALE_NAME')
if not language:
return False
is_default = settings.get('IS_DEFAULT') == 'true'
self.set_value(language, chosen_by_user=not is_default)
logging.debug("Loaded language setting '%s' (is default: %s)", language, is_default)
return True
def get_tree(self) -> Gtk.TreeStore: def get_tree(self) -> Gtk.TreeStore:
treestore = Gtk.TreeStore(GObject.TYPE_STRING, # id treestore = Gtk.TreeStore(GObject.TYPE_STRING, # id
GObject.TYPE_STRING) # name GObject.TYPE_STRING) # name
......
import os
import logging import logging
import pipes import pipes
import tailsgreeter.config import tailsgreeter.config
from tailsgreeter.settings.utils import read_settings, write_settings
class MacSpoofSetting(object): class MacSpoofSetting(object):
...@@ -10,10 +10,23 @@ class MacSpoofSetting(object): ...@@ -10,10 +10,23 @@ class MacSpoofSetting(object):
def __init__(self): def __init__(self):
self.value = True self.value = True
self.settings_file = tailsgreeter.config.macspoof_setting_path
def apply_to_upcoming_session(self): def apply_to_upcoming_session(self):
setting_file = tailsgreeter.config.macspoof_setting_path write_settings(self.settings_file, {
with open(setting_file, 'w') as f: 'TAILS_MACSPOOF_ENABLED': pipes.quote(str(self.value)).lower(),
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', self.settings_file)
logging.debug('macspoof setting written to %s', setting_file)
def load(self) -> bool:
try:
settings = read_settings(self.settings_file)
except FileNotFoundError:
logging.debug("No persistent macspoof settings file found (path: %s)", self.settings_file)
return False
value = settings.get('TAILS_MACSPOOF_ENABLED') == "true"
if value:
self.value = value
logging.debug("Loaded macspoof setting '%s'", value)
return True
import os
import logging import logging
import pipes import pipes
import tailsgreeter.config import tailsgreeter.config
from tailsgreeter.settings.utils import read_settings, write_settings
class NetworkSetting(object): class NetworkSetting(object):
...@@ -14,10 +14,23 @@ class NetworkSetting(object): ...@@ -14,10 +14,23 @@ class NetworkSetting(object):
def __init__(self): def __init__(self):
self.value = self.NETCONF_DIRECT self.value = self.NETCONF_DIRECT
self.settings_file = tailsgreeter.config.network_setting_path
def apply_to_upcoming_session(self): def apply_to_upcoming_session(self):
setting_file = tailsgreeter.config.network_setting_path write_settings(self.settings_file, {
with open(setting_file, 'w') as f: 'TAILS_NETCONF': pipes.quote(self.value),
os.chmod(setting_file, 0o600) })
f.write("TAILS_NETCONF=%s\n" % pipes.quote(self.value)) logging.debug('network setting written to %s', self.settings_file)
logging.debug('network setting written to %s', setting_file)
def load(self) -> bool:
try:
settings = read_settings(self.settings_file)
except FileNotFoundError:
logging.debug("No persistent network settings file found (path: %s)", self.settings_file)
return False
value = settings.get('TAILS_NETCONF')
if value:
self.value = value
logging.debug("Loaded network setting '%s'", value)
return True
...@@ -6,3 +6,17 @@ def write_settings(filename: str, settings: dict): ...@@ -6,3 +6,17 @@ def write_settings(filename: str, settings: dict):
os.chmod(filename, 0o600) os.chmod(filename, 0o600)
for key, value in settings.items(): for key, value in settings.items():
f.write('%s=%s\n' % (key, value)) f.write('%s=%s\n' % (key, value))
def read_settings(filename: str) -> dict:
with open(filename) as f:
lines = f.readlines()
settings = dict()
for line in lines:
try:
key, value = line.split('=', 1)
except ValueError:
continue
settings[key] = value.rstrip()
return settings
...@@ -131,7 +131,7 @@ class GreeterMainWindow(Gtk.Window, TranslatableWindow): ...@@ -131,7 +131,7 @@ class GreeterMainWindow(Gtk.Window, TranslatableWindow):
self.listbox_settings.set_placeholder(self.label_settings_default) self.listbox_settings.set_placeholder(self.label_settings_default)
# Persistent storage # Persistent storage
self.persistent_storage = PersistentStorage(self.persistence_setting, builder) self.persistent_storage = PersistentStorage(self.persistence_setting, greeter, builder)
# Add children to ApplicationWindow # Add children to ApplicationWindow
self.add(self.box_main) self.add(self.box_main)
...@@ -176,31 +176,17 @@ class GreeterMainWindow(Gtk.Window, TranslatableWindow): ...@@ -176,31 +176,17 @@ class GreeterMainWindow(Gtk.Window, TranslatableWindow):
# Actions # Actions
def add_setting(self, id_=None): def run_add_setting_dialog(self, id_=None):
response = self.dialog_add_setting.run(id_) response = self.dialog_add_setting.run(id_)
if response == Gtk.ResponseType.YES: if response == Gtk.ResponseType.YES:
row = self.listbox_add_setting.get_selected_row() row = self.listbox_add_setting.get_selected_row()
id_ = self.settings.id_from_row(row) id_ = self.settings.id_from_row(row)
setting = self.settings.additional_settings[id_] setting = self.settings.additional_settings[id_]
# The setting used to be applied by the dialog itself, but there
# we don't know the response type in all cases. For example, we
# previously didn't apply the admin password in all cases if the
# "Add" button was clicked to close the dialog (#13447).
setting.apply()
setting.update_value_label()
self.listbox_add_setting.remove(row)
self.listbox_settings.add(row)
self.dialog_add_setting.set_visible(False) self.dialog_add_setting.set_visible(False)
self.dialog_add_setting.stack.remove(setting.box) self.dialog_add_setting.stack.remove(setting.box)
setting.build_popover()
self.listbox_settings.unselect_all() self.add_setting(id_)
if True not in [c.get_visible() for c in
self.listbox_add_setting.get_children()]:
self.toolbutton_settings_add.set_sensitive(False)
self.dialog_add_setting.set_visible(False)
else: else:
old_details = self.dialog_add_setting.stack.get_child_by_name( old_details = self.dialog_add_setting.stack.get_child_by_name(
'setting-details') 'setting-details')
...@@ -208,11 +194,24 @@ class GreeterMainWindow(Gtk.Window, TranslatableWindow): ...@@ -208,11 +194,24 @@ class GreeterMainWindow(Gtk.Window, TranslatableWindow):
self.dialog_add_setting.stack.remove(old_details) self.dialog_add_setting.stack.remove(old_details)
self.dialog_add_setting.set_visible(False) self.dialog_add_setting.set_visible(False)
def add_setting(self, id_):
logging.debug("Adding setting '%s'", id_)
setting = self.settings.additional_settings[id_]
setting.apply()
setting.build_popover()
self.listbox_add_setting.remove(setting.listboxrow)
self.listbox_settings.add(setting.listboxrow)
self.listbox_settings.unselect_all()
if not self.listbox_add_setting.get_children():
self.toolbutton_settings_add.set_sensitive(False)
def edit_setting(self, id_): def edit_setting(self, id_):
if self.settings[id_].has_popover(): if self.settings[id_].has_popover():
self.settings[id_].listboxrow.emit("activate") self.settings[id_].listboxrow.emit("activate")
else: else:
self.add_setting(id_) self.run_add_setting_dialog(id_)
def show(self): def show(self):
super().show() super().show()
...@@ -351,11 +350,11 @@ class GreeterMainWindow(Gtk.Window, TranslatableWindow): ...@@ -351,11 +350,11 @@ class GreeterMainWindow(Gtk.Window, TranslatableWindow):
setting.apply() setting.apply()
def cb_toolbutton_settings_add_clicked(self, user_data=None): def cb_toolbutton_settings_add_clicked(self, user_data=None):
self.add_setting() self.run_add_setting_dialog()
return False return False