Add preferences dialog with TLS and timeout settings

- Add PreferencesDialog with settings for TLS verification and request timeout
- Update HttpClient to respect TLS verification setting
- Add GSettings schema keys for force-tls-verification and request-timeout
- Wire up preferences action to show dialog
- Settings changes apply immediately to HTTP session
This commit is contained in:
Pavel Baksy 2025-12-23 10:17:22 +01:00
parent 66719a5d0c
commit 2545cdd88d
7 changed files with 150 additions and 3 deletions

View File

@ -1,5 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<schemalist gettext-domain="roster"> <schemalist gettext-domain="roster">
<schema id="cz.vesp.roster" path="/cz/vesp/roster/"> <schema id="cz.vesp.roster" path="/cz/vesp/roster/">
<key name="force-tls-verification" type="b">
<default>true</default>
<summary>Force TLS verification</summary>
<description>When enabled, TLS certificates will be verified. Disable to test endpoints with self-signed or invalid certificates.</description>
</key>
<key name="request-timeout" type="i">
<default>30</default>
<summary>Request timeout</summary>
<description>Timeout for HTTP requests in seconds</description>
</key>
</schema> </schema>
</schemalist> </schemalist>

View File

@ -19,7 +19,7 @@
import gi import gi
gi.require_version('Soup', '3.0') gi.require_version('Soup', '3.0')
from gi.repository import Soup, GLib from gi.repository import Soup, GLib, Gio
import time import time
from typing import Optional, Callable, Any from typing import Optional, Callable, Any
@ -32,7 +32,36 @@ class HttpClient:
def __init__(self): def __init__(self):
"""Initialize HTTP client with reusable session.""" """Initialize HTTP client with reusable session."""
self.session = Soup.Session.new() self.session = Soup.Session.new()
self.session.set_timeout(30) # 30 second timeout
# Load settings
self.settings = Gio.Settings.new('cz.vesp.roster')
# Initialize TLS verification flag
self.force_tls_verification = True
# Apply initial settings
self._apply_settings()
# Listen for settings changes
self.settings.connect('changed::force-tls-verification', self._on_settings_changed)
self.settings.connect('changed::request-timeout', self._on_settings_changed)
def _apply_settings(self):
"""Apply settings to the HTTP session."""
# Store TLS verification preference (will be applied per-message)
self.force_tls_verification = self.settings.get_boolean('force-tls-verification')
# Apply timeout setting
timeout = self.settings.get_int('request-timeout')
self.session.set_timeout(timeout)
def _on_settings_changed(self, settings, key):
"""Handle settings changes."""
self._apply_settings()
def _accept_all_certificates(self, msg, tls_certificate, tls_errors):
"""Accept all TLS certificates when verification is disabled."""
return True # Accept certificate regardless of errors
def execute_request_async(self, request: HttpRequest, callback: Callable, user_data: Any = None): def execute_request_async(self, request: HttpRequest, callback: Callable, user_data: Any = None):
""" """
@ -59,6 +88,11 @@ class HttpClient:
callback(None, f"Invalid URL: {e}", user_data) callback(None, f"Invalid URL: {e}", user_data)
return return
# Handle TLS certificate verification
if not self.force_tls_verification:
# Connect signal to accept all certificates when verification is disabled
msg.connect('accept-certificate', self._accept_all_certificates)
# Add headers # Add headers
headers = msg.get_request_headers() headers = msg.get_request_headers()
for key, value in request.headers.items(): for key, value in request.headers.items():

View File

@ -25,6 +25,7 @@ gi.require_version('Adw', '1')
from gi.repository import Gtk, Gio, Adw from gi.repository import Gtk, Gio, Adw
from .window import RosterWindow from .window import RosterWindow
from .preferences_dialog import PreferencesDialog
class RosterApplication(Adw.Application): class RosterApplication(Adw.Application):
@ -67,7 +68,9 @@ class RosterApplication(Adw.Application):
def on_preferences_action(self, widget, _): def on_preferences_action(self, widget, _):
"""Callback for the app.preferences action.""" """Callback for the app.preferences action."""
print('app.preferences action activated') preferences = PreferencesDialog()
preferences.set_transient_for(self.props.active_window)
preferences.present()
def on_shortcuts_action(self, widget, _): def on_shortcuts_action(self, widget, _):
"""Callback for the app.shortcuts action.""" """Callback for the app.shortcuts action."""

