Skip to content

Friday, 7 April 2023

Flatpaks are amazing and all that. But application sandboxing, so an application cannot do anything it wants, is a challenge - even more so when you have two applications that need to talk to each other. Perhaps it shouldn’t come as a surprise that native-messaging sandboxing support for Flatpak has been in development for over a year. To celebrate its anniversary I thought I’d write down how to drill a native-messaging sized hole into the sandbox. This enables the use of native messaging even without portal integration, albeit also without sane degrees of sandboxing.

First off, please understand that this undermines the sandbox on a fairly fundamental level. So, don’t do this if you don’t keep your Firefox updated or visit particularly dodgy websites.

For the purposes of this post I’m assuming Firefox and KeePassXC are installed as Flatpaks in user scope.

First order of business is setting up KeePassXC so it writes its definition file in a place where Firefox can read it. Fortunately it has a setting for this:

~/.var/app/org.mozilla.firefox/.mozilla/native-messaging-hosts/ is the path inside Firefox’ home where the defintion file will be written. Naturally we’ll also need to adjust the Flatpak permissions so KeePassXC can write to this path.

flatpak override --user --filesystem=~/.var/app/org.mozilla.firefox/.mozilla/native-messaging-hosts org.keepassxc.KeePassXC

At this point Firefox knows about the native messaging host but it won’t be able to run it. Alas. We need some rigging here. The problem is that Firefox can’t simply flatpak run the native messaging host, it needs to spawn a host process (i.e. a process outside its sandbox) to then run the KeePassXC Flatpak and that then runs the NMH.

Fortunately the NMH definition files are fairly straight forward:

{"allowed_extensions":["keepassxc-browser@keepassxc.org"],
"description":"KeePassXC integration with native messaging support",
"name":"org.keepassxc.keepassxc_browser",
"path":"/home/me/.local/share/flatpak/exports/bin/org.keepassxc.KeePassXC",
"type":"stdio"}

The problem of course is that we cannot directly use that Flatpak bin but need the extra spawn step in between. What we need is a way to manipulate the definition file such that we can switch in a different path. systemd to the rescue!

systemctl edit --user --full --force keepassxc-native-messaging-mangler.path

# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
# SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org>

[Path]
PathChanged=/home/me/.var/app/org.mozilla.firefox/.mozilla/native-messaging-hosts/org.keepassxc.keepassxc_browser.json

[Install]
WantedBy=default.target

and the associated service file…

systemctl edit --user --full --force keepassxc-native-messaging-mangler.service

# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
# SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org>

[Unit]
Description=keepassxc mangler

[Service]
ExecStart=/home/me/keepassxc-native-messaging-mangler

lastly, enable the path unit.

systemctl --user enable --now keepassxc-native-messaging-mangler.path

Alright, there’s some stuff to unpack here. KeePassXC on startup writes the aforementioned definition file into Firefox’ NMH path. What we do with the help of systemd is monitor the file for changes and whenever it changes we’ll trigger our service, the service runs a mangler to modify the file so we can run another command instead. It’s basically an inotify watch.

Here’s the mangler (~/keepassxc-native-messaging-mangler):

#!/usr/bin/env ruby
# frozen_string_literal: true

# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
# SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org>

require 'json'

file = "#{Dir.home}/.var/app/org.mozilla.firefox/.mozilla/native-messaging-hosts/org.keepassxc.keepassxc_browser.json"
blob = JSON.parse(File.read(file))
blob['path'] = "#{Dir.home}/Downloads/keepassxc"
File.write(file, JSON.generate(blob))

It simply replaces the path of the executable with a wrapper script. Here’s the wrapper script (~/Downloads/keepassxc):

#!/bin/sh

# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
# SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org>

exec /usr/bin/flatpak-spawn --host --watch-bus "$HOME/.local/share/flatpak/exports/bin/org.keepassxc.KeePassXC" "$@"

flatpak-spawn is a special command that allows us to spawn processes outside the sandbox. To gain access we’ll have to allow Firefox to talk with the org.freedesktop.Flatpak DBus session service.

flatpak override --user --talk-name=org.freedesktop.Flatpak org.mozilla.firefox

And that’s it!

➡️ KeePassXC writes its NMH definition to Flatpak specific path ➡️ systemd acts on changes and starts mangler ➡️ mangler changes the path inside the definition to our wrapper ➡️ Firefox reads the definition and calls our wrapper ➡️ wrapper flatpak-spawns KeePassXC flatpak ➡️ Firefox (flatpak) talks to KeePassXC (flatpak)

