aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--testing/nemo-qml-plugin-systemsettings/0001-Remove-developer-mode.patch736
-rw-r--r--testing/nemo-qml-plugin-systemsettings/0002-Remove-broken-certificatemodel.patch996
-rw-r--r--testing/nemo-qml-plugin-systemsettings/APKBUILD29
3 files changed, 1761 insertions, 0 deletions
diff --git a/testing/nemo-qml-plugin-systemsettings/0001-Remove-developer-mode.patch b/testing/nemo-qml-plugin-systemsettings/0001-Remove-developer-mode.patch
new file mode 100644
index 0000000000..15f6236048
--- /dev/null
+++ b/testing/nemo-qml-plugin-systemsettings/0001-Remove-developer-mode.patch
@@ -0,0 +1,736 @@
+Developer mode is really SailfishOS specific and doesn't make sense to have on other platforms.
+Besides, it depends on PackageKit which is not available on Alpine.
+
+diff --git a/src/developermodesettings.cpp b/src/developermodesettings.cpp
+deleted file mode 100644
+index 5767eb7..0000000
+--- a/src/developermodesettings.cpp
++++ /dev/null
+@@ -1,524 +0,0 @@
+-/*
+- * Copyright (c) 2013 – 2019 Jolla Ltd.
+- * Copyright (c) 2019 Open Mobile Platform LLC.
+- * Contact: Thomas Perl <thomas.perl@jollamobile.com>
+- * Contact: Raine Makelainen <raine.makelainen@jolla.com>
+- *
+- * You may use this file under the terms of the BSD license as follows:
+- *
+- * "Redistribution and use in source and binary forms, with or without
+- * modification, are permitted provided that the following conditions are
+- * met:
+- * * Redistributions of source code must retain the above copyright
+- * notice, this list of conditions and the following disclaimer.
+- * * Redistributions in binary form must reproduce the above copyright
+- * notice, this list of conditions and the following disclaimer in
+- * the documentation and/or other materials provided with the
+- * distribution.
+- * * Neither the name of Nemo Mobile nor the names of its contributors
+- * may be used to endorse or promote products derived from this
+- * software without specific prior written permission.
+- *
+- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+- */
+-
+-#include "developermodesettings.h"
+-#include "logging_p.h"
+-
+-#include <QFile>
+-#include <QDir>
+-#include <QDBusReply>
+-#include <QNetworkInterface>
+-#include <transaction.h>
+-
+-/* Symbolic constants */
+-#define PROGRESS_INDETERMINATE (-1)
+-
+-/* Interfaces for IP addresses */
+-#define USB_NETWORK_FALLBACK_INTERFACE "usb0"
+-#define USB_NETWORK_FALLBACK_IP "192.168.2.15"
+-#define WLAN_NETWORK_INTERFACE "wlan0"
+-#define WLAN_NETWORK_FALLBACK_INTERFACE "tether"
+-
+-/* A file that is provided by the developer mode package */
+-#define DEVELOPER_MODE_PROVIDED_FILE "/usr/bin/devel-su"
+-#define DEVELOPER_MODE_PACKAGE "jolla-developer-mode"
+-#define DEVELOPER_MODE_PACKAGE_PRELOAD_DIR "/var/lib/jolla-developer-mode/preloaded/"
+-
+-/* D-Bus service */
+-#define USB_MODED_SERVICE "com.meego.usb_moded"
+-#define USB_MODED_PATH "/com/meego/usb_moded"
+-#define USB_MODED_INTERFACE "com.meego.usb_moded"
+-
+-/* D-Bus method names */
+-#define USB_MODED_GET_NET_CONFIG "get_net_config"
+-#define USB_MODED_SET_NET_CONFIG "net_config"
+-
+-/* USB Mode Daemon network configuration properties */
+-#define USB_MODED_CONFIG_IP "ip"
+-#define USB_MODED_CONFIG_INTERFACE "interface"
+-
+-static QMap<QString,QString> enumerate_network_interfaces()
+-{
+- QMap<QString,QString> result;
+-
+- for (const QNetworkInterface &intf : QNetworkInterface::allInterfaces()) {
+- for (const QNetworkAddressEntry &entry : intf.addressEntries()) {
+- if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) {
+- result[intf.name()] = entry.ip().toString();
+- }
+- }
+- }
+-
+- return result;
+-}
+-
+-static QString get_cached_package(const QString &version)
+-{
+- QDir dir(DEVELOPER_MODE_PACKAGE_PRELOAD_DIR);
+- QStringList filters;
+- filters << QStringLiteral("%1-%2.*.rpm").arg(DEVELOPER_MODE_PACKAGE).arg(version);
+- auto preloaded = dir.entryList(filters, QDir::Files, QDir::Name);
+- if (preloaded.empty())
+- return QString();
+- return dir.absoluteFilePath(preloaded.last());
+-}
+-
+-DeveloperModeSettings::DeveloperModeSettings(QObject *parent)
+- : QObject(parent)
+- , m_usbModeDaemon(USB_MODED_SERVICE, USB_MODED_PATH, USB_MODED_INTERFACE, QDBusConnection::systemBus())
+- , m_wlanIpAddress("-")
+- , m_usbInterface(USB_NETWORK_FALLBACK_INTERFACE)
+- , m_usbIpAddress(USB_NETWORK_FALLBACK_IP)
+- , m_username(qgetenv("USER"))
+- , m_developerModeEnabled(QFile::exists(DEVELOPER_MODE_PROVIDED_FILE))
+- , m_workStatus(Idle)
+- , m_workProgress(PROGRESS_INDETERMINATE)
+- , m_transactionRole(PackageKit::Transaction::RoleUnknown)
+- , m_transactionStatus(PackageKit::Transaction::StatusUnknown)
+- , m_refreshedForInstall(false)
+- , m_localInstallFailed(false)
+- , m_localDeveloperModePackagePath(get_cached_package(QStringLiteral("*"))) // Initialized to possibly incompatible package
+-{
+- // Resolve and update local package path
+- if (!m_localDeveloperModePackagePath.isEmpty()) {
+- PackageKit::Transaction *resolvePackage = PackageKit::Daemon::resolve(DEVELOPER_MODE_PACKAGE"-preload", PackageKit::Transaction::FilterInstalled);
+- connect(resolvePackage, &PackageKit::Transaction::errorCode, this, &DeveloperModeSettings::reportTransactionErrorCode);
+- connect(resolvePackage, &PackageKit::Transaction::package,
+- this, [this](PackageKit::Transaction::Info info, const QString &packageID, const QString &summary) {
+- Q_UNUSED(summary)
+- Q_ASSERT(info == PackageKit::Transaction::InfoInstalled);
+- const QString version = PackageKit::Transaction::packageVersion(packageID);
+- m_localDeveloperModePackagePath = get_cached_package(version);
+- if (m_localDeveloperModePackagePath.isEmpty()) {
+- emit repositoryAccessRequiredChanged();
+- }
+- qCDebug(lcDeveloperModeLog) << "Preload package version: " << version << ", local package path: " << m_localDeveloperModePackagePath;
+- });
+- }
+-
+- refresh();
+-
+- // TODO: Watch WLAN / USB IP addresses for changes
+- // TODO: Watch package manager for changes to developer mode
+-}
+-
+-DeveloperModeSettings::~DeveloperModeSettings()
+-{
+-}
+-
+-QString DeveloperModeSettings::wlanIpAddress() const
+-{
+- return m_wlanIpAddress;
+-}
+-
+-QString DeveloperModeSettings::usbIpAddress() const
+-{
+- return m_usbIpAddress;
+-}
+-
+-QString DeveloperModeSettings::username() const
+-{
+- return m_username;
+-}
+-
+-bool DeveloperModeSettings::developerModeEnabled() const
+-{
+- return m_developerModeEnabled;
+-}
+-
+-enum DeveloperModeSettings::Status DeveloperModeSettings::workStatus() const
+-{
+- return m_workStatus;
+-}
+-
+-int DeveloperModeSettings::workProgress() const
+-{
+- return m_workProgress;
+-}
+-
+-bool DeveloperModeSettings::repositoryAccessRequired() const
+-{
+- // Aka local-install-of-developer-mode-package-is-not-possible
+- return m_localInstallFailed || m_localDeveloperModePackagePath.isEmpty();
+-}
+-
+-void DeveloperModeSettings::setDeveloperMode(bool enabled)
+-{
+- if (m_developerModeEnabled != enabled) {
+- if (m_workStatus != Idle) {
+- qCWarning(lcDeveloperModeLog) << "DeveloperMode state change requested during activity, ignored.";
+- return;
+- }
+-
+- m_refreshedForInstall = false;
+- if (enabled) {
+- resolveAndExecute(InstallCommand);
+- } else {
+- resolveAndExecute(RemoveCommand);
+- }
+- }
+-}
+-
+-void DeveloperModeSettings::setUsbIpAddress(const QString &usbIpAddress)
+-{
+- if (m_usbIpAddress != usbIpAddress) {
+- usbModedSetConfig(USB_MODED_CONFIG_IP, usbIpAddress);
+- m_usbIpAddress = usbIpAddress;
+- emit usbIpAddressChanged();
+- }
+-}
+-
+-void DeveloperModeSettings::refresh()
+-{
+- /* Retrieve network configuration from usb_moded */
+- m_usbInterface = usbModedGetConfig(USB_MODED_CONFIG_INTERFACE, USB_NETWORK_FALLBACK_INTERFACE);
+- QString usbIp = usbModedGetConfig(USB_MODED_CONFIG_IP, USB_NETWORK_FALLBACK_IP);
+-
+- if (usbIp != m_usbIpAddress) {
+- m_usbIpAddress = usbIp;
+- emit usbIpAddressChanged();
+- }
+-
+- /* Retrieve network configuration from interfaces */
+- QMap<QString,QString> entries = enumerate_network_interfaces();
+-
+- if (entries.contains(m_usbInterface)) {
+- QString ip = entries[m_usbInterface];
+- if (m_usbIpAddress != ip) {
+- m_usbIpAddress = ip;
+- emit usbIpAddressChanged();
+- }
+- }
+-
+- if (entries.contains(WLAN_NETWORK_INTERFACE)) {
+- QString ip = entries[WLAN_NETWORK_INTERFACE];
+- if (m_wlanIpAddress != ip) {
+- m_wlanIpAddress = ip;
+- emit wlanIpAddressChanged();
+- }
+- } else if (entries.contains(WLAN_NETWORK_FALLBACK_INTERFACE)) {
+- // If the WLAN network interface does not have an IP address,
+- // but there is a "tether" interface that does have an IP, assume
+- // it is the WLAN interface in tethering mode, and use its IP.
+- QString ip = entries[WLAN_NETWORK_FALLBACK_INTERFACE];
+- if (m_wlanIpAddress != ip) {
+- m_wlanIpAddress = ip;
+- emit wlanIpAddressChanged();
+- }
+- }
+-
+- for (const QString &device : entries.keys()) {
+- qCDebug(lcDeveloperModeLog) << "Device:" << device << "IP:" << entries[device];
+- }
+-}
+-
+-void DeveloperModeSettings::refreshPackageCacheAndInstall()
+-{
+- m_refreshedForInstall = true;
+-
+- // Soft refresh, do not clear & reload valid cache.
+- PackageKit::Transaction *refreshCache = PackageKit::Daemon::refreshCache(false);
+- connect(refreshCache, &PackageKit::Transaction::errorCode, this, &DeveloperModeSettings::reportTransactionErrorCode);
+- connect(refreshCache, &PackageKit::Transaction::finished,
+- this, [this](PackageKit::Transaction::Exit status, uint runtime) {
+- qCDebug(lcDeveloperModeLog) << "Package cache updated:" << status << runtime;
+- resolveAndExecute(InstallCommand); // trying again regardless of success, some repositories might be updated
+- });
+-}
+-
+-void DeveloperModeSettings::resolveAndExecute(Command command)
+-{
+- setWorkStatus(Preparing);
+- m_workProgress = 0;
+- m_developerModePackageId.clear(); // might differ between installed/available
+-
+- if (command == InstallCommand && !m_localInstallFailed && !m_localDeveloperModePackagePath.isEmpty()) {
+- // Resolve which version of developer mode package is expected
+- PackageKit::Transaction *resolvePackage = PackageKit::Daemon::resolve(DEVELOPER_MODE_PACKAGE"-preload", PackageKit::Transaction::FilterInstalled);
+- connect(resolvePackage, &PackageKit::Transaction::errorCode, this, &DeveloperModeSettings::reportTransactionErrorCode);
+- connect(resolvePackage, &PackageKit::Transaction::package,
+- this, [this](PackageKit::Transaction::Info info, const QString &packageID, const QString &summary) {
+- Q_UNUSED(summary)
+- Q_ASSERT(info == PackageKit::Transaction::InfoInstalled);
+- const QString version = PackageKit::Transaction::packageVersion(packageID);
+- m_localDeveloperModePackagePath = get_cached_package(version);
+- emit repositoryAccessRequiredChanged();
+- qCDebug(lcDeveloperModeLog) << "Preload package version: " << version << ", local package path: " << m_localDeveloperModePackagePath;
+- });
+-
+- connect(resolvePackage, &PackageKit::Transaction::finished,
+- this, [this](PackageKit::Transaction::Exit status, uint runtime) {
+- Q_UNUSED(runtime)
+- if (status != PackageKit::Transaction::ExitSuccess || m_localDeveloperModePackagePath.isEmpty()) {
+- qCDebug(lcDeveloperModeLog) << "Preloaded package not found, must use remote package";
+- // No cached package => install from repos
+- resolveAndExecute(InstallCommand);
+- } else {
+- PackageKit::Transaction *tx = PackageKit::Daemon::installFiles(QStringList() << m_localDeveloperModePackagePath);
+- connectCommandSignals(tx);
+- connect(tx, &PackageKit::Transaction::finished,
+- this, [this](PackageKit::Transaction::Exit status, uint runtime) {
+- if (status == PackageKit::Transaction::ExitSuccess) {
+- qCDebug(lcDeveloperModeLog) << "Developer mode installation from local package transaction done:" << status << runtime;
+- resetState();
+- } else if (status == PackageKit::Transaction::ExitFailed) {
+- qCWarning(lcDeveloperModeLog) << "Developer mode installation from local package failed, trying from repos";
+- m_localInstallFailed = true;
+- emit repositoryAccessRequiredChanged();
+- resolveAndExecute(InstallCommand); // TODO: If repo access is not available this can not bail out
+- } // else ExitUnknown (ignored)
+- });
+- }
+- });
+-
+- } else {
+- PackageKit::Transaction::Filters filters;
+- if (command == RemoveCommand) {
+- filters = PackageKit::Transaction::FilterInstalled;
+- } else {
+- filters = PackageKit::Transaction::FilterNewest;
+- }
+- PackageKit::Transaction *resolvePackage = PackageKit::Daemon::resolve(DEVELOPER_MODE_PACKAGE, filters);
+-
+- connect(resolvePackage, &PackageKit::Transaction::errorCode, this, &DeveloperModeSettings::reportTransactionErrorCode);
+- connect(resolvePackage, &PackageKit::Transaction::package,
+- this, [this](PackageKit::Transaction::Info info, const QString &packageId, const QString &summary) {
+- qCDebug(lcDeveloperModeLog) << "Package transaction:" << info << packageId << "summary:" << summary;
+- m_developerModePackageId = packageId;
+- });
+-
+- connect(resolvePackage, &PackageKit::Transaction::finished,
+- this, [this, command](PackageKit::Transaction::Exit status, uint runtime) {
+- Q_UNUSED(runtime)
+-
+- if (status != PackageKit::Transaction::ExitSuccess || m_developerModePackageId.isEmpty()) {
+- if (command == InstallCommand) {
+- if (m_refreshedForInstall) {
+- qCWarning(lcDeveloperModeLog) << "Failed to install developer mode, package didn't resolve.";
+- resetState();
+- } else {
+- refreshPackageCacheAndInstall(); // try once if it helps
+- }
+- } else if (command == RemoveCommand) {
+- qCWarning(lcDeveloperModeLog) << "Removing developer mode but package didn't resolve into anything. Shouldn't happen.";
+- resetState();
+- }
+-
+- } else if (command == InstallCommand) {
+- PackageKit::Transaction *tx = PackageKit::Daemon::installPackage(m_developerModePackageId);
+- connectCommandSignals(tx);
+-
+- if (m_refreshedForInstall) {
+- connect(tx, &PackageKit::Transaction::finished,
+- this, [this](PackageKit::Transaction::Exit status, uint runtime) {
+- qCDebug(lcDeveloperModeLog) << "Developer mode installation transaction done (with refresh):" << status << runtime;
+- resetState();
+- });
+- } else {
+- connect(tx, &PackageKit::Transaction::finished,
+- this, [this](PackageKit::Transaction::Exit status, uint runtime) {
+- if (status == PackageKit::Transaction::ExitSuccess) {
+- qCDebug(lcDeveloperModeLog) << "Developer mode installation transaction done:" << status << runtime;
+- resetState();
+- } else {
+- qCDebug(lcDeveloperModeLog) << "Developer mode installation failed, trying again after refresh";
+- refreshPackageCacheAndInstall();
+- }
+- });
+- }
+- } else {
+- PackageKit::Transaction *tx = PackageKit::Daemon::removePackage(m_developerModePackageId, true, true);
+- connectCommandSignals(tx);
+- connect(tx, &PackageKit::Transaction::finished,
+- this, [this](PackageKit::Transaction::Exit status, uint runtime) {
+- qCDebug(lcDeveloperModeLog) << "Developer mode removal transaction done:" << status << runtime;
+- resetState();
+- });
+- }
+- });
+- }
+-}
+-
+-void DeveloperModeSettings::connectCommandSignals(PackageKit::Transaction *transaction)
+-{
+- connect(transaction, &PackageKit::Transaction::errorCode, this, &DeveloperModeSettings::reportTransactionErrorCode);
+- connect(transaction, &PackageKit::Transaction::percentageChanged, this, [this, transaction]() {
+- updateState(transaction->percentage(), m_transactionStatus, m_transactionRole);
+- });
+-
+- connect(transaction, &PackageKit::Transaction::statusChanged, this, [this, transaction]() {
+- updateState(m_workProgress, transaction->status(), m_transactionRole);
+- });
+-
+- connect(transaction, &PackageKit::Transaction::roleChanged, this, [this, transaction]() {
+- updateState(m_workProgress, m_transactionStatus, transaction->role());
+- });
+-}
+-
+-void DeveloperModeSettings::updateState(int percentage, PackageKit::Transaction::Status status, PackageKit::Transaction::Role role)
+-{
+- // Expected changes from PackageKit when installing packages:
+- // 1. Change to 'install packages' role or 'install files' if installing from local package file
+- // 2. Status changes:
+- // setup -> refresh cache -> query -> resolve deps -> install (refer to as 'Preparing' status)
+- // -> download ('DownloadingPackages' status)
+- // -> install ('InstallingPackages' status)
+- // -> finished
+- //
+- // If installing from local package fails, it starts over!
+- //
+- // Expected changes from PackageKit when removing packages:
+- // 1. Change to 'remove packages' role
+- // 2. Status changes:
+- // setup -> remove -> resolve deps (refer to as 'Preparing' status)
+- // -> remove ('RemovingPackages' status)
+- // -> finished
+- //
+- // Notice the 'install' and 'remove' packagekit status changes occur twice.
+-
+- int progress = m_workProgress;
+- DeveloperModeSettings::Status workStatus = m_workStatus;
+-
+- m_transactionRole = role;
+- m_transactionStatus = status;
+-
+- // Do not update progress when finished or role is unknown.
+- if (m_transactionStatus == PackageKit::Transaction::StatusFinished
+- || m_transactionRole == PackageKit::Transaction::RoleUnknown) {
+- return;
+- }
+-
+- if (percentage >= 0 && percentage <= 100) {
+- int rangeStart = 0;
+- int rangeEnd = 0;
+- if (m_transactionRole == PackageKit::Transaction::RoleInstallPackages
+- || m_transactionRole == PackageKit::Transaction::RoleInstallFiles) {
+- switch (m_transactionStatus) {
+- case PackageKit::Transaction::StatusRefreshCache: // 0-10 %
+- rangeStart = 0;
+- rangeEnd = 10;
+- break;
+- case PackageKit::Transaction::StatusQuery: // fall through; packagekit progress changes 0-100 over query->resolve stages
+- case PackageKit::Transaction::StatusDepResolve: // 10-20 %
+- rangeStart = 10;
+- rangeEnd = 20;
+- break;
+- case PackageKit::Transaction::StatusDownload: // 20-60 %
+- // Skip downloading when installing from local file
+- if (m_transactionRole != PackageKit::Transaction::RoleInstallFiles) {
+- workStatus = DownloadingPackages;
+- }
+- rangeStart = 20;
+- rangeEnd = 60;
+- break;
+- case PackageKit::Transaction::StatusInstall: // 60-100 %
+- workStatus = InstallingPackages;
+- rangeStart = 60;
+- rangeEnd = 100;
+- break;
+- default:
+- break;
+- }
+- } else if (m_transactionRole == PackageKit::Transaction::RoleRemovePackages) {
+- if (m_transactionStatus == PackageKit::Transaction::StatusSetup) {
+- // Let the setup to be bound between 0-20 %
+- rangeStart = 0;
+- rangeEnd = 20;
+- } else { // 20-100 %
+- workStatus = RemovingPackages;
+- rangeStart = 20;
+- rangeEnd = 100;
+- }
+- }
+- if (rangeEnd > 0 && rangeEnd > rangeStart) {
+- progress = rangeStart + ((rangeEnd - rangeStart) * (percentage / 100.0));
+- }
+- }
+-
+- progress = qBound(0, qMax(progress, m_workProgress), 100); // Ensure the emitted progress value never decreases.
+-
+- setWorkStatus(workStatus);
+-
+- if (m_workProgress != progress) {
+- m_workProgress = progress;
+- emit workProgressChanged();
+- }
+-}
+-
+-void DeveloperModeSettings::resetState()
+-{
+- bool enabled = QFile::exists(DEVELOPER_MODE_PROVIDED_FILE);
+- if (m_developerModeEnabled != enabled) {
+- m_developerModeEnabled = enabled;
+- emit developerModeEnabledChanged();
+- }
+-
+- setWorkStatus(Idle);
+-
+- if (m_workProgress != PROGRESS_INDETERMINATE) {
+- m_workProgress = PROGRESS_INDETERMINATE;
+- emit workProgressChanged();
+- }
+-}
+-
+-void DeveloperModeSettings::setWorkStatus(DeveloperModeSettings::Status status)
+-{
+- if (m_workStatus != status) {
+- m_workStatus = status;
+- emit workStatusChanged();
+- }
+-}
+-
+-void DeveloperModeSettings::reportTransactionErrorCode(PackageKit::Transaction::Error code, const QString &details)
+-{
+- qCWarning(lcDeveloperModeLog) << "Transaction error:" << code << details;
+-}
+-
+-QString DeveloperModeSettings::usbModedGetConfig(const QString &key, const QString &fallback)
+-{
+- QString value = fallback;
+-
+- QDBusMessage msg = m_usbModeDaemon.call(USB_MODED_GET_NET_CONFIG, key);
+- QList<QVariant> result = msg.arguments();
+- if (result[0].toString() == key && result.size() == 2) {
+- value = result[1].toString();
+- }
+-
+- return value;
+-}
+-
+-void DeveloperModeSettings::usbModedSetConfig(const QString &key, const QString &value)
+-{
+- m_usbModeDaemon.call(USB_MODED_SET_NET_CONFIG, key, value);
+-}
+diff --git a/src/developermodesettings.h b/src/developermodesettings.h
+deleted file mode 100644
+index d5473f7..0000000
+--- a/src/developermodesettings.h
++++ /dev/null
+@@ -1,130 +0,0 @@
+-/*
+- * Copyright (c) 2013 – 2019 Jolla Ltd.
+- * Copyright (c) 2019 Open Mobile Platform LLC.
+- * Contact: Thomas Perl <thomas.perl@jollamobile.com>
+- *
+- * You may use this file under the terms of the BSD license as follows:
+- *
+- * "Redistribution and use in source and binary forms, with or without
+- * modification, are permitted provided that the following conditions are
+- * met:
+- * * Redistributions of source code must retain the above copyright
+- * notice, this list of conditions and the following disclaimer.
+- * * Redistributions in binary form must reproduce the above copyright
+- * notice, this list of conditions and the following disclaimer in
+- * the documentation and/or other materials provided with the
+- * distribution.
+- * * Neither the name of Nemo Mobile nor the names of its contributors
+- * may be used to endorse or promote products derived from this
+- * software without specific prior written permission.
+- *
+- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+- */
+-
+-#ifndef DEVELOPERMODESETTINGS_H
+-#define DEVELOPERMODESETTINGS_H
+-
+-#include <QObject>
+-#include <QDBusInterface>
+-
+-#include <systemsettingsglobal.h>
+-#include <daemon.h>
+-
+-
+-QT_BEGIN_NAMESPACE
+-class QDBusPendingCallWatcher;
+-QT_END_NAMESPACE
+-
+-class SYSTEMSETTINGS_EXPORT DeveloperModeSettings : public QObject
+-{
+- Q_OBJECT
+- Q_ENUMS(Status)
+-
+- Q_PROPERTY(QString wlanIpAddress READ wlanIpAddress NOTIFY wlanIpAddressChanged)
+- Q_PROPERTY(QString usbIpAddress READ usbIpAddress NOTIFY usbIpAddressChanged)
+- Q_PROPERTY(QString username READ username CONSTANT)
+- Q_PROPERTY(bool developerModeEnabled READ developerModeEnabled NOTIFY developerModeEnabledChanged)
+- Q_PROPERTY(enum DeveloperModeSettings::Status workStatus READ workStatus NOTIFY workStatusChanged)
+- Q_PROPERTY(int workProgress READ workProgress NOTIFY workProgressChanged)
+- Q_PROPERTY(bool repositoryAccessRequired READ repositoryAccessRequired NOTIFY repositoryAccessRequiredChanged)
+-
+-public:
+- explicit DeveloperModeSettings(QObject *parent = NULL);
+- virtual ~DeveloperModeSettings();
+-
+- enum Status {
+- Idle = 0,
+- Preparing,
+- DownloadingPackages,
+- InstallingPackages,
+- RemovingPackages
+- };
+-
+- QString wlanIpAddress() const;
+- QString usbIpAddress() const;
+- QString username() const;
+- bool developerModeEnabled() const;
+- enum DeveloperModeSettings::Status workStatus() const;
+- int workProgress() const;
+- bool repositoryAccessRequired() const;
+-
+- Q_INVOKABLE void setDeveloperMode(bool enabled);
+- Q_INVOKABLE void setUsbIpAddress(const QString &usbIpAddress);
+- Q_INVOKABLE void refresh();
+-
+-signals:
+- void wlanIpAddressChanged();
+- void usbIpAddressChanged();
+- void developerModeEnabledChanged();
+- void workStatusChanged();
+- void workProgressChanged();
+- void repositoryAccessRequiredChanged();
+-
+-private slots:
+- void reportTransactionErrorCode(PackageKit::Transaction::Error code, const QString &details);
+- void updateState(int percentage, PackageKit::Transaction::Status status, PackageKit::Transaction::Role role);
+-
+-private:
+- enum Command {
+- InstallCommand,
+- RemoveCommand
+- };
+-
+- void resetState();
+- void setWorkStatus(Status status);
+- void refreshPackageCacheAndInstall();
+- void resolveAndExecute(Command command);
+- void connectCommandSignals(PackageKit::Transaction *transaction);
+-
+- QString usbModedGetConfig(const QString &key, const QString &fallback);
+- void usbModedSetConfig(const QString &key, const QString &value);
+-
+- QDBusInterface m_usbModeDaemon;
+- QString m_wlanIpAddress;
+- QString m_usbInterface;
+- QString m_usbIpAddress;
+- QString m_username;
+- QString m_developerModePackageId;
+- bool m_developerModeEnabled;
+- DeveloperModeSettings::Status m_workStatus;
+- int m_workProgress;
+- PackageKit::Transaction::Role m_transactionRole;
+- PackageKit::Transaction::Status m_transactionStatus;
+- bool m_refreshedForInstall;
+- bool m_localInstallFailed;
+- QString m_localDeveloperModePackagePath;
+-};
+-
+-Q_DECLARE_METATYPE(DeveloperModeSettings::Status)
+-
+-#endif /* DEVELOPERMODESETTINGS_H */
+diff --git a/src/plugin/plugin.cpp b/src/plugin/plugin.cpp
+index 8174c09..b7db9ae 100644
+--- a/src/plugin/plugin.cpp
++++ b/src/plugin/plugin.cpp
+@@ -43,7 +43,6 @@
+ #include "alarmtonemodel.h"
+ #include "displaysettings.h"
+ #include "aboutsettings.h"
+-#include "developermodesettings.h"
+ #include "batterystatus.h"
+ #include "diskusage.h"
+ #include "partitionmodel.h"
+@@ -104,10 +103,8 @@ public:
+ qmlRegisterType<AboutSettings>(uri, 1, 0, "AboutSettings");
+ qmlRegisterType<PartitionModel>(uri, 1, 0, "PartitionModel");
+ qRegisterMetaType<Partition>("Partition");
+- qmlRegisterType<DeveloperModeSettings>(uri, 1, 0, "DeveloperModeSettings");
+ qmlRegisterType<CertificateModel>(uri, 1, 0, "CertificateModel");
+ qmlRegisterSingletonType<SettingsVpnModel>(uri, 1, 0, "SettingsVpnModel", api_factory<SettingsVpnModel>);
+- qRegisterMetaType<DeveloperModeSettings::Status>("DeveloperModeSettings::Status");
+ qmlRegisterType<BatteryStatus>(uri, 1, 0, "BatteryStatus");
+ qmlRegisterType<DiskUsage>(uri, 1, 0, "DiskUsage");
+ qmlRegisterType<LocationSettings>(uri, 1, 0, "LocationSettings");
+diff --git a/src/plugin/plugin.pro b/src/plugin/plugin.pro
+index 9a05fa9..cfd58e7 100644
+--- a/src/plugin/plugin.pro
++++ b/src/plugin/plugin.pro
+@@ -6,7 +6,7 @@ CONFIG += qt plugin c++11 hide_symbols link_pkgconfig
+ QT += qml dbus network
+ QT -= gui
+
+-PKGCONFIG += profile usb-moded-qt5 nemomodels-qt5 libsailfishkeyprovider connman-qt5 packagekitqt5
++PKGCONFIG += profile usb-moded-qt5 nemomodels-qt5 libsailfishkeyprovider connman-qt5
+
+ target.path = $$[QT_INSTALL_QML]/$$PLUGIN_IMPORT_PATH
+ INSTALLS += target
+diff --git a/src/src.pro b/src/src.pro
+index 791a3f7..57f59b4 100644
+--- a/src/src.pro
++++ b/src/src.pro
+@@ -7,7 +7,7 @@ QT -= gui
+
+ CONFIG += c++11 hide_symbols link_pkgconfig
+ PKGCONFIG += profile mlite5 mce timed-qt5 blkid libcrypto nemomodels-qt5 libsailfishkeyprovider connman-qt5 glib-2.0
+-PKGCONFIG += ssu-sysinfo nemodbus packagekitqt5 libsystemd sailfishusermanager sailfishaccesscontrol
++PKGCONFIG += ssu-sysinfo nemodbus libsystemd sailfishusermanager sailfishaccesscontrol
+
+ system(qdbusxml2cpp -p mceiface.h:mceiface.cpp mce.xml)
+
+@@ -22,8 +22,7 @@ SOURCES += \
+ mceiface.cpp \
+ displaysettings.cpp \
+ aboutsettings.cpp \
+- certificatemodel.cpp \
+- developermodesettings.cpp \
++ certificatemodel.cpp \
+ batterystatus.cpp \
+ diskusage.cpp \
+ diskusage_impl.cpp \
+@@ -51,7 +50,6 @@ PUBLIC_HEADERS = \
+ aboutsettings.h \
+ certificatemodel.h \
+ settingsvpnmodel.h \
+- developermodesettings.h \
+ batterystatus.h \
+ udisks2block_p.h \
+ udisks2defines.h \
diff --git a/testing/nemo-qml-plugin-systemsettings/0002-Remove-broken-certificatemodel.patch b/testing/nemo-qml-plugin-systemsettings/0002-Remove-broken-certificatemodel.patch
new file mode 100644
index 0000000000..c12acc6599
--- /dev/null
+++ b/testing/nemo-qml-plugin-systemsettings/0002-Remove-broken-certificatemodel.patch
@@ -0,0 +1,996 @@
+Sadly this functionality is broken with no available patch to fix it.
+To make sure we can still build and use this package, let's just remove it.
+
+diff --git a/src/certificatemodel.cpp b/src/certificatemodel.cpp
+deleted file mode 100644
+index f568fec..0000000
+--- a/src/certificatemodel.cpp
++++ /dev/null
+@@ -1,800 +0,0 @@
+-/*
+- * Copyright (c) 2016 - 2019 Jolla Ltd.
+- * Copyright (c) 2019 Open Mobile Platform LLC.
+- *
+- * You may use this file under the terms of the BSD license as follows:
+- *
+- * "Redistribution and use in source and binary forms, with or without
+- * modification, are permitted provided that the following conditions are
+- * met:
+- * * Redistributions of source code must retain the above copyright
+- * notice, this list of conditions and the following disclaimer.
+- * * Redistributions in binary form must reproduce the above copyright
+- * notice, this list of conditions and the following disclaimer in
+- * the documentation and/or other materials provided with the
+- * distribution.
+- * * Neither the name of Nemo Mobile nor the names of its contributors
+- * may be used to endorse or promote products derived from this
+- * software without specific prior written permission.
+- *
+- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+- */
+-
+-#include "certificatemodel.h"
+-
+-#include <QFile>
+-#include <QRegularExpression>
+-#include <QDebug>
+-#include <functional>
+-
+-#include <openssl/opensslv.h>
+-#include <openssl/bio.h>
+-#include <openssl/conf.h>
+-#include <openssl/engine.h>
+-#include <openssl/err.h>
+-#include <openssl/evp.h>
+-#include <openssl/pem.h>
+-#include <openssl/x509v3.h>
+-
+-namespace {
+-
+-struct X509List;
+-
+-}
+-
+-struct X509Certificate
+-{
+- QList<QPair<QString, QString>> subjectList(bool shortForm = false) const
+- {
+- return nameList(X509_get_subject_name(x509), shortForm);
+- }
+- QString subject(bool shortForm = true, const QString &separator = QString(", ")) const
+- {
+- return toString(subjectList(shortForm), separator);
+- }
+- QString subjectElement(int nid) const
+- {
+- return nameElement(X509_get_subject_name(x509), nid);
+- }
+-
+- QList<QPair<QString, QString>> issuerList(bool shortForm = false) const
+- {
+- return nameList(X509_get_issuer_name(x509), shortForm);
+- }
+- QString issuer(bool shortForm = true, const QString &separator = QString(", ")) const
+- {
+- return toString(issuerList(shortForm), separator);
+- }
+- QString issuerElement(int nid) const
+- {
+- return nameElement(X509_get_issuer_name(x509), nid);
+- }
+-
+- QString version() const
+- {
+- return QString::number(X509_get_version(x509) + 1);
+- }
+-
+- QString serialNumber() const
+- {
+- return integerToString(X509_get_serialNumber(x509));
+- }
+-
+- QDateTime notBefore() const
+- {
+- return toDateTime(X509_get_notBefore(x509));
+- }
+-
+- QDateTime notAfter() const
+- {
+- return toDateTime(X509_get_notAfter(x509));
+- }
+-
+- QList<QPair<QString, QString>> publicKeyList(bool shortForm = false) const
+- {
+- QList<QPair<QString, QString>> rv;
+-
+- if (EVP_PKEY *key = X509_get_pubkey(x509)) {
+- rv.append(qMakePair(QStringLiteral("Algorithm"), idToString(EVP_PKEY_id(key), shortForm)));
+- rv.append(qMakePair(QStringLiteral("Bits"), QString::number(EVP_PKEY_bits(key))));
+-
+- BIO *b = BIO_new(BIO_s_mem());
+- EVP_PKEY_print_public(b, key, 0, 0);
+- const QList<QPair<QString, QString>> &details(parseData(bioToString(b)));
+- for (auto it = details.cbegin(), end = details.cend(); it != end; ++it) {
+- rv.append(qMakePair(it->first, it->second));
+- }
+- BIO_free(b);
+-
+- EVP_PKEY_free(key);
+- }
+-
+- return rv;
+- }
+-
+- QList<QPair<QString, QString>> extensionList(bool shortForm = false) const
+- {
+- QList<QPair<QString, QString>> rv;
+-
+- for (int i = 0, n = sk_X509_EXTENSION_num(x509->cert_info->extensions); i < n; ++i) {
+- X509_EXTENSION *extension = sk_X509_EXTENSION_value(x509->cert_info->extensions, i);
+-
+- ASN1_OBJECT *object = X509_EXTENSION_get_object(extension);
+- int nid = OBJ_obj2nid(object);
+- if (nid == NID_undef)
+- continue;
+-
+- QString name(objectToString(object, shortForm));
+- if (X509_EXTENSION_get_critical(extension) > 0) {
+- name.append(QStringLiteral(" (Critical)"));
+- }
+-
+- BIO *b = BIO_new(BIO_s_mem());
+- X509V3_EXT_print(b, extension, 0, 0);
+- rv.append(qMakePair(name, bioToString(b)));
+- BIO_free(b);
+- }
+-
+- return rv;
+- }
+-
+- QList<QPair<QString, QString>> signatureList(bool shortForm = false) const
+- {
+- QList<QPair<QString, QString>> rv;
+-
+- rv.append(qMakePair(QStringLiteral("Algorithm"), objectToString(x509->sig_alg->algorithm, shortForm)));
+-
+- BIO *b = BIO_new(BIO_s_mem());
+- X509_signature_dump(b, x509->signature, 0);
+- QString d(bioToString(b).replace(QChar('\n'), QString()));
+- rv.append(qMakePair(QStringLiteral("Data"), d.trimmed()));
+- BIO_free(b);
+-
+- return rv;
+- }
+-
+-private:
+- static QString stringToString(ASN1_STRING *data)
+- {
+- return QString::fromUtf8(reinterpret_cast<char*>(ASN1_STRING_data(data)));
+- }
+-
+- static QString timeToString(ASN1_TIME *data)
+- {
+- return stringToString(data);
+- }
+-
+- static QString idToString(int nid, bool shortForm)
+- {
+- return QString::fromUtf8(shortForm ? OBJ_nid2sn(nid) : OBJ_nid2ln(nid));
+- }
+-
+- static QString objectToString(ASN1_OBJECT *object, bool shortForm)
+- {
+- return idToString(OBJ_obj2nid(object), shortForm);
+- }
+-
+- static QString integerToString(ASN1_INTEGER *integer)
+- {
+- if (integer->type != V_ASN1_INTEGER && integer->type != V_ASN1_NEG_INTEGER)
+- return QString();
+-
+- quint64 value = 0;
+- for (size_t i = 0, n = qMin(integer->length, 8); i < n; ++i)
+- value = value << 8 | integer->data[i];
+-
+- QString rv = QString::number(value);
+- if (integer->type == V_ASN1_NEG_INTEGER)
+- rv.prepend(QStringLiteral("-"));
+- return rv;
+- }
+-
+- static QString bioToString(BIO *bio)
+- {
+- char *out = 0;
+- int n = BIO_get_mem_data(bio, &out);
+- return QString::fromUtf8(QByteArray::fromRawData(out, n));
+- }
+-
+- static QList<QPair<QString, QString>> nameList(X509_NAME *name, bool shortForm = true)
+- {
+- QList<QPair<QString, QString>> rv;
+-
+- for (int i = 0, n = X509_NAME_entry_count(name); i < n; ++i) {
+- X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i);
+- ASN1_OBJECT *object = X509_NAME_ENTRY_get_object(entry);
+- ASN1_STRING *data = X509_NAME_ENTRY_get_data(entry);
+- rv.append(qMakePair(objectToString(object, shortForm), stringToString(data)));
+- }
+-
+- return rv;
+- }
+-
+- static QString nameElement(X509_NAME *name, int nid)
+- {
+- for (int i = 0, n = X509_NAME_entry_count(name); i < n; ++i) {
+- X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i);
+- ASN1_OBJECT *object = X509_NAME_ENTRY_get_object(entry);
+- if (OBJ_obj2nid(object) == nid) {
+- ASN1_STRING *data = X509_NAME_ENTRY_get_data(entry);
+- return stringToString(data);
+- }
+- }
+-
+- return QString();
+- }
+-
+- static QString toString(const QList<QPair<QString, QString>> &list, const QString &separator = QString(", "))
+- {
+- QString rv;
+-
+- for (auto it = list.cbegin(), end = list.cend(); it != end; ++it) {
+- if (!rv.isEmpty()) {
+- rv.append(separator);
+- }
+- rv.append(it->first);
+- rv.append(QChar(':'));
+- rv.append(it->second);
+- }
+-
+- return rv;
+- }
+-
+- static QDateTime toDateTime(ASN1_TIME *time)
+- {
+- const QString ts(timeToString(time));
+- return (time->type == V_ASN1_GENERALIZEDTIME ? fromGENERALIZEDTIME(ts) : fromUTCTIME(ts));
+- }
+-
+- static QDateTime fromUTCTIME(const QString &ts)
+- {
+- QDate d;
+- QTime t;
+- int offset = 0;
+-
+- // "YYMMDDhhmm[ss](Z|(+|-)hhmm)"
+- const QRegularExpression re("([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})?(Z)?(([+-])([0-9]{2})([0-9]{2}))?");
+- QRegularExpressionMatch match = re.match(ts);
+- if (match.hasMatch()) {
+- int y = match.captured(1).toInt();
+- d = QDate((y < 70 ? 2000 : 1900) + y, match.captured(2).toInt(), match.captured(3).toInt());
+-
+- t = QTime(match.captured(4).toInt(), match.captured(5).toInt(), match.captured(6).toInt());
+-
+- if (match.lastCapturedIndex() > 7) {
+- offset = match.captured(11).toInt() * 60 + match.captured(10).toInt() * 60*60;
+- if (match.captured(9) == "-") {
+- offset = -offset;
+- }
+- }
+- }
+-
+- return QDateTime(d, t, Qt::OffsetFromUTC, offset);
+- }
+-
+- static QDateTime fromGENERALIZEDTIME(const QString &ts)
+- {
+- QDate d;
+- QTime t;
+- int offset = 0;
+-
+- // "YYYYMMDDhh[mm[ss[.fff]]](Z|(+|-)hhmm)" <- nested optionals can be treated as appearing sequentially
+- const QRegularExpression re("([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})?([0-9]{2})?(\\.[0-9]{1,3})?(Z)?(([+-])([0-9]{2})([0-9]{2}))?");
+- QRegularExpressionMatch match = re.match(ts);
+- if (match.hasMatch()) {
+- d = QDate(match.captured(1).toInt(), match.captured(2).toInt(), match.captured(3).toInt());
+-
+- double fraction = match.captured(7).toDouble();
+- int ms = (fraction * 1000);
+- t = QTime(match.captured(4).toInt(), match.captured(5).toInt(), match.captured(6).toInt(), ms);
+-
+- if (match.lastCapturedIndex() > 8) {
+- offset = match.captured(12).toInt() * 60 + match.captured(11).toInt() * 60*60;
+- if (match.captured(10) == "-") {
+- offset = -offset;
+- }
+- }
+- }
+-
+- return QDateTime(d, t, Qt::OffsetFromUTC, offset);
+- }
+-
+- static QList<QPair<QString, QString>> parseData(QString data)
+- {
+- QList<QPair<QString, QString>> rv;
+-
+- // Join any data with the preceding header
+- data.replace(QRegularExpression(": *\n +"), QStringLiteral(":"));
+- foreach (const QString &line, data.split(QString("\n"), QString::SkipEmptyParts)) {
+- int index = line.indexOf(QChar(':'));
+- if (index != -1) {
+- QString name(line.left(index));
+- QString value(line.mid(index + 1).trimmed());
+- rv.append(qMakePair(name, value));
+- }
+- }
+-
+- return rv;
+- }
+-
+- friend struct ::X509List;
+-
+- X509Certificate(X509 *x) : x509(x) {}
+-
+- X509 *x509;
+-};
+-
+-namespace {
+-
+-struct X509List
+-{
+- X509List()
+- : crlStack(0), certificateStack(0), pkcs7(0), pkcs7Signed(0)
+- {
+- crlStack = sk_X509_CRL_new_null();
+- if (!crlStack) {
+- qWarning() << "Unable to allocate CRL stack";
+- } else {
+- certificateStack = sk_X509_new_null();
+- if (!certificateStack) {
+- qWarning() << "Unable to allocate X509 stack";
+- } else {
+- pkcs7 = PKCS7_new();
+- pkcs7Signed = PKCS7_SIGNED_new();
+- if (!pkcs7 || !pkcs7Signed) {
+- qWarning() << "Unable to create PKCS7 structures";
+- } else {
+- pkcs7Signed->crl = crlStack;
+- pkcs7Signed->cert = certificateStack;
+-
+- pkcs7->type = OBJ_nid2obj(NID_pkcs7_signed);
+- pkcs7->d.sign = pkcs7Signed;
+- pkcs7Signed->contents->type = OBJ_nid2obj(NID_pkcs7_data);
+- if (!ASN1_INTEGER_set(pkcs7Signed->version, 1)) {
+- qWarning() << "Unable to set PKCS7 signed version";
+- }
+- }
+- }
+- }
+- }
+-
+- ~X509List()
+- {
+- /* Apparently, pkcs7Signed and pkcs7 cannot be safely freed...
+- if (pkcs7Signed)
+- PKCS7_SIGNED_free(pkcs7Signed);
+- if (pkcs7)
+- PKCS7_free(pkcs7);
+- */
+- if (certificateStack)
+- sk_X509_free(certificateStack);
+- if (crlStack)
+- sk_X509_CRL_free(crlStack);
+- }
+-
+- bool isValid() const
+- {
+- return pkcs7 && pkcs7Signed;
+- }
+-
+- int count() const
+- {
+- return sk_X509_num(certificateStack);
+- }
+-
+- void append(X509 *x509)
+- {
+- sk_X509_push(certificateStack, x509);
+- }
+-
+- void for_each(std::function<void (const X509Certificate &)> fn) const
+- {
+- for (int i = 0, n(count()); i < n; ++i) {
+- fn(X509Certificate(sk_X509_value(certificateStack, i)));
+- }
+- }
+-
+-private:
+- STACK_OF(X509_CRL) *crlStack;
+- STACK_OF(X509) *certificateStack;
+- PKCS7 *pkcs7;
+- PKCS7_SIGNED *pkcs7Signed;
+-};
+-
+-struct PKCS7File
+-{
+- explicit PKCS7File(const QString &path)
+- {
+- if (!isValid()) {
+- qWarning() << "Unable to prepare X509 certificates structure";
+- } else {
+- BIO *input = BIO_new(BIO_s_file());
+- if (!input) {
+- qWarning() << "Unable to allocate new BIO for:" << path;
+- } else {
+- const QByteArray filename(QFile::encodeName(path));
+- if (BIO_read_filename(input, const_cast<char *>(filename.constData())) <= 0) {
+- qWarning() << "Unable to open PKCS7 file:" << path;
+- } else {
+- read_pem_from_bio(input);
+- }
+-
+- BIO_free(input);
+- }
+- }
+- }
+-
+- explicit PKCS7File(const QByteArray &pem)
+- {
+- if (!isValid()) {
+- qWarning() << "Unable to prepare X509 certificates structure";
+- } else {
+- BIO *input = BIO_new_mem_buf(pem.constData(), pem.length());
+- if (!input) {
+- qWarning() << "Unable to allocate new BIO while importing in-memory PEM";
+- } else {
+- read_pem_from_bio(input);
+- BIO_free(input);
+- }
+- }
+- }
+-
+- void read_pem_from_bio(BIO *input) {
+- STACK_OF(X509_INFO) *certificateStack = PEM_X509_INFO_read_bio(input, NULL, NULL, NULL);
+- if (!certificateStack) {
+- qWarning() << "Unable to read PKCS7 data";
+- } else {
+- while (sk_X509_INFO_num(certificateStack)) {
+- X509_INFO *certificateInfo = sk_X509_INFO_shift(certificateStack);
+- if (certificateInfo->x509 != NULL) {
+- certs.append(certificateInfo->x509);
+- certificateInfo->x509 = NULL;
+- }
+- X509_INFO_free(certificateInfo);
+- }
+-
+- sk_X509_INFO_free(certificateStack);
+- }
+- }
+-
+- ~PKCS7File()
+- {
+- }
+-
+- bool isValid() const
+- {
+- return certs.isValid();
+- }
+-
+- int count() const
+- {
+- return certs.count();
+- }
+-
+- const X509List &getCertificates()
+- {
+- return certs;
+- }
+-
+-private:
+- X509List certs;
+-};
+-
+-class LibCrypto
+-{
+- struct Initializer
+- {
+- Initializer()
+- {
+- // As per: https://wiki.openssl.org/index.php/Library_Initialization#libcrypto_Initialization
+- OpenSSL_add_all_algorithms();
+- ERR_load_crypto_strings();
+- OPENSSL_config(NULL);
+- }
+-
+- ~Initializer()
+- {
+- FIPS_mode_set(0);
+- ENGINE_cleanup();
+- CONF_modules_unload(1);
+- EVP_cleanup();
+- CRYPTO_cleanup_all_ex_data();
+- ERR_remove_thread_state(NULL);
+- ERR_free_strings();
+- }
+- };
+-
+- static Initializer init;
+-
+-public:
+- template<class T>
+- static QList<Certificate> getCertificates(const T &bundleData)
+- {
+- PKCS7File bundle(bundleData);
+-
+- return bundleToCertificates(bundle);
+- }
+-private:
+- static QList<Certificate> bundleToCertificates(PKCS7File &bundle)
+- {
+- QList<Certificate> certificates;
+- if (bundle.isValid() && bundle.count() > 0) {
+- certificates.reserve(bundle.count());
+- bundle.getCertificates().for_each([&certificates](const X509Certificate &cert) {
+- certificates.append(Certificate(cert));
+- });
+- }
+-
+- return certificates;
+- }
+-};
+-
+-
+-LibCrypto::Initializer LibCrypto::init;
+-
+-const QList<QPair<QString, CertificateModel::BundleType> > &bundlePaths()
+-{
+- static QList<QPair<QString, CertificateModel::BundleType> > paths;
+- if (paths.isEmpty()) {
+- paths.append(qMakePair(QString("/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"), CertificateModel::TLSBundle));
+- paths.append(qMakePair(QString("/etc/pki/ca-trust/extracted/pem/email-ca-bundle.pem"), CertificateModel::EmailBundle));
+- paths.append(qMakePair(QString("/etc/pki/ca-trust/extracted/pem/objsign-ca-bundle.pem"), CertificateModel::ObjectSigningBundle));
+- }
+- return paths;
+-}
+-
+-CertificateModel::BundleType bundleType(const QString &path)
+-{
+- if (path.isEmpty())
+- return CertificateModel::NoBundle;
+-
+- const QList<QPair<QString, CertificateModel::BundleType> > &bundles(bundlePaths());
+- for (auto it = bundles.cbegin(), end = bundles.cend(); it != end; ++it) {
+- if (it->first == path)
+- return it->second;
+- }
+-
+- return CertificateModel::UserSpecifiedBundle;
+-}
+-
+-QString bundlePath(CertificateModel::BundleType type)
+-{
+- if (type == CertificateModel::UserSpecifiedBundle)
+- return QString();
+-
+- const QList<QPair<QString, CertificateModel::BundleType> > &bundles(bundlePaths());
+- for (auto it = bundles.cbegin(), end = bundles.cend(); it != end; ++it) {
+- if (it->second == type)
+- return it->first;
+- }
+-
+- return QStringLiteral("");
+-}
+-
+-}
+-
+-Certificate::Certificate(const X509Certificate &cert)
+- : m_commonName(cert.subjectElement(NID_commonName))
+- , m_countryName(cert.subjectElement(NID_countryName))
+- , m_organizationName(cert.subjectElement(NID_organizationName))
+- , m_organizationalUnitName(cert.subjectElement(NID_organizationalUnitName))
+- , m_notValidBefore(cert.notBefore())
+- , m_notValidAfter(cert.notAfter())
+-{
+- // Yield consistent names for the certificates, despite inconsistent naming policy
+- QString Certificate::*members[] = { &Certificate::m_commonName, &Certificate::m_organizationalUnitName, &Certificate::m_organizationName, &Certificate::m_countryName };
+- for (auto it = std::begin(members); it != std::end(members); ++it) {
+- const QString &s(this->*(*it));
+- if (!s.isEmpty()) {
+- if (m_primaryName.isEmpty()) {
+- m_primaryName = s;
+- } else if (m_secondaryName.isEmpty()) {
+- m_secondaryName = s;
+- break;
+- }
+- }
+- }
+-
+- // Matches QSslCertificate::issuerDisplayName() introducd in Qt 5.12
+- // Returns a name that describes the issuer. It returns the CommonName if
+- // available, otherwise falls back to the Organization or the first
+- // OrganizationalUnitName.
+- m_issuerDisplayName = cert.issuerElement(NID_commonName);
+- if (m_issuerDisplayName.isEmpty()) {
+- m_issuerDisplayName = cert.issuerElement(NID_countryName);
+- }
+- if (m_issuerDisplayName.isEmpty()) {
+- m_issuerDisplayName = cert.issuerElement(NID_organizationName);
+- }
+-
+- // Populate the details map
+- m_details.insert(QStringLiteral("Version"), QVariant(cert.version()));
+- m_details.insert(QStringLiteral("SerialNumber"), QVariant(cert.serialNumber()));
+- m_details.insert(QStringLiteral("SubjectDisplayName"), QVariant(m_primaryName));
+- m_details.insert(QStringLiteral("OrganizationName"), QVariant(m_organizationName));
+- m_details.insert(QStringLiteral("IssuerDisplayName"), QVariant(m_issuerDisplayName));
+-
+- QVariantMap validity;
+- validity.insert(QStringLiteral("NotBefore"), QVariant(cert.notBefore()));
+- validity.insert(QStringLiteral("NotAfter"), QVariant(cert.notAfter()));
+- m_details.insert(QStringLiteral("Validity"), QVariant(validity));
+-
+- QVariantMap issuer;
+- const QList<QPair<QString, QString>> &issuerDetails(cert.issuerList());
+- for (auto it = issuerDetails.cbegin(), end = issuerDetails.cend(); it != end; ++it) {
+- issuer.insert(it->first, QVariant(it->second));
+- }
+- m_details.insert(QStringLiteral("Issuer"), QVariant(issuer));
+-
+- QVariantMap subject;
+- const QList<QPair<QString, QString>> &subjectDetails(cert.subjectList());
+- for (auto it = subjectDetails.cbegin(), end = subjectDetails.cend(); it != end; ++it) {
+- subject.insert(it->first, QVariant(it->second));
+- }
+- m_details.insert(QStringLiteral("Subject"), QVariant(subject));
+-
+- QVariantMap publicKey;
+- const QList<QPair<QString, QString>> &keyDetails(cert.publicKeyList());
+- for (auto it = keyDetails.cbegin(), end = keyDetails.cend(); it != end; ++it) {
+- publicKey.insert(it->first, QVariant(it->second));
+- }
+- m_details.insert(QStringLiteral("SubjectPublicKeyInfo"), QVariant(publicKey));
+-
+- QVariantMap extensions;
+- const QList<QPair<QString, QString>> &extensionDetails(cert.extensionList());
+- for (auto it = extensionDetails.cbegin(), end = extensionDetails.cend(); it != end; ++it) {
+- extensions.insert(it->first, QVariant(it->second));
+- }
+- m_details.insert(QStringLiteral("Extensions"), extensions);
+-
+- QVariantMap signature;
+- const QList<QPair<QString, QString>> &signatureDetails(cert.signatureList());
+- for (auto it = signatureDetails.cbegin(), end = signatureDetails.cend(); it != end; ++it) {
+- signature.insert(it->first, QVariant(it->second));
+- }
+- m_details.insert(QStringLiteral("Signature"), signature);
+-}
+-
+-CertificateModel::CertificateModel(QObject *parent)
+- : QAbstractListModel(parent)
+- , m_type(NoBundle)
+-{
+-}
+-
+-CertificateModel::~CertificateModel()
+-{
+-}
+-
+-CertificateModel::BundleType CertificateModel::bundleType() const
+-{
+- return m_type;
+-}
+-
+-void CertificateModel::setBundleType(BundleType type)
+-{
+- if (m_type != type) {
+- m_type = type;
+-
+- const QString path(::bundlePath(m_type));
+- if (!path.isNull())
+- setBundlePath(path);
+-
+- emit bundleTypeChanged();
+- }
+-}
+-
+-QString CertificateModel::bundlePath() const
+-{
+- return m_path;
+-}
+-
+-void CertificateModel::setBundlePath(const QString &path)
+-{
+- if (m_path != path) {
+- m_path = path;
+- refresh();
+-
+- const BundleType type(::bundleType(m_path));
+- setBundleType(type);
+-
+- emit bundlePathChanged();
+- }
+-}
+-
+-int CertificateModel::rowCount(const QModelIndex & parent) const
+-{
+- Q_UNUSED(parent)
+- return m_certificates.count();
+-}
+-
+-QVariant CertificateModel::data(const QModelIndex &index, int role) const
+-{
+- int row = index.row();
+- if (row < 0 || row >= m_certificates.count()) {
+- return QVariant();
+- }
+-
+- const Certificate &cert = m_certificates.at(row);
+- switch (role) {
+- case CommonNameRole:
+- return cert.commonName();
+- case CountryNameRole:
+- return cert.countryName();
+- case OrganizationNameRole:
+- return cert.organizationName();
+- case OrganizationalUnitNameRole:
+- return cert.organizationalUnitName();
+- case PrimaryNameRole:
+- return cert.primaryName();
+- case SecondaryNameRole:
+- return cert.secondaryName();
+- case NotValidBeforeRole:
+- return cert.notValidBefore();
+- case NotValidAfterRole:
+- return cert.notValidAfter();
+- case DetailsRole:
+- return cert.details();
+- default:
+- break;
+- }
+-
+- return QVariant();
+-}
+-
+-QHash<int, QByteArray> CertificateModel::roleNames() const
+-{
+- QHash<int, QByteArray> roles;
+-
+- roles[CommonNameRole] = "commonName";
+- roles[CountryNameRole] = "countryName";
+- roles[OrganizationNameRole] = "organizationName";
+- roles[OrganizationalUnitNameRole] = "organizationalUnitName";
+- roles[PrimaryNameRole] = "primaryName";
+- roles[SecondaryNameRole] = "secondaryName";
+- roles[NotValidBeforeRole] = "notValidBefore";
+- roles[NotValidAfterRole] = "notValidAfter";
+- roles[DetailsRole] = "details";
+-
+- return roles;
+-}
+-void CertificateModel::refresh()
+-{
+- beginResetModel();
+- if (m_path.isEmpty()) {
+- m_certificates.clear();
+- } else {
+- m_certificates = getCertificates(m_path);
+- std::stable_sort(m_certificates.begin(), m_certificates.end(), [](const Certificate &lhs, const Certificate &rhs) {
+- int c = lhs.primaryName().compare(rhs.primaryName(), Qt::CaseInsensitive);
+- if (c < 0)
+- return true;
+- if (c > 0)
+- return false;
+- c = lhs.secondaryName().compare(rhs.secondaryName(), Qt::CaseInsensitive);
+- if (c < 0)
+- return true;
+- return false;
+- });
+- }
+- endResetModel();
+-}
+-
+-QList<Certificate> CertificateModel::getCertificates(const QString &bundlePath)
+-{
+- return LibCrypto::getCertificates(bundlePath);
+-}
+-
+-QList<Certificate> CertificateModel::getCertificates(const QByteArray &pem)
+-{
+- return LibCrypto::getCertificates(pem);
+-}
+diff --git a/src/certificatemodel.h b/src/certificatemodel.h
+deleted file mode 100644
+index 8573ac4..0000000
+--- a/src/certificatemodel.h
++++ /dev/null
+@@ -1,139 +0,0 @@
+-/*
+- * Copyright (c) 2016 - 2019 Jolla Ltd.
+- * Copyright (c) 2019 Open Mobile Platform LLC.
+- *
+- * You may use this file under the terms of the BSD license as follows:
+- *
+- * "Redistribution and use in source and binary forms, with or without
+- * modification, are permitted provided that the following conditions are
+- * met:
+- * * Redistributions of source code must retain the above copyright
+- * notice, this list of conditions and the following disclaimer.
+- * * Redistributions in binary form must reproduce the above copyright
+- * notice, this list of conditions and the following disclaimer in
+- * the documentation and/or other materials provided with the
+- * distribution.
+- * * Neither the name of Nemo Mobile nor the names of its contributors
+- * may be used to endorse or promote products derived from this
+- * software without specific prior written permission.
+- *
+- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+- */
+-
+-#ifndef CERTIFICATEMODEL_H
+-#define CERTIFICATEMODEL_H
+-
+-#include <QAbstractListModel>
+-#include <QDateTime>
+-#include <QList>
+-#include <QVariantMap>
+-
+-#include "systemsettingsglobal.h"
+-
+-
+-struct X509Certificate;
+-
+-class SYSTEMSETTINGS_EXPORT Certificate
+-{
+-public:
+- Certificate(const X509Certificate &cert);
+-
+- QString commonName() const { return m_commonName; }
+- QString countryName() const { return m_countryName; }
+- QString organizationName() const { return m_organizationName; }
+- QString organizationalUnitName() const { return m_organizationalUnitName; }
+- QString primaryName() const { return m_primaryName; }
+- QString secondaryName() const { return m_secondaryName; }
+-
+- QDateTime notValidBefore() const { return m_notValidBefore; }
+- QDateTime notValidAfter() const { return m_notValidAfter; }
+-
+- QVariantMap details() const { return m_details; }
+-
+- QString issuerDisplayName() const { return m_issuerDisplayName; }
+-
+-private:
+- QString m_commonName;
+- QString m_countryName;
+- QString m_organizationName;
+- QString m_organizationalUnitName;
+- QString m_primaryName;
+- QString m_secondaryName;
+-
+- QDateTime m_notValidBefore;
+- QDateTime m_notValidAfter;
+-
+- QString m_issuerDisplayName;
+-
+- QVariantMap m_details;
+-};
+-
+-class SYSTEMSETTINGS_EXPORT CertificateModel: public QAbstractListModel
+-{
+- Q_OBJECT
+- Q_PROPERTY(BundleType bundleType READ bundleType WRITE setBundleType NOTIFY bundleTypeChanged)
+- Q_PROPERTY(QString bundlePath READ bundlePath WRITE setBundlePath NOTIFY bundlePathChanged)
+- Q_ENUMS(BundleType)
+-
+-public:
+- enum BundleType {
+- NoBundle,
+- TLSBundle,
+- EmailBundle,
+- ObjectSigningBundle,
+- UserSpecifiedBundle,
+- };
+-
+- enum CertificateRoles {
+- CommonNameRole = Qt::UserRole + 1,
+- CountryNameRole = Qt::UserRole + 2,
+- OrganizationNameRole = Qt::UserRole + 3,
+- OrganizationalUnitNameRole = Qt::UserRole + 4,
+- PrimaryNameRole = Qt::UserRole + 5,
+- SecondaryNameRole = Qt::UserRole + 6,
+- NotValidBeforeRole = Qt::UserRole + 7,
+- NotValidAfterRole = Qt::UserRole + 8,
+- DetailsRole = Qt::UserRole + 9,
+- };
+-
+- explicit CertificateModel(QObject *parent = 0);
+- virtual ~CertificateModel();
+-
+- BundleType bundleType() const;
+- void setBundleType(BundleType type);
+-
+- QString bundlePath() const;
+- void setBundlePath(const QString &path);
+-
+- virtual int rowCount(const QModelIndex & parent = QModelIndex()) const;
+- virtual QVariant data(const QModelIndex &index, int role) const;
+-
+- static QList<Certificate> getCertificates(const QString &bundlePath);
+- static QList<Certificate> getCertificates(const QByteArray &pem);
+-
+-Q_SIGNALS:
+- void bundleTypeChanged();
+- void bundlePathChanged();
+-
+-protected:
+- void refresh();
+-
+- QHash<int, QByteArray> roleNames() const;
+-
+-private:
+- BundleType m_type;
+- QString m_path;
+- QList<Certificate> m_certificates;
+-};
+-
+-#endif
+diff --git a/src/plugin/plugin.cpp b/src/plugin/plugin.cpp
+index b7db9ae..b06f37a 100644
+--- a/src/plugin/plugin.cpp
++++ b/src/plugin/plugin.cpp
+@@ -46,7 +46,6 @@
+ #include "batterystatus.h"
+ #include "diskusage.h"
+ #include "partitionmodel.h"
+-#include "certificatemodel.h"
+ #include "settingsvpnmodel.h"
+ #include "locationsettings.h"
+ #include "deviceinfo.h"
+@@ -103,7 +102,6 @@ public:
+ qmlRegisterType<AboutSettings>(uri, 1, 0, "AboutSettings");
+ qmlRegisterType<PartitionModel>(uri, 1, 0, "PartitionModel");
+ qRegisterMetaType<Partition>("Partition");
+- qmlRegisterType<CertificateModel>(uri, 1, 0, "CertificateModel");
+ qmlRegisterSingletonType<SettingsVpnModel>(uri, 1, 0, "SettingsVpnModel", api_factory<SettingsVpnModel>);
+ qmlRegisterType<BatteryStatus>(uri, 1, 0, "BatteryStatus");
+ qmlRegisterType<DiskUsage>(uri, 1, 0, "DiskUsage");
+diff --git a/src/src.pro b/src/src.pro
+index 57f59b4..e5b7d37 100644
+--- a/src/src.pro
++++ b/src/src.pro
+@@ -21,8 +21,7 @@ SOURCES += \
+ alarmtonemodel.cpp \
+ mceiface.cpp \
+ displaysettings.cpp \
+- aboutsettings.cpp \
+- certificatemodel.cpp \
++ aboutsettings.cpp \
+ batterystatus.cpp \
+ diskusage.cpp \
+ diskusage_impl.cpp \
+@@ -48,7 +47,6 @@ PUBLIC_HEADERS = \
+ mceiface.h \
+ displaysettings.h \
+ aboutsettings.h \
+- certificatemodel.h \
+ settingsvpnmodel.h \
+ batterystatus.h \
+ udisks2block_p.h \
diff --git a/testing/nemo-qml-plugin-systemsettings/APKBUILD b/testing/nemo-qml-plugin-systemsettings/APKBUILD
new file mode 100644
index 0000000000..0434c7ad1a
--- /dev/null
+++ b/testing/nemo-qml-plugin-systemsettings/APKBUILD
@@ -0,0 +1,29 @@
+# Contributor: Bart Ribbers <bribbers@disroot.org>
+# Maintainer: Bart Ribbers <bribbers@disroot.org>
+pkgname=nemo-qml-plugin-systemsettings
+pkgver=0.5.47
+pkgrel=0
+pkgdesc="Nemo QML systemsettings plugin"
+url="https://git.sailfishos.org/mer-core/nemo-qml-plugin-systemsettings"
+arch="all"
+license="BSD-3-Clause"
+depends_dev="qt5-qtdeclarative-dev qt5-qtsystems-dev sailfish-access-control profiled-dev mlite-dev mce-dev timed-dev ssu-sysinfo-dev nemo-qml-plugin-dbus-dev user-managerd-dev libusb-moded-qt-dev"
+makedepends="$depends_dev qt5-qtbase-dev qt5-qttools-dev qtchooser libconnman-qt-dev libsailfishkeyprovider-dev nemo-qml-plugin-models-dev"
+subpackages="$pkgname-dev"
+source="https://git.sailfishos.org/mer-core/nemo-qml-plugin-systemsettings/-/archive/$pkgver/nemo-qml-plugin-systemsettings-$pkgver.tar.gz
+ 0001-Remove-developer-mode.patch
+ 0002-Remove-broken-certificatemodel.patch
+ "
+
+build() {
+ qmake-qt5
+ make
+}
+
+package() {
+ INSTALL_ROOT="$pkgdir" make install
+}
+
+sha512sums="3214f8acd1ecda421c03355e7c53265a7e9f4544354ed84f4f4b5f60484b958aedc551b22fdbe0b8e532eb038406fe3328f4148d480eda8bdbc9d3b1e188e1c1 nemo-qml-plugin-systemsettings-0.5.47.tar.gz
+f0b06a9634dcf81f31b8dcb17be70e9c0a7a8615aa7c4b8625c24db525c7e333ef372987db74e4356f36cabb9d8e9142e90390df5731f09e592a362cb26c1aa5 0001-Remove-developer-mode.patch
+b49f239c9dc7c3e52e5f62e5cc13359ae4a92d289d22906b9ca02a33e6b032d8682692b25a0f5f6163b167d643f318537854b4c9e08b185a423d40f76643c5c5 0002-Remove-broken-certificatemodel.patch"