View File

@ -37,6 +37,7 @@ roster_sources = [
'tab_manager.py', 'tab_manager.py',
'constants.py', 'constants.py',
'icon_picker_dialog.py', 'icon_picker_dialog.py',
'preferences_dialog.py',
] ]
install_data(roster_sources, install_dir: moduledir) install_data(roster_sources, install_dir: moduledir)

49
src/preferences-dialog.ui Normal file
View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<requires lib="Adw" version="1.0"/>
<template class="PreferencesDialog" parent="AdwPreferencesWindow">
<property name="title" translatable="yes">Preferences</property>
<property name="modal">True</property>
<property name="default-width">600</property>
<property name="default-height">400</property>
<child>
<object class="AdwPreferencesPage">
<property name="title" translatable="yes">General</property>
<property name="icon-name">preferences-system-symbolic</property>
<child>
<object class="AdwPreferencesGroup">
<property name="title" translatable="yes">Network</property>
<property name="description" translatable="yes">HTTP connection settings</property>
<child>
<object class="AdwSwitchRow" id="tls_verification_row">
<property name="title" translatable="yes">Force TLS Verification</property>
<property name="subtitle" translatable="yes">Verify TLS certificates for HTTPS connections. Disable to test endpoints with self-signed certificates.</property>
</object>
</child>
<child>
<object class="AdwSpinRow" id="timeout_row">
<property name="title" translatable="yes">Request Timeout</property>
<property name="subtitle" translatable="yes">Maximum time to wait for a response (in seconds)</property>
<property name="adjustment">
<object class="GtkAdjustment">
<property name="lower">5</property>
<property name="upper">300</property>
<property name="step-increment">5</property>
<property name="page-increment">10</property>
<property name="value">30</property>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</child>
</template>
</interface>

49
src/preferences_dialog.py Normal file
View File

@ -0,0 +1,49 @@
# preferences_dialog.py
#
# Copyright 2025 Pavel Baksy
#
# 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 <https://www.gnu.org/licenses/>.
#
# SPDX-License-Identifier: GPL-3.0-or-later
from gi.repository import Adw, Gtk, Gio
@Gtk.Template(resource_path='/cz/vesp/roster/preferences-dialog.ui')
class PreferencesDialog(Adw.PreferencesWindow):
__gtype_name__ = 'PreferencesDialog'
tls_verification_row = Gtk.Template.Child()
timeout_row = Gtk.Template.Child()
def __init__(self, **kwargs):
super().__init__(**kwargs)
# Get settings
self.settings = Gio.Settings.new('cz.vesp.roster')
# Bind settings to UI
self.settings.bind(
'force-tls-verification',
self.tls_verification_row,
'active',
Gio.SettingsBindFlags.DEFAULT
)
self.settings.bind(
'request-timeout',
self.timeout_row,
'value',
Gio.SettingsBindFlags.DEFAULT
)

View File

@ -3,6 +3,7 @@
<gresource prefix="/cz/vesp/roster"> <gresource prefix="/cz/vesp/roster">
<file preprocess="xml-stripblanks">main-window.ui</file> <file preprocess="xml-stripblanks">main-window.ui</file>
<file preprocess="xml-stripblanks">shortcuts-dialog.ui</file> <file preprocess="xml-stripblanks">shortcuts-dialog.ui</file>
<file preprocess="xml-stripblanks">preferences-dialog.ui</file>
<file preprocess="xml-stripblanks">icon-picker-dialog.ui</file> <file preprocess="xml-stripblanks">icon-picker-dialog.ui</file>
<file preprocess="xml-stripblanks">widgets/header-row.ui</file> <file preprocess="xml-stripblanks">widgets/header-row.ui</file>
<file preprocess="xml-stripblanks">widgets/history-item.ui</file> <file preprocess="xml-stripblanks">widgets/history-item.ui</file>