Thursday, 6 April 2023

Buttons are a fundamental element in user interfaces, but it’s easy to make some accessibility mistakes when using icon-only buttons in QML.

First, please avoid icon-only buttons and only use then, when the icon is very well known (e.g. arrows, delete or favorite icons) and the space is limited.

In case you still want to use an icon-only button. Make sure to set the text: property and that it is also translatable. Otherwise, a screen reader won’t know that to say about the button. This is because the text: property is used as default value for the Accessible.name: property, so when it is not set Accessible.name is empty and the screen reader can only say that the currently focused control is a button. The trick to have both the text: property set and an icon-only button is to use the display: property and assign it to the AbstractButton.IconOnly.

This gives us the following code:

import QtQuick 2.15
import QtQuick.Controls 2.15

Button {
 text: i18n("Favorite")
 icon.name: 'favorite'
 display: AbstractButton.IconOnly
}

Finally, another essential part is that an icon-only button requires a tooltip. We need the tooltip in case the user is unsure about the meaning of the icon and we need more details.

import QtQuick 2.15
import QtQuick.Controls 2.15

Button {
 text: i18n("Favorite")
 icon.name: 'favorite'
 display: AbstractButton.IconOnly

 ToolTip.text: text
 ToolTip.delay: Kirigami.Units.toolTipDelay
 ToolTip.visible: hovered
}

Note that this used the ToolTip attached property instead of a separate item ToolTip {} as it is more memory efficient. With the attached property, we share the tooltip instance across the entire application instead of instanciating a ToolTip popup for each button.

Wednesday, 29 March 2023

Another month in the year, another collection of bugfixes and features I contributed to KDE!

Documentation Improvements

Something I want to improve for KDE Frameworks 6 is the API documentation, it’s pretty bad sometimes:

How do I even import this component?

[Bugfix] For plasma-framework, we started marking private QML types as internal. I’m in the process of manually fixing up the custom QQuickItem types in plasma-framework too, although I haven’t gotten around to opening up a merge request for that.

[Feature] I opened a doxyqml1 merge request to make it’s output more useful, by automatically adding import statement hints to the page:

Example of the new doxyqml output

Plasma 6 Porting

[Feature] Early this month, I ended up porting most of our applets to Plasma 6! The current hitlist is:

Screenshot of the applets running on Plasma 6!

The two I’m still working on and need further testing and reviewing:

I also fixed activity switching, and fixed KRunner’s broken layout.

For those who are building Plasma 6 for the first time, extragear modules are now built by default!

PlasmaTube

[Feature] I started using PlasmaTube which is KDE’s Youtube Client, to replace FreeTube. I started by cleaning up and overhauling the account management, so it flows better.

The new login page

[Bugfix] I also made the info chips non-interactable, fixed the spacing on video grid text, added more loading placeholders and stop desktop users from being able to drag the video page.

Ruqola

[Feature] Because of $work, I discovered that we have a Rocket.chat client! I miss being able to quickly switch my presence via the tray icon, so that’s the first thing I opened a merge request for.

Screenshot of the presence context menu

Dr. Konqi

[Feature] Someone pointed out that the margins in the bug reporting wizard was terrible, so I started a redesign of the whole UI trying to update it to look similar to our modern applications:

WIP Bug Context Page WIP Backtrace Page

Tokodon

In preparation for the 23.04 gear release, I added a slew of new features and bugfixes!

Screenshot of the inline reply previewScreenshot of the newly added notification filter types

And of course, there are of course many bugfixes:

[Feature] As a treat for people who manage popular accounts - like our great KDE and Krita promo teams - I have a work-in-progress merge request to allow you to group notifications! I expect to find time next month to finish and polish this feature, but no promises yet.

Screenshot of the notification grouping (work in progress)

Miscellaneous


  1. Doxygen doesn’t support QML natively, so doxyqml is a plugin to help generate doxygen pages for QML types. ↩︎

Tuesday, 28 March 2023

gcompris 3.2

Today we are releasing GCompris version 3.2.

This new version contains some bug fixes on multiple activities such as "Discover the International Morse code", "Control the hose-pipe" and music activities.

It also contains new graphics for all memory activities and for "Baby puzzle".

A new command-line argument (--difficulty {value|min-max}) has been added which allows users to force the difficulty filter at a given value or range.

The Andika font has been updated to its latest version (6.200).

It is fully translated in the following languages:

  • Breton
  • Catalan
  • Catalan (Valencian)
  • Greek
  • UK English
  • Spanish
  • Basque
  • French
  • Croatian
  • Italian
  • Lithuanian
  • Malayalam
  • Dutch
  • Norwegian Nynorsk
  • Polish
  • Portuguese
  • Brazilian Portuguese
  • Romanian
  • Slovenian
  • Turkish
  • Ukrainian
  • Chinese Traditional

It is also partially translated in the following languages:

  • Azerbaijani (99%)
  • Belarusian (79%)
  • Czech (88%)
  • German (99%)
  • Estonian (99%)
  • Finnish (94%)
  • Hebrew (99%)
  • Hungarian (99%)
  • Indonesian (99%)
  • Macedonian (94%)
  • Russian (99%)
  • Slovak (77%)
  • Albanian (99%)
  • Swedish (98%)

Thank you all,
Timothée & Johnny

I’m happy to announce KTechLab release version 0.51.0. KTechLab is an IDE for microcontrollers and electronics. This new release contains the following changes:

  • updated and improved translations
  • the Serial Port component, for better compatibility, uses Qt’s QSerialPort, instead of operating-system specific library calls
  • experimental support for Windows; it requires MSVC 2019 compiler
  • various stability fixes
  • modernisation of the codebase, porting away from some deprecated APIs

The release tarball of version 0.51.0 can be downloaded from KDE servers. Its checksums are:

SHA256Sum: 046b9ce1f2c2a93e1da734a416674a5bb5da3203ac773d49ed693b8492f6d212
SHA1Sum: 7729b67050caee5b65fe4dd1dfbfce213cea44ac
MD5Sum: 2853c6867535995c1ead598e98fce6e3

KTechLab has a mailing list at KDE called ktechlab-devel: https://mail.kde.org/mailman/listinfo/ktechlab-devel

On IRC, developers are mostly reachable on freenode.net, on #ktechlab channel.

The source code of KTechLab is available in KDE Git, at https://invent.kde.org/sdk/ktechlab

Instructions for building and running KTechLab are available in the README file from the source code; online version of that file is available at https://invent.kde.org/sdk/ktechlab/-/blob/master/README ; The very short instructions are: run sh build-simple.sh and then sh run-simple.sh.

The notes from joining KDE: https://community.kde.org/Incubator/Projects/KTechLab

KTechLab bugs at KDE bugtracker: https://bugs.kde.org/buglist.cgi?quicksearch=ktechlab

We're happy to announce the new release 5.10.0 of KPhotoAlbum, the KDE photo management program!

The ChangeLog for this version is as follows:


Changed

  • View-related actions formerly found in the "Settings" menu were moved to the "View" menu.
  • Make options "Display Labels in Thumbnail View" and "Display Categories in Thumbnail View" reachable via the "View" menu and allow both actions to be assigned keyboard shortcuts (Implements: #145346).
  • Store the untagged tag information inside the index.xml file instead of the Settings (Implements: #461206).
  • Change scroll direction in the annotation dialog's date edit fields to match common (western) expectations and the date picker.
  • Prevent scrolling past the occupied areas of the date bar.
  • Files are now always created with group read/write permissions (Fixes: #438128).
  • When exiting the demo mode, the demo database is now always saved if it isn't deleted.

Dependencies

  • CMake: 3.18
  • Qt5: 5.15
  • KDE Frameworks: 5.78

Fixed

  • Improve readability of "Show Tooltips in Thumbnails Window" tooltip.
  • Fix image selection order for newly added images (Fixes: 442325).
  • Improve date bar behavior when zooming the date bar and changing views (Fixes: 357237).

Removed

  • Default shortcut for "View" images was removed.
    Pressing "Enter" to open the viewer is now the preferred way. To restore the old behavior, reassign the shortcut via "Settings | Configure Keyboard Shortcuts...".

Thanks to everybody having contributed to this release! The authors were (according to git log and in alphabetical order):

  • Christophe Giboudeaux
  • Tobias Leupold
  • Luigi Toscano
  • Johannes Zarl-Zierl

Have a lot of fun with the new version!

— Tobias

Sunday, 26 March 2023

As you probably have heard by now the lastest development versions of Plasma and KDE Frameworks require Qt6. This transition has been in the works for a few years by now, but it was only somewhat recently that we took the plunge and started relying on Qt6 exclusively for Plasma. Plasma 5.27 is the last Plasma 5 release and continues in bugfix-only mode.

For people who want to hack on Plasma features this raises the obvious question: How do I build Plasma 6 to hack on it?

Before diving into this, a word of warning: Current Plasma master is in no way “ready for production”. There are known-broken things and things may temporarily regress at any time. That said, the only way to get towards a stable thing is to dig in and fix things. So let’s see how to do that.

For this explanation I’m assuming you have build KDE software with kdesrc-build before. If not we have some extensive documentation for that. First you need at least Qt 6.4 installed. The usually best way to get that is from your distribution. If your distribution does not have Qt6 packaged yet please complain^Wtalk to them. CMake should complain about any missing Qt6 modules/development files, but make sure you have the qtpaths tool installed beforehand. It should be named something like qtpaths6 or qtpaths-qt6.

Now we need to teach kdesrc-build to build Plasma with the right configuration (git branches, CMake arguments etc) for building with Qt6. The easiest way to do that is by using a separate kdesrc-buildrc file. kdesrc-build comes with a sample Qt6 configuration file that you should use as a starting point. Replace the line include ~/kde6/usr/share/kdesrc-build/kf6-qt6-build-include with a path to your existing kdesrc-build installation. For me that would be include ~/kde/src/kdesrc-build/kf6-qt6-build-include. You can also apply other customizations that you usually do in your kdesrc-buildrc. Then save the file as e.g. ~/kde6/kdesrc-buildrc. This configuration will download, build, and install everything under ~/kde6. Currently it is very important to keep Qt5-based and Qt6-based builds separate since there will be conflicts between the installed files.

Now you can invoke kdesrc-build as usual, with one slight difference. Passing --rc-file=/home/user/kde6/kdesrc-buildrc will make it use the Qt6 configuration. If you omit the --rc-file argument it will use the old, Qt5-based configuration, so you can keep building Qt5- and Qt6-based stuff in parallel (but installed into different locations). Since kdesrc-build --rc-file=/home/user/kde6/kdesrc-buildrc is a bit of a mouthful you might want to configure a shell alias like kdesrc-build6 for it.

Now you can do kdesrc-build --rc-file=/home/user/kde6/kdesrc-buildrc workspace to build Plasma. Note that it’s expected that not all modules successfully build since not all of them are adjusted to Qt6 fully yet.

Once Plasma is build you want to log into the new session. To do that go to ~/kde6/build/plasma-workspace and run sudo ./login-sessions/install-sessions.sh once. Then you can select the new session at login in SDDM.

Now for some question that might be on your mind:

Q: Is it stable?

A: No, see above.

Q: When will it be stable?

A: Time will tell. It will be several more months before we can consider a Plasma 6 release.

Q: I tested it and found a problem, what should I do?

A: Report it to bugs.kde.org, and state that you are using Plasma master. Or try fixing it yourself.

Q: I want to work on a Plasma feature, what should I do?

A: Try developing it against Plasma master. If you find yourself blocked by anything please tell us.

Q: Does this apply to other KDE software as well?

A: Yes. Note that most applications allow building against Qt5 and Qt6 from the same branch though. And some apps aren’t ported to Qt6 at all.

Q: I did not understand a word of what you just said but I’d still like to help.

A: One way to help is by donating to KDE. Your donations help me have time to do things like working on Qt6 support or writing this blog post

After the recent release of KBibTeX 0.9.3.2 as the last release of the 0.9.x branch, it is now time to make a stable release of KBibTeX 0.10.0. Tar-balls are as usual available at KDE’s download mirrors. Some of the changes were documented more than two years ago in a pre-release (0.9.90), but here are the highlights taken from the ChangeLog:

  • New online search: Semantic Scholar
  • Migrating Inspire Hep to REST API
  • Fixing and revamping ACM's Digital Library (a.k.a. ACM Portal) online search
  • Refactoring NSA ADS to use official API
  • Updating BibSearch code: cover page improved, preparing code for translations, adding progress bar
  • Tabs in the entry editor can show short messages to use, e.g. in which tab DOIs or URLs are to be entered
  • Fixing UI issues with ColorLabelWidget
  • Using KRatingPainter instead of home-made StarRating's paint function
  • Various improvements and refactoring when (PDF) files get associated with an entry
  • Use Qt's own QOAuth1 class instead of external library QOAuth
  • Having ICU as an optional dependency only, provide internal, static translation from Basic Multilingual Plane (BMP) characters to ASCII-only representations
  • Greatly refactoring and modernizing CMakeLists.txt files, generation of camel-case headers, private/public linking to libraries, ...
  • Updating translations (contributions by various authors)
  • KDE Bug 421612: When suggesting entry ids, do not count ‘small words’
  • KDE Bug 424033: Can't associate a file with a relative path without having the file copied
  • KDE Bug 423976: When formatting IDs, non-word characters should be used as word separators
  • KDE Bug 426856: File encoding is not always stored
  • KDE Bug 379443: Slowdown when loading citation with many authors
  • KDE Bug 433005: Cannot unselect entry list view columns in BibLaTeX mode
  • KDE Bug 433084, KDE Bug 453455: Fixing crash when opening .bib file
  • KDE Invent issue 1: Properly handling letter modifiers such as \c{e} instead of \ce
  • Adding and extending numerous automated tests
  • Numerous other fixes, clean-ups, refactoring, ...

Thank you to everyone who contributed. Happy compiling and packaging!

Tuesday, 21 March 2023

Working on the Nextcloud desktop client, I stumbled into a nasty little bug. If you tried to add or remove a user account, the app would often freeze for a while.

This was super annoying during testing, so I decided to look into it. Surprisingly this wasn’t a new bug I’d unwittingly introduced; tracing the source of the freeze led all the way down into QtKeychain, which is what the desktop client uses to store user credentials.

QtKeychain, like Qt itself, is great at making life easy for developers targeting multiple platforms as it abstracts away the different keychain/keyring/wallet implementations for the different platforms. Looking at the Objective-C++ code handling Apple platforms, QtKeychain’s different job classes called code using Keychain Services functions, as expected.

Fortunately the problem was pretty clear. All of the calls to the Keychain Services functions — SecItemUpdate, SecItemDelete, SecItemAdd, SecItemCopyMatching — block the calling thread. From Apple’s documentation:

SecItemUpdate blocks the calling thread, so it can cause your app’s UI to hang if called from the main thread. Instead, call SecItemUpdate from a background dispatch queue or async function.

This means that, when called on the main thread, doing any QtKeychain operations would lead to freezing the UI. Since this is not a problem on other platforms (that I know of!), it’s pretty easy to overlook this quirk of Keychain Services’ API. 

void DeletePasswordJobPrivate::scheduledStart()
{
    const NSDictionary *const query = @{
            (__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword,
            (__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(),
            (__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(),
    };

    const OSStatus status = SecItemDelete((__bridge CFDictionaryRef) query);

    if (status == errSecSuccess) {
        q->;emitFinished();
    } else {
        const ErrorDescription error = ErrorDescription::fromStatus(status);
        q->;emitFinishedWithError(error.code, Job::tr("Could not remove private key from keystore: %1").arg(error.message));
    }
}

With use of Grand Central Dispatch, a fix was relatively straightforward. All that needed to be done was to call the offending functions from a different dispatch queue. I rewrote the Apple-specific keychain implementation in order to call the Keychain Services functions asynchronously from the global queue, passing an interface object that would call the relevant completion methods and signals on the read/write/delete QtKeychain jobs.

static void StartDeletePassword(const QString &service, const QString &key, AppleKeychainInterface * const interface)
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        NSDictionary * const query = @{
            (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword,
            (__bridge NSString *)kSecAttrService: service.toNSString(),
            (__bridge NSString *)kSecAttrAccount: key.toNSString(),
        };

        const OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);

        if (status == errSecSuccess) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [interface keychainTaskFinished];
                [interface release];
            });
        } else {
            NSString * const descriptiveErrorString = @"Could not remove private key from keystore";
            dispatch_async(dispatch_get_main_queue(), ^{
                [interface keychainTaskFinishedWithError:status descriptiveMessage:descriptiveErrorString];
                [interface release];
            });
        }
    });
}

Note that after these changes, your application will need to either use QGuiApplication or you will need to initialise NSApplication yourself if you use QCoreApplication.

Testing the changes with a fresh build of QtKeychain, I was happy to see the freezes are now gone in the Nextcloud desktop client. 🙂

vid

Thanks to Frank Osterfeld for reviewing the pull request!

Tuesday, 14 March 2023

KBibTeX 0.9.3.2 got released, tar-balls are available at KDE’s download mirrors. This is the last release of the 0.9.x branch. Next is 0.10, where I will prepare release candidate tar-balls in the next few days and hopefully we will see a final release 0.10.0 this Spring.

A number of issues got fixed, including but not limited to:

  • Bugs 463398, 459150, 464607, and 464606.
  • Various online searches got revamped and should work again.
  • Building against newer Qt versions (thanks to Albert Astals Cid)

See the full ChangeLog for all the details.

Some spoilers about what is happening in the ‘master’ branch: I have been working on an exporter to Microsoft Word’s bibliography XML. Writing scientific manuscripts with references in Word is a painful experience, but this exporter allows you to continue to use your favorite BibTeX editor even under dire circumstances.